summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES7
-rw-r--r--lib/git/diff.py65
-rw-r--r--test/git/test_diff.py19
3 files changed, 79 insertions, 12 deletions
diff --git a/CHANGES b/CHANGES
index 8b696ba9..da396fe0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -80,6 +80,13 @@ 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
+* Diffs are now returned as DiffIndex allowing to more quickly find the kind of
+ diffs you are interested in
+
+Diffing
+-------
+* Commit and Tree objects now support diffing natively with a common interface to
+ compare agains other Commits or Trees, against the working tree or against the index.
Blob
----
diff --git a/lib/git/diff.py b/lib/git/diff.py
index 6a6a097c..1774597a 100644
--- a/lib/git/diff.py
+++ b/lib/git/diff.py
@@ -22,6 +22,10 @@ class Diffable(object):
# them in this tuple
_diff_args = tuple()
+ # Temporary standin for Index type until we have a real index type
+ class Index(object):
+ pass
+
def diff(self, other=None, paths=None, create_patch=False, **kwargs):
"""
Creates diffs between two items being trees, trees and index or an
@@ -30,6 +34,7 @@ class Diffable(object):
``other``
Is the item to compare us with.
If None, we will be compared to the working tree.
+ If Index ( type ), it will be compared against the index
``paths``
is a list of paths or a single path to limit the diff to.
@@ -63,8 +68,10 @@ class Diffable(object):
if paths is not None and not isinstance(paths, (tuple,list)):
paths = [ paths ]
- if other is not None:
+ if other is not None and other is not self.Index:
args.insert(0, other)
+ if other is self.Index:
+ args.insert(0, "--cached")
args.insert(0,self)
@@ -90,7 +97,33 @@ class DiffIndex(list):
The class improves the diff handling convenience
"""
+ # change type invariant identifying possible ways a blob can have changed
+ # A = Added
+ # D = Deleted
+ # R = Renamed
+ # NOTE: 'Modified' mode is impllied as it wouldn't be listed as a diff otherwise
+ change_type = ("A", "D", "R")
+
+ def iter_change_type(self, change_type):
+ """
+ Return
+ iterator yieling Diff instances that match the given change_type
+
+ ``change_type``
+ Member of DiffIndex.change_type
+ """
+ if change_type not in self.change_type:
+ raise ValueError( "Invalid change type: %s" % change_type )
+
+ for diff in self:
+ if change_type == "A" and diff.new_file:
+ yield diff
+ elif change_type == "D" and diff.deleted_file:
+ yield diff
+ elif change_type == "R" and diff.renamed:
+ yield diff
+ # END for each diff
class Diff(object):
@@ -132,7 +165,7 @@ class Diff(object):
""", re.VERBOSE | re.MULTILINE)
re_is_null_hexsha = re.compile( r'^0{40}$' )
__slots__ = ("a_blob", "b_blob", "a_mode", "b_mode", "new_file", "deleted_file",
- "rename_from", "rename_to", "renamed", "diff")
+ "rename_from", "rename_to", "diff")
def __init__(self, repo, a_path, b_path, a_blob_id, b_blob_id, a_mode,
b_mode, new_file, deleted_file, rename_from,
@@ -148,17 +181,29 @@ class Diff(object):
self.a_mode = a_mode
self.b_mode = b_mode
+
if self.a_mode:
self.a_mode = blob.Blob._mode_str_to_int( self.a_mode )
if self.b_mode:
self.b_mode = blob.Blob._mode_str_to_int( self.b_mode )
+
self.new_file = new_file
self.deleted_file = deleted_file
- self.rename_from = rename_from
- self.rename_to = rename_to
- self.renamed = rename_from != rename_to
+
+ # be clear and use None instead of empty strings
+ self.rename_from = rename_from or None
+ self.rename_to = rename_to or None
+
self.diff = diff
+ @property
+ def renamed(self):
+ """
+ Returns:
+ True if the blob of our diff has been renamed
+ """
+ return self.rename_from != self.rename_to
+
@classmethod
def _index_from_patch_format(cls, repo, stream):
"""
@@ -210,20 +255,22 @@ class Diff(object):
if not line.startswith(":"):
continue
# END its not a valid diff line
- old_mode, new_mode, a_blob_id, b_blob_id, modification_id, path = line[1:].split()
+ old_mode, new_mode, a_blob_id, b_blob_id, change_type, path = line[1:].split()
a_path = path
b_path = path
deleted_file = False
new_file = False
- if modification_id == 'D':
+
+ # NOTE: We cannot conclude from the existance of a blob to change type
+ # as diffs with the working do not have blobs yet
+ if change_type == 'D':
b_path = None
deleted_file = True
- elif modification_id == 'A':
+ elif change_type == 'A':
a_path = None
new_file = True
# END add/remove handling
-
diff = Diff(repo, a_path, b_path, a_blob_id, b_blob_id, old_mode, new_mode,
new_file, deleted_file, None, None, '')
index.append(diff)
diff --git a/test/git/test_diff.py b/test/git/test_diff.py
index c9604faf..deae7cfc 100644
--- a/test/git/test_diff.py
+++ b/test/git/test_diff.py
@@ -42,22 +42,35 @@ class TestDiff(TestCase):
def test_diff_interface(self):
# test a few variations of the main diff routine
+ assertion_map = dict()
for i, commit in enumerate(self.repo.iter_commits('0.1.6', max_count=10)):
diff_item = commit
if i%2 == 0:
diff_item = commit.tree
# END use tree every second item
- for other in (None, commit.parents[0]):
+ for other in (None, commit.Index, commit.parents[0]):
for paths in (None, "CHANGES", ("CHANGES", "lib")):
for create_patch in range(2):
diff_index = diff_item.diff(other, paths, create_patch)
assert isinstance(diff_index, DiffIndex)
- # TODO: test diff index
+ if diff_index:
+ for ct in DiffIndex.change_type:
+ key = 'ct_%s'%ct
+ assertion_map.setdefault(key, 0)
+ assertion_map[key] = assertion_map[key]+len(list(diff_index.iter_change_type(ct)))
+ # END for each changetype
+ # END diff index checking
# END for each patch option
# END for each path option
# END for each other side
# END for each commit
- self.fail( "TODO: Test full diff interface on commits, trees, index, patch and non-patch" )
+ # assert we could always find at least one instance of the members we
+ # can iterate in the diff index - if not this indicates its not working correctly
+ # or our test does not span the whole range of possibilities
+ for key,value in assertion_map.items():
+ assert value, "Did not find diff for %s" % key
+ # END for each iteration type
+