summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES7
-rw-r--r--lib/git/objects/commit.py42
-rw-r--r--lib/git/refs.py6
-rw-r--r--lib/git/repo.py120
-rw-r--r--test/git/test_base.py2
-rw-r--r--test/git/test_commit.py10
-rw-r--r--test/git/test_performance.py2
-rw-r--r--test/git/test_repo.py4
8 files changed, 109 insertions, 84 deletions
diff --git a/CHANGES b/CHANGES
index 04d9b4c9..83156ecf 100644
--- a/CHANGES
+++ b/CHANGES
@@ -23,6 +23,11 @@ General
* All dates, like authored_date and committer_date, are stored as seconds since epoc
to consume less memory - they can be converted using time.gmtime in a more suitable
presentation format if needed.
+* Named method parameters changed on a wide scale to unify their use. Now git specific
+ terms are used everywhere, such as "Reference" ( ref ) and "Revision" ( rev ).
+ Prevously multiple terms where used making it harder to know which type was allowed
+ or not.
+
Item Iteration
--------------
@@ -48,6 +53,8 @@ Repo
- 'log' method as it as effectively the same as the 'commits' method
- 'commits_since' as it is just a flag given to rev-list in Commit.iter_items
- 'commit_count' as it was just a redirection to the respective commit method
+* Renamed commits to iter_commits to improve the performance, adjusted signature
+ to be more versatile
* 'commits' method has no max-count of returned commits anymore, it now behaves
like git-rev-list
diff --git a/lib/git/objects/commit.py b/lib/git/objects/commit.py
index 14839e12..ab8fdf26 100644
--- a/lib/git/objects/commit.py
+++ b/lib/git/objects/commit.py
@@ -100,17 +100,39 @@ class Commit(base.Object, Iterable):
First line of the commit message.
"""
return self.message.split('\n', 1)[0]
+
+ def iter_parents(self, paths='', **kwargs):
+ """
+ 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
+
+ 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 count(cls, repo, ref, paths=''):
+ def count(cls, repo, rev, paths=''):
"""
- 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
``paths``
is an optinal path or a list of paths restricting the return value
@@ -119,18 +141,18 @@ class Commit(base.Object, Iterable):
Returns
int
"""
- return len(repo.git.rev_list(ref, '--', paths).strip().splitlines())
+ return len(repo.git.rev_list(rev, '--', paths).strip().splitlines())
@classmethod
- def iter_items(cls, repo, ref, paths='', **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
``paths``
is an optinal path or list of paths, if set only Commits that include the path
@@ -149,7 +171,7 @@ class Commit(base.Object, Iterable):
options.update(kwargs)
# the test system might confront us with string values -
- proc = repo.git.rev_list(ref, '--', paths, **options)
+ proc = repo.git.rev_list(rev, '--', paths, **options)
return cls._iter_from_process_or_stream(repo, proc)
@classmethod
@@ -201,7 +223,7 @@ class Commit(base.Object, Iterable):
# END while there are message lines
message = '\n'.join(message_lines)
- yield Commit(repo, id=id, parents=parents, tree=tree, author=author, authored_date=authored_date,
+ 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
diff --git a/lib/git/refs.py b/lib/git/refs.py
index 3c9eb817..8ed578ef 100644
--- a/lib/git/refs.py
+++ b/lib/git/refs.py
@@ -10,7 +10,7 @@ from objects.base import Object
from objects.utils import get_object_type_by_name
from utils import LazyMixin, Iterable
-class Ref(LazyMixin, Iterable):
+class Reference(LazyMixin, Iterable):
"""
Represents a named reference to any object
"""
@@ -138,7 +138,7 @@ class Ref(LazyMixin, Iterable):
# return cls(repo, full_path, obj)
-class Head(Ref):
+class Head(Reference):
"""
A Head is a named reference to a Commit. Every Head instance contains a name
and a Commit object.
@@ -181,7 +181,7 @@ class Head(Ref):
-class TagRef(Ref):
+class TagRef(Reference):
"""
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
diff --git a/lib/git/repo.py b/lib/git/repo.py
index 40c71fd8..9b947d60 100644
--- a/lib/git/repo.py
+++ b/lib/git/repo.py
@@ -116,12 +116,12 @@ class Repo(object):
"""
return Tag.list_items(self)
- def blame(self, ref, file):
+ def blame(self, rev, file):
"""
- The blame information for the given file at the given ref.
+ The blame information for the given file at the given revision.
- ``ref``
- Ref object or Commit
+ ``rev``
+ revision specifier, see git-rev-parse for viable options.
Returns
list: [git.Commit, list: [<line>]]
@@ -199,37 +199,29 @@ class Repo(object):
# END distinguish hexsha vs other information
return blames
- def commits(self, start=None, paths='', max_count=None, skip=0):
+ def iter_commits(self, rev=None, paths='', **kwargs):
"""
A list of Commit objects representing the history of a given ref/commit
- ``start``
- is a Ref or Commit to start the commits from. If start is None,
- the active branch will be used
+ ``rev``
+ revision specifier, see git-rev-parse for viable options.
+ If None, the active branch will be used.
``paths``
is an optional path or a list of paths to limit the returned commits to
Commits that do not contain that path or the paths will not be returned.
-
- ``max_count``
- is the maximum number of commits to return (default None)
-
- ``skip``
- is the number of commits to skip (default 0) which will effectively
- move your commit-window by the given number.
+
+ ``kwargs``
+ Arguments to be passed to git-rev-parse - common ones are
+ max_count and skip
Returns
``git.Commit[]``
"""
- options = {'max_count': max_count,
- 'skip': skip}
-
- if max_count is None:
- options.pop('max_count')
- if start is None:
- start = self.active_branch
+ if rev is None:
+ rev = self.active_branch
- return Commit.list_items(self, start, paths, **options)
+ return Commit.list_items(self, rev, paths, **kwargs)
def commits_between(self, frm, to, *args, **kwargs):
"""
@@ -248,50 +240,31 @@ class Repo(object):
return reversed(Commit.list_items(self, "%s..%s" % (frm, to)))
- def commit(self, id=None, paths = ''):
+ def commit(self, rev=None):
"""
- The Commit object for the specified id
+ The Commit object for the specified revision
- ``id``
- is the SHA1 identifier of the commit or a ref or a ref name
- if None, it defaults to the active branch
+ ``rev``
+ revision specifier, see git-rev-parse for viable options.
-
- ``paths``
- is an optional path or a list of paths,
- if set the returned commit must contain the path or paths
-
Returns
``git.Commit``
"""
- if id is None:
- id = self.active_branch
- options = {'max_count': 1}
-
- commits = Commit.list_items(self, id, paths, **options)
-
- if not commits:
- raise ValueError, "Invalid identifier %s, or given path '%s' too restrictive" % ( id, path )
- return commits[0]
-
- def commit_deltas_from(self, other_repo, ref='master', other_ref='master'):
- """
- Returns a list of commits that is in ``other_repo`` but not in self
-
- Returns
- git.Commit[]
- """
- repo_refs = self.git.rev_list(ref, '--').strip().splitlines()
- other_repo_refs = other_repo.git.rev_list(other_ref, '--').strip().splitlines()
+ if rev is None:
+ rev = self.active_branch
+
+ # NOTE: currently we are not checking wheter rev really points to a commit
+ # If not, the system will barf on access of the object, but we don't do that
+ # here to safe cycles
+ c = Commit(self, rev)
+ return c
- diff_refs = list(set(other_repo_refs) - set(repo_refs))
- return map(lambda ref: Commit(other_repo, ref ), diff_refs)
- def tree(self, treeish=None):
+ def tree(self, ref=None):
"""
The Tree object for the given treeish reference
- ``treeish``
+ ``ref``
is a Ref instance defaulting to the active_branch if None.
Examples::
@@ -305,27 +278,42 @@ class Repo(object):
A ref is requried here to assure you point to a commit or tag. Otherwise
it is not garantueed that you point to the root-level tree.
- If you need a non-root level tree, find it by iterating the root tree.
- """
- if treeish is None:
- treeish = self.active_branch
- if not isinstance(treeish, Ref):
- raise ValueError( "Treeish reference required, got %r" % treeish )
+ If you need a non-root level tree, find it by iterating the root tree. Otherwise
+ it cannot know about its path relative to the repository root and subsequent
+ operations might have unexpected results.
+ """
+ if ref is None:
+ ref = self.active_branch
+ if not isinstance(ref, Reference):
+ raise ValueError( "Reference required, got %r" % ref )
# As we are directly reading object information, we must make sure
# we truly point to a tree object. We resolve the ref to a sha in all cases
# to assure the returned tree can be compared properly. Except for
# heads, ids should always be hexshas
- hexsha, typename, size = self.git.get_object_header( treeish )
+ hexsha, typename, size = self.git.get_object_header( ref )
if typename != "tree":
- hexsha, typename, size = self.git.get_object_header( str(treeish)+'^{tree}' )
+ # will raise if this is not a valid tree
+ hexsha, typename, size = self.git.get_object_header( str(ref)+'^{tree}' )
# END tree handling
- treeish = hexsha
+ ref = hexsha
# the root has an empty relative path and the default mode
- return Tree(self, treeish, 0, '')
+ return Tree(self, ref, 0, '')
+
+ def commit_deltas_from(self, other_repo, ref='master', other_ref='master'):
+ """
+ Returns a list of commits that is in ``other_repo`` but not in self
+ Returns
+ git.Commit[]
+ """
+ repo_refs = self.git.rev_list(ref, '--').strip().splitlines()
+ other_repo_refs = other_repo.git.rev_list(other_ref, '--').strip().splitlines()
+
+ diff_refs = list(set(other_repo_refs) - set(repo_refs))
+ return map(lambda ref: Commit(other_repo, ref ), diff_refs)
def diff(self, a, b, *paths):
"""
diff --git a/test/git/test_base.py b/test/git/test_base.py
index 48d628c3..04222e2e 100644
--- a/test/git/test_base.py
+++ b/test/git/test_base.py
@@ -62,7 +62,7 @@ class TestBase(object):
ref_count = 0
for ref in chain(self.repo.tags, self.repo.heads):
ref_count += 1
- assert isinstance(ref, refs.Ref)
+ assert isinstance(ref, refs.Reference)
assert str(ref) == ref.name
assert repr(ref)
assert ref == ref
diff --git a/test/git/test_commit.py b/test/git/test_commit.py
index 46aff635..4e698ed0 100644
--- a/test/git/test_commit.py
+++ b/test/git/test_commit.py
@@ -233,3 +233,13 @@ class TestCommit(object):
assert_equal(commit1, commit2)
assert_not_equal(commit2, commit3)
+ def test_iter_parents(self):
+ # should return all but ourselves, even if skip is defined
+ c = self.repo.commit('0.1.5')
+ for skip in (0, 1):
+ piter = c.iter_parents(skip=skip)
+ first_parent = piter.next()
+ assert first_parent != c
+ assert first_parent == c.parents[0]
+ # END for each
+
diff --git a/test/git/test_performance.py b/test/git/test_performance.py
index 96f13a2e..77567515 100644
--- a/test/git/test_performance.py
+++ b/test/git/test_performance.py
@@ -21,7 +21,7 @@ class TestPerformance(object):
# return quite a lot of commits, we just take one and hence abort the operation
st = time()
- for c in self.repo.commits():
+ for c in self.repo.iter_commits('0.1.6'):
num_commits += 1
c.author
c.authored_date
diff --git a/test/git/test_repo.py b/test/git/test_repo.py
index 24a13ed1..5869fb6a 100644
--- a/test/git/test_repo.py
+++ b/test/git/test_repo.py
@@ -42,7 +42,7 @@ class TestRepo(object):
def test_commits(self, git):
git.return_value = ListProcessAdapter(fixture('rev_list'))
- commits = self.repo.commits('master', max_count=10)
+ commits = list( self.repo.iter_commits('master', max_count=10) )
c = commits[0]
assert_equal('4c8124ffcf4039d292442eeccabdeca5af5c5017', c.id)
@@ -73,8 +73,6 @@ class TestRepo(object):
assert_equal("4c8124ffcf4039d292442eeccabdeca5af5c5017", commit.id)
- assert_true(git.called)
-
@patch_object(Repo, '__init__')
@patch_object(Git, '_call_process')
def test_init_bare(self, git, repo):