diff options
-rw-r--r-- | lib/git/refs.py | 51 | ||||
-rw-r--r-- | test/git/test_refs.py | 38 |
2 files changed, 78 insertions, 11 deletions
diff --git a/lib/git/refs.py b/lib/git/refs.py index d88a2331..6c57eb07 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -51,7 +51,7 @@ class SymbolicReference(object): """ return self.path - def _get_path(self): + def _abs_path(self): return join_path_native(self.repo.git_dir, self.path) @classmethod @@ -97,7 +97,7 @@ class SymbolicReference(object): point to, or None""" tokens = None try: - fp = open(self._get_path(), 'r') + fp = open(self._abs_path(), 'r') value = fp.read().rstrip() fp.close() tokens = value.split(" ") @@ -143,8 +143,15 @@ class SymbolicReference(object): def _set_commit(self, commit): """ Set our commit, possibly dereference our symbolic reference first. + If the reference does not exist, it will be created """ - if self.is_detached: + is_detached = True + try: + is_detached = self.is_detached + except ValueError: + pass + # END handle non-existing ones + if is_detached: return self._set_reference(commit) # set the commit on our reference @@ -197,7 +204,7 @@ class SymbolicReference(object): return # END non-detached handling - path = self._get_path() + path = self._abs_path() directory = os.path.dirname(path) if not os.path.isdir(directory): os.makedirs(directory) @@ -533,12 +540,28 @@ class Reference(SymbolicReference, LazyMixin, Iterable): """ Set our reference to point to the given ref. It will be converted to a specific hexsha. + If the reference does not exist, it will be created. Note: TypeChecking is done by the git command """ + # check for existence, touch it if required + abs_path = self._abs_path() + existed = True + if not os.path.isfile(abs_path): + existed = False + open(abs_path, 'wb').write(Object.NULL_HEX_SHA) + # END quick create + # do it safely by specifying the old value - self.repo.git.update_ref(self.path, ref, self._get_object().sha) + try: + self.repo.git.update_ref(self.path, ref, (existed and self._get_object().sha) or None) + except: + if not existed: + os.remove(abs_path) + # END remove file on error if it didn't exist before + raise + # END exception handling object = property(_get_object, _set_object, doc="Return the object our ref currently refers to") @@ -813,11 +836,12 @@ class TagReference(Reference): Returns Commit object the tag ref points to """ - if self.object.type == "commit": - return self.object - elif self.object.type == "tag": + obj = self.object + if obj.type == "commit": + return obj + elif obj.type == "tag": # it is a tag object which carries the commit as an object - we can point to anything - return self.object.object + return obj.object else: raise ValueError( "Tag %s points to a Blob or Tree - have never seen that before" % self ) @@ -828,10 +852,15 @@ class TagReference(Reference): Tag object this tag ref points to or None in case we are a light weight tag """ - if self.object.type == "tag": - return self.object + obj = self.object + if obj.type == "tag": + return obj return None + # make object read-only + # It should be reasonably hard to adjust an existing tag + object = property(Reference._get_object) + @classmethod def create(cls, repo, path, ref='HEAD', message=None, force=False, **kwargs): """ diff --git a/test/git/test_refs.py b/test/git/test_refs.py index 7c487bd0..958bcf85 100644 --- a/test/git/test_refs.py +++ b/test/git/test_refs.py @@ -39,6 +39,9 @@ class TestRefs(TestBase): assert isinstance( tagobj.tagged_date, int ) assert isinstance( tagobj.tagger_tz_offset, int ) assert tagobj.message + assert tag.object == tagobj + # can't assign the object + self.failUnlessRaises(AttributeError, setattr, tag, 'object', tagobj) # END if we have a tag object # END for tag in repo-tags assert tag_object_refs @@ -384,5 +387,40 @@ class TestRefs(TestBase): assert len(refs) == 1 + # test creation of new refs from scratch + for path in ("basename", "dir/somename", "dir2/subdir/basename"): + # REFERENCES + ############ + fpath = Reference.to_full_path(path) + ref_fp = Reference.from_path(rw_repo, fpath) + assert not ref_fp.is_valid() + ref = Reference(rw_repo, fpath) + assert ref == ref_fp + + # can be created by assigning a commit + ref.commit = rw_repo.head.commit + assert ref.is_valid() + + # if the assignment raises, the ref doesn't exist + Reference.delete(ref.repo, ref.path) + assert not ref.is_valid() + self.failUnlessRaises(ValueError, setattr, ref, 'commit', "nonsense") + assert not ref.is_valid() + + # I am sure I had my reason to make it a class method at first, but + # now it doesn't make so much sense anymore, want an instance method as well + # See http://byronimo.lighthouseapp.com/projects/51787-gitpython/tickets/27 + Reference.delete(ref.repo, ref.path) + assert not ref.is_valid() + + ref.object = rw_repo.head.commit + assert ref.is_valid() + + Reference.delete(ref.repo, ref.path) + assert not ref.is_valid() + self.failUnlessRaises(GitCommandError, setattr, ref, 'object', "nonsense") + assert not ref.is_valid() + + # END for each path |