Rev 59: Both regular import and _update_ancestry are now able to record ghosts. in http://bzr.arbash-meinel.com/plugins/history_db

John Arbash Meinel john at arbash-meinel.com
Wed Apr 7 21:06:36 BST 2010


At http://bzr.arbash-meinel.com/plugins/history_db

------------------------------------------------------------
revno: 59
revision-id: john at arbash-meinel.com-20100407200620-bg6ehrbisjpl2l3e
parent: john at arbash-meinel.com-20100407195110-7ezlf79dwi69npp3
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: history_db
timestamp: Wed 2010-04-07 15:06:20 -0500
message:
  Both regular import and _update_ancestry are now able to record ghosts.
-------------- next part --------------
=== modified file 'history_db.py'
--- a/history_db.py	2010-04-07 19:51:10 +0000
+++ b/history_db.py	2010-04-07 20:06:20 +0000
@@ -264,11 +264,8 @@
             # search
             pmap = self._branch.repository.get_parent_map([rev_id])
             parent_map.update(pmap)
-            parent_ids = pmap.get(rev_id, ())
-            if not parent_ids or parent_ids == NULL_PARENTS:
-                # XXX: We should handle 'not parent_ids' differently, because
-                #      that means they are a ghost. Currently the table cannot
-                #      distinguish between a ghost and a root revision.
+            parent_ids = pmap.get(rev_id, None)
+            if parent_ids is None or parent_ids == NULL_PARENTS:
                 # We can insert this rev directly, because we know its gdfo,
                 # as it has no parents.
                 parent_map[rev_id] = ()
@@ -276,6 +273,12 @@
                                      " VALUES (?, ?)", (rev_id, 1))
                 # Wrap around to populate known quickly
                 needed.append(rev_id)
+                if parent_ids is None:
+                    # This is a ghost, add it to the table
+                    self._cursor.execute("INSERT INTO ghost (db_id)"
+                                         " SELECT db_id FROM revision"
+                                         "  WHERE revision_id = ?",
+                                         (rev_id,))
                 continue
             for parent_id in pmap[rev_id]:
                 if parent_id not in known:

=== modified file 'schema.py'
--- a/schema.py	2010-04-04 16:53:08 +0000
+++ b/schema.py	2010-04-07 20:06:20 +0000
@@ -56,6 +56,15 @@
 _create_statements.append(parent_parent_index)
 # So we can find the *children* of a given node
 
+
+ghost_t = """
+CREATE TABLE ghost (
+    db_id INTEGER PRIMARY KEY REFERENCES revision
+);
+"""
+_create_statements.append(ghost_t)
+
+
 dotted_revno_t = """
 CREATE TABLE dotted_revno (
     tip_revision INTEGER REFERENCES revision NOT NULL,
@@ -188,12 +197,24 @@
             local_missing.discard(rev_id)
         missing.update(local_missing)
     if missing:
+        ghosts = set()
         def get_gdfo(rev_id):
-            return graph._nodes[(rev_id,)].gdfo
+            node = graph._nodes[(rev_id,)]
+            if node.gdfo == 1:
+                # First rev, see if this is actually a ghost
+                if node.parent_keys is None:
+                    ghosts.add(rev_id)
+            return node.gdfo
         cursor.executemany('INSERT INTO revision (revision_id, gdfo)'
                            ' VALUES (?, ?)',
                            [(m, get_gdfo(m)) for m in missing])
         ensure_revisions(cursor, missing, rev_id_to_db_id, graph=graph)
+        if ghosts:
+            # TODO: We could turn this into a "revision_id IN ()", instead...
+            cursor.executemany("INSERT INTO ghost (db_id)"
+                               " SELECT db_id FROM revision"
+                               "  WHERE revision_id = ?",
+                               [(g,) for g in ghosts])
 
 
 def create_dotted_revno(cursor, tip_revision, merged_revision, revno,

=== modified file 'test_importer.py'
--- a/test_importer.py	2010-04-07 19:51:10 +0000
+++ b/test_importer.py	2010-04-07 20:06:20 +0000
@@ -120,8 +120,27 @@
                     }
         return MockBranch(ancestry, 'O')
 
+    def make_branch_with_ghosts(self):
+        # Simple ancestry:
+        # A
+        # |
+        # B G?
+        # |/|
+        # C D
+        # |/
+        # E
+        #
+        # Both C and D refer to 'G' but that revision isn't actually present
+        ancestry = {'A': (),
+                    'B': ('A',),
+                    'C': ('B', 'G'),
+                    'D': ('G',),
+                    'E': ('C', 'D'),
+                   }
+        return MockBranch(ancestry, 'E')
+
     def grab_interesting_ids(self, rev_id_to_db_id):
-        for rev_id in 'ABCDEFGHIJKLMNO':
+        for rev_id in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
             setattr(self, '%s_id' % (rev_id,), rev_id_to_db_id.get(rev_id))
 
     def test_build(self):
@@ -138,8 +157,7 @@
         b = self.make_interesting_branch()
         importer = history_db.Importer(':memory:', b, incremental=False)
         importer.do_import()
-        db_conn = importer._db_conn
-        cur = db_conn.cursor()
+        cur = importer._db_conn.cursor()
         revs = cur.execute("SELECT revision_id, db_id, gdfo"
                            "  FROM revision").fetchall()
         # Track the db_ids that are assigned
@@ -171,6 +189,24 @@
             val.sort()
         self.assertEqual(expected, actual)
 
+    def test_import_records_ghosts(self):
+        b = self.make_branch_with_ghosts()
+        importer = history_db.Importer(':memory:', b, incremental=False)
+        importer.do_import()
+        cur = importer._db_conn.cursor()
+        res = cur.execute("SELECT revision_id"
+                          "  FROM ghost NATURAL JOIN revision")
+        self.assertEqual(['G'], [r[0] for r in res])
+
+    def test__update_ancestry_records_ghosts(self):
+        b = self.make_branch_with_ghosts()
+        importer = history_db.Importer(':memory:', b, incremental=False)
+        importer._update_ancestry('G')
+        cur = importer._db_conn.cursor()
+        res = cur.execute("SELECT revision_id"
+                          "  FROM ghost NATURAL JOIN revision")
+        self.assertEqual(['G'], [r[0] for r in res])
+
     def test__update_ancestry_from_scratch(self):
         b = self.make_interesting_branch()
         importer = history_db.Importer(':memory:', b, incremental=False)



More information about the bazaar-commits mailing list