Rev 3077: Implement a detection and workaround for when find_ancestry gives bad results. in http://bzr.arbash-meinel.com/branches/bzr/0.93-dev/status_after_merge_172657
John Arbash Meinel
john at arbash-meinel.com
Fri Dec 7 21:22:49 GMT 2007
At http://bzr.arbash-meinel.com/branches/bzr/0.93-dev/status_after_merge_172657
------------------------------------------------------------
revno: 3077
revision-id:john at arbash-meinel.com-20071207212220-qnh95jk65lc2f0xt
parent: john at arbash-meinel.com-20071204195523-3tong8h1fwvx0xgy
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: status_after_merge_172657
timestamp: Fri 2007-12-07 15:22:20 -0600
message:
Implement a detection and workaround for when find_ancestry gives bad results.
modified:
bzrlib/status.py status.py-20050505062338-431bfa63ec9b19e6
bzrlib/tests/test_status.py test_status.py-20060516190614-fbf6432e4a6e8aa5
-------------- next part --------------
=== modified file 'bzrlib/status.py'
--- a/bzrlib/status.py 2007-12-04 19:55:23 +0000
+++ b/bzrlib/status.py 2007-12-07 21:22:20 +0000
@@ -19,6 +19,7 @@
from bzrlib import (
delta as _mod_delta,
osutils,
+ trace,
tree,
tsort,
revision as _mod_revision,
@@ -157,11 +158,40 @@
to_file.write('pending merges:\n')
ignore = set([None, last_revision, _mod_revision.NULL_REVISION])
graph = branch.repository.get_graph()
+ last_ancestry = None
for merge in pending:
# Find all of the revisions in the merge source, which are not in the
# last committed revision.
# We don't care about last_extra
+ # XXX: find_difference is known incomplete when there are history
+ # shortcuts. This generally manifests itself by having
+ # NULL_REVISION show up in the "uncommon" revisions for one
+ # side.
last_extra, merge_extra = graph.find_difference(last_revision, merge)
+ if (_mod_revision.NULL_REVISION in last_extra
+ or _mod_revision.NULL_REVISION in merge_extra):
+ # Fall back to the slow method. Consider warning the user rather
+ # than just degrading performance silently
+ trace.mutter('History shortcut detected, using slow algorithm for'
+ ' pending revision detection.'
+ ' revision ids:\n %s\n %s', last_revision, merge)
+ if last_ancestry is None:
+ try:
+ last_ancestry = set(branch.repository.get_ancestry(
+ last_revision, topo_sorted=False))
+ except errors.NoSuchRevision: # ghost last-revision
+ last_ancestry = set([last_revision])
+ # repo.get_ancestry always includes None in the list, for some
+ # historical reason
+ last_ancestry.discard(None)
+ try:
+ merge_extra = set(branch.repository.get_ancestry(merge,
+ topo_sorted=False))
+ except errors.NoSuchRevision: # Ghost merge
+ merge_extra = set([merge])
+ merge_extra.difference_update(last_ancestry)
+ merge_extra.discard(None)
+
# Now that we have the revisions, we need to sort them to get a proper
# listing. We want to sort in reverse topological order (which
# MergeSorter gives us). MergeSorter requires that there are no
@@ -169,11 +199,11 @@
# present nodes.
merge_extra.discard(_mod_revision.NULL_REVISION)
merged_graph = {}
- for merge, parents in zip(merge_extra, graph.get_parents(merge_extra)):
+ for extra, parents in zip(merge_extra, graph.get_parents(merge_extra)):
if parents is None: # The revision does not exist in the repository
- merged_graph[merge] = []
+ merged_graph[extra] = []
else:
- merged_graph[merge] = [p for p in parents if p in merge_extra]
+ merged_graph[extra] = [p for p in parents if p in merge_extra]
sorter = tsort.MergeSorter(merged_graph, merge)
# Get a handle to all of the revisions we will need
width = osutils.terminal_width()
@@ -191,7 +221,9 @@
else:
rev_id_iterator = sorter.iter_topo_order()
num, first, depth, eom = rev_id_iterator.next()
- assert first == merge
+ assert first == merge, ("The sorted tip revision is incorrect\n"
+ "expected: %s\nactual: %s\n"
+ % (merge, first))
m_revision = revisions[merge]
if short:
prefix = 'P '
=== modified file 'bzrlib/tests/test_status.py'
--- a/bzrlib/tests/test_status.py 2007-12-04 19:55:23 +0000
+++ b/bzrlib/tests/test_status.py 2007-12-07 21:22:20 +0000
@@ -43,31 +43,71 @@
self.assertContainsRe(output.getvalue(), 'empty commit')
def test_multiple_pending(self):
+ """multiple pending merges shouldn't repeat revisions"""
config.GlobalConfig().set_user_option('email', 'Joe Foo <joe at foo.com>')
- tree = self.make_branch_and_tree('a')
- tree.commit('commit 1', timestamp=1196796819, timezone=0)
- tree2 = tree.bzrdir.clone('b').open_workingtree()
- tree.commit('commit 2', timestamp=1196796819, timezone=0)
- tree2.commit('commit 2b', timestamp=1196796819, timezone=0)
- tree3 = tree.bzrdir.clone('c').open_workingtree()
- tree2.commit('commit 3b', timestamp=1196796819, timezone=0)
- tree3.commit('commit 3c', timestamp=1196796819, timezone=0)
- tree.merge_from_branch(tree2.branch)
- tree.merge_from_branch(tree3.branch)
+ tree_a = self.make_branch_and_tree('a')
+ date_2007_12_04 = 1196790000 # A fixed timestamp
+ tree_a.commit('commit 1a', timestamp=date_2007_12_04, timezone=0)
+ tree_b = tree_a.bzrdir.clone('b').open_workingtree()
+ tree_a.commit('commit 2a', timestamp=date_2007_12_04, timezone=0)
+ tree_b.commit('commit 2b', timestamp=date_2007_12_04, timezone=0)
+ tree_c = tree_a.bzrdir.clone('c').open_workingtree()
+ tree_b.commit('commit 3b', timestamp=date_2007_12_04, timezone=0)
+ tree_c.commit('commit 3c', timestamp=date_2007_12_04, timezone=0)
+ tree_a.merge_from_branch(tree_b.branch)
+ tree_a.merge_from_branch(tree_c.branch)
output = StringIO()
- tree.lock_read()
+ tree_a.lock_read()
try:
- show_pending_merges(tree, output)
+ show_pending_merges(tree_a, output)
finally:
- tree.unlock()
+ tree_a.unlock()
# Even though 2b is merged by 3c also, it should only be displayed
- # the first time it shows u.
+ # the first time it appears.
self.assertEqual('pending merges:\n'
' Joe Foo 2007-12-04 commit 3b\n'
' Joe Foo 2007-12-04 commit 2b\n'
' Joe Foo 2007-12-04 commit 3c\n',
output.getvalue())
+ def test_history_shortcut(self):
+ """Fallback correctly when a history shortcut is detected."""
+ # See tests/test_graph.py for a picture of this ancestry
+ config.GlobalConfig().set_user_option('email', 'Joe Foo <joe at foo.com>')
+ date_2007_12_04 = 1196790000 # A fixed timestamp
+ tree1 = self.make_branch_and_tree('tree1')
+ tree1.commit('a', rev_id='rev-a')
+ tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
+ tree1.commit('b', rev_id='rev-b')
+ tree1.commit('c', rev_id='rev-c')
+ tree1.commit('d', rev_id='rev-d')
+ tree2.merge_from_branch(tree1.branch)
+ tree2.commit('f', rev_id='rev-f', timestamp=date_2007_12_04)
+ tree1.commit('e', rev_id='rev-e', timestamp=date_2007_12_04)
+ tree1.merge_from_branch(tree2.branch)
+
+ output = StringIO()
+ tree1.lock_read()
+ try:
+ show_pending_merges(tree1, to_file=output)
+ finally:
+ tree1.unlock()
+ self.assertEqual('pending merges:\n'
+ ' Joe Foo 2007-12-04 f\n',
+ output.getvalue())
+
+ # Neither direction should fail
+ tree2.merge_from_branch(tree1.branch)
+ output = StringIO()
+ tree2.lock_read()
+ try:
+ show_pending_merges(tree2, to_file=output)
+ finally:
+ tree2.unlock()
+ self.assertEqual('pending merges:\n'
+ ' Joe Foo 2007-12-04 e\n',
+ output.getvalue())
+
def tests_revision_to_revision(self):
"""doing a status between two revision trees should work."""
tree = self.make_branch_and_tree('.')
More information about the bazaar-commits
mailing list