Skip to content

Commit 2b3a371

Browse files
committed
Repo: handle worktrees better
This makes Repo("foo") work when foo/.git is a file of the form created by "git worktree add", i.e. it's a text file that says: gitdir: /home/me/project/.git/worktrees/bar and where /home/me/project/.git/ is the nominal gitdir, but /home/me/project/.git/worktrees/bar has this worktree's HEAD etc and a "gitdir" file that contains the path of foo/.git . Signed-off-by: Peter Jones <[email protected]>
1 parent 4bd708d commit 2b3a371

File tree

4 files changed

+35
-12
lines changed

4 files changed

+35
-12
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ Contributors are:
1818
-Bernard `Guyzmo` Pratz <[email protected]>
1919
-Timothy B. Hartman <tbhartman _at_ gmail.com>
2020
-Konstantin Popov <konstantin.popov.89 _at_ yandex.ru>
21+
-Peter Jones <pjones _at_ redhat.com>
2122

2223
Portions derived from other open source works and are clearly marked.

git/repo/base.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from git.util import Actor, finalize_process, decygpath, hex_to_bin
3333
import os.path as osp
3434

35-
from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch
35+
from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch, find_worktree_git_dir
3636
import gc
3737
import gitdb
3838

@@ -138,10 +138,15 @@ def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=Fals
138138
self._working_tree_dir = os.getenv('GIT_WORK_TREE', os.path.dirname(self.git_dir))
139139
break
140140

141-
sm_gitpath = find_submodule_git_dir(osp.join(curpath, '.git'))
141+
dotgit = osp.join(curpath, '.git')
142+
sm_gitpath = find_submodule_git_dir(dotgit)
142143
if sm_gitpath is not None:
143144
self.git_dir = osp.normpath(sm_gitpath)
144-
sm_gitpath = find_submodule_git_dir(osp.join(curpath, '.git'))
145+
146+
sm_gitpath = find_submodule_git_dir(dotgit)
147+
if sm_gitpath is None:
148+
sm_gitpath = find_worktree_git_dir(dotgit)
149+
145150
if sm_gitpath is not None:
146151
self.git_dir = _expand_path(sm_gitpath)
147152
self._working_tree_dir = curpath

git/repo/fun.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Package with general repository related functions"""
22
import os
3+
import stat
34
from string import digits
45

56
from git.compat import xrange
@@ -17,7 +18,7 @@
1718

1819

1920
__all__ = ('rev_parse', 'is_git_dir', 'touch', 'find_submodule_git_dir', 'name_to_object', 'short_to_long', 'deref_tag',
20-
'to_commit')
21+
'to_commit', 'find_worktree_git_dir')
2122

2223

2324
def touch(filename):
@@ -46,6 +47,23 @@ def is_git_dir(d):
4647
raise WorkTreeRepositoryUnsupported(d)
4748
return False
4849

50+
def find_worktree_git_dir(dotgit):
51+
"""Search for a gitdir for this worktree."""
52+
try:
53+
statbuf = os.stat(dotgit)
54+
except OSError:
55+
return None
56+
if not stat.S_ISREG(statbuf.st_mode):
57+
return None
58+
59+
try:
60+
lines = open(dotgit, 'r').readlines()
61+
for key, value in [line.strip().split(': ') for line in lines]:
62+
if key == 'gitdir':
63+
return value
64+
except ValueError:
65+
pass
66+
return None
4967

5068
def find_submodule_git_dir(d):
5169
"""Search for a submodule repo."""

git/test/test_repo.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -911,22 +911,21 @@ def test_is_ancestor(self):
911911
self.assertRaises(GitCommandError, repo.is_ancestor, i, j)
912912

913913
@with_rw_directory
914-
def test_work_tree_unsupported(self, rw_dir):
914+
def test_git_work_tree_dotgit(self, rw_dir):
915+
"""Check that we find .git as a worktree file and find the worktree
916+
based on it."""
915917
git = Git(rw_dir)
916918
if git.version_info[:3] < (2, 5, 1):
917919
raise SkipTest("worktree feature unsupported")
918920

919921
rw_master = self.rorepo.clone(join_path_native(rw_dir, 'master_repo'))
920-
rw_master.git.checkout('HEAD~10')
922+
branch = rw_master.create_head('aaaaaaaa')
921923
worktree_path = join_path_native(rw_dir, 'worktree_repo')
922924
if Git.is_cygwin():
923925
worktree_path = cygpath(worktree_path)
924-
try:
925-
rw_master.git.worktree('add', worktree_path, 'master')
926-
except Exception as ex:
927-
raise AssertionError(ex, "It's ok if TC not running from `master`.")
928-
929-
self.failUnlessRaises(InvalidGitRepositoryError, Repo, worktree_path)
926+
rw_master.git.worktree('add', worktree_path, branch.name)
927+
repo = Repo(worktree_path)
928+
self.assertIsInstance(repo, Repo)
930929

931930
@with_rw_directory
932931
def test_git_work_tree_env(self, rw_dir):

0 commit comments

Comments
 (0)