Rev 2660: Rough unfactored support for parentless KnitGraphIndexs. in http://people.ubuntu.com/~robertc/baz2.0/repository

Robert Collins robertc at robertcollins.net
Mon Jul 16 07:45:40 BST 2007


At http://people.ubuntu.com/~robertc/baz2.0/repository

------------------------------------------------------------
revno: 2660
revision-id: robertc at robertcollins.net-20070716064427-m35p0xd8m6qzh9rh
parent: robertc at robertcollins.net-20070715155254-fz24oh3lwg2jyys8
committer: Robert Collins <robertc at robertcollins.net>
branch nick: repository
timestamp: Mon 2007-07-16 16:44:27 +1000
message:
  Rough unfactored support for parentless KnitGraphIndexs.
modified:
  bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
  bzrlib/tests/test_knit.py      test_knit.py-20051212171302-95d4c00dd5f11f2b
=== modified file 'bzrlib/knit.py'
--- a/bzrlib/knit.py	2007-07-15 15:52:54 +0000
+++ b/bzrlib/knit.py	2007-07-16 06:44:27 +0000
@@ -1320,7 +1320,7 @@
 class KnitGraphIndex(object):
     """A knit index that builds on GraphIndex."""
 
-    def __init__(self, graph_index, deltas=False, add_callback=None):
+    def __init__(self, graph_index, deltas=False, parents=True, add_callback=None):
         """Construct a KnitGraphIndex on a graph_index.
 
         :param graph_index: An implementation of bzrlib.index.GraphIndex.
@@ -1328,21 +1328,43 @@
         :param add_callback: If not None, allow additions to the index and call
             this callback with a list of added GraphIndex nodes:
             [(node, value, node_refs), ...]
+        :param parents: If True, record knits parents, if not do not record 
+            parents.
         """
         self._graph_index = graph_index
         self._deltas = deltas
         self._add_callback = add_callback
+        self._parents = parents
+        if deltas and not parents:
+            raise KnitCorrupt(self, "Cannot do delta compression without "
+                "parent tracking.")
 
     def _get_entries(self, version_ids):
         """Get the entries for version_ids."""
-        return self._graph_index.iter_entries(version_ids)
+        if self._parents:
+            for node in self._graph_index.iter_entries(version_ids):
+                yield node
+        else:
+            # adapt parentless index to the rest of the code.
+            for node in self._graph_index.iter_entries(version_ids):
+                yield node[0], node[1], ()
 
     def _present_keys(self, version_ids):
         return set([
             node[0] for node in self._get_entries(version_ids)])
 
+    def _parentless_ancestry(self, versions):
+        """Honour the get_ancestry API for parentless knit indices."""
+        present_keys = self._present_keys(versions)
+        missing = set(versions).difference(present_keys)
+        if missing:
+            raise RevisionNotPresent(missing.pop(), self)
+        return list(present_keys)
+
     def get_ancestry(self, versions, topo_sorted=True):
         """See VersionedFile.get_ancestry."""
+        if not self._parents:
+            return self._parentless_ancestry(versions)
         # XXX: This will do len(history) index calls - perhaps
         # it should be altered to be a index core feature?
         # get a graph of all the mentioned versions:
@@ -1374,10 +1396,13 @@
 
     def get_ancestry_with_ghosts(self, versions):
         """See VersionedFile.get_ancestry."""
+        if not self._parents:
+            return self._parentless_ancestry(versions)
         # XXX: This will do len(history) index calls - perhaps
         # it should be altered to be a index core feature?
         # get a graph of all the mentioned versions:
         graph = {}
+        versions = set(versions)
         pending = set(versions)
         while pending:
             # get all pending nodes
@@ -1389,6 +1414,9 @@
                 # queue parents 
                 pending.update(graph[key])
             missing_versions = this_iteration.difference(graph)
+            missing_needed = versions.intersection(missing_versions)
+            if missing_needed:
+                raise RevisionNotPresent(missing_needed.pop(), self)
             for missing_version in missing_versions:
                 # add a key, no parents
                 graph[missing_version] = []
@@ -1398,6 +1426,8 @@
 
     def get_graph(self):
         """Return a list of the node:parents lists from this knit index."""
+        if not self._parents:
+            return [(key, ()) for key in self.get_versions()]
         return [(key, refs[0]) for (key, value, refs) in 
             self._graph_index.iter_all_entries()]
 
@@ -1451,12 +1481,16 @@
 
     def get_parents(self, version_id):
         """Return parents of specified version ignoring ghosts."""
+        if not self._parents:
+            return ()
         parents = self.get_parents_with_ghosts(version_id)
         present_parents = self._present_keys(parents)
         return [key for key in parents if key in present_parents]
 
     def get_parents_with_ghosts(self, version_id):
         """Return parents of specified version with ghosts."""
+        if not self._parents:
+            return ()
         return self._get_node(version_id)[2][0]
 
     def check_versions_present(self, version_ids):
@@ -1495,24 +1529,36 @@
             else:
                 value = ' '
             value += "%d %d" % (pos, size)
-            if self._deltas:
-                if 'line-delta' in options:
-                    node_refs = (tuple(parents), (parents[0],))
-                else:
-                    node_refs = (tuple(parents), ())
-            else:
+            if not self._deltas:
                 if 'line-delta' in options:
                     raise KnitCorrupt(self, "attempt to add line-delta in non-delta knit")
-                node_refs = (tuple(parents), )
+            if self._parents:
+                if self._deltas:
+                    if 'line-delta' in options:
+                        node_refs = (tuple(parents), (parents[0],))
+                    else:
+                        node_refs = (tuple(parents), ())
+                else:
+                    node_refs = (tuple(parents), )
+            else:
+                if parents:
+                    raise KnitCorrupt(self, "attempt to add node with parents "
+                        "in parentless index.")
+                node_refs = ()
             keys[version_id] = (value, node_refs)
         present_nodes = self._get_entries(keys)
         for (key, value, node_refs) in present_nodes:
             if (value, node_refs) != keys[key]:
-                raise KnitCorrupt(self, "inconsistent details in add_versions")
+                raise KnitCorrupt(self, "inconsistent details in add_versions"
+                    ": %s %s" % ((value, node_refs), keys[key]))
             del keys[key]
         result = []
-        for key, (value, node_refs) in keys.iteritems():
-            result.append((key, value, node_refs))
+        if self._parents:
+            for key, (value, node_refs) in keys.iteritems():
+                result.append((key, value, node_refs))
+        else:
+            for key, (value, node_refs) in keys.iteritems():
+                result.append((key, value))
         self._add_callback(result)
         
 

=== modified file 'bzrlib/tests/test_knit.py'
--- a/bzrlib/tests/test_knit.py	2007-07-15 15:52:54 +0000
+++ b/bzrlib/tests/test_knit.py	2007-07-16 06:44:27 +0000
@@ -1568,17 +1568,6 @@
         return KnitGraphIndex(combined_index, deltas=deltas,
             add_callback=add_callback)
 
-    def two_graph_index_no_ghosts(self):
-        # build a complex graph across several indices.
-        index1 = self.make_g_index('1', 1, [
-            ('tip', (['parent'], ), ''),
-            ('tail', ([], ), '')])
-        index2 = self.make_g_index('2', 1, [
-            ('parent', (['tail'], ), ''),
-            ('separate', ([], ), '')])
-        combined_index = CombinedGraphIndex([index1, index2])
-        return KnitGraphIndex(combined_index)
-
     def test_get_graph(self):
         index = self.two_graph_index()
         self.assertEqual(set([
@@ -1634,7 +1623,7 @@
              ['separate', 'ghost', 'tail', 'parent', 'tip'],
             ))
         # asking for a ghost makes it go boom.
-        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
+        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
 
     def test_num_versions(self):
         index = self.two_graph_index()
@@ -1800,3 +1789,229 @@
             [('tip', 'fulltext,no-eol', 0, 100, ['parent']),
              ('tip', 'no-eol,line-delta', 0, 100, ['parent'])])
         self.assertEqual([], self.caught_entries)
+
+
+class TestNoParentsGraphIndexKnit(KnitTests):
+    """Tests for knits using KnitGraphIndex with no parents."""
+
+    def make_g_index(self, name, ref_lists=0, nodes=[]):
+        builder = GraphIndexBuilder(ref_lists)
+        for node, references in nodes:
+            builder.add_node(node, references)
+        stream = builder.finish()
+        trans = self.get_transport()
+        trans.put_file(name, stream)
+        return GraphIndex(trans, name)
+
+    def test_parents_deltas_incompatible(self):
+        index = CombinedGraphIndex([])
+        self.assertRaises(errors.KnitError, KnitGraphIndex, index,
+            deltas=True, parents=False)
+
+    def two_graph_index(self, catch_adds=False):
+        """Build a two-graph index.
+
+        :param deltas: If true, use underlying indices with two node-ref
+            lists and 'parent' set to a delta-compressed against tail.
+        """
+        # put several versions in the index.
+        index1 = self.make_g_index('1', 0, [
+            ('tip', 'N0 100'),
+            ('tail', '')])
+        index2 = self.make_g_index('2', 0, [
+            ('parent', ' 100 78'),
+            ('separate', '')])
+        combined_index = CombinedGraphIndex([index1, index2])
+        if catch_adds:
+            self.combined_index = combined_index
+            self.caught_entries = []
+            add_callback = self.catch_add
+        else:
+            add_callback = None
+        return KnitGraphIndex(combined_index, parents=False,
+            add_callback=add_callback)
+
+    def test_get_graph(self):
+        index = self.two_graph_index()
+        self.assertEqual(set([
+            ('tip', ()),
+            ('tail', ()),
+            ('parent', ()),
+            ('separate', ()),
+            ]), set(index.get_graph()))
+
+    def test_get_ancestry(self):
+        # with no parents, ancestry is always just the key.
+        index = self.two_graph_index()
+        self.assertEqual([], index.get_ancestry([]))
+        self.assertEqual(['separate'], index.get_ancestry(['separate']))
+        self.assertEqual(['tail'], index.get_ancestry(['tail']))
+        self.assertEqual(['parent'], index.get_ancestry(['parent']))
+        self.assertEqual(['tip'], index.get_ancestry(['tip']))
+        self.assertTrue(index.get_ancestry(['tip', 'separate']) in
+            (['tip', 'separate'],
+             ['separate', 'tip'],
+            ))
+        # asking for a ghost makes it go boom.
+        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
+
+    def test_get_ancestry_with_ghosts(self):
+        index = self.two_graph_index()
+        self.assertEqual([], index.get_ancestry_with_ghosts([]))
+        self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
+        self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
+        self.assertEqual(['parent'], index.get_ancestry_with_ghosts(['parent']))
+        self.assertEqual(['tip'], index.get_ancestry_with_ghosts(['tip']))
+        self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
+            (['tip', 'separate'],
+             ['separate', 'tip'],
+            ))
+        # asking for a ghost makes it go boom.
+        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
+
+    def test_num_versions(self):
+        index = self.two_graph_index()
+        self.assertEqual(4, index.num_versions())
+
+    def test_get_versions(self):
+        index = self.two_graph_index()
+        self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
+            set(index.get_versions()))
+
+    def test_has_version(self):
+        index = self.two_graph_index()
+        self.assertTrue(index.has_version('tail'))
+        self.assertFalse(index.has_version('ghost'))
+
+    def test_get_position(self):
+        index = self.two_graph_index()
+        self.assertEqual((0, 100), index.get_position('tip'))
+        self.assertEqual((100, 78), index.get_position('parent'))
+
+    def test_get_method(self):
+        index = self.two_graph_index()
+        self.assertEqual('fulltext', index.get_method('tip'))
+        self.assertEqual('fulltext', index.get_options('parent'))
+
+    def test_get_options(self):
+        index = self.two_graph_index()
+        self.assertEqual('fulltext,no-eol', index.get_options('tip'))
+        self.assertEqual('fulltext', index.get_options('parent'))
+
+    def test_get_parents(self):
+        index = self.two_graph_index()
+        self.assertEqual((), index.get_parents('parent'))
+
+    def test_get_parents_with_ghosts(self):
+        index = self.two_graph_index()
+        self.assertEqual((), index.get_parents_with_ghosts('parent'))
+
+    def test_check_versions_present(self):
+        index = self.two_graph_index()
+        self.assertRaises(RevisionNotPresent, index.check_versions_present,
+            ['missing'])
+        self.assertRaises(RevisionNotPresent, index.check_versions_present,
+            ['tail', 'missing'])
+        index.check_versions_present(['tail', 'separate'])
+
+    def catch_add(self, entries):
+        self.caught_entries.append(entries)
+
+    def test_add_no_callback_errors(self):
+        index = self.two_graph_index()
+        self.assertRaises(errors.ReadOnlyError, index.add_version,
+            'new', 'fulltext,no-eol', 50, 60, ['separate'])
+
+    def test_add_version_smoke(self):
+        index = self.two_graph_index(catch_adds=True)
+        index.add_version('new', 'fulltext,no-eol', 50, 60, [])
+        self.assertEqual([[('new', 'N50 60')]],
+            self.caught_entries)
+
+    def test_add_version_delta_not_delta_index(self):
+        index = self.two_graph_index(catch_adds=True)
+        self.assertRaises(errors.KnitCorrupt, index.add_version,
+            'new', 'no-eol,line-delta', 0, 100, [])
+        self.assertEqual([], self.caught_entries)
+
+    def test_add_version_same_dup(self):
+        index = self.two_graph_index(catch_adds=True)
+        # options can be spelt two different ways
+        index.add_version('tip', 'fulltext,no-eol', 0, 100, [])
+        index.add_version('tip', 'no-eol,fulltext', 0, 100, [])
+        # but neither should have added data.
+        self.assertEqual([[], []], self.caught_entries)
+        
+    def test_add_version_different_dup(self):
+        index = self.two_graph_index(catch_adds=True)
+        # change options
+        self.assertRaises(errors.KnitCorrupt, index.add_version,
+            'tip', 'no-eol,line-delta', 0, 100, [])
+        self.assertRaises(errors.KnitCorrupt, index.add_version,
+            'tip', 'line-delta,no-eol', 0, 100, [])
+        self.assertRaises(errors.KnitCorrupt, index.add_version,
+            'tip', 'fulltext', 0, 100, [])
+        # position/length
+        self.assertRaises(errors.KnitCorrupt, index.add_version,
+            'tip', 'fulltext,no-eol', 50, 100, [])
+        self.assertRaises(errors.KnitCorrupt, index.add_version,
+            'tip', 'fulltext,no-eol', 0, 1000, [])
+        # parents
+        self.assertRaises(errors.KnitCorrupt, index.add_version,
+            'tip', 'fulltext,no-eol', 0, 100, ['parent'])
+        self.assertEqual([], self.caught_entries)
+        
+    def test_add_versions(self):
+        index = self.two_graph_index(catch_adds=True)
+        index.add_versions([
+                ('new', 'fulltext,no-eol', 50, 60, []),
+                ('new2', 'fulltext', 0, 6, []),
+                ])
+        self.assertEqual([('new', 'N50 60'), ('new2', ' 0 6')],
+            sorted(self.caught_entries[0]))
+        self.assertEqual(1, len(self.caught_entries))
+
+    def test_add_versions_delta_not_delta_index(self):
+        index = self.two_graph_index(catch_adds=True)
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('new', 'no-eol,line-delta', 0, 100, ['parent'])])
+        self.assertEqual([], self.caught_entries)
+
+    def test_add_versions_parents_not_parents_index(self):
+        index = self.two_graph_index(catch_adds=True)
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('new', 'no-eol,fulltext', 0, 100, ['parent'])])
+        self.assertEqual([], self.caught_entries)
+
+    def test_add_versions_same_dup(self):
+        index = self.two_graph_index(catch_adds=True)
+        # options can be spelt two different ways
+        index.add_versions([('tip', 'fulltext,no-eol', 0, 100, [])])
+        index.add_versions([('tip', 'no-eol,fulltext', 0, 100, [])])
+        # but neither should have added data.
+        self.assertEqual([[], []], self.caught_entries)
+        
+    def test_add_versions_different_dup(self):
+        index = self.two_graph_index(catch_adds=True)
+        # change options
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('tip', 'no-eol,line-delta', 0, 100, [])])
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('tip', 'line-delta,no-eol', 0, 100, [])])
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('tip', 'fulltext', 0, 100, [])])
+        # position/length
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('tip', 'fulltext,no-eol', 50, 100, [])])
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('tip', 'fulltext,no-eol', 0, 1000, [])])
+        # parents
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('tip', 'fulltext,no-eol', 0, 100, ['parent'])])
+        # change options in the second record
+        self.assertRaises(errors.KnitCorrupt, index.add_versions,
+            [('tip', 'fulltext,no-eol', 0, 100, []),
+             ('tip', 'no-eol,line-delta', 0, 100, [])])
+        self.assertEqual([], self.caught_entries)
+
+



More information about the bazaar-commits mailing list