[MERGE] Common dominator ancestor picker
John A Meinel
john at arbash-meinel.com
Fri Feb 17 23:14:34 GMT 2006
Aaron Bentley wrote:
> Hi all,
>
> I've implemented an ancestor picker based on the closest common
> dominator. This speeds up find-merge-base by a factor of 10 on the bzr
> source tree, and is likely to do even better as the bzr ancestry gets
> bigger.
>
> The old algorithm scaled linearly with the number of nodes in the
> ancestry. This algorithm scales linearly with the number of nodes
> between each revision and their closest common dominator. I invented it
> myself, so let me know if there's anything wrong with the theory.
>
> Not only is this algorithm faster, but it's also more correct, as it
> will handle criss-cross merges better.
>
> Monotone's is slightly fancier; they find the common merge that is a
> dominator of one. I don't think the difference will be significant, and
> we can always implement it later.
>
> Could someone give me a review? If approved, I'll stick it in my
> integration.
>
> Aaron
I don't have time to do a full review, but I found at least 1 bug.
------------------------------------------------------------------------
=== modified file 'bzrlib/revision.py'
--- bzrlib/revision.py
+++ bzrlib/revision.py
@@ -279,20 +279,6 @@
return root, ancestors, descendants, common
-def common_ancestor(revision_a, revision_b, revision_source):
- try:
- root, ancestors, descendants, common = \
- combined_graph(revision_a, revision_b, revision_source)
- except bzrlib.errors.NoCommonRoot:
- raise bzrlib.errors.NoCommonAncestor(revision_a, revision_b)
-
- distances = node_distances (descendants, ancestors, root)
- farthest = select_farthest(distances, common)
- if farthest is None or farthest == NULL_REVISION:
- raise bzrlib.errors.NoCommonAncestor(revision_a, revision_b)
- return farthest
-
-
class MultipleRevisionSources(object):
"""Proxy that looks in multiple branches for revisions."""
def __init__(self, *args):
@@ -350,3 +336,94 @@
next = best_ancestor(next)
path.reverse()
return path
+
+
+def dominators(revision_id, revision_source):
+ """Return the dominators of a revision.
+ This is based on the observation that in a DAG, the dominators of x
+ are x + common_dominators(parents(x)). This applies recursively,
of course.
+ """
+ while True:
+ yield revision_id
+ if revision_id is NULL_REVISION:
+ return
+ try:
+ revision = revision_source.get_revision(revision_id)
+ except NoSuchRevision:
+ return
+
+ if len(revision.parent_ids) == 0:
+ revision_id = NULL_REVISION
+ elif len(revision.parent_ids) == 1:
+ (revision_id,) = revision.parent_ids
+ else:
+ revision_id = common_dominator(*revision.parent_ids)
+
Here you aren't passing the 'revision_source' to the 'common_dominator'
algorithm.
Make sure you have test cases to exercise this code path.
John
=:->
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 249 bytes
Desc: OpenPGP digital signature
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20060217/4b9897ba/attachment.pgp
More information about the bazaar
mailing list