Rev 3516: Handle ghost revisions in the ancestry by falling back to whole-ancestry ops. in http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/lazy_revno

John Arbash Meinel john at arbash-meinel.com
Wed Aug 6 19:16:30 BST 2008


At http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/lazy_revno

------------------------------------------------------------
revno: 3516
revision-id: john at arbash-meinel.com-20080806181613-jf965wcz4u912oiv
parent: john at arbash-meinel.com-20080806175607-po8br94kwbhnywwc
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: lazy_revno
timestamp: Wed 2008-08-06 13:16:13 -0500
message:
  Handle ghost revisions in the ancestry by falling back to whole-ancestry ops.
  It is inefficient, but hey, it works.
-------------- next part --------------
=== modified file 'bzrlib/graph.py'
--- a/bzrlib/graph.py	2008-08-06 17:56:07 +0000
+++ b/bzrlib/graph.py	2008-08-06 18:16:13 +0000
@@ -1567,6 +1567,25 @@
         if node.dotted_revno is not None:
             return node.dotted_revno
 
+        if mainline_node is None:
+            # The revision requested does not have an ancestry which intersects
+            # ours. Unfortunately, there are 2 cases:
+            # 1) Different ancestry (like looking for a bzrtools revision in a
+            #    bzr branch)
+            # 2) Ghost, this is referenced as a parent of our ancestry, but not
+            #    as a child of our ancestry.
+            # To find ghosts, you have to start from its children and walk
+            # backwards, but we don't know where to look, so we would have to
+            # iterate the whole graph. And the numbering code doesn't like it
+            # much anyway. (We could cheat and use NULL_REVISION, which is what
+            # happens when you merge in another project.)
+            # Anyway, because we have to search a lot, for now we just punt,
+            # load the whole graph, and do a regular 'merge_sort' on the whole
+            # thing. Note that this is *very* inefficient when we already have
+            # a decent portion of the graph numbered
+            self._number_all()
+            return node.dotted_revno
+
         # Now, finish searching
         # TODO: Finish hooking this up
         interesting_ids = self._get_interesting_merged_ids(node, mainline_node)
@@ -1697,6 +1716,8 @@
         # direct information about the nodes
         # TODO: I probably don't want to do this here, but instead wait until
         #       later.
+        if mainline_node is None:
+            return None
         mainline_revno = mainline_node.mainline_revno
         next_node = node
         while next_node is not None:
@@ -2006,6 +2027,36 @@
             else:
                 node.dotted_revno = real_dotted_revno
 
+    def _number_all(self):
+        """We've decided to just fall back to merge_sort on the whole graph."""
+        # TODO: it would be nice to make use of the information we already
+        #       have, but it is easier to just switch purposes
+        g = Graph(self._parent_provider)
+        all_ancestors = dict(g.iter_ancestry([self._tip_revision_id]))
+        # Now filter out unreferenced nodes and NULL_REVISION because
+        # merge_sort doesn't like them
+        if revision.NULL_REVISION in all_ancestors:
+            del all_ancestors[revision.NULL_REVISION]
+        culled_parent_map = {}
+        for revision_id, parent_ids in all_ancestors.iteritems():
+            if parent_ids is None:
+                continue
+            culled_parent_ids = tuple([p for p in parent_ids
+                                          if p in all_ancestors])
+            culled_parent_map[revision_id] = culled_parent_ids
+        for (seq_num, rev_id, depth, dotted_revno,
+             end_of_merge) in tsort.merge_sort(culled_parent_map,
+                                               self._tip_revision_id,
+                                               None, generate_revno=True):
+            node = self._get_node(rev_id)
+            if node.dotted_revno is not None:
+                if dotted_revno != node.dotted_revno:
+                    print real_dotted_revno, node.dotted_revno
+                    import pdb; pdb.set_trace()
+            else:
+                node.dotted_revno = dotted_revno
+
+
 
 def collapse_linear_regions(parent_map):
     """Collapse regions of the graph that are 'linear'.

=== modified file 'bzrlib/tests/test_graph.py'
--- a/bzrlib/tests/test_graph.py	2008-08-06 17:56:07 +0000
+++ b/bzrlib/tests/test_graph.py	2008-08-06 18:16:13 +0000
@@ -1774,16 +1774,21 @@
         alt_with_ghost = dict(with_ghost)
         # assertAllDottedRevno expects NULL_REVISION to not be in the ancestry
         # map, as it adds another revision to everything.
-        # However, this only accidentally succeeds because we prune 'g' from
-        # the ancestry because it is a ghost. So it never gets numbered. If we
-        # fake it with 'g':(), then it fails because it cannot find a mainline.
+        # Node 'd' references 'g'. We have to insert it 'g' into the ancestry,
+        # so that our merge_sort test tries to number that revision
+        # specifically.
         alt_with_ghost.pop(NULL_REVISION)
+        alt_with_ghost['g'] = ()
         self.assertAllDottedRevno(alt_with_ghost, 'a')
         self.assertAllDottedRevno(alt_with_ghost, 'c')
 
     def test_get_dotted_revno_with_shortcut(self):
         self.assertAllDottedRevno(shortcut_extra_root, 'f')
 
+    def test_get_dotted_revno_not_in_ancestry(self):
+        mapper = self.make_mapper(ancestry_1, 'rev4')
+        self.assertIs(None, mapper.get_dotted_revno('not-an-ancestor'))
+
     def test__step_mainline_node(self):
         mapper = self.make_mapper(with_tail, 'l')
         node = mapper._get_node('l')



More information about the bazaar-commits mailing list