Skip to content

Commit 12276fe

Browse files
committed
refs: Implemented low-level (Symbolic)Reference renaming as some references cannot be reamed using the git-branch command if they are not in refs/heads, that is in a non-standard refs folder
1 parent 7d00fa4 commit 12276fe

File tree

3 files changed

+70
-15
lines changed

3 files changed

+70
-15
lines changed

lib/git/refs.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,47 @@ def create(cls, repo, path, reference='HEAD', force=False ):
309309
This does not alter the current HEAD, index or Working Tree
310310
"""
311311
return cls._create(repo, path, False, reference, force)
312+
313+
def rename(self, new_path, force=False):
314+
"""
315+
Rename self to a new path
316+
317+
``new_path``
318+
Either a simple name or a full path, i.e. new_name or features/new_name.
319+
The prefix refs/ is implied for references and will be set as needed.
320+
In case this is a symbolic ref, there is no implied prefix
321+
322+
``force``
323+
If True, the rename will succeed even if a head with the target name
324+
already exists. It will be overwritten in that case
325+
326+
Returns
327+
self
328+
329+
Raises OSError:
330+
In case a file at path with that name already exists
331+
"""
332+
new_path = self._to_full_path(self.repo, new_path)
333+
if self.path == new_path:
334+
return self
335+
336+
new_abs_path = os.path.join(self.repo.git_dir, new_path)
337+
if os.path.isfile(new_abs_path):
338+
if not force:
339+
raise OSError("File at path %r already exists" % new_abs_path)
340+
os.remove(new_abs_path)
341+
# END handle existing target file
342+
343+
dirname = os.path.dirname(new_abs_path)
344+
if not os.path.isdir(dirname):
345+
os.makedirs(dirname)
346+
# END create directory
347+
348+
cur_abs_path = os.path.join(self.repo.git_dir, self.path)
349+
os.rename(cur_abs_path, new_abs_path)
350+
self.path = new_path
351+
352+
return self
312353

313354

314355
class Reference(SymbolicReference, LazyMixin, Iterable):
@@ -330,7 +371,7 @@ def __init__(self, repo, path):
330371
refs/heads/master
331372
332373
"""
333-
if not path.startswith(self._common_path_default):
374+
if not path.startswith(self._common_path_default+'/'):
334375
raise ValueError("Cannot instantiate %s from path %s" % ( self.__class__.__name__, path ))
335376
super(Reference, self).__init__(repo, path)
336377

@@ -472,6 +513,7 @@ def create(cls, repo, path, commit='HEAD', force=False ):
472513
"""
473514
return cls._create(repo, path, True, commit, force)
474515

516+
475517

476518
class HEAD(SymbolicReference):
477519
"""
@@ -623,6 +665,9 @@ def rename(self, new_path, force=False):
623665
624666
Returns
625667
self
668+
669+
Note
670+
respects the ref log as git commands are used
626671
"""
627672
flag = "-m"
628673
if force:

lib/git/utils.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -362,18 +362,4 @@ def iter_items(cls, repo, *args, **kwargs):
362362
"""
363363
raise NotImplementedError("To be implemented by Subclass")
364364

365-
def needs_working_tree(func):
366-
"""
367-
Decorator assuring the wrapped method may only run if the repository has a
368-
working tree, hence it is not bare.
369-
"""
370-
def check_default_index(self, *args, **kwargs):
371-
if self.repo.working_tree_dir is None:
372-
raise AssertionError( "Cannot call %r bare git repositories" % func.__name__ )
373-
return func(self, *args, **kwargs)
374-
# END wrpaper method
375-
376-
check_default_index.__name__ = func.__name__
377-
return check_default_index
378-
379365

test/git/test_refs.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,21 @@ def test_head_reset(self, rw_repo):
255255
assert ref.path == full_ref
256256
assert ref.object == rw_repo.head.commit.parents[0]
257257

258+
# rename it
259+
orig_obj = ref.object
260+
for name in ('refs/absname', 'rela_name', 'feature/rela_name'):
261+
ref_new_name = ref.rename(name)
262+
assert isinstance(ref_new_name, Reference)
263+
assert name in ref_new_name.path
264+
assert ref_new_name.object == orig_obj
265+
assert ref_new_name == ref
266+
# END for each name type
267+
# exists, fail unless we force
268+
ex_ref_path = far_away_head.path
269+
self.failUnlessRaises(OSError, ref.rename, ex_ref_path)
270+
assert ref.rename(ex_ref_path, force=True).path == ex_ref_path and ref.object == orig_obj
271+
assert ref.rename(ref.path).path == ex_ref_path # rename to same name
272+
258273
# create symbolic refs
259274
symref_path = "symrefs/sym"
260275
symref = SymbolicReference.create(rw_repo, symref_path, cur_head.reference)
@@ -279,6 +294,15 @@ def test_head_reset(self, rw_repo):
279294
assert os.path.isfile(symbol_ref_abspath)
280295
assert symref.commit == new_head.commit
281296

297+
for name in ('absname','folder/rela_name'):
298+
symref_new_name = symref.rename(name)
299+
assert isinstance(symref_new_name, SymbolicReference)
300+
assert name in symref_new_name.path
301+
assert symref_new_name.reference == new_head
302+
assert symref_new_name == symref
303+
assert not symref.is_detached
304+
# END for each ref
305+
282306
# test ref listing - assure we have packed refs
283307
rw_repo.git.pack_refs(all=True, prune=True)
284308
heads = rw_repo.heads

0 commit comments

Comments
 (0)