Skip to content

Commit a5f0343

Browse files
authored
Merge pull request #654 from vathpela/worktrees
worktrees: make non-packed refs also work correctly.
2 parents fb43244 + d1c40f4 commit a5f0343

File tree

5 files changed

+45
-29
lines changed

5 files changed

+45
-29
lines changed

Diff for: 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:

Diff for: 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

Diff for: git/remote.py

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

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

659659
l_fil = len(fetch_info_lines)

Diff for: git/repo/base.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Repo(object):
7171
working_dir = None
7272
_working_tree_dir = None
7373
git_dir = None
74+
_common_dir = None
7475

7576
# precompiled regex
7677
re_whitespace = re.compile(r'\s+')
@@ -172,17 +173,23 @@ def __init__(self, path=None, odbt=DefaultDBType, search_parent_directories=Fals
172173
# lets not assume the option exists, although it should
173174
pass
174175

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

181-
self.working_dir = self._working_tree_dir or self.git_dir
188+
self.working_dir = self._working_tree_dir or self.common_dir
182189
self.git = self.GitCommandWrapperType(self.working_dir)
183190

184191
# special handling, in special times
185-
args = [osp.join(self.git_dir, 'objects')]
192+
args = [osp.join(self.common_dir, 'objects')]
186193
if issubclass(odbt, GitCmdObjectDB):
187194
args.append(self.git)
188195
self.odb = odbt(*args)
@@ -239,6 +246,13 @@ def working_tree_dir(self):
239246
"""
240247
return self._working_tree_dir
241248

249+
@property
250+
def common_dir(self):
251+
""":return: The git dir that holds everything except possibly HEAD,
252+
FETCH_HEAD, ORIG_HEAD, COMMIT_EDITMSG, index, and logs/ .
253+
"""
254+
return self._common_dir or self.git_dir
255+
242256
@property
243257
def bare(self):
244258
""":return: True if the repository is bare"""
@@ -577,7 +591,7 @@ def _set_alternates(self, alts):
577591
:note:
578592
The method does not check for the existence of the paths in alts
579593
as the caller is responsible."""
580-
alternates_path = osp.join(self.git_dir, 'objects', 'info', 'alternates')
594+
alternates_path = osp.join(self.common_dir, 'objects', 'info', 'alternates')
581595
if not alts:
582596
if osp.isfile(alternates_path):
583597
os.remove(alternates_path)
@@ -940,7 +954,7 @@ def clone(self, path, progress=None, **kwargs):
940954
* All remaining keyword arguments are given to the git-clone command
941955
942956
:return: ``git.Repo`` (the newly cloned repo)"""
943-
return self._clone(self.git, self.git_dir, path, type(self.odb), progress, **kwargs)
957+
return self._clone(self.git, self.common_dir, path, type(self.odb), progress, **kwargs)
944958

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

Diff for: 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)