Rev 3380: merge in the status uses find_differences code, though it exposes that find_differences is slower than whole-ancestry work in http://bzr.arbash-meinel.com/branches/bzr/1.4-dev/find_differences

John Arbash Meinel john at arbash-meinel.com
Tue Apr 22 21:10:47 BST 2008


At http://bzr.arbash-meinel.com/branches/bzr/1.4-dev/find_differences

------------------------------------------------------------
revno: 3380
revision-id: john at arbash-meinel.com-20080422200449-mknvyea4ndx8uxw0
parent: john at arbash-meinel.com-20080422185952-nxeuwil7ykk5krna
parent: john at arbash-meinel.com-20071204195523-3tong8h1fwvx0xgy
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: find_differences
timestamp: Tue 2008-04-22 15:04:49 -0500
message:
  merge in the status uses find_differences code, though it exposes that find_differences is slower than whole-ancestry work
modified:
  bzrlib/status.py               status.py-20050505062338-431bfa63ec9b19e6
  bzrlib/tests/test_status.py    test_status.py-20060516190614-fbf6432e4a6e8aa5
    ------------------------------------------------------------
    revno: 3074.3.2
    revision-id: john at arbash-meinel.com-20071204195523-3tong8h1fwvx0xgy
    parent: john at arbash-meinel.com-20071204190203-vh8zea8nt8ravz0z
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_after_merge_172657
    timestamp: Tue 2007-12-04 13:55:23 -0600
    message:
      update the ignore list in one-go.
      Add a test that ancestors that are already listed are properly hidden.
    modified:
      bzrlib/status.py               status.py-20050505062338-431bfa63ec9b19e6
      bzrlib/tests/test_status.py    test_status.py-20060516190614-fbf6432e4a6e8aa5
    ------------------------------------------------------------
    revno: 3074.3.1
    revision-id: john at arbash-meinel.com-20071204190203-vh8zea8nt8ravz0z
    parent: pqm at pqm.ubuntu.com-20071204035213-2kot5u403spjchen
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_after_merge_172657
    timestamp: Tue 2007-12-04 13:02:03 -0600
    message:
      Fix bug #172657, use Graph.find_difference() rather than ancestry set operations.
      When showing pending merges, we can figure out what is new, without having to load the entire
      ancestry and do a set difference.
      This also puts us really close to displaying them properly nested, rather than
      using a single depth for all children of a merge.
    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	2008-04-03 02:22:55 +0000
+++ b/bzrlib/status.py	2008-04-22 20:04:49 +0000
@@ -18,7 +18,10 @@
 
 from bzrlib import (
     delta as _mod_delta,
+    osutils,
     tree,
+    tsort,
+    revision as _mod_revision,
     )
 from bzrlib.diff import _raise_if_nonexistent
 import bzrlib.errors as errors
@@ -153,25 +156,48 @@
     last_revision = parents[0]
     if not short:
         to_file.write('pending merges:\n')
-    if last_revision is not None:
-        try:
-            ignore = set(branch.repository.get_ancestry(last_revision,
-                                                        topo_sorted=False))
-        except errors.NoSuchRevision:
-            # the last revision is a ghost : assume everything is new 
-            # except for it
-            ignore = set([None, last_revision])
-    else:
-        ignore = set([None])
-    # TODO: this could be improved using merge_sorted - we'd get the same 
-    # output rather than one level of indent.
+    ignore = set([None, last_revision, _mod_revision.NULL_REVISION])
+    graph = branch.repository.get_graph()
     for merge in pending:
-        ignore.add(merge)
+        # Find all of the revisions in the merge source, which are not in the
+        # last committed revision.
+        # We don't care about last_extra
+        last_extra, merge_extra = graph.find_difference(last_revision, merge)
+        # 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
+        # dangling references, though, so clean up the graph to point to only
+        # present nodes.
+        merge_extra.discard(_mod_revision.NULL_REVISION)
+        merged_graph = {}
+        for merge, 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] = []
+            else:
+                merged_graph[merge] = [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()
         try:
             from bzrlib.osutils import terminal_width
             width = terminal_width() - 1    # we need one extra space to avoid
                                             # extra blank lines
             m_revision = branch.repository.get_revision(merge)
+            revisions = dict((rev.revision_id, rev) for rev in
+                             branch.repository.get_revisions(merge_extra))
+        except errors.NoSuchRevision:
+            # If we are missing a revision, just print out the revision id
+            if short:
+                prefix = 'P  '
+            else:
+                prefix = ' '
+            to_file.write(prefix + ' ' + merge)
+            to_file.write('\n')
+        else:
+            rev_id_iterator = sorter.iter_topo_order()
+            num, first, depth, eom = rev_id_iterator.next()
+            assert first == merge
+            m_revision = revisions[merge]
             if short:
                 prefix = 'P   '
             else:
@@ -179,14 +205,10 @@
             to_file.write(prefix)
             to_file.write(line_log(m_revision, width - len(prefix)))
             to_file.write('\n')
-            inner_merges = branch.repository.get_ancestry(merge)
-            assert inner_merges[0] is None
-            inner_merges.pop(0)
-            inner_merges.reverse()
-            for mmerge in inner_merges:
+            for num, mmerge, depth, eom in rev_id_iterator:
                 if mmerge in ignore:
                     continue
-                mm_revision = branch.repository.get_revision(mmerge)
+                mm_revision = revisions[mmerge]
                 if short:
                     prefix = 'P.   '
                 else:
@@ -194,11 +216,4 @@
                 to_file.write(prefix)
                 to_file.write(line_log(mm_revision, width - len(prefix)))
                 to_file.write('\n')
-                ignore.add(mmerge)
-        except errors.NoSuchRevision:
-            if short:
-                prefix = 'P  '
-            else:
-                prefix = ' '
-            to_file.write(prefix + ' ' + merge)
-            to_file.write('\n')
+        ignore.update(merge_extra)

=== modified file 'bzrlib/tests/test_status.py'
--- a/bzrlib/tests/test_status.py	2008-03-20 17:14:01 +0000
+++ b/bzrlib/tests/test_status.py	2008-04-22 20:04:49 +0000
@@ -17,6 +17,7 @@
 
 from StringIO import StringIO
 
+from bzrlib import config
 from bzrlib.revisionspec import RevisionSpec
 from bzrlib.status import show_pending_merges, show_tree_status
 from bzrlib.tests import TestCaseWithTransport
@@ -34,9 +35,39 @@
         # do a merge
         tree2.merge_from_branch(tree.branch)
         output = StringIO()
-        show_pending_merges(tree2, output)
+        tree2.lock_read()
+        try:
+            show_pending_merges(tree2, output)
+        finally:
+            tree2.unlock()
         self.assertContainsRe(output.getvalue(), 'empty commit')
 
+    def test_multiple_pending(self):
+        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)
+        output = StringIO()
+        tree.lock_read()
+        try:
+            show_pending_merges(tree, output)
+        finally:
+            tree.unlock()
+        # Even though 2b is merged by 3c also, it should only be displayed
+        # the first time it shows u.
+        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 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