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