Merge lp:~jelmer/brz/mainline-ghosts into lp:brz

Proposed by Jelmer Vernooij
Status: Superseded
Proposed branch: lp:~jelmer/brz/mainline-ghosts
Merge into: lp:brz
Diff against target: 493 lines (+142/-115)
13 files modified
breezy/annotate.py (+4/-4)
breezy/bzr/groupcompress_repo.py (+1/-1)
breezy/bzr/remote.py (+13/-23)
breezy/bzr/vf_repository.py (+21/-36)
breezy/check.py (+1/-1)
breezy/log.py (+47/-23)
breezy/plugins/stats/cmds.py (+2/-2)
breezy/plugins/weave_fmt/repository.py (+0/-5)
breezy/repository.py (+21/-3)
breezy/status.py (+2/-14)
breezy/tests/blackbox/test_log.py (+5/-3)
breezy/tests/per_repository/test_repository.py (+19/-0)
doc/en/release-notes/brz-3.0.txt (+6/-0)
To merge this branch: bzr merge lp:~jelmer/brz/mainline-ghosts
Reviewer Review Type Date Requested Status
Martin Packman Pending
Review via email: mp+326120@code.launchpad.net

This proposal has been superseded by a proposal from 2017-06-22.

Commit message

Don't apply matcher logic unless --match is specified in ``bzr log``.

Description of the change

Quick performance fix for ``bzr log`` - don't run regexes over all logs that are printed *unless* one of the --match options is specified.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/annotate.py'
2--- breezy/annotate.py 2017-06-04 18:09:30 +0000
3+++ breezy/annotate.py 2017-06-22 01:54:53 +0000
4@@ -188,10 +188,10 @@
5 revision_id_to_revno[CURRENT_REVISION] = (
6 "%d?" % (branch.revno() + 1),)
7 revisions[CURRENT_REVISION] = current_rev
8- revision_ids = [o for o in revision_ids if
9- repository.has_revision(o)]
10- revisions.update((r.revision_id, r) for r in
11- repository.get_revisions(revision_ids))
12+ revisions.update(
13+ entry for entry in
14+ repository.iter_revisions(revision_ids)
15+ if entry[1] is not None)
16 for origin, text in annotations:
17 text = text.rstrip('\r\n')
18 if origin == last_origin:
19
20=== modified file 'breezy/bzr/groupcompress_repo.py'
21--- breezy/bzr/groupcompress_repo.py 2017-06-22 00:21:13 +0000
22+++ breezy/bzr/groupcompress_repo.py 2017-06-22 01:54:53 +0000
23@@ -1136,7 +1136,7 @@
24 raise AssertionError()
25 vf = self.revisions
26 if revisions_iterator is None:
27- revisions_iterator = self._iter_revisions(self.all_revision_ids())
28+ revisions_iterator = self.iter_revisions(self.all_revision_ids())
29 for revid, revision in revisions_iterator:
30 if revision is None:
31 pass
32
33=== modified file 'breezy/bzr/remote.py'
34--- breezy/bzr/remote.py 2017-06-22 00:23:56 +0000
35+++ breezy/bzr/remote.py 2017-06-22 01:54:53 +0000
36@@ -2658,39 +2658,29 @@
37 yield serializer.read_revision_from_string("".join(chunks))
38
39 @needs_read_lock
40- def get_revisions(self, revision_ids):
41+ def iter_revisions(self, revision_ids):
42 for rev_id in revision_ids:
43 if not rev_id or not isinstance(rev_id, bytes):
44 raise errors.InvalidRevisionId(
45 revision_id=rev_id, branch=self)
46 try:
47 missing = set(revision_ids)
48- revs = {}
49 for rev in self._iter_revisions_rpc(revision_ids):
50 missing.remove(rev.revision_id)
51- revs[rev.revision_id] = rev
52+ yield (rev.revision_id, rev)
53+ for fallback in self._fallback_repositories:
54+ if not missing:
55+ break
56+ for (revid, rev) in fallback.iter_revisions(missing):
57+ if rev is not None:
58+ yield (revid, rev)
59+ missing.remove(revid)
60+ for revid in missing:
61+ yield (revid, None)
62 except errors.UnknownSmartMethod:
63 self._ensure_real()
64- return self._real_repository.get_revisions(revision_ids)
65- for fallback in self._fallback_repositories:
66- if not missing:
67- break
68- for revid in list(missing):
69- # XXX JRV 2011-11-20: It would be nice if there was a
70- # public method on Repository that could be used to query
71- # for revision objects *without* failing completely if one
72- # was missing. There is VersionedFileRepository._iter_revisions,
73- # but unfortunately that's private and not provided by
74- # all repository implementations.
75- try:
76- revs[revid] = fallback.get_revision(revid)
77- except errors.NoSuchRevision:
78- pass
79- else:
80- missing.remove(revid)
81- if missing:
82- raise errors.NoSuchRevision(self, list(missing)[0])
83- return [revs[revid] for revid in revision_ids]
84+ for entry in self._real_repository.iter_revisions(revision_ids):
85+ yield entry
86
87 def supports_rich_root(self):
88 return self._format.rich_root_data
89
90=== modified file 'breezy/bzr/vf_repository.py'
91--- breezy/bzr/vf_repository.py 2017-06-22 00:21:13 +0000
92+++ breezy/bzr/vf_repository.py 2017-06-22 01:54:53 +0000
93@@ -1102,28 +1102,9 @@
94 be used by reconcile, or reconcile-alike commands that are correcting
95 or testing the revision graph.
96 """
97- return self._get_revisions([revision_id])[0]
98-
99- @needs_read_lock
100- def get_revisions(self, revision_ids):
101- """Get many revisions at once.
102-
103- Repositories that need to check data on every revision read should
104- subclass this method.
105- """
106- return self._get_revisions(revision_ids)
107-
108- @needs_read_lock
109- def _get_revisions(self, revision_ids):
110- """Core work logic to get many revisions without sanity checks."""
111- revs = {}
112- for revid, rev in self._iter_revisions(revision_ids):
113- if rev is None:
114- raise errors.NoSuchRevision(self, revid)
115- revs[revid] = rev
116- return [revs[revid] for revid in revision_ids]
117-
118- def _iter_revisions(self, revision_ids):
119+ return self.get_revisions([revision_id])[0]
120+
121+ def iter_revisions(self, revision_ids):
122 """Iterate over revision objects.
123
124 :param revision_ids: An iterable of revisions to examine. None may be
125@@ -1133,19 +1114,23 @@
126 :return: An iterator of (revid, revision) tuples. Absent revisions (
127 those asked for but not available) are returned as (revid, None).
128 """
129- for rev_id in revision_ids:
130- if not rev_id or not isinstance(rev_id, bytes):
131- raise errors.InvalidRevisionId(revision_id=rev_id, branch=self)
132- keys = [(key,) for key in revision_ids]
133- stream = self.revisions.get_record_stream(keys, 'unordered', True)
134- for record in stream:
135- revid = record.key[0]
136- if record.storage_kind == 'absent':
137- yield (revid, None)
138- else:
139- text = record.get_bytes_as('fulltext')
140- rev = self._serializer.read_revision_from_string(text)
141- yield (revid, rev)
142+ self.lock_read()
143+ try:
144+ for rev_id in revision_ids:
145+ if not rev_id or not isinstance(rev_id, bytes):
146+ raise errors.InvalidRevisionId(revision_id=rev_id, branch=self)
147+ keys = [(key,) for key in revision_ids]
148+ stream = self.revisions.get_record_stream(keys, 'unordered', True)
149+ for record in stream:
150+ revid = record.key[0]
151+ if record.storage_kind == 'absent':
152+ yield (revid, None)
153+ else:
154+ text = record.get_bytes_as('fulltext')
155+ rev = self._serializer.read_revision_from_string(text)
156+ yield (revid, rev)
157+ finally:
158+ self.unlock()
159
160 @needs_write_lock
161 def add_signature_text(self, revision_id, signature):
162@@ -1677,7 +1662,7 @@
163 raise AssertionError()
164 vf = self.revisions
165 if revisions_iterator is None:
166- revisions_iterator = self._iter_revisions(self.all_revision_ids())
167+ revisions_iterator = self.iter_revisions(self.all_revision_ids())
168 for revid, revision in revisions_iterator:
169 if revision is None:
170 pass
171
172=== modified file 'breezy/check.py'
173--- breezy/check.py 2017-06-22 00:21:13 +0000
174+++ breezy/check.py 2017-06-22 01:54:53 +0000
175@@ -181,7 +181,7 @@
176
177 def check_revisions(self):
178 """Scan revisions, checking data directly available as we go."""
179- revision_iterator = self.repository._iter_revisions(
180+ revision_iterator = self.repository.iter_revisions(
181 self.repository.all_revision_ids())
182 revision_iterator = self._check_revisions(revision_iterator)
183 # We read the all revisions here:
184
185=== modified file 'breezy/log.py'
186--- breezy/log.py 2017-06-05 20:48:31 +0000
187+++ breezy/log.py 2017-06-22 01:54:53 +0000
188@@ -409,8 +409,12 @@
189
190 # Find and print the interesting revisions
191 generator = self._generator_factory(self.branch, rqst)
192- for lr in generator.iter_log_revisions():
193- lf.log_revision(lr)
194+ try:
195+ for lr in generator.iter_log_revisions():
196+ lf.log_revision(lr)
197+ except errors.GhostRevisionUnusableHere:
198+ raise errors.BzrCommandError(
199+ gettext('Further revision history missing.'))
200 lf.show_advice()
201
202 def _generator_factory(self, branch, rqst):
203@@ -456,6 +460,8 @@
204 continue
205 if omit_merges and len(rev.parent_ids) > 1:
206 continue
207+ if rev is None:
208+ raise errors.GhostRevisionUnusableHere(rev_id)
209 if diff_type is None:
210 diff = None
211 else:
212@@ -723,6 +729,7 @@
213 :param exclude_common_ancestry: Whether the start_rev_id should be part of
214 the iterated revisions.
215 :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
216+ dotted_revno will be None for ghosts
217 :raises _StartNotLinearAncestor: if a start_rev_id is specified but
218 is not found walking the left-hand history
219 """
220@@ -731,27 +738,46 @@
221 graph = repo.get_graph()
222 if start_rev_id is None and end_rev_id is None:
223 cur_revno = br_revno
224- for revision_id in graph.iter_lefthand_ancestry(br_rev_id,
225- (_mod_revision.NULL_REVISION,)):
226- yield revision_id, str(cur_revno), 0
227- cur_revno -= 1
228+ graph_iter = graph.iter_lefthand_ancestry(br_rev_id,
229+ (_mod_revision.NULL_REVISION,))
230+ while True:
231+ try:
232+ revision_id = graph_iter.next()
233+ except StopIteration:
234+ raise
235+ except errors.RevisionNotPresent as e:
236+ # Oops, a ghost.
237+ yield e.revision_id, None, None
238+ break
239+ else:
240+ yield revision_id, str(cur_revno), 0
241+ cur_revno -= 1
242 else:
243 if end_rev_id is None:
244 end_rev_id = br_rev_id
245 found_start = start_rev_id is None
246- for revision_id in graph.iter_lefthand_ancestry(end_rev_id,
247- (_mod_revision.NULL_REVISION,)):
248- revno_str = _compute_revno_str(branch, revision_id)
249- if not found_start and revision_id == start_rev_id:
250- if not exclude_common_ancestry:
251+ graph_iter = graph.iter_lefthand_ancestry(end_rev_id,
252+ (_mod_revision.NULL_REVISION,))
253+ while True:
254+ try:
255+ revision_id = graph_iter.next()
256+ except StopIteration:
257+ break
258+ except errors.RevisionNotPresent as e:
259+ # Oops, a ghost.
260+ yield e.revision_id, None, None
261+ break
262+ else:
263+ revno_str = _compute_revno_str(branch, revision_id)
264+ if not found_start and revision_id == start_rev_id:
265+ if not exclude_common_ancestry:
266+ yield revision_id, revno_str, 0
267+ found_start = True
268+ break
269+ else:
270 yield revision_id, revno_str, 0
271- found_start = True
272- break
273- else:
274- yield revision_id, revno_str, 0
275- else:
276- if not found_start:
277- raise _StartNotLinearAncestor()
278+ if not found_start:
279+ raise _StartNotLinearAncestor()
280
281
282 def _graph_view_revisions(branch, start_rev_id, end_rev_id,
283@@ -861,7 +887,7 @@
284 :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
285 delta).
286 """
287- if match is None:
288+ if not match:
289 return log_rev_iterator
290 searchRE = [(k, [re.compile(x, re.IGNORECASE) for x in v])
291 for k, v in match.items()]
292@@ -998,10 +1024,8 @@
293 for revs in log_rev_iterator:
294 # r = revision_id, n = revno, d = merge depth
295 revision_ids = [view[0] for view, _, _ in revs]
296- revisions = repository.get_revisions(revision_ids)
297- revs = [(rev[0], revision, rev[2]) for rev, revision in
298- zip(revs, revisions)]
299- yield revs
300+ revisions = dict(repository.iter_revisions(revision_ids))
301+ yield [(rev[0], revisions[rev[0][0]], rev[2]) for rev in revs]
302
303
304 def _make_batch_filter(branch, generate_delta, search, log_rev_iterator):
305
306=== modified file 'breezy/plugins/stats/cmds.py'
307--- breezy/plugins/stats/cmds.py 2017-06-05 21:24:34 +0000
308+++ breezy/plugins/stats/cmds.py 2017-06-22 01:54:53 +0000
309@@ -148,8 +148,8 @@
310 pb = ui.ui_factory.nested_progress_bar()
311 try:
312 trace.note('getting revisions')
313- revisions = a_repo.get_revisions(revids)
314- for count, rev in enumerate(revisions):
315+ revisions = a_repo.iter_revisions(revids)
316+ for count, (revid, rev) in enumerate(revisions):
317 pb.update('checking', count, len(revids))
318 for author in rev.get_apparent_authors():
319 # XXX: There is a chance sometimes with svn imports that the
320
321=== modified file 'breezy/plugins/weave_fmt/repository.py'
322--- breezy/plugins/weave_fmt/repository.py 2017-06-14 23:29:06 +0000
323+++ breezy/plugins/weave_fmt/repository.py 2017-06-22 01:54:53 +0000
324@@ -166,11 +166,6 @@
325 self.start_write_group()
326 return result
327
328- @needs_read_lock
329- def get_revisions(self, revision_ids):
330- revs = self._get_revisions(revision_ids)
331- return revs
332-
333 def _inventory_add_lines(self, revision_id, parents, lines,
334 check_content=True):
335 """Store lines in inv_vf and return the sha1 of the inventory."""
336
337=== modified file 'breezy/repository.py'
338--- breezy/repository.py 2017-06-20 01:35:59 +0000
339+++ breezy/repository.py 2017-06-22 01:54:53 +0000
340@@ -822,11 +822,29 @@
341
342 def get_revisions(self, revision_ids):
343 """Get many revisions at once.
344-
345- Repositories that need to check data on every revision read should
346+
347+ Repositories that need to check data on every revision read should
348 subclass this method.
349 """
350- raise NotImplementedError(self.get_revisions)
351+ revs = {}
352+ for revid, rev in self.iter_revisions(revision_ids):
353+ if rev is None:
354+ raise errors.NoSuchRevision(self, revid)
355+ revs[revid] = rev
356+ return [revs[revid] for revid in revision_ids]
357+
358+ def iter_revisions(self, revision_ids):
359+ """Iterate over revision objects.
360+
361+ :param revision_ids: An iterable of revisions to examine. None may be
362+ passed to request all revisions known to the repository. Note that
363+ not all repositories can find unreferenced revisions; for those
364+ repositories only referenced ones will be returned.
365+ :return: An iterator of (revid, revision) tuples. Absent revisions (
366+ those asked for but not available) are returned as (revid, None).
367+ N.B.: Revisions are not necessarily yielded in order.
368+ """
369+ raise NotImplementedError(self.iter_revisions)
370
371 def get_deltas_for_revisions(self, revisions, specific_fileids=None):
372 """Produce a generator of revision deltas.
373
374=== modified file 'breezy/status.py'
375--- breezy/status.py 2017-05-25 01:35:55 +0000
376+++ breezy/status.py 2017-06-22 01:54:53 +0000
377@@ -297,7 +297,7 @@
378 log_formatter = log.LineLogFormatter(to_file)
379 for merge in pending:
380 try:
381- rev = branch.repository.get_revisions([merge])[0]
382+ rev = branch.repository.get_revision(merge)
383 except errors.NoSuchRevision:
384 # If we are missing a revision, just print out the revision id
385 to_file.write(first_prefix + '(ghost) ' + merge + '\n')
386@@ -316,19 +316,7 @@
387 merge_extra.discard(_mod_revision.NULL_REVISION)
388
389 # Get a handle to all of the revisions we will need
390- try:
391- revisions = dict((rev.revision_id, rev) for rev in
392- branch.repository.get_revisions(merge_extra))
393- except errors.NoSuchRevision:
394- # One of the sub nodes is a ghost, check each one
395- revisions = {}
396- for revision_id in merge_extra:
397- try:
398- rev = branch.repository.get_revisions([revision_id])[0]
399- except errors.NoSuchRevision:
400- revisions[revision_id] = None
401- else:
402- revisions[revision_id] = rev
403+ revisions = dict(branch.repository.iter_revisions(merge_extra))
404
405 # Display the revisions brought in by this merge.
406 rev_id_iterator = _get_sorted_revisions(merge, merge_extra,
407
408=== modified file 'breezy/tests/blackbox/test_log.py'
409--- breezy/tests/blackbox/test_log.py 2017-06-10 00:17:06 +0000
410+++ breezy/tests/blackbox/test_log.py 2017-06-22 01:54:53 +0000
411@@ -995,16 +995,18 @@
412 self.assertLogRevnos([], ["2", "1"])
413
414 def test_log_range_open_begin(self):
415- self.knownFailure("log with ghosts fails. bug #726466")
416 (stdout, stderr) = self.run_bzr(['log', '-r..2'], retcode=3)
417 self.assertEqual(["2", "1"],
418 [r.revno for r in self.get_captured_revisions()])
419- self.assertEqual("brz: ERROR: Further revision history missing.", stderr)
420+ self.assertEqual("brz: ERROR: Further revision history missing.\n",
421+ stderr)
422
423 def test_log_range_open_end(self):
424 self.assertLogRevnos(["-r1.."], ["2", "1"])
425
426+
427 class TestLogMatch(TestLogWithLogCatcher):
428+
429 def prepare_tree(self):
430 tree = self.make_branch_and_tree('')
431 self.build_tree(
432@@ -1013,7 +1015,7 @@
433 tree.commit(message='message1', committer='committer1', authors=['author1'])
434 tree.add('goodbye.txt')
435 tree.commit(message='message2', committer='committer2', authors=['author2'])
436-
437+
438 def test_message(self):
439 self.prepare_tree()
440 self.assertLogRevnos(["-m", "message1"], ["1"])
441
442=== modified file 'breezy/tests/per_repository/test_repository.py'
443--- breezy/tests/per_repository/test_repository.py 2017-06-11 14:07:05 +0000
444+++ breezy/tests/per_repository/test_repository.py 2017-06-22 01:54:53 +0000
445@@ -424,6 +424,25 @@
446 self.assertEqual(revision.revision_id, revision_id)
447 self.assertEqual(revision, repo.get_revision(revision_id))
448
449+ def test_iter_revisions(self):
450+ tree = self.make_branch_and_tree('.')
451+ tree.commit('initial empty commit', rev_id='a-rev',
452+ allow_pointless=True)
453+ tree.commit('second empty commit', rev_id='b-rev',
454+ allow_pointless=True)
455+ tree.commit('third empty commit', rev_id='c-rev',
456+ allow_pointless=True)
457+ repo = tree.branch.repository
458+ revision_ids = ['a-rev', 'c-rev', 'b-rev', 'd-rev']
459+ revid_with_rev = repo.iter_revisions(revision_ids)
460+ self.assertEqual(
461+ set((revid, rev.revision_id if rev is not None else None)
462+ for (revid, rev) in revid_with_rev),
463+ {('a-rev', 'a-rev'),
464+ ('b-rev', 'b-rev'),
465+ ('c-rev', 'c-rev'),
466+ ('d-rev', None)})
467+
468 def test_root_entry_has_revision(self):
469 tree = self.make_branch_and_tree('.')
470 tree.commit('message', rev_id='rev_id')
471
472=== modified file 'doc/en/release-notes/brz-3.0.txt'
473--- doc/en/release-notes/brz-3.0.txt 2017-06-22 00:21:13 +0000
474+++ doc/en/release-notes/brz-3.0.txt 2017-06-22 01:54:53 +0000
475@@ -104,6 +104,9 @@
476 * Support ``brz commit -x`` in combination with iter_changes.
477 (Jelmer Vernooij, #796582, #403811, #694946, #268135, #299879)
478
479+* Print a proper error when encountering ghost revisions in
480+ mainline in ``bzr log``. (Jelmer Vernooij, #726466)
481+
482 Documentation
483 *************
484
485@@ -151,6 +154,9 @@
486 * ``Repository.get_revisions`` no longer accepts ``None`` as
487 argument. (Jelmer Vernooij)
488
489+ * A new ``Repository.iter_revisions`` method has been added.
490+ (Jelmer Vernooij)
491+
492 Internals
493 *********
494

Subscribers

People subscribed via source and target branches