summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGES14
-rw-r--r--lib/git/blob.py2
-rw-r--r--lib/git/cmd.py2
-rw-r--r--lib/git/commit.py9
-rw-r--r--lib/git/diff.py55
-rw-r--r--lib/git/tree.py2
-rw-r--r--test/git/test_blob.py8
-rw-r--r--test/git/test_commit.py62
-rw-r--r--test/git/test_repo.py76
-rw-r--r--test/git/test_tree.py8
11 files changed, 120 insertions, 119 deletions
diff --git a/.gitignore b/.gitignore
index 68709910..37c5e30c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
/lib/GitPython.egg-info
/build
/dist
+/doc/_build
diff --git a/CHANGES b/CHANGES
index 8c67a7e5..63ca47e8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -49,6 +49,20 @@ Repo
* Corrected ``commits_between`` always returning None instead of the reversed
list.
+
+0.1.X
+=====
+( Future Release )
+General
+-------
+* See changes in Diff class as your client code needs adjustments to work with it
+
+Diff
+----
+* Members a a_commit and b_commit renamed to a_blob and b_blob - they are populated
+ with Blob objects if possible
+* Members a_path and b_path removed as this information is kept in the blobs
+
0.1.5
=====
diff --git a/lib/git/blob.py b/lib/git/blob.py
index eea04490..82a41f73 100644
--- a/lib/git/blob.py
+++ b/lib/git/blob.py
@@ -152,7 +152,7 @@ class Blob(object):
m = re.search(r'^\t(.*)$', line)
text, = m.groups()
blames[-1][0] = c
- blames[-1][1] += text
+ blames[-1][1].append( text )
info = None
return blames
diff --git a/lib/git/cmd.py b/lib/git/cmd.py
index 796928b3..aef53350 100644
--- a/lib/git/cmd.py
+++ b/lib/git/cmd.py
@@ -35,7 +35,7 @@ class Git(object):
of the command to stdout.
Set its value to 'full' to see details about the returned values.
"""
- def __init__(self, git_dir):
+ def __init__(self, git_dir=None):
"""
Initialize this instance with:
diff --git a/lib/git/commit.py b/lib/git/commit.py
index 1cb863ca..edfe47ca 100644
--- a/lib/git/commit.py
+++ b/lib/git/commit.py
@@ -73,6 +73,12 @@ class Commit(LazyMixin):
if tree is not None:
self.tree = Tree(repo, id=tree)
+ def __eq__(self, other):
+ return self.id == other.id
+
+ def __ne__(self, other):
+ return self.id != other.id
+
def __bake__(self):
"""
Called by LazyMixin superclass when the first uninitialized member needs
@@ -126,6 +132,7 @@ class Commit(LazyMixin):
def find_all(cls, repo, ref, path='', **kwargs):
"""
Find all commits matching the given criteria.
+
``repo``
is the Repo
@@ -164,7 +171,7 @@ class Commit(LazyMixin):
Returns
git.Commit[]
"""
- lines = [l for l in text.splitlines() if l.strip()]
+ lines = [l for l in text.splitlines() if l.strip('\r\n')]
commits = []
diff --git a/lib/git/diff.py b/lib/git/diff.py
index 0216e061..44f55602 100644
--- a/lib/git/diff.py
+++ b/lib/git/diff.py
@@ -5,28 +5,44 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
import re
-import commit
+import blob
class Diff(object):
"""
A Diff contains diff information between two commits.
+
+ It contains two sides a and b of the diff, members are prefixed with
+ "a" and "b" respectively to inidcate that.
+
+ Diffs keep information about the changed blob objects, the file mode, renames,
+ deletions and new files.
+
+ There are a few cases where None has to be expected as member variable value:
+
+ ``New File``::
+
+ a_mode is None
+ a_blob is None
+
+ ``Deleted File``::
+
+ b_mode is None
+ b_blob is NOne
"""
- def __init__(self, repo, a_path, b_path, a_commit, b_commit, a_mode,
+ def __init__(self, repo, a_path, b_path, a_blob, b_blob, a_mode,
b_mode, new_file, deleted_file, rename_from,
rename_to, diff):
self.repo = repo
- self.a_path = a_path
- self.b_path = b_path
- if not a_commit or re.search(r'^0{40}$', a_commit):
- self.a_commit = None
+ if not a_blob or re.search(r'^0{40}$', a_blob):
+ self.a_blob = None
else:
- self.a_commit = commit.Commit(repo, id=a_commit)
- if not b_commit or re.search(r'^0{40}$', b_commit):
- self.b_commit = None
+ self.a_blob = blob.Blob(repo, id=a_blob, mode=a_mode, name=a_path)
+ if not b_blob or re.search(r'^0{40}$', b_blob):
+ self.b_blob = None
else:
- self.b_commit = commit.Commit(repo, id=b_commit)
+ self.b_blob = blob.Blob(repo, id=b_blob, mode=b_mode, name=b_path)
self.a_mode = a_mode
self.b_mode = b_mode
@@ -39,6 +55,17 @@ class Diff(object):
@classmethod
def list_from_string(cls, repo, text):
+ """
+ Create a new diff object from the given text
+ ``repo``
+ is the repository we are operating on - it is required
+
+ ``text``
+ result of 'git diff' between two commits or one commit and the index
+
+ Returns
+ git.Diff[]
+ """
diffs = []
diff_header = re.compile(r"""
@@ -51,8 +78,8 @@ class Diff(object):
^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
(?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
(?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
- (?:^index[ ](?P<a_commit>[0-9A-Fa-f]+)
- \.\.(?P<b_commit>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
+ (?:^index[ ](?P<a_blob>[0-9A-Fa-f]+)
+ \.\.(?P<b_blob>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
""", re.VERBOSE | re.MULTILINE).match
for diff in ('\n' + text).split('\ndiff --git')[1:]:
@@ -60,10 +87,10 @@ class Diff(object):
a_path, b_path, similarity_index, rename_from, rename_to, \
old_mode, new_mode, new_file_mode, deleted_file_mode, \
- a_commit, b_commit, b_mode = header.groups()
+ a_blob, b_blob, b_mode = header.groups()
new_file, deleted_file = bool(new_file_mode), bool(deleted_file_mode)
- diffs.append(Diff(repo, a_path, b_path, a_commit, b_commit,
+ diffs.append(Diff(repo, a_path, b_path, a_blob, b_blob,
old_mode or deleted_file_mode, new_mode or new_file_mode or b_mode,
new_file, deleted_file, rename_from, rename_to, diff[header.end():]))
diff --git a/lib/git/tree.py b/lib/git/tree.py
index dea10908..cfb0881c 100644
--- a/lib/git/tree.py
+++ b/lib/git/tree.py
@@ -45,7 +45,7 @@ class Tree(LazyMixin):
``git.Blob`` or ``git.Tree``
"""
try:
- mode, typ, id, name = text.expandtabs(1).split(" ", 4)
+ mode, typ, id, name = text.expandtabs(1).split(" ", 3)
except:
return None
diff --git a/test/git/test_blob.py b/test/git/test_blob.py
index 8f83f391..5bd74ff7 100644
--- a/test/git/test_blob.py
+++ b/test/git/test_blob.py
@@ -69,6 +69,7 @@ class TestBlob(object):
git.return_value = fixture('blame')
b = Blob.blame(self.repo, 'master', 'lib/git.py')
assert_equal(13, len(b))
+ assert_equal( 2, len(b[0]) )
# assert_equal(25, reduce(lambda acc, x: acc + len(x[-1]), b))
assert_equal(hash(b[0][0]), hash(b[9][0]))
c = b[0][0]
@@ -83,6 +84,13 @@ class TestBlob(object):
assert_equal('tom@mojombo.com', c.committer.email)
assert_equal(time.gmtime(1191997100), c.committed_date)
assert_equal('initial grit setup', c.message)
+
+ # test the 'lines per commit' entries
+ tlist = b[0][1]
+ assert_true( tlist )
+ assert_true( isinstance( tlist[0], basestring ) )
+ assert_true( len( tlist ) < sum( len(t) for t in tlist ) ) # test for single-char bug
+
def test_should_return_appropriate_representation(self):
blob = Blob(self.repo, **{'id': 'abc'})
diff --git a/test/git/test_commit.py b/test/git/test_commit.py
index 3e37a7a4..d6bd6120 100644
--- a/test/git/test_commit.py
+++ b/test/git/test_commit.py
@@ -37,18 +37,19 @@ class TestCommit(object):
assert_equal(15, len(diffs))
- assert_equal('.gitignore', diffs[0].a_path)
- assert_equal('.gitignore', diffs[0].b_path)
- assert_equal('4ebc8aea50e0a67e000ba29a30809d0a7b9b2666', diffs[0].a_commit.id)
- assert_equal('2dd02534615434d88c51307beb0f0092f21fd103', diffs[0].b_commit.id)
- assert_equal('100644', diffs[0].b_mode)
+ assert_equal('.gitignore', diffs[0].a_blob.name)
+ assert_equal('.gitignore', diffs[0].b_blob.name)
+ assert_equal('4ebc8aea50e0a67e000ba29a30809d0a7b9b2666', diffs[0].a_blob.id)
+ assert_equal('2dd02534615434d88c51307beb0f0092f21fd103', diffs[0].b_blob.id)
+ assert_equal('100644', diffs[0].b_blob.mode)
assert_equal(False, diffs[0].new_file)
assert_equal(False, diffs[0].deleted_file)
assert_equal("--- a/.gitignore\n+++ b/.gitignore\n@@ -1 +1,2 @@\n coverage\n+pkg", diffs[0].diff)
- assert_equal('lib/grit/actor.rb', diffs[5].a_path)
- assert_equal(None, diffs[5].a_commit)
- assert_equal('f733bce6b57c0e5e353206e692b0e3105c2527f4', diffs[5].b_commit.id)
+ assert_equal('lib/grit/actor.rb', diffs[5].b_blob.name)
+ assert_equal(None, diffs[5].a_blob)
+ assert_equal('f733bce6b57c0e5e353206e692b0e3105c2527f4', diffs[5].b_blob.id)
+ assert_equal( None, diffs[5].a_mode )
assert_equal(True, diffs[5].new_file)
assert_true(git.called)
@@ -88,7 +89,7 @@ class TestCommit(object):
diffs = Commit.diff(self.repo, '59ddc32', ['lib'])
assert_equal(1, len(diffs))
- assert_equal('lib/grit/diff.rb', diffs[0].a_path)
+ assert_equal('lib/grit/diff.rb', diffs[0].a_blob.name)
assert_true(git.called)
assert_equal(git.call_args, (('diff', '-M', '59ddc32', '--', 'lib'), {'full_index': True}))
@@ -100,7 +101,7 @@ class TestCommit(object):
diffs = Commit.diff(self.repo, '59ddc32', '13d27d5', ['lib'])
assert_equal(1, len(diffs))
- assert_equal('lib/grit/commit.rb', diffs[0].a_path)
+ assert_equal('lib/grit/commit.rb', diffs[0].a_blob.name)
assert_true(git.called)
assert_equal(git.call_args, (('diff', '-M', '59ddc32', '13d27d5', '--', 'lib'), {'full_index': True}))
@@ -114,18 +115,18 @@ class TestCommit(object):
assert_equal(15, len(diffs))
- assert_equal('.gitignore', diffs[0].a_path)
- assert_equal('.gitignore', diffs[0].b_path)
- assert_equal('4ebc8aea50e0a67e000ba29a30809d0a7b9b2666', diffs[0].a_commit.id)
- assert_equal('2dd02534615434d88c51307beb0f0092f21fd103', diffs[0].b_commit.id)
- assert_equal('100644', diffs[0].b_mode)
+ assert_equal('.gitignore', diffs[0].a_blob.name)
+ assert_equal('.gitignore', diffs[0].b_blob.name)
+ assert_equal('4ebc8aea50e0a67e000ba29a30809d0a7b9b2666', diffs[0].a_blob.id)
+ assert_equal('2dd02534615434d88c51307beb0f0092f21fd103', diffs[0].b_blob.id)
+ assert_equal('100644', diffs[0].b_blob.mode)
assert_equal(False, diffs[0].new_file)
assert_equal(False, diffs[0].deleted_file)
assert_equal("--- a/.gitignore\n+++ b/.gitignore\n@@ -1 +1,2 @@\n coverage\n+pkg", diffs[0].diff)
- assert_equal('lib/grit/actor.rb', diffs[5].a_path)
- assert_equal(None, diffs[5].a_commit)
- assert_equal('f733bce6b57c0e5e353206e692b0e3105c2527f4', diffs[5].b_commit.id)
+ assert_equal('lib/grit/actor.rb', diffs[5].b_blob.name)
+ assert_equal(None, diffs[5].a_blob)
+ assert_equal('f733bce6b57c0e5e353206e692b0e3105c2527f4', diffs[5].b_blob.id)
assert_equal(True, diffs[5].new_file)
assert_true(git.called)
@@ -144,18 +145,17 @@ class TestCommit(object):
assert_equal(10, len(diffs))
- assert_equal('History.txt', diffs[0].a_path)
- assert_equal('History.txt', diffs[0].b_path)
- assert_equal(None, diffs[0].a_commit)
- assert_equal('100644', diffs[0].b_mode)
- assert_equal('81d2c27608b352814cbe979a6acd678d30219678', diffs[0].b_commit.id)
+ assert_equal('History.txt', diffs[0].b_blob.name)
+ assert_equal(None, diffs[0].a_blob)
+ assert_equal('100644', diffs[0].b_blob.mode)
+ assert_equal('81d2c27608b352814cbe979a6acd678d30219678', diffs[0].b_blob.id)
assert_equal(True, diffs[0].new_file)
assert_equal(False, diffs[0].deleted_file)
assert_equal("--- /dev/null\n+++ b/History.txt\n@@ -0,0 +1,5 @@\n+== 1.0.0 / 2007-10-09\n+\n+* 1 major enhancement\n+ * Birthday!\n+", diffs[0].diff)
- assert_equal('lib/grit.rb', diffs[5].a_path)
- assert_equal(None, diffs[5].a_commit)
- assert_equal('32cec87d1e78946a827ddf6a8776be4d81dcf1d1', diffs[5].b_commit.id)
+ assert_equal('lib/grit.rb', diffs[5].b_blob.name)
+ assert_equal(None, diffs[5].a_blob)
+ assert_equal('32cec87d1e78946a827ddf6a8776be4d81dcf1d1', diffs[5].b_blob.id)
assert_equal(True, diffs[5].new_file)
assert_true(git.called)
@@ -181,7 +181,10 @@ class TestCommit(object):
commit.__bake_it__()
diffs = commit.diffs
+ # in case of mode-only changes, there is no blob
assert_equal(23, len(diffs))
+ assert_equal(None, diffs[0].a_blob)
+ assert_equal(None, diffs[0].b_blob)
assert_equal('100644', diffs[0].a_mode)
assert_equal('100755', diffs[0].b_mode)
@@ -236,3 +239,10 @@ class TestCommit(object):
def test_repr(self):
commit = Commit(self.repo, id='abc')
assert_equal('<git.Commit "abc">', repr(commit))
+
+ def test_equality(self):
+ commit1 = Commit(self.repo, id='abc')
+ commit2 = Commit(self.repo, id='abc')
+ commit3 = Commit(self.repo, id='zyx')
+ assert_equal(commit1, commit2)
+ assert_not_equal(commit2, commit3)
diff --git a/test/git/test_repo.py b/test/git/test_repo.py
index c0236799..a9d3beaf 100644
--- a/test/git/test_repo.py
+++ b/test/git/test_repo.py
@@ -116,7 +116,7 @@ class TestRepo(object):
@patch_object(Repo, '__init__')
@patch_object(Git, '_call_process')
- def test_init_bare(self, repo, git):
+ def test_init_bare(self, git, repo):
git.return_value = True
repo.return_value = None
@@ -129,7 +129,7 @@ class TestRepo(object):
@patch_object(Repo, '__init__')
@patch_object(Git, '_call_process')
- def test_init_bare_with_options(self, repo, git):
+ def test_init_bare_with_options(self, git, repo):
git.return_value = True
repo.return_value = None
@@ -142,7 +142,7 @@ class TestRepo(object):
@patch_object(Repo, '__init__')
@patch_object(Git, '_call_process')
- def test_fork_bare(self, repo, git):
+ def test_fork_bare(self, git, repo):
git.return_value = None
repo.return_value = None
@@ -155,7 +155,7 @@ class TestRepo(object):
@patch_object(Repo, '__init__')
@patch_object(Git, '_call_process')
- def test_fork_bare_with_options(self, repo, git):
+ def test_fork_bare_with_options(self, git, repo):
git.return_value = None
repo.return_value = None
@@ -185,7 +185,7 @@ class TestRepo(object):
assert_equal(git.call_args, (('diff', 'master^', 'master', '--', 'foo/bar', 'foo/baz'), {}))
@patch_object(Git, '_call_process')
- def test_diff(self, git):
+ def test_diff_with_parents(self, git):
git.return_value = fixture('diff_p')
diffs = self.repo.commit_diff('master')
@@ -207,19 +207,6 @@ class TestRepo(object):
self.repo.daemon_serve = True
assert_true(self.repo.daemon_serve)
- # @patch_object(os.path, 'exists')
- # @patch_object('__builtin__', 'open')
- # def test_alternates_with_two_alternates(self, exists, read):
- # # File.expects(:exist?).with("#{absolute_project_path}/.git/objects/info/alternates").returns(true)
- # # File.expects(:read).returns("/path/to/repo1/.git/objects\n/path/to/repo2.git/objects\n")
- # exists.return_value = True
- # read.return_value = ("/path/to/repo1/.git/objects\n/path/to/repo2.git/objects\n")
- #
- # assert_equal(["/path/to/repo1/.git/objects", "/path/to/repo2.git/objects"], self.repo.alternates)
- #
- # assert_true(exists.called)
- # assert_true(read.called)
- #
@patch_object(os.path, 'exists')
def test_alternates_no_file(self, os):
os.return_value = False
@@ -227,32 +214,6 @@ class TestRepo(object):
assert_true(os.called)
- # @patch_object(os.path, 'exists')
- # def test_alternates_setter_ok(self, os):
- # os.return_value = True
- # alts = ['/path/to/repo.git/objects', '/path/to/repo2.git/objects']
- #
- # # File.any_instance.expects(:write).with(alts.join("\n"))
- #
- # self.repo.alternates = alts
- #
- # assert_true(os.called)
- # # assert_equal(os.call_args, ((alts,), {}))
- # # for alt in alts:
- #
- # @patch_object(os.path, 'exists')
- # @raises(NoSuchPathError)
- # def test_alternates_setter_bad(self, os):
- # os.return_value = False
- #
- # alts = ['/path/to/repo.git/objects']
- # # File.any_instance.expects(:write).never
- # self.repo.alternates = alts
- #
- # for alt in alts:
- # assert_true(os.called)
- # assert_equal(os.call_args, (alt, {}))
-
@patch_object(os, 'remove')
def test_alternates_setter_empty(self, os):
self.repo.alternates = []
@@ -278,33 +239,6 @@ class TestRepo(object):
assert_true(git.called)
assert_equal(git.call_args, (('log', 'master', '--', 'file.rb'), {'pretty': 'raw', 'max_count': 1}))
- # @patch_object(Git, '_call_process')
- # @patch_object(Git, '_call_process')
- # def test_commit_deltas_from_nothing_new(self, gitb, gita):
- # gitb.return_value = fixture("rev_list_delta_b")
- # gita.return_value = fixture("rev_list_delta_a")
- # other_repo = Repo(GIT_REPO)
- # # self.repo.git.expects(:rev_list).with({}, "master").returns(fixture("rev_list_delta_b"))
- # # other_repo.git.expects(:rev_list).with({}, "master").returns(fixture("rev_list_delta_a"))
- #
- # delta_commits = self.repo.commit_deltas_from(other_repo)
- # assert_equal(0, len(delta_commits))
- # assert_true(gitb.called)
- # assert_equal(gitb.call_args, (('rev_list', 'master'), {}))
- # assert_true(gita.called)
- # assert_equal(gita.call_args, (('rev_list', 'master'), {}))
- #
- # def test_commit_deltas_from_when_other_has_new(self):
- # other_repo = Repo(GIT_REPO)
- # # self.repo.git.expects(:rev_list).with({}, "master").returns(fixture("rev_list_delta_a"))
- # # other_repo.git.expects(:rev_list).with({}, "master").returns(fixture("rev_list_delta_b"))
- # # for ref in ['4c8124ffcf4039d292442eeccabdeca5af5c5017',
- # # '634396b2f541a9f2d58b00be1a07f0c358b999b3',
- # # 'ab25fd8483882c3bda8a458ad2965d2248654335']:
- # # Commit.expects(:find_all).with(other_repo, ref, :max_count => 1).returns([stub()])
- # delta_commits = self.repo.commit_deltas_from(other_repo)
- # assert_equal(3, len(delta_commits))
-
def test_is_dirty_with_bare_repository(self):
self.repo.bare = True
assert_false(self.repo.is_dirty)
diff --git a/test/git/test_tree.py b/test/git/test_tree.py
index c9fc2640..947b0ffb 100644
--- a/test/git/test_tree.py
+++ b/test/git/test_tree.py
@@ -56,7 +56,7 @@ class TestTree(object):
@patch_object(Blob, 'size')
@patch_object(Git, '_call_process')
- def test_slash(self, blob, git):
+ def test_slash(self, git, blob):
git.return_value = fixture('ls_tree_a')
blob.return_value = 1
@@ -70,7 +70,7 @@ class TestTree(object):
@patch_object(Blob, 'size')
@patch_object(Git, '_call_process')
- def test_slash_with_zero_length_file(self, blob, git):
+ def test_slash_with_zero_length_file(self, git, blob):
git.return_value = fixture('ls_tree_a')
blob.return_value = 0
@@ -97,7 +97,7 @@ class TestTree(object):
@patch_object(Blob, 'size')
@patch_object(Git, '_call_process')
- def test_dict(self, blob, git):
+ def test_dict(self, git, blob):
git.return_value = fixture('ls_tree_a')
blob.return_value = 1
@@ -111,7 +111,7 @@ class TestTree(object):
@patch_object(Blob, 'size')
@patch_object(Git, '_call_process')
- def test_dict_with_zero_length_file(self, blob, git):
+ def test_dict_with_zero_length_file(self, git, blob):
git.return_value = fixture('ls_tree_a')
blob.return_value = 0