Skip to content

Commit d1c40f4

Browse files
committed
worktrees: make non-packed refs also work correctly.
Turns out aec58a9 did the right thing for /packed/ refs, but didn't work correctly on /unpacked/ refs. So this patch gives unpacked refs the same treatment. Without the fix here, the test added will cause this traceback: ====================================================================== ERROR: Check that we find .git as a worktree file and find the worktree ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pjones/devel/github.com/GitPython/git/test/lib/helper.py", line 92, in wrapper return func(self, path) File "/home/pjones/devel/github.com/GitPython/git/test/test_repo.py", line 938, in test_git_work_tree_dotgit self.assertIsInstance(repo.heads['aaaaaaaa'], Head) File "/home/pjones/devel/github.com/GitPython/git/util.py", line 893, in __getitem__ raise IndexError("No item found with id %r" % (self._prefix + index)) IndexError: No item found with id 'aaaaaaaa' Woops. Things I've learned: - test_remote doesn't work currently if you start on a branch. I think it never did? - Because of 346424d, all *sorts* of stuff in the test suite doesn't work if you name your development branch "packed-refs" (This seems like a bug...) Signed-off-by: Peter Jones <[email protected]>
1 parent cf8dc25 commit d1c40f4

File tree

5 files changed

+45
-29
lines changed

5 files changed

+45
-29
lines changed

git/refs/remote.py

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ def delete(cls, repo, *refs, **kwargs):
3636
# are generally ignored in the refs/ folder. We don't though
3737
# and delete remainders manually
3838
for ref in refs:
39+
try:
40+
os.remove(osp.join(repo.common_dir, ref.path))
41+
except OSError:
42+
pass
3943
try:
4044
os.remove(osp.join(repo.git_dir, ref.path))
4145
except OSError:

git/refs/symbolic.py

+20-24
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
__all__ = ["SymbolicReference"]
2727

2828

29+
def _git_dir(repo, path):
30+
""" Find the git dir that's appropriate for the path"""
31+
name = "%s" % (path,)
32+
if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']:
33+
return repo.git_dir
34+
return repo.common_dir
35+
36+
2937
class SymbolicReference(object):
3038

3139
"""Represents a special case of a reference such that this reference is symbolic.
@@ -71,16 +79,11 @@ def name(self):
7179

7280
@property
7381
def abspath(self):
74-
return join_path_native(self.repo.git_dir, self.path)
82+
return join_path_native(_git_dir(self.repo, self.path), self.path)
7583

7684
@classmethod
7785
def _get_packed_refs_path(cls, repo):
78-
try:
79-
commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip()
80-
except (OSError, IOError):
81-
commondir = '.'
82-
repodir = osp.join(repo.git_dir, commondir)
83-
return osp.join(repodir, 'packed-refs')
86+
return osp.join(repo.common_dir, 'packed-refs')
8487

8588
@classmethod
8689
def _iter_packed_refs(cls, repo):
@@ -127,11 +130,12 @@ def dereference_recursive(cls, repo, ref_path):
127130
# END recursive dereferencing
128131

129132
@classmethod
130-
def _get_ref_info_helper(cls, repo, repodir, ref_path):
133+
def _get_ref_info_helper(cls, repo, ref_path):
131134
"""Return: (str(sha), str(target_ref_path)) if available, the sha the file at
132135
rela_path points to, or None. target_ref_path is the reference we
133136
point to, or None"""
134137
tokens = None
138+
repodir = _git_dir(repo, ref_path)
135139
try:
136140
with open(osp.join(repodir, ref_path), 'rt') as fp:
137141
value = fp.read().rstrip()
@@ -169,16 +173,7 @@ def _get_ref_info(cls, repo, ref_path):
169173
"""Return: (str(sha), str(target_ref_path)) if available, the sha the file at
170174
rela_path points to, or None. target_ref_path is the reference we
171175
point to, or None"""
172-
try:
173-
return cls._get_ref_info_helper(repo, repo.git_dir, ref_path)
174-
except ValueError:
175-
try:
176-
commondir = open(osp.join(repo.git_dir, 'commondir'), 'rt').readlines()[0].strip()
177-
except (OSError, IOError):
178-
commondir = '.'
179-
180-
repodir = osp.join(repo.git_dir, commondir)
181-
return cls._get_ref_info_helper(repo, repodir, ref_path)
176+
return cls._get_ref_info_helper(repo, ref_path)
182177

183178
def _get_object(self):
184179
"""
@@ -433,7 +428,7 @@ def delete(cls, repo, path):
433428
or just "myreference", hence 'refs/' is implied.
434429
Alternatively the symbolic reference to be deleted"""
435430
full_ref_path = cls.to_full_path(path)
436-
abs_path = osp.join(repo.git_dir, full_ref_path)
431+
abs_path = osp.join(repo.common_dir, full_ref_path)
437432
if osp.exists(abs_path):
438433
os.remove(abs_path)
439434
else:
@@ -484,8 +479,9 @@ def _create(cls, repo, path, resolve, reference, force, logmsg=None):
484479
a proper symbolic reference. Otherwise it will be resolved to the
485480
corresponding object and a detached symbolic reference will be created
486481
instead"""
482+
git_dir = _git_dir(repo, path)
487483
full_ref_path = cls.to_full_path(path)
488-
abs_ref_path = osp.join(repo.git_dir, full_ref_path)
484+
abs_ref_path = osp.join(git_dir, full_ref_path)
489485

490486
# figure out target data
491487
target = reference
@@ -559,8 +555,8 @@ def rename(self, new_path, force=False):
559555
if self.path == new_path:
560556
return self
561557

562-
new_abs_path = osp.join(self.repo.git_dir, new_path)
563-
cur_abs_path = osp.join(self.repo.git_dir, self.path)
558+
new_abs_path = osp.join(_git_dir(self.repo, new_path), new_path)
559+
cur_abs_path = osp.join(_git_dir(self.repo, self.path), self.path)
564560
if osp.isfile(new_abs_path):
565561
if not force:
566562
# if they point to the same file, its not an error
@@ -594,7 +590,7 @@ def _iter_items(cls, repo, common_path=None):
594590

595591
# walk loose refs
596592
# Currently we do not follow links
597-
for root, dirs, files in os.walk(join_path_native(repo.git_dir, common_path)):
593+
for root, dirs, files in os.walk(join_path_native(repo.common_dir, common_path)):
598594
if 'refs' not in root.split(os.sep): # skip non-refs subfolders
599595
refs_id = [d for d in dirs if d == 'refs']
600596
if refs_id:
@@ -605,7 +601,7 @@ def _iter_items(cls, repo, common_path=None):
605601
if f == 'packed-refs':
606602
continue
607603
abs_path = to_native_path_linux(join_path(root, f))
608-
rela_paths.add(abs_path.replace(to_native_path_linux(repo.git_dir) + '/', ""))
604+
rela_paths.add(abs_path.replace(to_native_path_linux(repo.common_dir) + '/', ""))
609605
# END for each file in root directory
610606
# END for each directory to walk
611607

git/remote.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ def _get_fetch_info_from_stderr(self, proc, progress):
652652
continue
653653

654654
# read head information
655-
with open(osp.join(self.repo.git_dir, 'FETCH_HEAD'), 'rb') as fp:
655+
with open(osp.join(self.repo.common_dir, 'FETCH_HEAD'), 'rb') as fp:
656656
fetch_head_info = [l.decode(defenc) for l in fp.readlines()]
657657

658658
l_fil = len(fetch_info_lines)

git/repo/base.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class Repo(object):
7474
working_dir = None
7575
_working_tree_dir = None
7676
git_dir = None
77+
_common_dir = None
7778

7879
# precompiled regex
7980
re_whitespace = re.compile(r'\s+')
@@ -169,17 +170,23 @@ def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=Fals
169170
# lets not assume the option exists, although it should
170171
pass
171172

173+
try:
174+
common_dir = open(osp.join(self.git_dir, 'commondir'), 'rt').readlines()[0].strip()
175+
self._common_dir = osp.join(self.git_dir, common_dir)
176+
except (OSError, IOError):
177+
self._common_dir = None
178+
172179
# adjust the wd in case we are actually bare - we didn't know that
173180
# in the first place
174181
if self._bare:
175182
self._working_tree_dir = None
176183
# END working dir handling
177184

178-
self.working_dir = self._working_tree_dir or self.git_dir
185+
self.working_dir = self._working_tree_dir or self.common_dir
179186
self.git = self.GitCommandWrapperType(self.working_dir)
180187

181188
# special handling, in special times
182-
args = [osp.join(self.git_dir, 'objects')]
189+
args = [osp.join(self.common_dir, 'objects')]
183190
if issubclass(odbt, GitCmdObjectDB):
184191
args.append(self.git)
185192
self.odb = odbt(*args)
@@ -236,6 +243,13 @@ def working_tree_dir(self):
236243
"""
237244
return self._working_tree_dir
238245

246+
@property
247+
def common_dir(self):
248+
""":return: The git dir that holds everything except possibly HEAD,
249+
FETCH_HEAD, ORIG_HEAD, COMMIT_EDITMSG, index, and logs/ .
250+
"""
251+
return self._common_dir or self.git_dir
252+
239253
@property
240254
def bare(self):
241255
""":return: True if the repository is bare"""
@@ -574,7 +588,7 @@ def _set_alternates(self, alts):
574588
:note:
575589
The method does not check for the existence of the paths in alts
576590
as the caller is responsible."""
577-
alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates')
591+
alternates_path = osp.join(self.common_dir, 'objects', 'info', 'alternates')
578592
if not alts:
579593
if osp.isfile(alternates_path):
580594
os.remove(alternates_path)
@@ -932,7 +946,7 @@ def clone(self, path, progress=None, **kwargs):
932946
* All remaining keyword arguments are given to the git-clone command
933947
934948
:return: ``git.Repo`` (the newly cloned repo)"""
935-
return self._clone(self.git, self.git_dir, path, type(self.odb), progress, **kwargs)
949+
return self._clone(self.git, self.common_dir, path, type(self.odb), progress, **kwargs)
936950

937951
@classmethod
938952
def clone_from(cls, url, to_path, progress=None, env=None, **kwargs):

git/test/test_repo.py

+2
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,8 @@ def test_git_work_tree_dotgit(self, rw_dir):
935935
commit = repo.head.commit
936936
self.assertIsInstance(commit, Object)
937937

938+
self.assertIsInstance(repo.heads['aaaaaaaa'], Head)
939+
938940
@with_rw_directory
939941
def test_git_work_tree_env(self, rw_dir):
940942
"""Check that we yield to GIT_WORK_TREE"""

0 commit comments

Comments
 (0)