dulwich-users team mailing list archive
-
dulwich-users team
-
Mailing list archive
-
Message #00609
[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