← Back to team overview

dulwich-users team mailing list archive

[PATCH 11/34] walk: Add WalkEntry method to get tree changes.

 

From: Dave Borowitz <dborowitz@xxxxxxxxxx>

Change-Id: I191f88472413c9d0350b14e1d36e28ab5a106340
---
 dulwich/tests/test_walk.py |   57 +++++++++++++++++++++++++++++++++++++++++++-
 dulwich/walk.py            |   46 ++++++++++++++++++++++++++++++-----
 2 files changed, 95 insertions(+), 8 deletions(-)

diff --git a/dulwich/tests/test_walk.py b/dulwich/tests/test_walk.py
index ac9d964..a910679 100644
--- a/dulwich/tests/test_walk.py
+++ b/dulwich/tests/test_walk.py
@@ -18,11 +18,17 @@
 
 """Tests for commit walking functionality."""
 
+from dulwich.diff_tree import (
+    CHANGE_ADD,
+    CHANGE_MODIFY,
+    TreeChange,
+    )
 from dulwich.object_store import (
     MemoryObjectStore,
     )
 from dulwich.objects import (
     Commit,
+    Blob,
     )
 from dulwich.walk import (
     WalkEntry,
@@ -30,10 +36,30 @@ from dulwich.walk import (
     )
 from dulwich.tests import TestCase
 from utils import (
+    F,
+    make_object,
     build_commit_graph,
     )
 
 
+class TestWalkEntry(object):
+
+    def __init__(self, commit, changes):
+        self.commit = commit
+        self.changes = changes
+
+    def __repr__(self):
+        return '<TestWalkEntry commit=%s, changes=%r>' % (
+          self.commit.id, self.changes)
+
+    def __eq__(self, other):
+        if not isinstance(other, WalkEntry) or self.commit != other.commit:
+            return False
+        if self.changes is None:
+            return True
+        return self.changes == other.changes()
+
+
 class WalkerTest(TestCase):
 
     def setUp(self):
@@ -55,7 +81,7 @@ class WalkerTest(TestCase):
         walker = Walker(self.store, *args, **kwargs)
         for i, entry in enumerate(expected):
             if isinstance(entry, Commit):
-                expected[i] = WalkEntry(entry)
+                expected[i] = TestWalkEntry(entry, None)
         actual = list(walker)
         self.assertEqual(expected, actual)
 
@@ -95,3 +121,32 @@ class WalkerTest(TestCase):
                               reverse=True)
         self.assertWalkYields([c2, c3], [c3.id], max_entries=2, reverse=True)
         self.assertWalkYields([c3], [c3.id], max_entries=1, reverse=True)
+
+    def test_changes_one_parent(self):
+        blob_a1 = make_object(Blob, data='a1')
+        blob_a2 = make_object(Blob, data='a2')
+        blob_b2 = make_object(Blob, data='b2')
+        c1, c2 = self.make_linear_commits(
+          2, trees={1: [('a', blob_a1)],
+                    2: [('a', blob_a2), ('b', blob_b2)]})
+        e1 = TestWalkEntry(c1, [TreeChange.add(('a', F, blob_a1.id))])
+        e2 = TestWalkEntry(c2, [TreeChange(CHANGE_MODIFY, ('a', F, blob_a1.id),
+                                           ('a', F, blob_a2.id)),
+                                TreeChange.add(('b', F, blob_b2.id))])
+        self.assertWalkYields([e2, e1], [c2.id])
+
+    def test_changes_multiple_parents(self):
+        blob_a1 = make_object(Blob, data='a1')
+        blob_b2 = make_object(Blob, data='b2')
+        blob_a3 = make_object(Blob, data='a3')
+        c1, c2, c3 = self.make_commits(
+          [[1], [2], [3, 1, 2]],
+          trees={1: [('a', blob_a1)], 2: [('b', blob_b2)],
+                 3: [('a', blob_a3), ('b', blob_b2)]})
+        # a is a modify/add conflict and b is not conflicted.
+        changes = [[
+          TreeChange(CHANGE_MODIFY, ('a', F, blob_a1.id), ('a', F, blob_a3.id)),
+          TreeChange.add(('a', F, blob_a3.id)),
+          ]]
+        self.assertWalkYields([TestWalkEntry(c3, changes)], [c3.id],
+                              exclude=[c1.id, c2.id])
diff --git a/dulwich/walk.py b/dulwich/walk.py
index 3a84864..2364a2a 100644
--- a/dulwich/walk.py
+++ b/dulwich/walk.py
@@ -21,17 +21,47 @@
 import heapq
 import itertools
 
+from dulwich.diff_tree import (
+    tree_changes,
+    tree_changes_for_merge,
+    )
+
 ORDER_DATE = 'date'
 
 
 class WalkEntry(object):
     """Object encapsulating a single result from a walk."""
 
-    def __init__(self, commit):
+    def __init__(self, store, commit):
         self.commit = commit
+        self._store = store
+        self._changes = None
 
-    def __eq__(self, other):
-        return isinstance(other, WalkEntry) and self.commit == other.commit
+    def changes(self):
+        """Get the tree changes for this entry.
+
+        :return: For commits with up to one parent, a list of TreeChange
+            objects; if the commit has no parents, these will be relative to the
+            empty tree. For merge commits, a list of lists of TreeChange
+            objects; see dulwich.diff.tree_changes_for_merge.
+        """
+        if self._changes is None:
+            commit = self.commit
+            if not commit.parents:
+                changes = list(tree_changes(self._store, None, commit.tree))
+            elif len(commit.parents) == 1:
+                ptree = self._store[commit.parents[0]].tree
+                changes = list(tree_changes(self._store, ptree, commit.tree))
+            else:
+                ptrees = [self._store[p].tree for p in commit.parents]
+                changes = list(tree_changes_for_merge(self._store, ptrees,
+                                                      commit.tree))
+            self._changes = changes
+        return self._changes
+
+    def __repr__(self):
+        return '<WalkEntry commit=%s, changes=%r>' % (
+          self.commit.id, self.changes())
 
 
 class Walker(object):
@@ -100,14 +130,16 @@ class Walker(object):
                 return commit
         return None
 
+    def _make_entry(self, commit):
+        if commit is None:
+            return None
+        return WalkEntry(self._store, commit)
+
     def _next(self):
         limit = self._max_entries
         if limit is not None and len(self._done) >= limit:
             return None
-        commit = self._pop()
-        if commit is None:
-            return None
-        return WalkEntry(commit)
+        return self._make_entry(self._pop())
 
     def __iter__(self):
         results = iter(self._next, None)
-- 
1.7.3.1



References