Rev 3289: Deprecate VersionedFile.get_parents, breaking pulling from a ghost containing knit or pack repository to weaves, which improves correctness and allows simplification of core code. in http://people.ubuntu.com/~robertc/baz2.0/versioned_files

Robert Collins robertc at robertcollins.net
Wed Mar 19 04:39:11 GMT 2008


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

------------------------------------------------------------
revno: 3289
revision-id:robertc at robertcollins.net-20080319043904-7g4imzvybc7q9l8h
parent: robertc at robertcollins.net-20080318011335-oq6210j5dgu9rz7d
committer: Robert Collins <robertc at robertcollins.net>
branch nick: versionedfile.apicleanup
timestamp: Wed 2008-03-19 15:39:04 +1100
message:
  Deprecate VersionedFile.get_parents, breaking pulling from a ghost containing knit or pack repository to weaves, which improves correctness and allows simplification of core code.
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/bundle/serializer/v4.py v10.py-20070611062757-5ggj7k18s9dej0fr-1
  bzrlib/fetch.py                fetch.py-20050818234941-26fea6105696365d
  bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
  bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
  bzrlib/multiparent.py          __init__.py-20070410133617-n1jdhcc1n1mibarp-1
  bzrlib/reconcile.py            reweave_inventory.py-20051108164726-1e5e0934febac06e
  bzrlib/repofmt/knitrepo.py     knitrepo.py-20070206081537-pyy4a00xdas0j4pf-1
  bzrlib/repofmt/weaverepo.py    presplitout.py-20070125045333-wfav3tsh73oxu3zk-1
  bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
  bzrlib/tests/interversionedfile_implementations/test_join.py test_join.py-20060302012326-9b5e9b0f0a03fedc
  bzrlib/tests/repository_implementations/test_check_reconcile.py test_broken.py-20070928125406-62236394w0jpbpd6-2
  bzrlib/tests/repository_implementations/test_iter_reverse_revision_history.py test_iter_reverse_re-20070217015036-spu7j5ggch7pbpyd-1
  bzrlib/tests/test_bundle.py    test.py-20050630184834-092aa401ab9f039c
  bzrlib/tests/test_knit.py      test_knit.py-20051212171302-95d4c00dd5f11f2b
  bzrlib/tests/test_versionedfile.py test_versionedfile.py-20060222045249-db45c9ed14a1c2e5
  bzrlib/versionedfile.py        versionedfile.py-20060222045106-5039c71ee3b65490
  bzrlib/weave.py                knit.py-20050627021749-759c29984154256b
=== modified file 'NEWS'
--- a/NEWS	2008-03-16 16:58:03 +0000
+++ b/NEWS	2008-03-19 04:39:04 +0000
@@ -22,10 +22,20 @@
 
   API BREAKS:
 
+    * Attempting to pull data from a ghost aware repository (e.g. knits) into a
+      non-ghost aware repository such as weaves will now fail if there are
+      ghosts.  (Robert Collins)
+
+    * ``VersionedFile.get_parents`` is deprecated, please use
+      ``VersionedFile.get_parent_map``. (Robert Collins)
+
   TESTING:
 
   INTERNALS:
 
+    * ``VersionedFile`` now has a new method ``get_parent_map`` which, like
+      ``Graph.get_parent_map`` returns a dict of key:parents. (Robert Collins)
+
 
 bzr 1.3rc1 2008-03-16
 ---------------------

=== modified file 'bzrlib/bundle/serializer/v4.py'
--- a/bzrlib/bundle/serializer/v4.py	2007-12-18 20:57:34 +0000
+++ b/bzrlib/bundle/serializer/v4.py	2008-03-19 04:39:04 +0000
@@ -327,7 +327,7 @@
                 if revision_id in self.base_ancestry:
                     continue
                 new_revision_ids.add(revision_id)
-                pending.extend(vf.get_parents(revision_id))
+                pending.extend(vf.get_parent_map([revision_id])[revision_id])
             yield vf, file_id, new_revision_ids
 
     def write_files(self):
@@ -376,8 +376,9 @@
         revision_ids = list(multiparent.topo_iter(vf, revision_ids))
         mpdiffs = vf.make_mpdiffs(revision_ids)
         sha1s = vf.get_sha1s(revision_ids)
+        parent_map = vf.get_parent_map(revision_ids)
         for mpdiff, revision_id, sha1, in zip(mpdiffs, revision_ids, sha1s):
-            parents = vf.get_parents(revision_id)
+            parents = parent_map[revision_id]
             text = ''.join(mpdiff.to_patch())
             self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
                                                revision_id, file_id)

=== modified file 'bzrlib/fetch.py'
--- a/bzrlib/fetch.py	2008-01-17 05:30:53 +0000
+++ b/bzrlib/fetch.py	2008-03-19 04:39:04 +0000
@@ -356,12 +356,15 @@
         parent_texts = {}
         versionedfile = {}
         to_store = self.target.weave_store
+        parent_map = self.source.get_graph().get_parent_map(revs)
         for tree in self.iter_rev_trees(revs):
             revision_id = tree.inventory.root.revision
             root_id = tree.get_root_id()
-            parents = inventory_weave.get_parents(revision_id)
+            parents = parent_map[revision_id]
+            if parents[0] == NULL_REVISION:
+                parents = ()
             if root_id not in versionedfile:
-                versionedfile[root_id] = to_store.get_weave_or_empty(root_id, 
+                versionedfile[root_id] = to_store.get_weave_or_empty(root_id,
                     self.target.get_transaction())
             _, _, parent_texts[root_id] = versionedfile[root_id].add_lines(
                 revision_id, parents, [], parent_texts)

=== modified file 'bzrlib/knit.py'
--- a/bzrlib/knit.py	2008-03-18 01:13:35 +0000
+++ b/bzrlib/knit.py	2008-03-19 04:39:04 +0000
@@ -720,7 +720,7 @@
     def get_delta(self, version_id):
         """Get a delta for constructing version from some other version."""
         self.check_not_reserved_id(version_id)
-        parents = self.get_parents(version_id)
+        parents = self.get_parent_map([version_id])[version_id]
         if len(parents):
             parent = parents[0]
         else:
@@ -977,11 +977,11 @@
         self._index.check_versions_present(version_ids)
 
     def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts,
-        nostore_sha, random_id, check_content):
+        nostore_sha, random_id, check_content, left_matching_blocks):
         """See VersionedFile.add_lines_with_ghosts()."""
         self._check_add(version_id, lines, random_id, check_content)
         return self._add(version_id, lines, parents, self.delta,
-            parent_texts, None, nostore_sha, random_id)
+            parent_texts, left_matching_blocks, nostore_sha, random_id)
 
     def _add_lines(self, version_id, parents, lines, parent_texts,
         left_matching_blocks, nostore_sha, random_id, check_content):
@@ -1265,7 +1265,7 @@
         for version_id in version_ids:
             try:
                 result[version_id] = tuple(lookup(version_id))
-            except KeyError:
+            except (KeyError, RevisionNotPresent):
                 pass
         return result
 
@@ -2828,9 +2828,10 @@
             # do the join:
             count = 0
             total = len(version_list)
+            parent_map = self.source.get_parent_map(version_list)
             for version_id in version_list:
                 pb.update("Converting to knit", count, total)
-                parents = self.source.get_parents(version_id)
+                parents = parent_map[version_id]
                 # check that its will be a consistent copy:
                 for parent in parents:
                     # if source has the parent, we must already have it

=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2008-03-14 16:23:28 +0000
+++ b/bzrlib/merge.py	2008-03-19 04:39:04 +0000
@@ -1403,7 +1403,7 @@
         """
         if version_id not in self.uncommon:
             return set()
-        parents = self.vf.get_parents(version_id)
+        parents = self.vf.get_parent_map([version_id])[version_id]
         if len(parents) == 0:
             return set(range(len(self.vf.get_lines(version_id))))
         new = None

=== modified file 'bzrlib/multiparent.py'
--- a/bzrlib/multiparent.py	2007-10-04 05:50:44 +0000
+++ b/bzrlib/multiparent.py	2008-03-19 04:39:04 +0000
@@ -23,6 +23,7 @@
 from StringIO import StringIO
 
 from bzrlib import (
+    errors,
     patiencediff,
     trace,
     ui,
@@ -37,11 +38,12 @@
     descendants = {}
     if versions is None:
         versions = vf.versions()
+    parents = vf.get_parent_map(versions)
     def pending_parents(version):
-        return [v for v in vf.get_parents(version) if v in versions and
+        return [v for v in parents[version] if v in versions and
                 v not in seen]
     for version_id in versions:
-        for parent_id in vf.get_parents(version_id):
+        for parent_id in parents[version_id]:
             descendants.setdefault(parent_id, []).append(version_id)
     cur = [v for v in versions if len(pending_parents(v)) == 0]
     while len(cur) > 0:
@@ -508,7 +510,10 @@
         self._parents[version_id] = parent_ids
 
     def get_diff(self, version_id):
-        return self._diffs[version_id]
+        try:
+            return self._diffs[version_id]
+        except KeyError:
+            raise errors.RevisionNotPresent(version_id, self)
 
     def destroy(self):
         self._diffs = {}

=== modified file 'bzrlib/reconcile.py'
--- a/bzrlib/reconcile.py	2007-11-27 23:39:22 +0000
+++ b/bzrlib/reconcile.py	2008-03-19 04:39:04 +0000
@@ -228,15 +228,15 @@
                 parents.append(parent)
             else:
                 mutter('found ghost %s', parent)
-        self._rev_graph[rev_id] = parents   
+        self._rev_graph[rev_id] = parents
         if self._parents_are_inconsistent(rev_id, parents):
             self.inconsistent_parents += 1
             mutter('Inconsistent inventory parents: id {%s} '
                    'inventory claims %r, '
                    'available parents are %r, '
                    'unavailable parents are %r',
-                   rev_id, 
-                   set(self.inventory.get_parents(rev_id)),
+                   rev_id,
+                   set(self.inventory.get_parent_map([rev_id])[rev_id]),
                    set(parents),
                    set(rev.parent_ids).difference(set(parents)))
 
@@ -248,7 +248,7 @@
         differences.
         Otherwise only the ghost differences are evaluated.
         """
-        weave_parents = self.inventory.get_parents(rev_id)
+        weave_parents = self.inventory.get_parent_map([rev_id])[rev_id]
         weave_missing_old_ghosts = set(weave_parents) != set(parents)
         first_parent_is_wrong = (
             len(weave_parents) and len(parents) and
@@ -333,16 +333,20 @@
 
         # we have topological order of revisions and non ghost parents ready.
         self._setup_steps(len(self.revisions))
-        for rev_id in TopoSorter(self.revisions.get_graph().items()).iter_topo_order():
-            parents = self.revisions.get_parents(rev_id)
-            # double check this really is in topological order.
-            unavailable = [p for p in parents if p not in new_inventory_vf]
+        graph = self.revisions.get_graph()
+        parent_map = self.revisions.get_parent_map(graph.keys())
+        for rev_id in TopoSorter(graph.items()).iter_topo_order():
+            parents = parent_map[rev_id]
+            # double check this really is in topological order, ignoring existing ghosts.
+            unavailable = [p for p in parents if p not in new_inventory_vf and
+                p in self.revisions]
             assert len(unavailable) == 0
             # this entry has all the non ghost parents in the inventory
             # file already.
             self._reweave_step('adding inventories')
             # ugly but needed, weaves are just way tooooo slow else.
-            new_inventory_vf.add_lines(rev_id, parents, self.inventory.get_lines(rev_id))
+            new_inventory_vf.add_lines_with_ghosts(rev_id, parents,
+                self.inventory.get_lines(rev_id))
 
         # if this worked, the set of new_inventory_vf.names should equal
         # self.pending
@@ -412,7 +416,7 @@
             elif version in versions_with_bad_parents:
                 parents = versions_with_bad_parents[version][1]
             else:
-                parents = vf.get_parents(version)
+                parents = vf.get_parent_map([version])[version]
             new_parents[version] = parents
         if not len(new_parents):
             # No used versions, remove the VF.

=== modified file 'bzrlib/repofmt/knitrepo.py'
--- a/bzrlib/repofmt/knitrepo.py	2008-03-10 15:39:56 +0000
+++ b/bzrlib/repofmt/knitrepo.py	2008-03-19 04:39:04 +0000
@@ -269,13 +269,6 @@
         vf = self._revision_store.get_revision_file(self.get_transaction())
         return vf
 
-    def _get_history_vf(self):
-        """Get a versionedfile whose history graph reflects all revisions.
-
-        For knit repositories, this is the revision knit.
-        """
-        return self._get_revision_vf()
-
     def has_revisions(self, revision_ids):
         """See Repository.has_revisions()."""
         result = set()

=== modified file 'bzrlib/repofmt/weaverepo.py'
--- a/bzrlib/repofmt/weaverepo.py	2008-02-12 02:38:27 +0000
+++ b/bzrlib/repofmt/weaverepo.py	2008-03-19 04:39:04 +0000
@@ -107,10 +107,11 @@
         consistency and is only applicable to inventory-weave-for-ancestry
         using repository formats & fetchers.
         """
-        weave_parents = inventory.get_parents(revision.revision_id)
-        weave_names = inventory.versions()
+        weave_parents = inventory.get_parent_map(
+            [revision.revision_id])[revision.revision_id]
+        parent_map = inventory.get_parent_map(revision.parent_ids)
         for parent_id in revision.parent_ids:
-            if parent_id in weave_names:
+            if parent_id in parent_map:
                 # this parent must not be a ghost.
                 if not parent_id in weave_parents:
                     # but it is a ghost
@@ -158,8 +159,7 @@
         a_weave = self.get_inventory_weave()
         all_revisions = self._eliminate_revisions_not_present(
                                 a_weave.versions())
-        entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for 
-                             node in all_revisions])
+        entire_graph = a_weave.get_parent_map(all_revisions)
         if revision_id is None:
             return entire_graph
         elif revision_id not in entire_graph:
@@ -247,10 +247,11 @@
         consistency and is only applicable to inventory-weave-for-ancestry
         using repository formats & fetchers.
         """
-        weave_parents = inventory.get_parents(revision.revision_id)
-        weave_names = inventory.versions()
+        weave_parents = inventory.get_parent_map(
+            [revision.revision_id])[revision.revision_id]
+        parent_map = inventory.get_parent_map(revision.parent_ids)
         for parent_id in revision.parent_ids:
-            if parent_id in weave_names:
+            if parent_id in parent_map:
                 # this parent must not be a ghost.
                 if not parent_id in weave_parents:
                     # but it is a ghost
@@ -299,8 +300,7 @@
         a_weave = self.get_inventory_weave()
         all_revisions = self._eliminate_revisions_not_present(
                                 a_weave.versions())
-        entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for 
-                             node in all_revisions])
+        entire_graph = a_weave.get_parent_map(all_revisions)
         if revision_id is None:
             return entire_graph
         elif revision_id not in entire_graph:

=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2008-03-14 10:55:37 +0000
+++ b/bzrlib/repository.py	2008-03-19 04:39:04 +0000
@@ -1607,26 +1607,19 @@
             done.add(revision_id)
         return result
 
-    def _get_history_vf(self):
-        """Get a versionedfile whose history graph reflects all revisions.
-
-        For weave repositories, this is the inventory weave.
-        """
-        return self.get_inventory_weave()
-
     def iter_reverse_revision_history(self, revision_id):
         """Iterate backwards through revision ids in the lefthand history
 
         :param revision_id: The revision id to start with.  All its lefthand
             ancestors will be traversed.
         """
-        if revision_id in (None, _mod_revision.NULL_REVISION):
-            return
+        graph = self.get_graph()
         next_id = revision_id
-        versionedfile = self._get_history_vf()
         while True:
+            if next_id in (None, _mod_revision.NULL_REVISION):
+                return
             yield next_id
-            parents = versionedfile.get_parents(next_id)
+            parents = graph.get_parent_map([next_id])[next_id]
             if len(parents) == 0:
                 return
             else:
@@ -3124,7 +3117,9 @@
         """
         wrong_parents = {}
         unused_versions = set()
-        for num, revision_id in enumerate(weave.versions()):
+        versions = weave.versions()
+        parent_map = weave.get_parent_map(versions)
+        for num, revision_id in enumerate(versions):
             try:
                 correct_parents = self.calculate_file_version_parents(
                     revision_id, file_id)
@@ -3133,7 +3128,7 @@
                 unused_versions.add(revision_id)
             else:
                 try:
-                    knit_parents = tuple(weave.get_parents(revision_id))
+                    knit_parents = tuple(parent_map[revision_id])
                 except errors.RevisionNotPresent:
                     knit_parents = None
                 if correct_parents != knit_parents:

=== modified file 'bzrlib/tests/interversionedfile_implementations/test_join.py'
--- a/bzrlib/tests/interversionedfile_implementations/test_join.py	2007-09-21 00:27:04 +0000
+++ b/bzrlib/tests/interversionedfile_implementations/test_join.py	2008-03-19 04:39:04 +0000
@@ -96,8 +96,7 @@
                          'v-1 v-2 v-3'.split())
         self.assertEqualDiff(w1.get_text('v-3'),
                 'line 1\n')
-        self.assertEqual(sorted(w1.get_parents('v-3')),
-                ['v-1'])
+        self.assertEqual({'v-3':('v-1',)}, w1.get_parent_map(['v-3']))
         ann = list(w1.annotate('v-3'))
         self.assertEqual(len(ann), 1)
         self.assertEqual(ann[0][0], 'v-1')
@@ -122,11 +121,11 @@
     def verify_weave1(self, w1):
         self.assertEqual(sorted(w1.versions()), ['v1', 'v2', 'v3'])
         self.assertEqual(w1.get_lines('v1'), ['hello\n'])
-        self.assertEqual([], w1.get_parents('v1'))
+        self.assertEqual({'v1':()}, w1.get_parent_map(['v1']))
         self.assertEqual(w1.get_lines('v2'), ['hello\n', 'world\n'])
-        self.assertEqual(['v1'], w1.get_parents('v2'))
+        self.assertEqual({'v2':('v1',)}, w1.get_parent_map(['v2']))
         self.assertEqual(w1.get_lines('v3'), ['hello\n', 'cruel\n', 'world\n'])
-        self.assertEqual(['v2'], w1.get_parents('v3'))
+        self.assertEqual({'v3':('v2',)}, w1.get_parent_map(['v3']))
 
     def test_join_source_has_less_parents_preserves_parents(self):
         # when the target has a text with more parent info, join 
@@ -138,7 +137,7 @@
         t.add_lines('base', [], [])
         t.add_lines('text', ['base'], [])
         t.join(s)
-        self.assertEqual(['base'], t.get_parents('text'))
+        self.assertEqual({'text':('base',)}, t.get_parent_map(['text']))
 
     def test_join_with_ghosts(self):
         """Join that inserts parents of an existing revision.
@@ -167,7 +166,7 @@
         self.assertEqual(['v1', 'v2', 'v3', 'x1',], sorted(w1.versions()))
         self.assertEqual('line from x1\n', w1.get_text('x1'))
         self.assertEqual(['hello\n', 'world\n'], w1.get_lines('v2'))
-        self.assertEqual(['v1'], w1.get_parents('v2'))
+        self.assertEqual({'v2':('v1',)}, w1.get_parent_map(['v2']))
 
     def test_join_with_ignore_missing_versions(self):
         # test that ignore_missing=True makes a listed but absent version id
@@ -182,7 +181,7 @@
         eq(sorted(w1.versions()), ['v1', 'v2', 'v3', 'x1'])
         eq(w1.get_text('x1'), 'line from x1\n')
         eq(w1.get_lines('v2'), ['hello\n', 'world\n'])
-        eq(w1.get_parents('v2'), ['v1'])
+        self.assertEqual({'v2':('v1',)}, w1.get_parent_map(['v2']))
     
     def build_source_weave(self, name, *pattern):
         w = self.get_source(name)
@@ -232,25 +231,32 @@
 
         # try filling target with ghosts and filling in reverse -  
         target.add_lines_with_ghosts('notbase', ['base'], [])
-        source.join(target)
+        try:
+            source.join(target)
+        except errors.RevisionNotPresent:
+            # can't join a ghost containing target onto a non-ghost supporting
+            # source.
+            self.assertFalse(source_ghosts)
+            return
+        else:
+            self.assertTrue(source_ghosts)
         # legacy apis should behave
         self.assertEqual(['notbase'], source.get_ancestry(['notbase']))
-        self.assertEqual([], source.get_parents('notbase'))
         self.assertEqual({'notbase':()}, source.get_graph())
         self.assertFalse(source.has_version('base'))
-        if source_ghosts:
-            # ghost data should have been preserved
-            self.assertEqual(['base', 'notbase'], source.get_ancestry_with_ghosts(['notbase']))
-            self.assertEqual(['base'], source.get_parents_with_ghosts('notbase'))
-            self.assertEqual({'notbase':['base']}, source.get_graph_with_ghosts())
-            self.assertTrue(source.has_ghost('base'))
+        # ghost data should have been preserved
+        self.assertEqual(['base', 'notbase'], source.get_ancestry_with_ghosts(['notbase']))
+        self.assertEqual(['base'], source.get_parents_with_ghosts('notbase'))
+        self.assertEqual({'notbase':('base',)}, source.get_parent_map(['notbase']))
+        self.assertEqual({'notbase':['base']}, source.get_graph_with_ghosts())
+        self.assertTrue(source.has_ghost('base'))
 
         # if we add something that is fills out what is a ghost, then 
         # when joining into a ghost aware join it should flesh out the ghosts.
         source.add_lines('base', [], [])
         target.join(source, version_ids=['base']) 
         self.assertEqual(['base', 'notbase'], target.get_ancestry(['notbase']))
-        self.assertEqual(['base'], target.get_parents('notbase'))
+        self.assertEqual({'notbase':('base',)}, target.get_parent_map(['notbase']))
         self.assertEqual({'base':(),
                           'notbase':('base', ),
                           },

=== modified file 'bzrlib/tests/repository_implementations/test_check_reconcile.py'
--- a/bzrlib/tests/repository_implementations/test_check_reconcile.py	2007-11-15 02:07:37 +0000
+++ b/bzrlib/tests/repository_implementations/test_check_reconcile.py	2008-03-19 04:39:04 +0000
@@ -114,8 +114,8 @@
                     "Format does not support text parent reconciliation")
 
     def file_parents(self, repo, revision_id):
-        return tuple(repo.weave_store.get_weave('a-file-id',
-            repo.get_transaction()).get_parents(revision_id))
+        return repo.weave_store.get_weave('a-file-id',
+            repo.get_transaction()).get_parent_map([revision_id])[revision_id]
 
     def assertFileVersionAbsent(self, repo, revision_id):
         self.assertFalse(repo.weave_store.get_weave('a-file-id',

=== modified file 'bzrlib/tests/repository_implementations/test_iter_reverse_revision_history.py'
--- a/bzrlib/tests/repository_implementations/test_iter_reverse_revision_history.py	2007-09-25 08:14:12 +0000
+++ b/bzrlib/tests/repository_implementations/test_iter_reverse_revision_history.py	2008-03-19 04:39:04 +0000
@@ -90,7 +90,8 @@
     def test_is_generator(self):
         tree = self.create_linear_history()
         repo = tree.branch.repository
-
+        repo.lock_read()
+        self.addCleanup(repo.unlock)
         rev_history = repo.iter_reverse_revision_history('rev4')
         self.assertEqual('rev4', rev_history.next())
         self.assertEqual('rev3', rev_history.next())
@@ -100,7 +101,11 @@
 
     def assertRevHistoryList(self, expected, repo, revision_id):
         """Assert the return values of iter_reverse_revision_history."""
-        actual = list(repo.iter_reverse_revision_history(revision_id))
+        repo.lock_read()
+        try:
+            actual = list(repo.iter_reverse_revision_history(revision_id))
+        finally:
+            repo.unlock()
         self.assertEqual(expected, actual)
 
     def test_linear_history(self):
@@ -119,7 +124,6 @@
     def test_revision_ids_are_utf8(self):
         tree = self.create_linear_history_with_utf8()
         repo = tree.branch.repository
-
         self.assertRevHistoryList(['rev-\xc3\xa5', 'rev-\xc2\xb5'],
                                   repo, 'rev-\xc3\xa5')
 

=== modified file 'bzrlib/tests/test_bundle.py'
--- a/bzrlib/tests/test_bundle.py	2007-12-29 22:42:51 +0000
+++ b/bzrlib/tests/test_bundle.py	2008-03-19 04:39:04 +0000
@@ -1339,7 +1339,8 @@
         self.assertEqual('contents2\nstatic\n', vf.get_text('rev2'))
         rtree = target_repo.revision_tree('rev2')
         inventory_vf = target_repo.get_inventory_weave()
-        self.assertEqual(['rev1'], inventory_vf.get_parents('rev2'))
+        self.assertEqual({'rev2':('rev1',)},
+            inventory_vf.get_parent_map(['rev2']))
         self.assertEqual('changed file',
                          target_repo.get_revision('rev2').message)
 

=== modified file 'bzrlib/tests/test_knit.py'
--- a/bzrlib/tests/test_knit.py	2008-02-26 22:26:00 +0000
+++ b/bzrlib/tests/test_knit.py	2008-03-19 04:39:04 +0000
@@ -1221,8 +1221,8 @@
         """Store in knit with parents"""
         k = self.make_test_knit()
         self.add_stock_one_and_one_a(k)
-        self.assertEquals(k.get_parents('text-1'), [])
-        self.assertEquals(k.get_parents('text-1a'), ['text-1'])
+        self.assertEqual({'text-1':(), 'text-1a':('text-1',)},
+            k.get_parent_map(['text-1', 'text-1a']))
 
     def test_ancestry(self):
         """Store in knit with parents"""
@@ -1485,7 +1485,7 @@
         # and when reading it revid3 should now appear.
         knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
         self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
-        self.assertEqual(['revid2'], knit.get_parents('revid3'))
+        self.assertEqual({'revid3':('revid2',)}, knit.get_parent_map(['revid3']))
 
     def test_delay_create(self):
         """Test that passing delay_create=True creates files late"""
@@ -2385,14 +2385,6 @@
         self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
         self.assertEqual(['fulltext'], index.get_options('parent'))
 
-    def test_get_parents(self):
-        # get_parents ignores ghosts
-        index = self.two_graph_index()
-        self.assertEqual(('tail', ), index.get_parents('parent'))
-        # and errors on ghosts.
-        self.assertRaises(errors.RevisionNotPresent,
-            index.get_parents, 'ghost')
-
     def test_get_parents_with_ghosts(self):
         index = self.two_graph_index()
         self.assertEqual(('tail', 'ghost'), index.get_parents_with_ghosts('parent'))
@@ -2660,13 +2652,6 @@
         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'))
-        # and errors on ghosts.
-        self.assertRaises(errors.RevisionNotPresent,
-            index.get_parents, 'ghost')
-
     def test_get_parents_with_ghosts(self):
         index = self.two_graph_index()
         self.assertEqual((), index.get_parents_with_ghosts('parent'))

=== modified file 'bzrlib/tests/test_versionedfile.py'
--- a/bzrlib/tests/test_versionedfile.py	2008-03-18 01:13:35 +0000
+++ b/bzrlib/tests/test_versionedfile.py	2008-03-19 04:39:04 +0000
@@ -39,6 +39,7 @@
     KnitAnnotateFactory,
     KnitPlainFactory,
     )
+from bzrlib.symbol_versioning import one_three
 from bzrlib.tests import TestCaseWithMemoryTransport, TestSkipped
 from bzrlib.tests.http_utils import TestCaseWithWebserver
 from bzrlib.trace import mutter
@@ -240,7 +241,7 @@
         new_vf = self.get_file('bar')
         for version in multiparent.topo_iter(vf):
             mpdiff = vf.make_mpdiffs([version])[0]
-            new_vf.add_mpdiffs([(version, vf.get_parents(version),
+            new_vf.add_mpdiffs([(version, vf.get_parent_map([version])[version],
                                  vf.get_sha1(version), mpdiff)])
             self.assertEqualDiff(vf.get_text(version),
                                  new_vf.get_text(version))
@@ -370,8 +371,7 @@
         def verify_file(f):
             self.assertEquals(f.get_lines('r1'), f.get_lines('r0'))
             self.assertEquals(f.get_lines('r1'), ['a\n', 'b\n'])
-            self.assertEquals(f.get_parents('r1'), ['r0'])
-    
+            self.assertEqual({'r1':('r0',)}, f.get_parent_map(['r1']))
             self.assertRaises(RevisionNotPresent,
                 f.clone_text, 'r2', 'rX', [])
             self.assertRaises(RevisionAlreadyPresent,
@@ -453,10 +453,10 @@
         f.add_lines('r2', [], ['a\n', 'b\n'])
         f.add_lines('r3', [], ['a\n', 'b\n'])
         f.add_lines('m', ['r0', 'r1', 'r2', 'r3'], ['a\n', 'b\n'])
-        self.assertEquals(f.get_parents('m'), ['r0', 'r1', 'r2', 'r3'])
-
+        self.assertEqual(['r0', 'r1', 'r2', 'r3'],
+            self.applyDeprecated(one_three, f.get_parents, 'm'))
         self.assertRaises(RevisionNotPresent,
-            f.get_parents, 'y')
+            self.applyDeprecated, one_three, f.get_parents, 'y')
 
     def test_get_parent_map(self):
         f = self.get_file()
@@ -640,7 +640,8 @@
         # has_version
         # - these are ghost unaware and must not be reflect ghosts
         self.assertEqual(['notbxbfse'], vf.get_ancestry('notbxbfse'))
-        self.assertEqual([], vf.get_parents('notbxbfse'))
+        self.assertEqual([],
+            self.applyDeprecated(one_three, vf.get_parents, 'notbxbfse'))
         self.assertEqual({'notbxbfse':()}, vf.get_graph())
         self.assertFalse(vf.has_version(parent_id_utf8))
         # we have _with_ghost apis to give us ghost information.
@@ -652,7 +653,8 @@
         # results of the prior apis
         vf.add_lines(parent_id_utf8, [], [])
         self.assertEqual([parent_id_utf8, 'notbxbfse'], vf.get_ancestry(['notbxbfse']))
-        self.assertEqual([parent_id_utf8], vf.get_parents('notbxbfse'))
+        self.assertEqual({'notbxbfse':(parent_id_utf8,)},
+            vf.get_parent_map(['notbxbfse']))
         self.assertEqual({parent_id_utf8:(),
                           'notbxbfse':(parent_id_utf8, ),
                           },
@@ -865,12 +867,16 @@
 
     def test_get_parents(self):
         self.setup_abcde()
-        self.assertEqual(['A'], self.plan_merge_vf.get_parents('B'))
-        self.assertEqual(['C'], self.plan_merge_vf.get_parents('D'))
-        self.assertEqual(['B', 'D'], self.plan_merge_vf.get_parents('E:'))
-        error = self.assertRaises(errors.RevisionNotPresent,
-                                  self.plan_merge_vf.get_parents, 'F')
-        self.assertContainsRe(str(error), '{F} not present in "root"')
+        self.assertEqual({'B':('A',)}, self.plan_merge_vf.get_parent_map(['B']))
+        self.assertEqual({'D':('C',)}, self.plan_merge_vf.get_parent_map(['D']))
+        self.assertEqual({'E:':('B', 'D')},
+            self.plan_merge_vf.get_parent_map(['E:']))
+        self.assertEqual({}, self.plan_merge_vf.get_parent_map(['F']))
+        self.assertEqual({
+                'B':('A',),
+                'D':('C',),
+                'E:':('B', 'D'),
+                }, self.plan_merge_vf.get_parent_map(['B', 'D', 'E:', 'F']))
 
     def test_get_lines(self):
         self.setup_abcde()

=== modified file 'bzrlib/versionedfile.py'
--- a/bzrlib/versionedfile.py	2008-03-18 01:13:35 +0000
+++ b/bzrlib/versionedfile.py	2008-03-19 04:39:04 +0000
@@ -36,6 +36,7 @@
 from cStringIO import StringIO
 
 from bzrlib.inter import InterObject
+from bzrlib.symbol_versioning import *
 from bzrlib.textmerge import TextMerge
 
 
@@ -128,17 +129,17 @@
 
     def add_lines_with_ghosts(self, version_id, parents, lines,
         parent_texts=None, nostore_sha=None, random_id=False,
-        check_content=True):
+        check_content=True, left_matching_blocks=None):
         """Add lines to the versioned file, allowing ghosts to be present.
         
         This takes the same parameters as add_lines and returns the same.
         """
         self._check_write_ok()
         return self._add_lines_with_ghosts(version_id, parents, lines,
-            parent_texts, nostore_sha, random_id, check_content)
+            parent_texts, nostore_sha, random_id, check_content, left_matching_blocks)
 
     def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts,
-        nostore_sha, random_id, check_content):
+        nostore_sha, random_id, check_content, left_matching_blocks):
         """Helper to do class specific add_lines_with_ghosts."""
         raise NotImplementedError(self.add_lines_with_ghosts)
 
@@ -213,15 +214,25 @@
     def make_mpdiffs(self, version_ids):
         """Create multiparent diffs for specified versions."""
         knit_versions = set()
+        knit_versions.update(version_ids)
+        parent_map = self.get_parent_map(version_ids)
         for version_id in version_ids:
-            knit_versions.add(version_id)
-            knit_versions.update(self.get_parents(version_id))
+            try:
+                knit_versions.update(parent_map[version_id])
+            except KeyError:
+                raise RevisionNotPresent(version_id, self)
+        # We need to filter out ghosts, because we can't diff against them.
+        knit_versions = set(self.get_parent_map(knit_versions).keys())
         lines = dict(zip(knit_versions,
             self._get_lf_split_line_list(knit_versions)))
         diffs = []
         for version_id in version_ids:
             target = lines[version_id]
-            parents = [lines[p] for p in self.get_parents(version_id)]
+            try:
+                parents = [lines[p] for p in parent_map[version_id] if p in
+                    knit_versions]
+            except KeyError:
+                raise RevisionNotPresent(version_id, self)
             if len(parents) > 0:
                 left_parent_blocks = self._extract_blocks(version_id,
                                                           parents[0], target)
@@ -251,8 +262,9 @@
         for version, parent_ids, expected_sha1, mpdiff in records:
             needed_parents.update(p for p in parent_ids
                                   if not mpvf.has_version(p))
-        for parent_id, lines in zip(needed_parents,
-                                 self._get_lf_split_line_list(needed_parents)):
+        present_parents = set(self.get_parent_map(needed_parents).keys())
+        for parent_id, lines in zip(present_parents,
+                                 self._get_lf_split_line_list(present_parents)):
             mpvf.add_version(lines, parent_id, [])
         for (version, parent_ids, expected_sha1, mpdiff), lines in\
             zip(records, mpvf.get_line_list(versions)):
@@ -261,8 +273,16 @@
                     mpvf.get_diff(parent_ids[0]).num_lines()))
             else:
                 left_matching_blocks = None
-            _, _, version_text = self.add_lines(version, parent_ids, lines,
-                vf_parents, left_matching_blocks=left_matching_blocks)
+            try:
+                _, _, version_text = self.add_lines_with_ghosts(version,
+                    parent_ids, lines, vf_parents,
+                    left_matching_blocks=left_matching_blocks)
+            except NotImplementedError:
+                # The vf can't handle ghosts, so add lines normally, which will
+                # (reasonably) fail if there are ghosts in the data.
+                _, _, version_text = self.add_lines(version,
+                    parent_ids, lines, vf_parents,
+                    left_matching_blocks=left_matching_blocks)
             vf_parents[version] = version_text
         for (version, parent_ids, expected_sha1, mpdiff), sha1 in\
              zip(records, self.get_sha1s(versions)):
@@ -379,6 +399,7 @@
         """
         raise NotImplementedError(self.get_parent_map)
 
+    @deprecated_method(one_three)
     def get_parents(self, version_id):
         """Return version names for parents of a version.
 
@@ -471,11 +492,7 @@
             The order is undefined, allowing for different optimisations in
             the underlying implementation.
         """
-        for version_id in version_ids:
-            try:
-                yield version_id, tuple(self.get_parents(version_id))
-            except errors.RevisionNotPresent:
-                pass
+        return self.get_parent_map(version_ids).iteritems()
 
     def transaction_finished(self):
         """The transaction that this file was opened in has finished.
@@ -567,7 +584,7 @@
             raise ValueError('Parents may not be None')
         if lines is None:
             raise ValueError('Lines may not be None')
-        self._parents[version_id] = parents
+        self._parents[version_id] = tuple(parents)
         self._lines[version_id] = lines
 
     def get_lines(self, version_id):
@@ -610,18 +627,23 @@
             ancestry.update(self.get_ancestry(parent, topo_sorted=False))
         return ancestry
 
-    def get_parents(self, version_id):
-        """See VersionedFile.get_parents"""
-        parents = self._parents.get(version_id)
-        if parents is not None:
-            return parents
+    def get_parent_map(self, version_ids):
+        """See VersionedFile.get_parent_map"""
+        result = {}
+        pending = set(version_ids)
+        for key in version_ids:
+            try:
+                result[key] = self._parents[key]
+            except KeyError:
+                pass
+        pending = pending - set(result.keys())
         for versionedfile in self.fallback_versionedfiles:
-            try:
-                return versionedfile.get_parents(version_id)
-            except errors.RevisionNotPresent:
-                continue
-        else:
-            raise errors.RevisionNotPresent(version_id, self._file_id)
+            parents = versionedfile.get_parent_map(pending)
+            result.update(parents)
+            pending = pending - set(parents.keys())
+            if not pending:
+                return result
+        return result
 
     def _get_graph(self):
         from bzrlib.graph import (
@@ -772,10 +794,11 @@
             # memory pressure reduction. RBC 20060313
             # pb.update('Converting versioned data', 0, len(order))
             total = len(order)
+            parent_map = self.source.get_parent_map(order)
             for index, version in enumerate(order):
                 pb.update('Converting versioned data', index, total)
                 _, _, parent_text = target.add_lines(version,
-                                               self.source.get_parents(version),
+                                               parent_map[version],
                                                self.source.get_lines(version),
                                                parent_texts=parent_texts)
                 parent_texts[version] = parent_text

=== modified file 'bzrlib/weave.py'
--- a/bzrlib/weave.py	2008-03-18 01:13:35 +0000
+++ b/bzrlib/weave.py	2008-03-19 04:39:04 +0000
@@ -768,8 +768,11 @@
         version_ids = set(other.versions()).intersection(set(version_ids))
         # pull in the referenced graph.
         version_ids = other.get_ancestry(version_ids)
-        pending_graph = [(version, other.get_parents(version)) for
-                         version in version_ids]
+        pending_parents = other.get_parent_map(version_ids)
+        pending_graph = pending_parents.items()
+        if len(pending_graph) != len(version_ids):
+            raise RevisionNotPresent(
+                set(version_ids) - pending_parents.keys(), self)
         for name in topo_sort(pending_graph):
             other_idx = other._name_map[name]
             # returns True if we have it, False if we need it.
@@ -777,7 +780,6 @@
                 names_to_join.append((other_idx, name))
             processed += 1
 
-
         if pb and not msg:
             msg = 'weave join'
 



More information about the bazaar-commits mailing list