summaryrefslogtreecommitdiff
path: root/lib/git/refs.py
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2009-10-15 10:04:17 +0200
committerSebastian Thiel <byronimo@gmail.com>2009-10-15 10:04:17 +0200
commit6226720b0e6a5f7cb9223fc50363def487831315 (patch)
tree10f70f8e41c91f5bf57f04b616f3e5afdb9f8407 /lib/git/refs.py
parentb0e84a3401c84507dc017d6e4f57a9dfdb31de53 (diff)
parent4186a2dbbd48fd67ff88075c63bbd3e6c1d8a2df (diff)
downloadgitpython-6226720b0e6a5f7cb9223fc50363def487831315.tar.gz
Initial set of improvementes merged into master, including a class hierarchy redesign and performance improvements
Merge commit 'origin/improvements' * commit 'origin/improvements': (38 commits) test_performance: module containing benchmarks to get an idea of the achieved throughput Removed plenty of mocked tree tests as they cannot work anymore with persistent commands that require stdin AND binary data - not even an adapter would help here. These tests will have to be replaced. tree: now reads tress directly by parsing the binary data, allowing it to safe possibly hundreds of command calls Refs are now truly dynamic - this costs a little bit of (persistent command) work, but assures refs behave as expected persistent command signature changed to also return the hexsha from a possible input ref - the objects pointed to by refs are now baked on demand - perhaps it should change to always be re-retrieved using a property as it is relatively fast - this way refs can always be cached test_blob: removed many redundant tests that would fail now as the mock cannot handle the complexity of the command backend Implemented git command facility to keep persistent commands for fast object information retrieval test: Added time-consuming test which could also be a benchmark in fact - currently it cause hundreds of command invocations which is slow cmd: added option to return the process directly, allowing to read the output directly from the output stream added Iterable interface to Ref type renamed find_all to list_all, changed commit to use iterable interface in preparation for command changes Added base for all iteratable objects unified name of utils module, recently it was named util and utils in different packages tree: renamed content_from_string to _from_string to make it private. Removed tests that were testing that method tree: now behaves like a list with string indexing functionality - using a dict as cache is a problem as the tree is ordered, added blobs, trees and traverse method test_base: Improved basic object creation as well as set hash tests repo.active_branch now returns a Head object, not a string IndexObjects are now checking their slots to raise a proper error message in case someone tries to access an unset path or mode - this information cannot be retrieved afterwards as IndexObject information is kept in the object that pointed at them. To find this information, one would have to search all objects which is not feasible refs now take repo as first argument and derive from LazyMixin to allow them to dynamically retrieve their objects renamed from_string and list_from_string to _from_string and _list_from_string to indicate their new status as private method, adjusted all callers respectively ...
Diffstat (limited to 'lib/git/refs.py')
-rw-r--r--lib/git/refs.py238
1 files changed, 238 insertions, 0 deletions
diff --git a/lib/git/refs.py b/lib/git/refs.py
new file mode 100644
index 00000000..3c9eb817
--- /dev/null
+++ b/lib/git/refs.py
@@ -0,0 +1,238 @@
+# refs.py
+# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
+#
+# This module is part of GitPython and is released under
+# the BSD License: http://www.opensource.org/licenses/bsd-license.php
+"""
+Module containing all ref based objects
+"""
+from objects.base import Object
+from objects.utils import get_object_type_by_name
+from utils import LazyMixin, Iterable
+
+class Ref(LazyMixin, Iterable):
+ """
+ Represents a named reference to any object
+ """
+ __slots__ = ("repo", "path")
+
+ def __init__(self, repo, path, object = None):
+ """
+ Initialize this instance
+ ``repo``
+ Our parent repository
+
+ ``path``
+ Path relative to the .git/ directory pointing to the ref in question, i.e.
+ refs/heads/master
+
+ ``object``
+ Object instance, will be retrieved on demand if None
+ """
+ self.repo = repo
+ self.path = path
+ if object is not None:
+ self.object = object
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return '<git.%s "%s">' % (self.__class__.__name__, self.path)
+
+ def __eq__(self, other):
+ return self.path == other.path and self.object == other.object
+
+ def __ne__(self, other):
+ return not ( self == other )
+
+ def __hash__(self):
+ return hash(self.path)
+
+ @property
+ def name(self):
+ """
+ Returns
+ (shortest) Name of this reference - it may contain path components
+ """
+ # first two path tokens are can be removed as they are
+ # refs/heads or refs/tags or refs/remotes
+ tokens = self.path.split('/')
+ if len(tokens) < 3:
+ return self.path # could be refs/HEAD
+
+ return '/'.join(tokens[2:])
+
+ @property
+ def object(self):
+ """
+ Returns
+ The object our ref currently refers to. Refs can be cached, they will
+ always point to the actual object as it gets re-created on each query
+ """
+ # have to be dynamic here as we may be a tag which can point to anything
+ hexsha, typename, size = self.repo.git.get_object_header(self.path)
+ return get_object_type_by_name(typename)(self.repo, hexsha)
+
+ @classmethod
+ def iter_items(cls, repo, common_path = "refs", **kwargs):
+ """
+ Find all refs in the repository
+
+ ``repo``
+ is the Repo
+
+ ``common_path``
+ Optional keyword argument to the path which is to be shared by all
+ returned Ref objects
+
+ ``kwargs``
+ Additional options given as keyword arguments, will be passed
+ to git-for-each-ref
+
+ Returns
+ git.Ref[]
+
+ List is sorted by committerdate
+ The returned objects are compatible to the Ref base, but represent the
+ actual type, such as Head or Tag
+ """
+
+ options = {'sort': "committerdate",
+ 'format': "%(refname)%00%(objectname)%00%(objecttype)%00%(objectsize)"}
+
+ options.update(kwargs)
+
+ output = repo.git.for_each_ref(common_path, **options)
+ return cls._iter_from_stream(repo, iter(output.splitlines()))
+
+ @classmethod
+ def _iter_from_stream(cls, repo, stream):
+ """ Parse out ref information into a list of Ref compatible objects
+ Returns git.Ref[] list of Ref objects """
+ heads = []
+
+ for line in stream:
+ heads.append(cls._from_string(repo, line))
+
+ return heads
+
+ @classmethod
+ def _from_string(cls, repo, line):
+ """ Create a new Ref instance from the given string.
+ Format
+ name: [a-zA-Z_/]+
+ <null byte>
+ id: [0-9A-Fa-f]{40}
+ Returns git.Head """
+ full_path, hexsha, type_name, object_size = line.split("\x00")
+
+ # No, we keep the object dynamic by allowing it to be retrieved by
+ # our path on demand - due to perstent commands it is fast.
+ # This reduces the risk that the object does not match
+ # the changed ref anymore in case it changes in the meanwhile
+ return cls(repo, full_path)
+
+ # obj = get_object_type_by_name(type_name)(repo, hexsha)
+ # obj.size = object_size
+ # return cls(repo, full_path, obj)
+
+
+class Head(Ref):
+ """
+ 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'
+ """
+
+ @property
+ def commit(self):
+ """
+ Returns
+ Commit object the head points to
+ """
+ return self.object
+
+ @classmethod
+ def iter_items(cls, repo, common_path = "refs/heads", **kwargs):
+ """
+ Returns
+ Iterator yielding Head items
+
+ For more documentation, please refer to git.base.Ref.list_items
+ """
+ return super(Head,cls).iter_items(repo, common_path, **kwargs)
+
+ def __repr__(self):
+ return '<git.Head "%s">' % self.name
+
+
+
+class TagRef(Ref):
+ """
+ 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
+ or the tag-creator, is available.
+
+ 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]
+ print tagref.commit.message
+ if tagref.tag is not None:
+ print tagref.tag.message
+ """
+
+ __slots__ = tuple()
+
+ @property
+ def commit(self):
+ """
+ Returns
+ Commit object the tag ref points to
+ """
+ if self.object.type == "commit":
+ return self.object
+ elif self.object.type == "tag":
+ # it is a tag object which carries the commit as an object - we can point to anything
+ return self.object.object
+ else:
+ raise ValueError( "Tag %s points to a Blob or Tree - have never seen that before" % self )
+
+ @property
+ def tag(self):
+ """
+ Returns
+ 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
+ return None
+
+ @classmethod
+ def iter_items(cls, repo, common_path = "refs/tags", **kwargs):
+ """
+ Returns
+ Iterator yielding commit items
+
+ For more documentation, please refer to git.base.Ref.list_items
+ """
+ return super(TagRef,cls).iter_items(repo, common_path, **kwargs)
+
+
+# provide an alias
+Tag = TagRef