summaryrefslogtreecommitdiff
path: root/lib/git/objects
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git/objects')
-rw-r--r--lib/git/objects/base.py15
-rw-r--r--lib/git/objects/commit.py188
-rw-r--r--lib/git/objects/tag.py12
-rw-r--r--lib/git/objects/utils.py20
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))