diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2009-10-11 16:36:51 +0200 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2009-10-11 16:36:51 +0200 |
commit | 20f202d83bdf1f332a3cb8f010bcf8bf3c2807bd (patch) | |
tree | 97eff8e6a86c55606f118218968c678a7647b552 | |
parent | 9ee31065abea645cbc2cf3e54b691d5983a228b2 (diff) | |
download | gitpython-20f202d83bdf1f332a3cb8f010bcf8bf3c2807bd.tar.gz |
Re-designed the tag testing - it does not use fixtures anymore but dyamically checks the existance of tags within the repository - it basically tests the interface and checks that expected return types are actually returned
-rw-r--r-- | lib/git/base.py | 161 | ||||
-rw-r--r-- | lib/git/head.py | 89 | ||||
-rw-r--r-- | lib/git/tag.py | 166 | ||||
-rw-r--r-- | test/fixtures/for_each_ref | bin | 58 -> 0 bytes | |||
-rw-r--r-- | test/fixtures/for_each_ref_tags | bin | 58 -> 0 bytes | |||
-rw-r--r-- | test/git/test_base.py | 8 | ||||
-rw-r--r-- | test/git/test_tag.py | 45 |
7 files changed, 303 insertions, 166 deletions
diff --git a/lib/git/base.py b/lib/git/base.py index 687fb50a..84dd0754 100644 --- a/lib/git/base.py +++ b/lib/git/base.py @@ -3,10 +3,13 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php +import os class LazyMixin(object): lazy_properties = [] - + + __slots__ = "__baked__" + def __init__(self): self.__baked__ = False @@ -38,7 +41,7 @@ class Object(LazyMixin): """ TYPES = ("blob", "tree", "commit", "tag") __slots__ = ("repo", "id", "size") - type = None # to be set by subclass + type = None # to be set by subclass def __init__(self, repo, id, size=None): """ @@ -46,9 +49,11 @@ class Object(LazyMixin): will be set on demand if None. ``repo`` - repository this object is located in + repository this object is located in + ``id`` SHA1 or ref suitable for git-rev-parse + ``size`` Size of the object's data in bytes """ @@ -97,3 +102,153 @@ class Object(LazyMixin): string with pythonic representation of our object """ return '<git.%s "%s">' % (self.__class__.__name__, self.id) + + @classmethod + def get_type_by_name(cls, object_type_name): + """ + Returns + type suitable to handle the given object type name. + Use the type to create new instances. + + ``object_type_name`` + Member of TYPES + + Raises + ValueError: In case object_type_name is unknown + """ + if object_type_name == "commit": + import commit + return commit.Commit + elif object_type_name == "tag": + import tag + return tag.TagObject + elif object_type_name == "blob": + import blob + return blob.Blob + elif object_type_name == "tree": + import tree + return tree.Tree + else: + raise ValueError("Cannot handle unknown object type: %s" % object_type_name) + + +class Ref(object): + """ + Represents a named reference to any object + """ + __slots__ = ("path", "object") + + def __init__(self, path, object = None): + """ + Initialize this instance + + ``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.path = path + 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 + Name of this reference + """ + return os.path.basename(self.path) + + @classmethod + def find_all(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.list_from_string(repo, output) + + @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 + """ + heads = [] + + for line in text.splitlines(): + 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. + + ``repo`` + is the Repo + + ``line`` + is the formatted ref information + + 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") + obj = Object.get_type_by_name(type_name)(repo, hexsha, object_size) + return cls(full_path, obj) diff --git a/lib/git/head.py b/lib/git/head.py index 639cee40..3c3f13ac 100644 --- a/lib/git/head.py +++ b/lib/git/head.py @@ -5,8 +5,9 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import commit +import base -class Head(object): +class Head(base.Ref): """ A Head is a named reference to a Commit. Every Head instance contains a name and a Commit object. @@ -26,93 +27,37 @@ class Head(object): '1c09f116cbc2cb4100fb6935bb162daa4723f455' """ - def __init__(self, name, commit): + def __init__(self, path, commit): """ Initialize a newly instanced Head - `name` - is the name of the head + ``path`` + is the path to the head ref, relative to the .git directory, i.e. + refs/heads/master `commit` is the Commit object that the head points to """ - self.name = name - self.commit = commit + super(Head, self).__init__(name, commit) - @classmethod - def find_all(cls, repo, **kwargs): - """ - Find all Heads in the repository - - `repo` - is the Repo - - `kwargs` - Additional options given as keyword arguments, will be passed - to git-for-each-ref - - Returns - git.Head[] - - List is sorted by committerdate - """ - - options = {'sort': "committerdate", - 'format': "%(refname)%00%(objectname)"} - options.update(kwargs) - - output = repo.git.for_each_ref("refs/heads", **options) - return cls.list_from_string(repo, output) - @classmethod - def list_from_string(cls, repo, text): + @property + def commit(self): """ - Parse out head information into a list of head objects - - ``repo`` - is the Repo - ``text`` - is the text output from the git-for-each-ref command - Returns - git.Head[] + Commit object the head points to """ - heads = [] - - for line in text.splitlines(): - heads.append(cls.from_string(repo, line)) - - return heads - + return self.object + @classmethod - def from_string(cls, repo, line): + def find_all(cls, repo, common_path = "refs/heads", **kwargs): """ - Create a new Head instance from the given string. - - ``repo`` - is the Repo - - ``line`` - is the formatted head information - - Format:: - - name: [a-zA-Z_/]+ - <null byte> - id: [0-9A-Fa-f]{40} - Returns - git.Head + git.Head[] + + For more documentation, please refer to git.base.Ref.find_all """ - full_name, ids = line.split("\x00") - - if full_name.startswith('refs/heads/'): - name = full_name[len('refs/heads/'):] - else: - name = full_name - - c = commit.Commit(repo, id=ids) - return Head(name, c) + return super(Head,cls).find_all(repo, common_path, **kwargs) def __repr__(self): return '<git.Head "%s">' % self.name diff --git a/lib/git/tag.py b/lib/git/tag.py index df3158a6..0c4122ab 100644 --- a/lib/git/tag.py +++ b/lib/git/tag.py @@ -4,95 +4,125 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -from commit import Commit +import commit +import base -class Tag(object): +class TagRef(base.Ref): """ - Class representing a tag reference which either points to a commit + 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.find_all(repo)[0] + print tagref.commit.message + if tagref.tag is not None: + print tagref.tag.message """ - def __init__(self, name, commit): + __slots__ = "tag" + + def __init__(self, path, commit_or_tag): """ Initialize a newly instantiated Tag - ``name`` - is the name of the head + ``path`` + is the full path to the tag - ``commit`` - is the Commit that the head points to + ``commit_or_tag`` + is the Commit or TagObject that this tag ref points to """ - self.name = name - self.commit = commit - - @classmethod - def find_all(cls, repo, **kwargs): + 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 + + @property + def commit(self): """ - Find all Tags in the repository - - ``repo`` - is the Repo - - ``kwargs`` - Additional options given as keyword arguments, will be passed - to git-for-each-ref - Returns - ``git.Tag[]`` - - List is sorted by committerdate + Commit object the tag ref points to """ - options = {'sort': "committerdate", - 'format': "%(refname)%00%(objectname)"} - options.update(**kwargs) - - output = repo.git.for_each_ref("refs/tags", **options) - return cls.list_from_string(repo, output) + if self.object.type == "commit": + return self.object + # it is a tag object + return self.object.object @classmethod - def list_from_string(cls, repo, text): + def find_all(cls, repo, common_path = "refs/tags", **kwargs): """ - Parse out tag information into an array of Tag objects - - ``repo`` - is the Repo - - ``text`` - is the text output from the git-for-each command - Returns git.Tag[] + + For more documentation, please refer to git.base.Ref.find_all """ - tags = [] - for line in text.splitlines(): - tags.append(cls.from_string(repo, line)) - return tags - - @classmethod - def from_string(cls, repo, line): + return super(TagRef,cls).find_all(repo, common_path, **kwargs) + + +# provide an alias +Tag = TagRef + +class TagObject(base.Object): + """ + Non-Lightweight tag carrying additional information about an object we are pointing + to. + """ + type = "tag" + __slots__ = ( "object", "tag", "tagger", "tagged_date", "message" ) + + def __init__(self, repo, id, size=None, object=None, tag=None, + tagger=None, tagged_date=None, message=None): """ - Create a new Tag instance from the given string. - + Initialize a tag object with additional data + ``repo`` - is the Repo - - ``line`` - is the formatted tag information - - Format:: + repository this object is located in - name: [a-zA-Z_/]+ - <null byte> - id: [0-9A-Fa-f]{40} - - Returns - git.Tag + ``id`` + SHA1 or ref suitable for git-rev-parse + + ``size`` + Size of the object's data in bytes + + ``object`` + Object instance of object we are pointing to + + ``tag`` + name of this tag + + ``tagger`` + Actor identifying the tagger + + ``tagged_date`` : (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst) + is the DateTime of the tag creation """ - full_name, ids = line.split("\x00") - name = full_name.split("/")[-1] - commit = Commit(repo, id=ids) - return Tag(name, commit) - - def __repr__(self): - return '<git.Tag "%s">' % self.name + super(TagObject, self).__init__(repo, id , size) + self.object = object + self.tag = tag + self.tagger = tagger + self.tagged_date = tagged_date + self.message = message + + def __bake__(self): + super(TagObject, self).__bake__() + + output = self.repo.git.cat_file(self.type,self.id) + lines = output.split("\n") + + obj, hexsha = lines[0].split(" ") # object <hexsha> + type_token, type_name = lines[1].split(" ") # type <type_name> + self.object = base.Object.get_type_by_name(type_name)(self.repo, hexsha) + + self.tag = lines[2][4:] # tag <tag name> + + tagger_info = lines[3][7:]# tagger <actor> <date> + self.tagger, self.tagged_date = commit.Commit._actor(tagger_info) + + # line 4 empty - check git source to figure out purpose + self.message = "\n".join(lines[5:]) + + diff --git a/test/fixtures/for_each_ref b/test/fixtures/for_each_ref Binary files differdeleted file mode 100644 index e56f5262..00000000 --- a/test/fixtures/for_each_ref +++ /dev/null diff --git a/test/fixtures/for_each_ref_tags b/test/fixtures/for_each_ref_tags Binary files differdeleted file mode 100644 index c4df85c6..00000000 --- a/test/fixtures/for_each_ref_tags +++ /dev/null diff --git a/test/git/test_base.py b/test/git/test_base.py index 46869f63..787b92b6 100644 --- a/test/git/test_base.py +++ b/test/git/test_base.py @@ -7,6 +7,7 @@ import time from test.testlib import * from git import * +import git.base as base class TestBase(object): @@ -33,4 +34,11 @@ class TestBase(object): def test_tags(self): # tag refs can point to tag objects or to commits assert False, "TODO: Tag handling" + + def test_get_type_by_name(self): + for tname in base.Object.TYPES: + assert base.Object in base.Object.get_type_by_name(tname).mro() + # END for each known type + + assert_raises( ValueError, base.Object.get_type_by_name, "doesntexist" ) diff --git a/test/git/test_tag.py b/test/git/test_tag.py index 732bbd45..52f7898c 100644 --- a/test/git/test_tag.py +++ b/test/git/test_tag.py @@ -7,30 +7,29 @@ from mock import * from test.testlib import * from git import * +from git.tag import TagObject +import time class TestTag(object): - def setup(self): - self.repo = Repo(GIT_REPO) + def setup(self): + self.repo = Repo(GIT_REPO) - @patch_object(Git, '_call_process') - def test_list_from_string(self, git): - git.return_value = fixture('for_each_ref_tags') - - tags = self.repo.tags - - assert_equal(1, len(tags)) - assert_equal('v0.7.1', tags[0].name) - assert_equal('634396b2f541a9f2d58b00be1a07f0c358b999b3', tags[0].commit.id) - - assert_true(git.called) - assert_equal(git.call_args, (('for_each_ref', 'refs/tags'), {'sort': 'committerdate', 'format': '%(refname)%00%(objectname)'})) + def test_tag_base(self): + tag_object_refs = list() + for tag in self.repo.tags: + assert "refs/tags" in tag.path + assert "/" not in tag.name + assert isinstance( tag.commit, Commit ) + if tag.tag is not None: + tag_object_refs.append( tag ) + tagobj = tag.tag + assert isinstance( tagobj, TagObject ) + assert tagobj.tag == tag.name + assert isinstance( tagobj.tagger, Actor ) + assert isinstance( tagobj.tagged_date, time.struct_time ) + assert tagobj.message + # END if we have a tag object + # END for tag in repo-tags + assert tag_object_refs + - @patch_object(Git, '_call_process') - def test_repr(self, git): - git.return_value = fixture('for_each_ref') - - tag = self.repo.tags[0] - assert_equal('<git.Tag "%s">' % tag.name, repr(tag)) - - assert_true(git.called) - assert_equal(git.call_args, (('for_each_ref', 'refs/tags'), {'sort': 'committerdate', 'format': '%(refname)%00%(objectname)'})) |