From 4c73e9cd66c77934f8a262b0c1bab9c2f15449ba Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 12 Oct 2009 17:03:01 +0200 Subject: refs now take repo as first argument and derive from LazyMixin to allow them to dynamically retrieve their objects Improved way commits are returned by refs as they now use the path to be sure they always point to the ref even if it changes - previously it would use the sha intead so it would not update after being cached on the ref object --- lib/git/refs.py | 94 +++++++++++++++++++++++++-------------------------------- 1 file changed, 41 insertions(+), 53 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 820150d3..bc5cc005 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -8,16 +8,19 @@ Module containing all ref based objects """ from objects.base import Object from objects.util import get_object_type_by_name +from utils import LazyMixin -class Ref(object): +class Ref(LazyMixin): """ Represents a named reference to any object """ - __slots__ = ("path", "object") + __slots__ = ("repo", "path", "object") - def __init__(self, path, object = None): + 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. @@ -26,8 +29,19 @@ class Ref(object): ``object`` Object instance, will be retrieved on demand if None """ + self.repo = repo self.path = path - self.object = object + if object is not None: + self.object = object + + def _set_cache_(self, attr): + if attr == "object": + # have to be dynamic here as we may be a tag which can point to anything + # it uses our path to stay dynamic + type_string = self.repo.git.cat_file(self.path, t=True).rstrip() + self.object = get_object_type_by_name(type_string)(self.repo, self.path) + else: + super(Ref, self)._set_cache_(attr) def __str__(self): return self.name @@ -92,19 +106,8 @@ class Ref(object): @classmethod def _list_from_string(cls, repo, text): - """ - Parse out ref information into a list of Ref compatible objects - - ``repo`` - is the Repo - ``text`` - is the text output from the git-for-each-ref command - - Returns - git.Ref[] - - list of Ref objects - """ + """ Parse out ref information into a list of Ref compatible objects + Returns git.Ref[] list of Ref objects """ heads = [] for line in text.splitlines(): @@ -114,28 +117,16 @@ class Ref(object): @classmethod def _from_string(cls, repo, line): - """ - Create a new Ref instance from the given string. - - ``repo`` - is the Repo - - ``line`` - is the formatted ref information - - Format:: - + """ Create a new Ref instance from the given string. + Format name: [a-zA-Z_/]+ id: [0-9A-Fa-f]{40} - - Returns - git.Head - """ + Returns git.Head """ full_path, hexsha, type_name, object_size = line.split("\x00") obj = get_object_type_by_name(type_name)(repo, hexsha) obj.size = object_size - return cls(full_path, obj) + return cls(repo, full_path, obj) class Head(Ref): @@ -196,24 +187,7 @@ class TagRef(Ref): print tagref.tag.message """ - __slots__ = "tag" - - def __init__(self, path, commit_or_tag): - """ - Initialize a newly instantiated Tag - - ``path`` - is the full path to the tag - - ``commit_or_tag`` - is the Commit or TagObject that this tag ref points to - """ - super(TagRef, self).__init__(path, commit_or_tag) - self.tag = None - - if commit_or_tag.type == "tag": - self.tag = commit_or_tag - # END tag object handling + __slots__ = tuple() @property def commit(self): @@ -223,8 +197,22 @@ class TagRef(Ref): """ if self.object.type == "commit": return self.object - # it is a tag object - return self.object.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 find_all(cls, repo, common_path = "refs/tags", **kwargs): -- cgit v1.2.1 From 5eb0f2c241718bc7462be44e5e8e1e36e35f9b15 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 13 Oct 2009 17:50:26 +0200 Subject: unified name of utils module, recently it was named util and utils in different packages --- lib/git/refs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index bc5cc005..bdfff6f4 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -7,7 +7,7 @@ Module containing all ref based objects """ from objects.base import Object -from objects.util import get_object_type_by_name +from objects.utils import get_object_type_by_name from utils import LazyMixin class Ref(LazyMixin): -- cgit v1.2.1 From 6acec357c7609fdd2cb0f5fdb1d2756726c7fe98 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 13 Oct 2009 21:26:19 +0200 Subject: renamed find_all to list_all, changed commit to use iterable interface in preparation for command changes --- lib/git/refs.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index bdfff6f4..32bc7784 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -73,7 +73,7 @@ class Ref(LazyMixin): return '/'.join(tokens[2:]) @classmethod - def find_all(cls, repo, common_path = "refs", **kwargs): + def list_items(cls, repo, common_path = "refs", **kwargs): """ Find all refs in the repository @@ -158,14 +158,14 @@ class Head(Ref): return self.object @classmethod - def find_all(cls, repo, common_path = "refs/heads", **kwargs): + def list_items(cls, repo, common_path = "refs/heads", **kwargs): """ Returns git.Head[] - For more documentation, please refer to git.base.Ref.find_all + For more documentation, please refer to git.base.Ref.list_items """ - return super(Head,cls).find_all(repo, common_path, **kwargs) + return super(Head,cls).list_items(repo, common_path, **kwargs) def __repr__(self): return '' % self.name @@ -181,7 +181,7 @@ class TagRef(Ref): This tag object will always point to a commit object, but may carray additional information in a tag object:: - tagref = TagRef.find_all(repo)[0] + tagref = TagRef.list_items(repo)[0] print tagref.commit.message if tagref.tag is not None: print tagref.tag.message @@ -215,14 +215,14 @@ class TagRef(Ref): return None @classmethod - def find_all(cls, repo, common_path = "refs/tags", **kwargs): + def list_items(cls, repo, common_path = "refs/tags", **kwargs): """ Returns git.Tag[] - For more documentation, please refer to git.base.Ref.find_all + For more documentation, please refer to git.base.Ref.list_items """ - return super(TagRef,cls).find_all(repo, common_path, **kwargs) + return super(TagRef,cls).list_items(repo, common_path, **kwargs) # provide an alias -- cgit v1.2.1 From ac1cec7066eaa12a8d1a61562bfc6ee77ff5f54d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 13 Oct 2009 21:49:33 +0200 Subject: added Iterable interface to Ref type --- lib/git/refs.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 32bc7784..df914b78 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -8,9 +8,9 @@ Module containing all ref based objects """ from objects.base import Object from objects.utils import get_object_type_by_name -from utils import LazyMixin +from utils import LazyMixin, Iterable -class Ref(LazyMixin): +class Ref(LazyMixin, Iterable): """ Represents a named reference to any object """ @@ -73,7 +73,7 @@ class Ref(LazyMixin): return '/'.join(tokens[2:]) @classmethod - def list_items(cls, repo, common_path = "refs", **kwargs): + def iter_items(cls, repo, common_path = "refs", **kwargs): """ Find all refs in the repository @@ -102,15 +102,15 @@ class Ref(LazyMixin): options.update(kwargs) output = repo.git.for_each_ref(common_path, **options) - return cls._list_from_string(repo, output) + return cls._iter_from_stream(repo, iter(output.splitlines())) @classmethod - def _list_from_string(cls, repo, text): + 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 text.splitlines(): + for line in stream: heads.append(cls._from_string(repo, line)) return heads @@ -158,14 +158,14 @@ class Head(Ref): return self.object @classmethod - def list_items(cls, repo, common_path = "refs/heads", **kwargs): + def iter_items(cls, repo, common_path = "refs/heads", **kwargs): """ Returns - git.Head[] + Iterator yielding Head items For more documentation, please refer to git.base.Ref.list_items """ - return super(Head,cls).list_items(repo, common_path, **kwargs) + return super(Head,cls).iter_items(repo, common_path, **kwargs) def __repr__(self): return '' % self.name @@ -215,14 +215,14 @@ class TagRef(Ref): return None @classmethod - def list_items(cls, repo, common_path = "refs/tags", **kwargs): + def iter_items(cls, repo, common_path = "refs/tags", **kwargs): """ Returns - git.Tag[] + Iterator yielding commit items For more documentation, please refer to git.base.Ref.list_items """ - return super(TagRef,cls).list_items(repo, common_path, **kwargs) + return super(TagRef,cls).iter_items(repo, common_path, **kwargs) # provide an alias -- cgit v1.2.1 From 6745f4542cfb74bbf3b933dba7a59ef2f54a4380 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 14 Oct 2009 19:34:45 +0200 Subject: test_blob: removed many redundant tests that would fail now as the mock cannot handle the complexity of the command backend All objects but Tree now use the persistent command to read their object information - Trees get binary data and would need their own pretty-printing or they need to parse the data themselves which is my favorite --- lib/git/refs.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index df914b78..9754f65d 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -38,8 +38,10 @@ class Ref(LazyMixin, Iterable): if attr == "object": # have to be dynamic here as we may be a tag which can point to anything # it uses our path to stay dynamic - type_string = self.repo.git.cat_file(self.path, t=True).rstrip() - self.object = get_object_type_by_name(type_string)(self.repo, self.path) + typename, size = self.repo.git.get_object_header(self.path) + # explicitly do not set the size as it may change if the our ref path points + # at some other place when the head changes for instance ... + self.object = get_object_type_by_name(typename)(self.repo, self.path) else: super(Ref, self)._set_cache_(attr) @@ -124,9 +126,14 @@ class Ref(LazyMixin, Iterable): id: [0-9A-Fa-f]{40} Returns git.Head """ full_path, hexsha, type_name, object_size = line.split("\x00") - obj = get_object_type_by_name(type_name)(repo, hexsha) - obj.size = object_size - return cls(repo, full_path, obj) + + # No, we keep the object dynamic by allowing it to be retrieved by + # our path on demand - due to perstent commands it is fast + 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): -- cgit v1.2.1 From c5df44408218003eb49e3b8fc94329c5e8b46c7d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 14 Oct 2009 19:41:27 +0200 Subject: 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 --- lib/git/refs.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index 9754f65d..be02fb40 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -38,10 +38,11 @@ class Ref(LazyMixin, Iterable): if attr == "object": # have to be dynamic here as we may be a tag which can point to anything # it uses our path to stay dynamic - typename, size = self.repo.git.get_object_header(self.path) - # explicitly do not set the size as it may change if the our ref path points - # at some other place when the head changes for instance ... - self.object = get_object_type_by_name(typename)(self.repo, self.path) + hexsha, typename, size = self.repo.git.get_object_header(self.path) + # pin-point our object to a specific sha, even though it might not + # reflect the our cached object anymore in case our rev now points + # to a different commit + self.object = get_object_type_by_name(typename)(self.repo, hexsha) else: super(Ref, self)._set_cache_(attr) @@ -128,7 +129,9 @@ class Ref(LazyMixin, Iterable): 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 + # 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) -- cgit v1.2.1 From 832b56394b079c9f6e4c777934447a9e224facfe Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 14 Oct 2009 19:46:24 +0200 Subject: Refs are now truly dynamic - this costs a little bit of (persistent command) work, but assures refs behave as expected --- lib/git/refs.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'lib/git/refs.py') diff --git a/lib/git/refs.py b/lib/git/refs.py index be02fb40..3c9eb817 100644 --- a/lib/git/refs.py +++ b/lib/git/refs.py @@ -14,7 +14,7 @@ class Ref(LazyMixin, Iterable): """ Represents a named reference to any object """ - __slots__ = ("repo", "path", "object") + __slots__ = ("repo", "path") def __init__(self, repo, path, object = None): """ @@ -34,18 +34,6 @@ class Ref(LazyMixin, Iterable): if object is not None: self.object = object - def _set_cache_(self, attr): - if attr == "object": - # have to be dynamic here as we may be a tag which can point to anything - # it uses our path to stay dynamic - hexsha, typename, size = self.repo.git.get_object_header(self.path) - # pin-point our object to a specific sha, even though it might not - # reflect the our cached object anymore in case our rev now points - # to a different commit - self.object = get_object_type_by_name(typename)(self.repo, hexsha) - else: - super(Ref, self)._set_cache_(attr) - def __str__(self): return self.name @@ -74,7 +62,18 @@ class Ref(LazyMixin, Iterable): 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): """ -- cgit v1.2.1