diff options
Diffstat (limited to 'lib/git/objects')
-rw-r--r-- | lib/git/objects/base.py | 15 | ||||
-rw-r--r-- | lib/git/objects/commit.py | 188 | ||||
-rw-r--r-- | lib/git/objects/tag.py | 12 | ||||
-rw-r--r-- | lib/git/objects/utils.py | 20 |
4 files changed, 141 insertions, 94 deletions
diff --git a/lib/git/objects/base.py b/lib/git/objects/base.py index 07538ada..3b48e066 100644 --- a/lib/git/objects/base.py +++ b/lib/git/objects/base.py @@ -5,17 +5,32 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import os from git.utils import LazyMixin +import utils _assertion_msg_format = "Created object %r whose python type %r disagrees with the acutal git object type %r" class Object(LazyMixin): """ Implements an Object which may be Blobs, Trees, Commits and Tags + + This Object also serves as a constructor for instances of the correct type:: + + inst = Object(repo,id) """ TYPES = ("blob", "tree", "commit", "tag") __slots__ = ("repo", "id", "size", "data" ) type = None # to be set by subclass + def __new__(cls, repo, id, *args, **kwargs): + if cls is Object: + hexsha, typename, size = repo.git.get_object_header(id) + obj_type = utils.get_object_type_by_name(typename) + inst = super(Object,cls).__new__(obj_type, repo, hexsha, *args, **kwargs) + inst.size = size + return inst + else: + return super(Object,cls).__new__(cls, repo, id, *args, **kwargs) + def __init__(self, repo, id): """ Initialize an object by identifying it by its id. All keyword arguments diff --git a/lib/git/objects/commit.py b/lib/git/objects/commit.py index 101014ab..847f4dec 100644 --- a/lib/git/objects/commit.py +++ b/lib/git/objects/commit.py @@ -4,14 +4,12 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -import re -import time from git.utils import Iterable -from git.actor import Actor import git.diff as diff import git.stats as stats from tree import Tree import base +import utils class Commit(base.Object, Iterable): """ @@ -20,8 +18,6 @@ class Commit(base.Object, Iterable): This class will act lazily on some of its attributes and will query the value on demand only if it involves calling the git binary. """ - # precompiled regex - re_actor_epoch = re.compile(r'^.+? (.*) (\d+) .*$') # object configuration type = "commit" @@ -48,14 +44,16 @@ class Commit(base.Object, Iterable): ``author`` : Actor is the author string ( will be implicitly converted into an Actor object ) - ``authored_date`` : (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst ) - is the authored DateTime + ``authored_date`` : int_seconds_since_epoch + is the authored DateTime - use time.gmtime() to convert it into a + different format ``committer`` : Actor is the committer string - ``committed_date`` : (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst) - is the committed DateTime + ``committed_date`` : int_seconds_since_epoch + is the committed DateTime - use time.gmtime() to convert it into a + different format ``message`` : string is the commit message @@ -102,45 +100,49 @@ class Commit(base.Object, Iterable): First line of the commit message. """ return self.message.split('\n', 1)[0] - + @classmethod - def count(cls, repo, ref, path=''): + def count(cls, repo, rev, paths='', **kwargs): """ - Count the number of commits reachable from this ref + Count the number of commits reachable from this revision ``repo`` is the Repo - ``ref`` - is the ref from which to begin (SHA1 or name) + ``rev`` + revision specifier, see git-rev-parse for viable options - ``path`` - is an optinal path + ``paths`` + is an optinal path or a list of paths restricting the return value + to commits actually containing the paths + ``kwargs`` + Additional options to be passed to git-rev-list Returns int """ - return len(repo.git.rev_list(ref, '--', path).strip().splitlines()) + return len(repo.git.rev_list(rev, '--', paths, **kwargs).strip().splitlines()) @classmethod - def iter_items(cls, repo, ref, path='', **kwargs): + def iter_items(cls, repo, rev, paths='', **kwargs): """ Find all commits matching the given criteria. ``repo`` is the Repo - ``ref`` - is the ref from which to begin (SHA1, Head or name) + ``rev`` + revision specifier, see git-rev-parse for viable options - ``path`` - is an optinal path, if set only Commits that include the path - will be considered + ``paths`` + is an optinal path or list of paths, if set only Commits that include the path + or paths will be considered ``kwargs`` - optional keyword arguments to git where + optional keyword arguments to git rev-list where ``max_count`` is the maximum number of commits to fetch ``skip`` is the number of commits to skip + ``since`` all commits since i.e. '1970-01-01' Returns iterator yielding Commit items @@ -149,61 +151,30 @@ class Commit(base.Object, Iterable): options.update(kwargs) # the test system might confront us with string values - - proc = repo.git.rev_list(ref, '--', path, **options) + proc = repo.git.rev_list(rev, '--', paths, **options) return cls._iter_from_process_or_stream(repo, proc) - - @classmethod - def _iter_from_process_or_stream(cls, repo, proc_or_stream): - """ - Parse out commit information into a list of Commit objects - - ``repo`` - is the Repo - - ``proc`` - git-rev-list process instance (raw format) - - Returns - iterator returning Commit objects + + def iter_parents(self, paths='', **kwargs): """ - stream = proc_or_stream - if not hasattr(stream,'next'): - stream = proc_or_stream.stdout - - for line in stream: - id = line.split()[1] - assert line.split()[0] == "commit" - tree = stream.next().split()[1] - - parents = [] - next_line = None - for parent_line in stream: - if not parent_line.startswith('parent'): - next_line = parent_line - break - # END abort reading parents - parents.append(parent_line.split()[-1]) - # END for each parent line - - author, authored_date = cls._actor(next_line) - committer, committed_date = cls._actor(stream.next()) - - # empty line - stream.next() - - message_lines = [] - next_line = None - for msg_line in stream: - if not msg_line.startswith(' '): - break - # END abort message reading - message_lines.append(msg_line.strip()) - # END while there are message lines - message = '\n'.join(message_lines) + Iterate _all_ parents of this commit. + + ``paths`` + Optional path or list of paths limiting the Commits to those that + contain at least one of the paths + + ``kwargs`` + All arguments allowed by git-rev-list - yield Commit(repo, id=id, parents=parents, tree=tree, author=author, authored_date=authored_date, - committer=committer, committed_date=committed_date, message=message) - # END for each line in stream + Return: + Iterator yielding Commit objects which are parents of self + """ + # skip ourselves + skip = kwargs.get("skip", 1) + if skip == 0: # skip ourselves + skip = 1 + kwargs['skip'] = skip + + return self.iter_items( self.repo, self, paths, **kwargs ) @classmethod def diff(cls, repo, a, b=None, paths=None): @@ -279,6 +250,60 @@ class Commit(base.Object, Iterable): text = self.repo.git.diff(self.parents[0].id, self.id, '--', numstat=True) return stats.Stats._list_from_string(self.repo, text) + @classmethod + def _iter_from_process_or_stream(cls, repo, proc_or_stream): + """ + Parse out commit information into a list of Commit objects + + ``repo`` + is the Repo + + ``proc`` + git-rev-list process instance (raw format) + + Returns + iterator returning Commit objects + """ + stream = proc_or_stream + if not hasattr(stream,'next'): + stream = proc_or_stream.stdout + + for line in stream: + id = line.split()[1] + assert line.split()[0] == "commit" + tree = stream.next().split()[1] + + parents = [] + next_line = None + for parent_line in stream: + if not parent_line.startswith('parent'): + next_line = parent_line + break + # END abort reading parents + parents.append(parent_line.split()[-1]) + # END for each parent line + + author, authored_date = utils.parse_actor_and_date(next_line) + committer, committed_date = utils.parse_actor_and_date(stream.next()) + + # empty line + stream.next() + + message_lines = [] + next_line = None + for msg_line in stream: + if not msg_line.startswith(' '): + break + # END abort message reading + message_lines.append(msg_line.strip()) + # END while there are message lines + message = '\n'.join(message_lines) + + yield Commit(repo, id=id, parents=tuple(parents), tree=tree, author=author, authored_date=authored_date, + committer=committer, committed_date=committed_date, message=message) + # END for each line in stream + + def __str__(self): """ Convert commit to string which is SHA1 """ return self.id @@ -286,14 +311,3 @@ class Commit(base.Object, Iterable): def __repr__(self): return '<git.Commit "%s">' % self.id - @classmethod - def _actor(cls, line): - """ - Parse out the actor (author or committer) info - - Returns - [Actor, gmtime(acted at time)] - """ - m = cls.re_actor_epoch.search(line) - actor, epoch = m.groups() - return (Actor._from_string(actor), time.gmtime(int(epoch))) diff --git a/lib/git/objects/tag.py b/lib/git/objects/tag.py index ecf6349d..f54d4b64 100644 --- a/lib/git/objects/tag.py +++ b/lib/git/objects/tag.py @@ -7,8 +7,7 @@ Module containing all object based types. """ import base -import commit -from utils import get_object_type_by_name +import utils class TagObject(base.Object): """ @@ -38,8 +37,9 @@ class TagObject(base.Object): ``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 + ``tagged_date`` : int_seconds_since_epoch + is the DateTime of the tag creation - use time.gmtime to convert + it into a different format """ super(TagObject, self).__init__(repo, id ) self._set_self_from_args_(locals()) @@ -53,12 +53,12 @@ class TagObject(base.Object): obj, hexsha = lines[0].split(" ") # object <hexsha> type_token, type_name = lines[1].split(" ") # type <type_name> - self.object = get_object_type_by_name(type_name)(self.repo, hexsha) + self.object = utils.get_object_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) + self.tagger, self.tagged_date = utils.parse_actor_and_date(tagger_info) # line 4 empty - check git source to figure out purpose self.message = "\n".join(lines[5:]) diff --git a/lib/git/objects/utils.py b/lib/git/objects/utils.py index 15c1d114..367ed2b7 100644 --- a/lib/git/objects/utils.py +++ b/lib/git/objects/utils.py @@ -6,7 +6,8 @@ """ Module for general utility functions """ -import commit, tag, blob, tree +import re +from git.actor import Actor def get_object_type_by_name(object_type_name): """ @@ -34,3 +35,20 @@ def get_object_type_by_name(object_type_name): return tree.Tree else: raise ValueError("Cannot handle unknown object type: %s" % object_type_name) + + +# precompiled regex +_re_actor_epoch = re.compile(r'^.+? (.*) (\d+) .*$') + +def parse_actor_and_date(line): + """ + Parse out the actor (author or committer) info from a line like:: + + author Tom Preston-Werner <tom@mojombo.com> 1191999972 -0700 + + Returns + [Actor, int_seconds_since_epoch] + """ + m = _re_actor_epoch.search(line) + actor, epoch = m.groups() + return (Actor._from_string(actor), int(epoch)) |