summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/git/refs.py51
-rw-r--r--test/git/test_refs.py38
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