diff options
Diffstat (limited to 'lib/git/refs.py')
-rw-r--r-- | lib/git/refs.py | 169 |
1 files changed, 137 insertions, 32 deletions
diff --git a/lib/git/refs.py b/lib/git/refs.py index 9a03b6f5..97d0a6eb 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -6,6 +6,7 @@ """ Module containing all ref based objects """ +import os from objects.base import Object from objects.utils import get_object_type_by_name from utils import LazyMixin, Iterable @@ -31,6 +32,9 @@ class Reference(LazyMixin, Iterable): ``object`` Object instance, will be retrieved on demand if None """ + if not path.startswith(self._common_path_default): + raise ValueError("Cannot instantiate %s Reference from path %s" % ( self.__class__.__name__, path )) + self.repo = repo self.path = path if object is not None: @@ -75,6 +79,17 @@ class Reference(LazyMixin, Iterable): # have to be dynamic here as we may be a tag which can point to anything # Our path will be resolved to the hexsha which will be used accordingly return Object.new(self.repo, self.path) + + @property + def commit(self): + """ + Returns + Commit object the head points to + """ + commit = self.object + if commit.type != "commit": + raise TypeError("Object of reference %s did not point to a commit" % self) + return commit @classmethod def iter_items(cls, repo, common_path = None, **kwargs): @@ -112,6 +127,29 @@ class Reference(LazyMixin, Iterable): output = repo.git.for_each_ref(common_path, **options) return cls._iter_from_stream(repo, iter(output.splitlines())) + + @classmethod + def from_path(cls, repo, path): + """ + Return + Instance of type Reference, Head, Tag, SymbolicReference or HEAD + depending on the given path + """ + if path == 'HEAD': + return HEAD(repo, path) + + if '/' not in path: + return SymbolicReference(repo, path) + + for ref_type in (Head, RemoteReference, TagReference, Reference): + try: + return ref_type(repo, path) + except ValueError: + pass + # END exception handling + # END for each type to try + raise ValueError("Could not find reference type suitable to handle path %r" % path) + @classmethod def _iter_from_stream(cls, repo, stream): @@ -145,47 +183,91 @@ class Reference(LazyMixin, Iterable): # return cls(repo, full_path, obj) -class Head(Reference): +class SymbolicReference(object): """ - A Head is a named reference to a Commit. Every Head instance contains a name - and a Commit object. - - Examples:: - - >>> repo = Repo("/path/to/repo") - >>> head = repo.heads[0] - - >>> head.name - 'master' - - >>> head.commit - <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> - - >>> head.commit.id - '1c09f116cbc2cb4100fb6935bb162daa4723f455' + Represents a special case of a reference such that this reference is symbolic. + It does not point to a specific commit, but to another Head, which itself + specifies a commit. + + A typical example for a symbolic reference is HEAD. """ - _common_path_default = "refs/heads" + __slots__ = ("repo", "name") + def __init__(self, repo, name): + if '/' in name: + raise ValueError("SymbolicReferences are not located within a directory, got %s" % name) + self.repo = repo + self.name = name + + def __str__(self): + return self.name + + def __repr__(self): + return '<git.%s "%s">' % (self.__class__.__name__, self.name) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return not ( self == other ) + + def __hash__(self): + return hash(self.name) + @property - def commit(self): + def reference(self): """ Returns - Commit object the head points to + Reference Object we point to """ - return self.object + fp = open(os.path.join(self.repo.path, self.name), 'r') + try: + tokens = fp.readline().split(' ') + if tokens[0] != 'ref:': + raise TypeError("%s is a detached symbolic reference as it points to %r" % tokens[0]) + return Reference.from_path(self.repo, tokens[1]) + finally: + fp.close() - @classmethod - def reset(cls, repo, commit='HEAD', index=True, working_tree = False, + # alias + ref = reference + + @property + def is_detached(self): + """ + Returns + True if we are a detached reference, hence we point to a specific commit + instead to another reference + """ + try: + self.reference + return False + except TypeError: + return True + + +class HEAD(SymbolicReference): + """ + Special case of a Symbolic Reference as it represents the repository's + HEAD reference. + """ + __slots__ = tuple() + + def __init__(self, repo, name): + if name != 'HEAD': + raise ValueError("HEAD instance must point to 'HEAD', got %s" % name) + super(HEAD, self).__init__(repo, name) + + + def reset(self, commit='HEAD', index=True, working_tree = False, paths=None, **kwargs): """ - Reset the current head to the given commit optionally synchronizing + Reset our HEAD to the given commit optionally synchronizing the index and working tree. - ``repo`` - Repository containing commit - ``commit`` - Commit object, Reference Object or string identifying a revision + Commit object, Reference Object or string identifying a revision we + should reset HEAD to. ``index`` If True, the index will be set to match the given commit. Otherwise @@ -204,7 +286,7 @@ class Head(Reference): Additional arguments passed to git-reset. Returns - Head pointing to the specified commit + self """ mode = "--soft" if index: @@ -219,9 +301,32 @@ class Head(Reference): repo.git.reset(mode, commit, paths, **kwargs) # we always point to the active branch as it is the one changing - return repo.active_branch + self + + +class Head(Reference): + """ + A Head is a named reference to a Commit. Every Head instance contains a name + and a Commit object. + + Examples:: + + >>> repo = Repo("/path/to/repo") + >>> head = repo.heads[0] + + >>> head.name + 'master' + + >>> head.commit + <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> + + >>> head.commit.id + '1c09f116cbc2cb4100fb6935bb162daa4723f455' + """ + _common_path_default = "refs/heads" + -class TagReference(Head): +class TagReference(Reference): """ Class representing a lightweight tag reference which either points to a commit or to a tag object. In the latter case additional information, like the signature @@ -230,7 +335,7 @@ class TagReference(Head): This tag object will always point to a commit object, but may carray additional information in a tag object:: - tagref = TagRef.list_items(repo)[0] + tagref = TagReference.list_items(repo)[0] print tagref.commit.message if tagref.tag is not None: print tagref.tag.message |