Merge lp:~jelmer/brz/get-delta-revisions-files into lp:brz/3.1

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/get-delta-revisions-files
Merge into: lp:brz/3.1
Diff against target: 285 lines (+160/-19)
7 files modified
breezy/bzr/remote.py (+8/-6)
breezy/bzr/tests/per_repository_vf/test_repository.py (+13/-2)
breezy/git/tree.py (+8/-6)
breezy/log.py (+1/-1)
breezy/plugins/stats/cmds.py (+2/-2)
breezy/repository.py (+38/-0)
breezy/tests/per_repository/test_repository.py (+90/-2)
To merge this branch: bzr merge lp:~jelmer/brz/get-delta-revisions-files
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+386469@code.launchpad.net

Commit message

Add Repository.get_revision_deltas.

Description of the change

Add Repository.get_revision_deltas.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/bzr/remote.py'
2--- breezy/bzr/remote.py 2020-03-22 19:12:43 +0000
3+++ breezy/bzr/remote.py 2020-06-28 18:07:07 +0000
4@@ -2590,12 +2590,14 @@
5 # file-id based, so filtering before or afterwards is
6 # currently easier.
7 if specific_fileids is None:
8- trees = dict((t.get_revision_id(), t) for
9- t in self.revision_trees(required_trees))
10+ trees = {
11+ t.get_revision_id(): t
12+ for t in self.revision_trees(required_trees)}
13 else:
14- trees = dict((t.get_revision_id(), t) for
15- t in self._filtered_revision_trees(required_trees,
16- specific_fileids))
17+ trees = {
18+ t.get_revision_id(): t
19+ for t in self._filtered_revision_trees(
20+ required_trees, specific_fileids)}
21
22 # Calculate the deltas
23 for revision in revisions:
24@@ -2608,7 +2610,7 @@
25 def get_revision_delta(self, revision_id):
26 with self.lock_read():
27 r = self.get_revision(revision_id)
28- return list(self.get_deltas_for_revisions([r]))[0]
29+ return list(self.get_revision_deltas([r]))[0]
30
31 def revision_trees(self, revision_ids):
32 with self.lock_read():
33
34=== modified file 'breezy/bzr/tests/per_repository_vf/test_repository.py'
35--- breezy/bzr/tests/per_repository_vf/test_repository.py 2020-06-10 02:56:53 +0000
36+++ breezy/bzr/tests/per_repository_vf/test_repository.py 2020-06-28 18:07:07 +0000
37@@ -364,8 +364,19 @@
38 revisions = [repository.get_revision(r) for r in
39 [b'rev1', b'rev2', b'rev3', b'rev4']]
40 deltas1 = list(repository.get_deltas_for_revisions(revisions))
41- deltas2 = [repository.get_revision_delta(r.revision_id) for r in
42- revisions]
43+ deltas2 = [repository.get_revision_delta(r.revision_id)
44+ for r in revisions]
45+ self.assertEqual(deltas1, deltas2)
46+
47+ def test_get_revision_deltas(self):
48+ repository = self.controldir.open_repository()
49+ repository.lock_read()
50+ self.addCleanup(repository.unlock)
51+ revisions = [repository.get_revision(r) for r in
52+ [b'rev1', b'rev2', b'rev3', b'rev4']]
53+ deltas1 = list(repository.get_revision_deltas(revisions))
54+ deltas2 = [repository.get_revision_delta(r.revision_id)
55+ for r in revisions]
56 self.assertEqual(deltas1, deltas2)
57
58 def test_all_revision_ids(self):
59
60=== modified file 'breezy/git/tree.py'
61--- breezy/git/tree.py 2020-06-27 14:48:19 +0000
62+++ breezy/git/tree.py 2020-06-28 18:07:07 +0000
63@@ -1029,10 +1029,11 @@
64 ret = {}
65 changes = self._iter_git_changes(specific_files=paths)[0]
66 for (change_type, old, new) in changes:
67- oldpath = old[0]
68- newpath = new[0]
69+ if old[0] is None:
70+ continue
71+ oldpath = decode_git_path(old[0])
72 if oldpath in paths:
73- ret[oldpath] = newpath
74+ ret[oldpath] = decode_git_path(new[0]) if new[0] else None
75 for path in paths:
76 if path not in ret:
77 if self.source.has_filename(path):
78@@ -1049,10 +1050,11 @@
79 ret = {}
80 changes = self._iter_git_changes(specific_files=paths)[0]
81 for (change_type, old, new) in changes:
82- oldpath = old[0]
83- newpath = new[0]
84+ if new[0] is None:
85+ continue
86+ newpath = decode_git_path(new[0])
87 if newpath in paths:
88- ret[newpath] = oldpath
89+ ret[newpath] = decode_git_path(old[0]) if old[0] else None
90 for path in paths:
91 if path not in ret:
92 if self.target.has_filename(path):
93
94=== modified file 'breezy/log.py'
95--- breezy/log.py 2020-06-21 02:15:25 +0000
96+++ breezy/log.py 2020-06-28 18:07:07 +0000
97@@ -982,7 +982,7 @@
98 revisions = [rev[1] for rev in revs]
99 new_revs = []
100 if delta_type == 'full' and not check_fileids:
101- deltas = repository.get_deltas_for_revisions(revisions)
102+ deltas = repository.get_revision_deltas(revisions)
103 for rev, delta in zip(revs, deltas):
104 new_revs.append((rev[0], rev[1], delta))
105 else:
106
107=== modified file 'breezy/plugins/stats/cmds.py'
108--- breezy/plugins/stats/cmds.py 2019-12-23 00:34:25 +0000
109+++ breezy/plugins/stats/cmds.py 2020-06-28 18:07:07 +0000
110@@ -315,7 +315,7 @@
111 with ui.ui_factory.nested_progress_bar() as pb:
112 with repository.lock_read():
113 i = 0
114- for delta in repository.get_deltas_for_revisions(revs):
115+ for delta in repository.get_revision_deltas(revs):
116 pb.update("classifying commits", i, len(revs))
117 for c in classify_delta(delta):
118 if c not in ret:
119@@ -364,7 +364,7 @@
120 if ps is not None and r != NULL_REVISION]
121 revs = repository.get_revisions(ancestry)
122 with ui.ui_factory.nested_progress_bar() as pb:
123- iterator = zip(revs, repository.get_deltas_for_revisions(revs))
124+ iterator = zip(revs, repository.get_revision_deltas(revs))
125 for i, (rev, delta) in enumerate(iterator):
126 pb.update("analysing revisions", i, len(revs))
127 # Don't count merges
128
129=== modified file 'breezy/repository.py'
130--- breezy/repository.py 2020-01-20 00:03:54 +0000
131+++ breezy/repository.py 2020-06-28 18:07:07 +0000
132@@ -913,6 +913,44 @@
133 r = self.get_revision(revision_id)
134 return list(self.get_deltas_for_revisions([r]))[0]
135
136+ def get_revision_deltas(self, revisions, specific_files=None):
137+ """Produce a generator of revision deltas.
138+
139+ Note that the input is a sequence of REVISIONS, not revision ids.
140+ Trees will be held in memory until the generator exits.
141+ Each delta is relative to the revision's lefthand predecessor.
142+
143+ specific_files should exist in the first revision.
144+
145+ :param specific_files: if not None, the result is filtered
146+ so that only those files, their parents and their
147+ children are included.
148+ """
149+ from .tree import InterTree
150+ # Get the revision-ids of interest
151+ required_trees = set()
152+ for revision in revisions:
153+ required_trees.add(revision.revision_id)
154+ required_trees.update(revision.parent_ids[:1])
155+
156+ trees = {
157+ t.get_revision_id(): t
158+ for t in self.revision_trees(required_trees)}
159+
160+ # Calculate the deltas
161+ for revision in revisions:
162+ if not revision.parent_ids:
163+ old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
164+ else:
165+ old_tree = trees[revision.parent_ids[0]]
166+ intertree = InterTree.get(old_tree, trees[revision.revision_id])
167+ yield intertree.compare(specific_files=specific_files)
168+ if specific_files is not None:
169+ specific_files = [
170+ p for p in intertree.find_source_paths(
171+ specific_files).values()
172+ if p is not None]
173+
174 def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
175 raise NotImplementedError(self.store_revision_signature)
176
177
178=== modified file 'breezy/tests/per_repository/test_repository.py'
179--- breezy/tests/per_repository/test_repository.py 2019-09-21 16:12:01 +0000
180+++ breezy/tests/per_repository/test_repository.py 2020-06-28 18:07:07 +0000
181@@ -997,10 +997,10 @@
182 self.assertEqual(repo.control_url, repo.control_transport.base)
183
184
185-class TestDeltaRevisionFiltered(per_repository.TestCaseWithRepository):
186+class TestDeltaRevisionFileIdFiltered(per_repository.TestCaseWithRepository):
187
188 def setUp(self):
189- super(TestDeltaRevisionFiltered, self).setUp()
190+ super(TestDeltaRevisionFileIdFiltered, self).setUp()
191 self.tree_a = self.make_branch_and_tree('a')
192 self.build_tree(['a/foo', 'a/bar/', 'a/bar/b1', 'a/bar/b2', 'a/baz'])
193 self.tree_a.add(['foo', 'bar', 'bar/b1', 'bar/b2', 'baz'])
194@@ -1067,3 +1067,91 @@
195 self.assertEqual([
196 ('bar/b3', 'file'),
197 ], [(c.path[1], c.kind[1]) for c in delta.added])
198+
199+
200+class TestDeltaRevisionFilesFiltered(per_repository.TestCaseWithRepository):
201+
202+ def setUp(self):
203+ super(TestDeltaRevisionFilesFiltered, self).setUp()
204+ self.tree_a = self.make_branch_and_tree('a')
205+ self.build_tree(
206+ ['a/foo', 'a/bar/', 'a/bar/b1', 'a/bar/b2', 'a/baz', 'a/oldname'])
207+ self.tree_a.add(['foo', 'bar', 'bar/b1', 'bar/b2', 'baz', 'oldname'])
208+ self.rev1 = self.tree_a.commit('rev1')
209+ self.build_tree(['a/bar/b3'])
210+ self.tree_a.add('bar/b3')
211+ self.tree_a.rename_one('oldname', 'newname')
212+ self.rev2 = self.tree_a.commit('rev2')
213+ self.repository = self.tree_a.branch.repository
214+ self.addCleanup(self.repository.lock_read().unlock)
215+
216+ def test_multiple_files(self):
217+ # Test multiple files
218+ delta = list(self.repository.get_revision_deltas(
219+ [self.repository.get_revision(self.rev1)], specific_files=[
220+ 'foo', 'baz']))[0]
221+ self.assertIsInstance(delta, _mod_delta.TreeDelta)
222+ self.assertEqual([
223+ ('baz', 'file'),
224+ ('foo', 'file'),
225+ ], [(c.path[1], c.kind[1]) for c in delta.added])
226+
227+ def test_directory(self):
228+ # Test a directory
229+ delta = list(self.repository.get_revision_deltas(
230+ [self.repository.get_revision(self.rev1)],
231+ specific_files=['bar']))[0]
232+ self.assertIsInstance(delta, _mod_delta.TreeDelta)
233+ self.assertEqual([
234+ ('bar', 'directory'),
235+ ('bar/b1', 'file'),
236+ ('bar/b2', 'file'),
237+ ], [(c.path[1], c.kind[1]) for c in delta.added])
238+
239+ def test_unrelated(self):
240+ # Try another revision
241+ delta = list(self.repository.get_revision_deltas(
242+ [self.repository.get_revision(self.rev2)],
243+ specific_files=['foo']))[0]
244+ self.assertIsInstance(delta, _mod_delta.TreeDelta)
245+ self.assertEqual([], delta.added)
246+
247+ def test_renamed(self):
248+ # Try another revision
249+ self.assertTrue(
250+ self.repository.revision_tree(self.rev2).has_filename('newname'))
251+ self.assertTrue(
252+ self.repository.revision_tree(self.rev1).has_filename('oldname'))
253+ revs = [
254+ self.repository.get_revision(self.rev2),
255+ self.repository.get_revision(self.rev1)]
256+ delta2, delta1 = list(self.repository.get_revision_deltas(
257+ revs, specific_files=['newname']))
258+ self.assertIsInstance(delta1, _mod_delta.TreeDelta)
259+ self.assertEqual([('oldname', 'newname')], [c.path for c in delta2.renamed])
260+ self.assertIsInstance(delta2, _mod_delta.TreeDelta)
261+ self.assertEqual(['oldname'], [c.path[1] for c in delta1.added])
262+
263+ def test_file_in_directory(self):
264+ # Test a file in a directory, both of which were added
265+ delta = list(self.repository.get_revision_deltas(
266+ [self.repository.get_revision(self.rev1)],
267+ specific_files=['bar/b2']))[0]
268+ self.assertIsInstance(delta, _mod_delta.TreeDelta)
269+ self.assertEqual([
270+ ('bar', 'directory'),
271+ ('bar/b2', 'file'),
272+ ], [(c.path[1], c.kind[1]) for c in delta.added])
273+
274+ def test_file_in_unchanged_directory(self):
275+ delta = list(self.repository.get_revision_deltas(
276+ [self.repository.get_revision(self.rev2)],
277+ specific_files=['bar/b3']))[0]
278+ self.assertIsInstance(delta, _mod_delta.TreeDelta)
279+ if [(c.path[1], c.kind[1]) for c in delta.added] == [
280+ ('bar', 'directory'), ('bar/b3', 'file')]:
281+ self.knownFailure("bzr incorrectly reports 'bar' as added - "
282+ "bug 878217")
283+ self.assertEqual([
284+ ('bar/b3', 'file'),
285+ ], [(c.path[1], c.kind[1]) for c in delta.added])

Subscribers

People subscribed via source and target branches