Merge lp:~vila/bzr/320119-exclude-ancestry into lp:bzr
- 320119-exclude-ancestry
- Merge into bzr.dev
Status: | Merged |
---|---|
Approved by: | Vincent Ladeuil |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~vila/bzr/320119-exclude-ancestry |
Merge into: | lp:bzr |
Prerequisite: | lp:~vila/bzr/cleanup-log-direction |
Diff against target: |
428 lines (+189/-17) 8 files modified
NEWS (+7/-1) bzrlib/branch.py (+16/-2) bzrlib/builtins.py (+14/-2) bzrlib/log.py (+35/-11) bzrlib/tests/blackbox/test_log.py (+12/-0) bzrlib/tests/per_branch/test_iter_merge_sorted_revisions.py (+44/-0) bzrlib/tests/per_repository_reference/__init__.py (+1/-1) bzrlib/tests/test_log.py (+60/-0) |
To merge this branch: | bzr merge lp:~vila/bzr/320119-exclude-ancestry |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Bennetts | Needs Fixing | ||
Review via email: mp+23394@code.launchpad.net |
Commit message
Add --exclude-
Description of the change
This patch adds an --exclude-
a real graph difference (killing two birds with one stone, it also fixes bug #320119).
From now on, I consider that the log has reached the point where it's really
hard to add new features. The major culprit is the optimization to avoid
loading the whole graph, so no need to spend much time on the log code
unless this problem is correctly addressed.
The performance impact may be significant but I'd like real-life feedback on its
usage before trying to replace the is_ancestor() call by any caching of the X ancestry
(which may be quite significant anyway).
Again, having a better lazy-loaded graph ancestry is needed there.
Robert Collins (lifeless) wrote : | # |
Andrew Bennetts (spiv) wrote : | # |
You have a few conflicts because this branch waited so long for a review. Sorry :(
You define a new stop_rule for iter_merge_
It's a shame that make_branch_
(And further, it's a shame that the description of the ancestry is duplicated within that code... if only there were some way to do "make_branch_
Other than those, this seems ok to me. Once you fix those issues you can consider my vote upgraded to Approve.
I get the feeling that the next time we are tempted to another parameter to log we should think about refactoring the way we pass log options through the various layers (maybe with a LogOptions object? Or maybe Robert's idea about an algebra...), but this is ok for now.
Vincent Ladeuil (vila) wrote : | # |
>>>>> Andrew Bennetts <email address hidden> writes:
> Review: Needs Fixing
> You have a few conflicts because this branch waited so long for a review. Sorry :(
> You define a new stop_rule for iter_merge_
> you haven't added it to the docstring. Please fix that.
Done.
> It's a shame that make_branch_
> duplicated in test_log and
> per_branch.
> function and have the latter import it from the former? Please at
> least add comments in both places noting the duplication.
Since there is a valuable slight variation, I went with adding comments.
> (And further, it's a shame that the description of the ancestry is
> duplicated within that code... if only there were some way to do
> "make_branch_
Hehe, ideally I'd prefer some GUI to define the graph and get both the
ascii art and the branchbuilder stuff from that. ascii art is *not* fun
to write ;)
> Other than those, this seems ok to me. Once you fix those issues
> you can consider my vote upgraded to Approve.
Thanks.
> I get the feeling that the next time we are tempted to another
> parameter to log we should think about refactoring the way we pass
> log options through the various layers (maybe with a LogOptions
> object?
Definitely, but we also need to wire that up to the command line as many
log formatters will want their own options, I've punted on that one so
far for lack of time to research how to catch the options not recognized
by the parser and allow plugins or any external code to give it a try.
> Or maybe Robert's idea about an algebra...), but this is ok for
> now.
bzr PQM (bzr-pqm) wrote : | # |
Successful steps
Failure output:
All lines of log output:
Executing star-merge lp:~vila/bzr/320119-exclude-ancestry at Wed Apr 28 10:41:57 2010
['Nothing to merge.']
Vincent Ladeuil (vila) wrote : | # |
Meh, where is that pqm failure coming from ?
I sent a single submission with feed-pqm and it was merged.... (I didn't get a success email for it though).
Robert Collins (lifeless) wrote : | # |
This is probably due to lp taking too long to mark the branch as
merged: if it takes longer than pqm takes to try again, then pqm will
see it as still pending. Possibly we should:
mark things we succeed at as approved
adding a comment that it landed ok
or mark it as merged.
I'm a little worried about triggering launchpadlib errors though,
because lp is going to be updating the status at the same time - we
can collide.
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Robert Collins wrote:
> This is probably due to lp taking too long to mark the branch as
> merged: if it takes longer than pqm takes to try again, then pqm will
> see it as still pending. Possibly we should:
> mark things we succeed at as approved
> adding a comment that it landed ok
>
> or mark it as merged.
>
> I'm a little worried about triggering launchpadlib errors though,
> because lp is going to be updating the status at the same time - we
> can collide.
I think it would be reasonable to have your script notice that "Nothing
to be Merged" obviously means that the branch is already merged...
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAkv
V8YAn34mPZMKIzB
=hVtp
-----END PGP SIGNATURE-----
Preview Diff
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2010-04-27 18:09:12 +0000 | |||
3 | +++ NEWS 2010-04-28 07:18:51 +0000 | |||
4 | @@ -34,6 +34,11 @@ | |||
5 | 34 | better with sudo. | 34 | better with sudo. |
6 | 35 | (Martin <gzlist@googlemail.com>, Parth Malwankar, #376388) | 35 | (Martin <gzlist@googlemail.com>, Parth Malwankar, #376388) |
7 | 36 | 36 | ||
8 | 37 | * ``bzr log --exclude-common-ancestry -r X..Y`` displays the revisions that | ||
9 | 38 | are part of Y ancestry but not part of X ancestry (aka the graph | ||
10 | 39 | difference). | ||
11 | 40 | (Vincent Ladeuil, #320119) | ||
12 | 41 | |||
13 | 37 | * ``bzr selftest --parallel=fork`` wait for its children avoiding zombies. | 42 | * ``bzr selftest --parallel=fork`` wait for its children avoiding zombies. |
14 | 38 | (Vincent Ladeuil, #566670) | 43 | (Vincent Ladeuil, #566670) |
15 | 39 | 44 | ||
16 | @@ -145,7 +150,8 @@ | |||
17 | 145 | (Andrew Bennetts) | 150 | (Andrew Bennetts) |
18 | 146 | 151 | ||
19 | 147 | * When invoked with a range revision, ``bzr log`` doesn't show revisions | 152 | * When invoked with a range revision, ``bzr log`` doesn't show revisions |
21 | 148 | that are not part of the ancestry anymore. | 153 | that are not part of the Y revisions ancestry anymore when invoked with |
22 | 154 | -rX..Y. | ||
23 | 149 | (Vincent Ladeuil, #474807) | 155 | (Vincent Ladeuil, #474807) |
24 | 150 | 156 | ||
25 | 151 | Improvements | 157 | Improvements |
26 | 152 | 158 | ||
27 | === modified file 'bzrlib/branch.py' | |||
28 | --- bzrlib/branch.py 2010-04-23 07:15:23 +0000 | |||
29 | +++ bzrlib/branch.py 2010-04-28 07:18:51 +0000 | |||
30 | @@ -420,6 +420,8 @@ | |||
31 | 420 | * 'include' - the stop revision is the last item in the result | 420 | * 'include' - the stop revision is the last item in the result |
32 | 421 | * 'with-merges' - include the stop revision and all of its | 421 | * 'with-merges' - include the stop revision and all of its |
33 | 422 | merged revisions in the result | 422 | merged revisions in the result |
34 | 423 | * 'with-merges-without-common-ancestry' - filter out revisions | ||
35 | 424 | that are in both ancestries | ||
36 | 423 | :param direction: either 'reverse' or 'forward': | 425 | :param direction: either 'reverse' or 'forward': |
37 | 424 | * reverse means return the start_revision_id first, i.e. | 426 | * reverse means return the start_revision_id first, i.e. |
38 | 425 | start at the most recent revision and go backwards in history | 427 | start at the most recent revision and go backwards in history |
39 | @@ -456,7 +458,7 @@ | |||
40 | 456 | stop_revision_id, stop_rule) | 458 | stop_revision_id, stop_rule) |
41 | 457 | # Make sure we don't return revisions that are not part of the | 459 | # Make sure we don't return revisions that are not part of the |
42 | 458 | # start_revision_id ancestry. | 460 | # start_revision_id ancestry. |
44 | 459 | filtered = self._filter_non_ancestors(filtered) | 461 | filtered = self._filter_start_non_ancestors(filtered) |
45 | 460 | if direction == 'reverse': | 462 | if direction == 'reverse': |
46 | 461 | return filtered | 463 | return filtered |
47 | 462 | if direction == 'forward': | 464 | if direction == 'forward': |
48 | @@ -499,6 +501,18 @@ | |||
49 | 499 | node.end_of_merge) | 501 | node.end_of_merge) |
50 | 500 | if rev_id == stop_revision_id: | 502 | if rev_id == stop_revision_id: |
51 | 501 | return | 503 | return |
52 | 504 | elif stop_rule == 'with-merges-without-common-ancestry': | ||
53 | 505 | # We want to exclude all revisions that are already part of the | ||
54 | 506 | # stop_revision_id ancestry. | ||
55 | 507 | graph = self.repository.get_graph() | ||
56 | 508 | ancestors = graph.find_unique_ancestors(start_revision_id, | ||
57 | 509 | [stop_revision_id]) | ||
58 | 510 | for node in rev_iter: | ||
59 | 511 | rev_id = node.key[-1] | ||
60 | 512 | if rev_id not in ancestors: | ||
61 | 513 | continue | ||
62 | 514 | yield (rev_id, node.merge_depth, node.revno, | ||
63 | 515 | node.end_of_merge) | ||
64 | 502 | elif stop_rule == 'with-merges': | 516 | elif stop_rule == 'with-merges': |
65 | 503 | stop_rev = self.repository.get_revision(stop_revision_id) | 517 | stop_rev = self.repository.get_revision(stop_revision_id) |
66 | 504 | if stop_rev.parent_ids: | 518 | if stop_rev.parent_ids: |
67 | @@ -527,7 +541,7 @@ | |||
68 | 527 | else: | 541 | else: |
69 | 528 | raise ValueError('invalid stop_rule %r' % stop_rule) | 542 | raise ValueError('invalid stop_rule %r' % stop_rule) |
70 | 529 | 543 | ||
72 | 530 | def _filter_non_ancestors(self, rev_iter): | 544 | def _filter_start_non_ancestors(self, rev_iter): |
73 | 531 | # If we started from a dotted revno, we want to consider it as a tip | 545 | # If we started from a dotted revno, we want to consider it as a tip |
74 | 532 | # and don't want to yield revisions that are not part of its | 546 | # and don't want to yield revisions that are not part of its |
75 | 533 | # ancestry. Given the order guaranteed by the merge sort, we will see | 547 | # ancestry. Given the order guaranteed by the merge sort, we will see |
76 | 534 | 548 | ||
77 | === modified file 'bzrlib/builtins.py' | |||
78 | --- bzrlib/builtins.py 2010-04-23 11:11:22 +0000 | |||
79 | +++ bzrlib/builtins.py 2010-04-28 07:18:51 +0000 | |||
80 | @@ -2299,6 +2299,10 @@ | |||
81 | 2299 | help='Show changes made in each revision as a patch.'), | 2299 | help='Show changes made in each revision as a patch.'), |
82 | 2300 | Option('include-merges', | 2300 | Option('include-merges', |
83 | 2301 | help='Show merged revisions like --levels 0 does.'), | 2301 | help='Show merged revisions like --levels 0 does.'), |
84 | 2302 | Option('exclude-common-ancestry', | ||
85 | 2303 | help='Display only the revisions that are not part' | ||
86 | 2304 | ' of both ancestries (require -rX..Y)' | ||
87 | 2305 | ) | ||
88 | 2302 | ] | 2306 | ] |
89 | 2303 | encoding_type = 'replace' | 2307 | encoding_type = 'replace' |
90 | 2304 | 2308 | ||
91 | @@ -2314,13 +2318,19 @@ | |||
92 | 2314 | message=None, | 2318 | message=None, |
93 | 2315 | limit=None, | 2319 | limit=None, |
94 | 2316 | show_diff=False, | 2320 | show_diff=False, |
96 | 2317 | include_merges=False): | 2321 | include_merges=False, |
97 | 2322 | exclude_common_ancestry=False, | ||
98 | 2323 | ): | ||
99 | 2318 | from bzrlib.log import ( | 2324 | from bzrlib.log import ( |
100 | 2319 | Logger, | 2325 | Logger, |
101 | 2320 | make_log_request_dict, | 2326 | make_log_request_dict, |
102 | 2321 | _get_info_for_log_files, | 2327 | _get_info_for_log_files, |
103 | 2322 | ) | 2328 | ) |
104 | 2323 | direction = (forward and 'forward') or 'reverse' | 2329 | direction = (forward and 'forward') or 'reverse' |
105 | 2330 | if (exclude_common_ancestry | ||
106 | 2331 | and (revision is None or len(revision) != 2)): | ||
107 | 2332 | raise errors.BzrCommandError( | ||
108 | 2333 | '--exclude-common-ancestry requires -r with two revisions') | ||
109 | 2324 | if include_merges: | 2334 | if include_merges: |
110 | 2325 | if levels is None: | 2335 | if levels is None: |
111 | 2326 | levels = 0 | 2336 | levels = 0 |
112 | @@ -2419,7 +2429,9 @@ | |||
113 | 2419 | direction=direction, specific_fileids=file_ids, | 2429 | direction=direction, specific_fileids=file_ids, |
114 | 2420 | start_revision=rev1, end_revision=rev2, limit=limit, | 2430 | start_revision=rev1, end_revision=rev2, limit=limit, |
115 | 2421 | message_search=message, delta_type=delta_type, | 2431 | message_search=message, delta_type=delta_type, |
117 | 2422 | diff_type=diff_type, _match_using_deltas=match_using_deltas) | 2432 | diff_type=diff_type, _match_using_deltas=match_using_deltas, |
118 | 2433 | exclude_common_ancestry=exclude_common_ancestry, | ||
119 | 2434 | ) | ||
120 | 2423 | Logger(b, rqst).show(lf) | 2435 | Logger(b, rqst).show(lf) |
121 | 2424 | 2436 | ||
122 | 2425 | 2437 | ||
123 | 2426 | 2438 | ||
124 | === modified file 'bzrlib/log.py' | |||
125 | --- bzrlib/log.py 2010-04-14 10:38:57 +0000 | |||
126 | +++ bzrlib/log.py 2010-04-28 07:18:51 +0000 | |||
127 | @@ -220,14 +220,18 @@ | |||
128 | 220 | 'direction': 'reverse', | 220 | 'direction': 'reverse', |
129 | 221 | 'levels': 1, | 221 | 'levels': 1, |
130 | 222 | 'generate_tags': True, | 222 | 'generate_tags': True, |
131 | 223 | 'exclude_common_ancestry': False, | ||
132 | 223 | '_match_using_deltas': True, | 224 | '_match_using_deltas': True, |
133 | 224 | } | 225 | } |
134 | 225 | 226 | ||
135 | 226 | 227 | ||
136 | 227 | def make_log_request_dict(direction='reverse', specific_fileids=None, | 228 | def make_log_request_dict(direction='reverse', specific_fileids=None, |
140 | 228 | start_revision=None, end_revision=None, limit=None, | 229 | start_revision=None, end_revision=None, limit=None, |
141 | 229 | message_search=None, levels=1, generate_tags=True, delta_type=None, | 230 | message_search=None, levels=1, generate_tags=True, |
142 | 230 | diff_type=None, _match_using_deltas=True): | 231 | delta_type=None, |
143 | 232 | diff_type=None, _match_using_deltas=True, | ||
144 | 233 | exclude_common_ancestry=False, | ||
145 | 234 | ): | ||
146 | 231 | """Convenience function for making a logging request dictionary. | 235 | """Convenience function for making a logging request dictionary. |
147 | 232 | 236 | ||
148 | 233 | Using this function may make code slightly safer by ensuring | 237 | Using this function may make code slightly safer by ensuring |
149 | @@ -271,6 +275,9 @@ | |||
150 | 271 | algorithm used for matching specific_fileids. This parameter | 275 | algorithm used for matching specific_fileids. This parameter |
151 | 272 | may be removed in the future so bzrlib client code should NOT | 276 | may be removed in the future so bzrlib client code should NOT |
152 | 273 | use it. | 277 | use it. |
153 | 278 | |||
154 | 279 | :param exclude_common_ancestry: Whether -rX..Y should be interpreted as a | ||
155 | 280 | range operator or as a graph difference. | ||
156 | 274 | """ | 281 | """ |
157 | 275 | return { | 282 | return { |
158 | 276 | 'direction': direction, | 283 | 'direction': direction, |
159 | @@ -283,6 +290,7 @@ | |||
160 | 283 | 'generate_tags': generate_tags, | 290 | 'generate_tags': generate_tags, |
161 | 284 | 'delta_type': delta_type, | 291 | 'delta_type': delta_type, |
162 | 285 | 'diff_type': diff_type, | 292 | 'diff_type': diff_type, |
163 | 293 | 'exclude_common_ancestry': exclude_common_ancestry, | ||
164 | 286 | # Add 'private' attributes for features that may be deprecated | 294 | # Add 'private' attributes for features that may be deprecated |
165 | 287 | '_match_using_deltas': _match_using_deltas, | 295 | '_match_using_deltas': _match_using_deltas, |
166 | 288 | } | 296 | } |
167 | @@ -459,7 +467,8 @@ | |||
168 | 459 | self.branch, self.start_rev_id, self.end_rev_id, | 467 | self.branch, self.start_rev_id, self.end_rev_id, |
169 | 460 | rqst.get('direction'), | 468 | rqst.get('direction'), |
170 | 461 | generate_merge_revisions=generate_merge_revisions, | 469 | generate_merge_revisions=generate_merge_revisions, |
172 | 462 | delayed_graph_generation=delayed_graph_generation) | 470 | delayed_graph_generation=delayed_graph_generation, |
173 | 471 | exclude_common_ancestry=rqst.get('exclude_common_ancestry')) | ||
174 | 463 | 472 | ||
175 | 464 | # Apply the other filters | 473 | # Apply the other filters |
176 | 465 | return make_log_rev_iterator(self.branch, view_revisions, | 474 | return make_log_rev_iterator(self.branch, view_revisions, |
177 | @@ -474,7 +483,8 @@ | |||
178 | 474 | rqst = self.rqst | 483 | rqst = self.rqst |
179 | 475 | view_revisions = _calc_view_revisions( | 484 | view_revisions = _calc_view_revisions( |
180 | 476 | self.branch, self.start_rev_id, self.end_rev_id, | 485 | self.branch, self.start_rev_id, self.end_rev_id, |
182 | 477 | rqst.get('direction'), generate_merge_revisions=True) | 486 | rqst.get('direction'), generate_merge_revisions=True, |
183 | 487 | exclude_common_ancestry=rqst.get('exclude_common_ancestry')) | ||
184 | 478 | if not isinstance(view_revisions, list): | 488 | if not isinstance(view_revisions, list): |
185 | 479 | view_revisions = list(view_revisions) | 489 | view_revisions = list(view_revisions) |
186 | 480 | view_revisions = _filter_revisions_touching_file_id(self.branch, | 490 | view_revisions = _filter_revisions_touching_file_id(self.branch, |
187 | @@ -485,12 +495,18 @@ | |||
188 | 485 | 495 | ||
189 | 486 | 496 | ||
190 | 487 | def _calc_view_revisions(branch, start_rev_id, end_rev_id, direction, | 497 | def _calc_view_revisions(branch, start_rev_id, end_rev_id, direction, |
192 | 488 | generate_merge_revisions, delayed_graph_generation=False): | 498 | generate_merge_revisions, |
193 | 499 | delayed_graph_generation=False, | ||
194 | 500 | exclude_common_ancestry=False, | ||
195 | 501 | ): | ||
196 | 489 | """Calculate the revisions to view. | 502 | """Calculate the revisions to view. |
197 | 490 | 503 | ||
198 | 491 | :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples OR | 504 | :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples OR |
199 | 492 | a list of the same tuples. | 505 | a list of the same tuples. |
200 | 493 | """ | 506 | """ |
201 | 507 | if (exclude_common_ancestry and start_rev_id == end_rev_id): | ||
202 | 508 | raise errors.BzrCommandError( | ||
203 | 509 | '--exclude-common-ancestry requires two different revisions') | ||
204 | 494 | if direction not in ('reverse', 'forward'): | 510 | if direction not in ('reverse', 'forward'): |
205 | 495 | raise ValueError('invalid direction %r' % direction) | 511 | raise ValueError('invalid direction %r' % direction) |
206 | 496 | br_revno, br_rev_id = branch.last_revision_info() | 512 | br_revno, br_rev_id = branch.last_revision_info() |
207 | @@ -511,7 +527,8 @@ | |||
208 | 511 | iter_revs = reversed(iter_revs) | 527 | iter_revs = reversed(iter_revs) |
209 | 512 | else: | 528 | else: |
210 | 513 | iter_revs = _generate_all_revisions(branch, start_rev_id, end_rev_id, | 529 | iter_revs = _generate_all_revisions(branch, start_rev_id, end_rev_id, |
212 | 514 | direction, delayed_graph_generation) | 530 | direction, delayed_graph_generation, |
213 | 531 | exclude_common_ancestry) | ||
214 | 515 | if direction == 'forward': | 532 | if direction == 'forward': |
215 | 516 | iter_revs = _rebase_merge_depth(reverse_by_depth(list(iter_revs))) | 533 | iter_revs = _rebase_merge_depth(reverse_by_depth(list(iter_revs))) |
216 | 517 | return iter_revs | 534 | return iter_revs |
217 | @@ -542,7 +559,8 @@ | |||
218 | 542 | 559 | ||
219 | 543 | 560 | ||
220 | 544 | def _generate_all_revisions(branch, start_rev_id, end_rev_id, direction, | 561 | def _generate_all_revisions(branch, start_rev_id, end_rev_id, direction, |
222 | 545 | delayed_graph_generation): | 562 | delayed_graph_generation, |
223 | 563 | exclude_common_ancestry=False): | ||
224 | 546 | # On large trees, generating the merge graph can take 30-60 seconds | 564 | # On large trees, generating the merge graph can take 30-60 seconds |
225 | 547 | # so we delay doing it until a merge is detected, incrementally | 565 | # so we delay doing it until a merge is detected, incrementally |
226 | 548 | # returning initial (non-merge) revisions while we can. | 566 | # returning initial (non-merge) revisions while we can. |
227 | @@ -594,7 +612,8 @@ | |||
228 | 594 | # indented at the end seems slightly nicer in that case. | 612 | # indented at the end seems slightly nicer in that case. |
229 | 595 | view_revisions = chain(iter(initial_revisions), | 613 | view_revisions = chain(iter(initial_revisions), |
230 | 596 | _graph_view_revisions(branch, start_rev_id, end_rev_id, | 614 | _graph_view_revisions(branch, start_rev_id, end_rev_id, |
232 | 597 | rebase_initial_depths=(direction == 'reverse'))) | 615 | rebase_initial_depths=(direction == 'reverse'), |
233 | 616 | exclude_common_ancestry=exclude_common_ancestry)) | ||
234 | 598 | return view_revisions | 617 | return view_revisions |
235 | 599 | 618 | ||
236 | 600 | 619 | ||
237 | @@ -659,7 +678,8 @@ | |||
238 | 659 | 678 | ||
239 | 660 | 679 | ||
240 | 661 | def _graph_view_revisions(branch, start_rev_id, end_rev_id, | 680 | def _graph_view_revisions(branch, start_rev_id, end_rev_id, |
242 | 662 | rebase_initial_depths=True): | 681 | rebase_initial_depths=True, |
243 | 682 | exclude_common_ancestry=False): | ||
244 | 663 | """Calculate revisions to view including merges, newest to oldest. | 683 | """Calculate revisions to view including merges, newest to oldest. |
245 | 664 | 684 | ||
246 | 665 | :param branch: the branch | 685 | :param branch: the branch |
247 | @@ -669,9 +689,13 @@ | |||
248 | 669 | revision is found? | 689 | revision is found? |
249 | 670 | :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples. | 690 | :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples. |
250 | 671 | """ | 691 | """ |
251 | 692 | if exclude_common_ancestry: | ||
252 | 693 | stop_rule = 'with-merges-without-common-ancestry' | ||
253 | 694 | else: | ||
254 | 695 | stop_rule = 'with-merges' | ||
255 | 672 | view_revisions = branch.iter_merge_sorted_revisions( | 696 | view_revisions = branch.iter_merge_sorted_revisions( |
256 | 673 | start_revision_id=end_rev_id, stop_revision_id=start_rev_id, | 697 | start_revision_id=end_rev_id, stop_revision_id=start_rev_id, |
258 | 674 | stop_rule="with-merges") | 698 | stop_rule=stop_rule) |
259 | 675 | if not rebase_initial_depths: | 699 | if not rebase_initial_depths: |
260 | 676 | for (rev_id, merge_depth, revno, end_of_merge | 700 | for (rev_id, merge_depth, revno, end_of_merge |
261 | 677 | ) in view_revisions: | 701 | ) in view_revisions: |
262 | 678 | 702 | ||
263 | === modified file 'bzrlib/tests/blackbox/test_log.py' | |||
264 | --- bzrlib/tests/blackbox/test_log.py 2010-03-24 14:15:01 +0000 | |||
265 | +++ bzrlib/tests/blackbox/test_log.py 2010-04-28 07:18:51 +0000 | |||
266 | @@ -365,6 +365,18 @@ | |||
267 | 365 | 'options are "utc", "original", "local".'], | 365 | 'options are "utc", "original", "local".'], |
268 | 366 | ['log', '--timezone', 'foo']) | 366 | ['log', '--timezone', 'foo']) |
269 | 367 | 367 | ||
270 | 368 | def test_log_exclude_ancestry_no_range(self): | ||
271 | 369 | self.make_linear_branch() | ||
272 | 370 | self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry' | ||
273 | 371 | ' requires -r with two revisions'], | ||
274 | 372 | ['log', '--exclude-common-ancestry']) | ||
275 | 373 | |||
276 | 374 | def test_log_exclude_ancestry_single_revision(self): | ||
277 | 375 | self.make_merged_branch() | ||
278 | 376 | self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry' | ||
279 | 377 | ' requires two different revisions'], | ||
280 | 378 | ['log', '--exclude-common-ancestry', | ||
281 | 379 | '-r1.1.1..1.1.1']) | ||
282 | 368 | 380 | ||
283 | 369 | class TestLogTags(TestLog): | 381 | class TestLogTags(TestLog): |
284 | 370 | 382 | ||
285 | 371 | 383 | ||
286 | === modified file 'bzrlib/tests/per_branch/test_iter_merge_sorted_revisions.py' | |||
287 | --- bzrlib/tests/per_branch/test_iter_merge_sorted_revisions.py 2010-04-02 15:05:24 +0000 | |||
288 | +++ bzrlib/tests/per_branch/test_iter_merge_sorted_revisions.py 2010-04-28 07:18:51 +0000 | |||
289 | @@ -229,6 +229,37 @@ | |||
290 | 229 | self.addCleanup(br.unlock) | 229 | self.addCleanup(br.unlock) |
291 | 230 | return br | 230 | return br |
292 | 231 | 231 | ||
293 | 232 | def make_branch_with_alternate_ancestries(self, relpath='.'): | ||
294 | 233 | # See test_merge_sorted_exclude_ancestry below for the difference with | ||
295 | 234 | # bt.test_log.TestLogExcludeAncestry. | ||
296 | 235 | # make_branch_with_alternate_ancestries and | ||
297 | 236 | # test_merge_sorted_exclude_ancestry | ||
298 | 237 | # See the FIXME in assertLogRevnos there too. | ||
299 | 238 | builder = self.make_branch_builder(relpath) | ||
300 | 239 | # 1 | ||
301 | 240 | # |\ | ||
302 | 241 | # | 1.1.1 | ||
303 | 242 | # | /| \ | ||
304 | 243 | # 2 | | | ||
305 | 244 | # | | 1.2.1 | ||
306 | 245 | # | | / | ||
307 | 246 | # | 1.1.2 | ||
308 | 247 | # | / | ||
309 | 248 | # 3 | ||
310 | 249 | builder.start_series() | ||
311 | 250 | builder.build_snapshot('1', None, [ | ||
312 | 251 | ('add', ('', 'TREE_ROOT', 'directory', '')),]) | ||
313 | 252 | builder.build_snapshot('1.1.1', ['1'], []) | ||
314 | 253 | builder.build_snapshot('2', ['1', '1.1.1'], []) | ||
315 | 254 | builder.build_snapshot('1.2.1', ['1.1.1'], []) | ||
316 | 255 | builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], []) | ||
317 | 256 | builder.build_snapshot('3', ['2', '1.1.2'], []) | ||
318 | 257 | builder.finish_series() | ||
319 | 258 | br = builder.get_branch() | ||
320 | 259 | br.lock_read() | ||
321 | 260 | self.addCleanup(br.unlock) | ||
322 | 261 | return br | ||
323 | 262 | |||
324 | 232 | def assertIterRevids(self, expected, branch, *args, **kwargs): | 263 | def assertIterRevids(self, expected, branch, *args, **kwargs): |
325 | 233 | # We don't care about depths and revnos here, only about returning the | 264 | # We don't care about depths and revnos here, only about returning the |
326 | 234 | # right revids. | 265 | # right revids. |
327 | @@ -259,3 +290,16 @@ | |||
328 | 259 | self.assertIterRevids(['2.2.1', '2.1.1', '2', '1'], | 290 | self.assertIterRevids(['2.2.1', '2.1.1', '2', '1'], |
329 | 260 | branch, start_revision_id='2.2.1', | 291 | branch, start_revision_id='2.2.1', |
330 | 261 | stop_rule='with-merges') | 292 | stop_rule='with-merges') |
331 | 293 | |||
332 | 294 | def test_merge_sorted_exclude_ancestry(self): | ||
333 | 295 | branch = self.make_branch_with_alternate_ancestries() | ||
334 | 296 | self.assertIterRevids(['3', '1.1.2', '1.2.1', '2', '1.1.1', '1'], | ||
335 | 297 | branch) | ||
336 | 298 | # '2' is not part of the ancestry even if merge_sort order will make it | ||
337 | 299 | # appear before 1.1.1 | ||
338 | 300 | self.assertIterRevids(['1.1.2', '1.2.1'], | ||
339 | 301 | branch, | ||
340 | 302 | stop_rule='with-merges-without-common-ancestry', | ||
341 | 303 | start_revision_id='1.1.2', | ||
342 | 304 | stop_revision_id='1.1.1') | ||
343 | 305 | |||
344 | 262 | 306 | ||
345 | === modified file 'bzrlib/tests/per_repository_reference/__init__.py' | |||
346 | --- bzrlib/tests/per_repository_reference/__init__.py 2010-04-16 06:51:59 +0000 | |||
347 | +++ bzrlib/tests/per_repository_reference/__init__.py 2010-04-28 07:18:51 +0000 | |||
348 | @@ -1,4 +1,4 @@ | |||
350 | 1 | # Copyright (C) 2008, 2009 Canonical Ltd | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd |
351 | 2 | # | 2 | # |
352 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
353 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
354 | 5 | 5 | ||
355 | === modified file 'bzrlib/tests/test_log.py' | |||
356 | --- bzrlib/tests/test_log.py 2010-03-25 08:14:04 +0000 | |||
357 | +++ bzrlib/tests/test_log.py 2010-04-28 07:18:51 +0000 | |||
358 | @@ -18,6 +18,7 @@ | |||
359 | 18 | from cStringIO import StringIO | 18 | from cStringIO import StringIO |
360 | 19 | 19 | ||
361 | 20 | from bzrlib import ( | 20 | from bzrlib import ( |
362 | 21 | branchbuilder, | ||
363 | 21 | errors, | 22 | errors, |
364 | 22 | log, | 23 | log, |
365 | 23 | registry, | 24 | registry, |
366 | @@ -1541,3 +1542,62 @@ | |||
367 | 1541 | 1542 | ||
368 | 1542 | def test_bugs_handler_present(self): | 1543 | def test_bugs_handler_present(self): |
369 | 1543 | self.properties_handler_registry.get('bugs_properties_handler') | 1544 | self.properties_handler_registry.get('bugs_properties_handler') |
370 | 1545 | |||
371 | 1546 | class TestLogExcludeAncestry(tests.TestCaseWithTransport): | ||
372 | 1547 | |||
373 | 1548 | def make_branch_with_alternate_ancestries(self, relpath='.'): | ||
374 | 1549 | # See test_merge_sorted_exclude_ancestry below for the difference with | ||
375 | 1550 | # bt.per_branch.test_iter_merge_sorted_revision. | ||
376 | 1551 | # TestIterMergeSortedRevisionsBushyGraph. | ||
377 | 1552 | # make_branch_with_alternate_ancestries | ||
378 | 1553 | # and test_merge_sorted_exclude_ancestry | ||
379 | 1554 | # See the FIXME in assertLogRevnos too. | ||
380 | 1555 | builder = branchbuilder.BranchBuilder(self.get_transport(relpath)) | ||
381 | 1556 | # 1 | ||
382 | 1557 | # |\ | ||
383 | 1558 | # 2 \ | ||
384 | 1559 | # | | | ||
385 | 1560 | # | 1.1.1 | ||
386 | 1561 | # | | \ | ||
387 | 1562 | # | | 1.2.1 | ||
388 | 1563 | # | | / | ||
389 | 1564 | # | 1.1.2 | ||
390 | 1565 | # | / | ||
391 | 1566 | # 3 | ||
392 | 1567 | builder.start_series() | ||
393 | 1568 | builder.build_snapshot('1', None, [ | ||
394 | 1569 | ('add', ('', 'TREE_ROOT', 'directory', '')),]) | ||
395 | 1570 | builder.build_snapshot('1.1.1', ['1'], []) | ||
396 | 1571 | builder.build_snapshot('2', ['1'], []) | ||
397 | 1572 | builder.build_snapshot('1.2.1', ['1.1.1'], []) | ||
398 | 1573 | builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], []) | ||
399 | 1574 | builder.build_snapshot('3', ['2', '1.1.2'], []) | ||
400 | 1575 | builder.finish_series() | ||
401 | 1576 | br = builder.get_branch() | ||
402 | 1577 | br.lock_read() | ||
403 | 1578 | self.addCleanup(br.unlock) | ||
404 | 1579 | return br | ||
405 | 1580 | |||
406 | 1581 | def assertLogRevnos(self, expected_revnos, b, start, end, | ||
407 | 1582 | exclude_common_ancestry): | ||
408 | 1583 | # FIXME: the layering in log makes it hard to test intermediate levels, | ||
409 | 1584 | # I wish adding filters with their parameters were easier... | ||
410 | 1585 | # -- vila 20100413 | ||
411 | 1586 | iter_revs = log._calc_view_revisions( | ||
412 | 1587 | b, start, end, direction='reverse', | ||
413 | 1588 | generate_merge_revisions=True, | ||
414 | 1589 | exclude_common_ancestry=exclude_common_ancestry) | ||
415 | 1590 | self.assertEqual(expected_revnos, | ||
416 | 1591 | [revid for revid, revno, depth in iter_revs]) | ||
417 | 1592 | |||
418 | 1593 | def test_merge_sorted_exclude_ancestry(self): | ||
419 | 1594 | b = self.make_branch_with_alternate_ancestries() | ||
420 | 1595 | self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'], | ||
421 | 1596 | b, '1', '3', False) | ||
422 | 1597 | # '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so | ||
423 | 1598 | # it should be mentioned even if merge_sort order will make it appear | ||
424 | 1599 | # after 1.1.1 | ||
425 | 1600 | self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'], | ||
426 | 1601 | b, '1.1.1', '3', True) | ||
427 | 1602 | |||
428 | 1603 |
This seems like something other commands than log will want; perhaps we
should tackle it using an algreba, like git does?