Rev 3073: Speed up annotate on packs in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Dec 4 00:42:51 GMT 2007
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 3073
revision-id:pqm at pqm.ubuntu.com-20071204004243-cgss0sl9yf0ayepc
parent: pqm at pqm.ubuntu.com-20071203222135-gjk2xshgdfgxje6m
parent: abentley at panoramicfeedback.com-20071203213807-inz8pur6ejnc1ax5
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2007-12-04 00:42:43 +0000
message:
Speed up annotate on packs
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/merge.py merge.py-20050513021216-953b65a438527106
bzrlib/tests/test_merge.py testmerge.py-20050905070950-c1b5aa49ff911024
bzrlib/tests/test_versionedfile.py test_versionedfile.py-20060222045249-db45c9ed14a1c2e5
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
bzrlib/versionedfile.py versionedfile.py-20060222045106-5039c71ee3b65490
------------------------------------------------------------
revno: 3062.1.14
revision-id:abentley at panoramicfeedback.com-20071203213807-inz8pur6ejnc1ax5
parent: abentley at panoramicfeedback.com-20071203213336-tpv1ftv3kxpp8x08
committer: Aaron Bentley <abentley at panoramicfeedback.com>
branch nick: fast-plan-merge
timestamp: Mon 2007-12-03 16:38:07 -0500
message:
Use topo_sorted=False with get_ancestry
modified:
bzrlib/merge.py merge.py-20050513021216-953b65a438527106
bzrlib/tests/test_versionedfile.py test_versionedfile.py-20060222045249-db45c9ed14a1c2e5
bzrlib/versionedfile.py versionedfile.py-20060222045106-5039c71ee3b65490
------------------------------------------------------------
revno: 3062.1.13
revision-id:abentley at panoramicfeedback.com-20071203213336-tpv1ftv3kxpp8x08
parent: aaron.bentley at utoronto.ca-20071203003633-pb9662ni2fwad2g0
committer: Aaron Bentley <abentley at panoramicfeedback.com>
branch nick: fast-plan-merge
timestamp: Mon 2007-12-03 16:33:36 -0500
message:
Make _PlanMerge an implementation detail of _PlanMergeVersionedFile
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/merge.py merge.py-20050513021216-953b65a438527106
bzrlib/tests/test_merge.py testmerge.py-20050905070950-c1b5aa49ff911024
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
bzrlib/versionedfile.py versionedfile.py-20060222045106-5039c71ee3b65490
------------------------------------------------------------
revno: 3062.1.12
revision-id:aaron.bentley at utoronto.ca-20071203003633-pb9662ni2fwad2g0
parent: aaron.bentley at utoronto.ca-20071203003609-m2vo52shpr7zbjkf
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 19:36:33 -0500
message:
Implement simple text cache
modified:
bzrlib/merge.py merge.py-20050513021216-953b65a438527106
------------------------------------------------------------
revno: 3062.1.11
revision-id:aaron.bentley at utoronto.ca-20071203003609-m2vo52shpr7zbjkf
parent: aaron.bentley at utoronto.ca-20071203001650-nfj8jveb1wk65sh8
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 19:36:09 -0500
message:
Update references
modified:
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
------------------------------------------------------------
revno: 3062.1.10
revision-id:aaron.bentley at utoronto.ca-20071203001650-nfj8jveb1wk65sh8
parent: aaron.bentley at utoronto.ca-20071202235203-e1c69qbqegre7nfj
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 19:16:50 -0500
message:
Update NEWS
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
------------------------------------------------------------
revno: 3062.1.9
revision-id:aaron.bentley at utoronto.ca-20071202235203-e1c69qbqegre7nfj
parent: aaron.bentley at utoronto.ca-20071202182312-1red2zttrs87iamc
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 18:52:03 -0500
message:
Move PlanMerge into merge and _PlanMergeVersionedFile into versionedfile
removed:
bzrlib/plan_merge.py plan_merge.py-20071201150758-662g2gkq8nlz483v-1
bzrlib/tests/test_plan_merge.py test_plan_merge.py-20071201150758-662g2gkq8nlz483v-2
modified:
bzrlib/merge.py merge.py-20050513021216-953b65a438527106
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/test_merge.py testmerge.py-20050905070950-c1b5aa49ff911024
bzrlib/tests/test_versionedfile.py test_versionedfile.py-20060222045249-db45c9ed14a1c2e5
bzrlib/versionedfile.py versionedfile.py-20060222045106-5039c71ee3b65490
------------------------------------------------------------
revno: 3062.1.8
revision-id:aaron.bentley at utoronto.ca-20071202182312-1red2zttrs87iamc
parent: aaron.bentley at utoronto.ca-20071202180356-fzo74355tuzq3r6m
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 13:23:12 -0500
message:
Clean up names and add documentation
modified:
bzrlib/plan_merge.py plan_merge.py-20071201150758-662g2gkq8nlz483v-1
bzrlib/tests/test_plan_merge.py test_plan_merge.py-20071201150758-662g2gkq8nlz483v-2
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
------------------------------------------------------------
revno: 3062.1.7
revision-id:aaron.bentley at utoronto.ca-20071202180356-fzo74355tuzq3r6m
parent: aaron.bentley at utoronto.ca-20071202175848-xvxy2xv8i9rxkxss
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 13:03:56 -0500
message:
Update NEWS entry
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
------------------------------------------------------------
revno: 3062.1.6
revision-id:aaron.bentley at utoronto.ca-20071202175848-xvxy2xv8i9rxkxss
parent: aaron.bentley at utoronto.ca-20071202162059-3ag1cy5qx26ours9
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 12:58:48 -0500
message:
PlanMergeVersionedfile now has multiple Versionedfile fallbacks
modified:
bzrlib/plan_merge.py plan_merge.py-20071201150758-662g2gkq8nlz483v-1
bzrlib/tests/test_plan_merge.py test_plan_merge.py-20071201150758-662g2gkq8nlz483v-2
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
------------------------------------------------------------
revno: 3062.1.5
revision-id:aaron.bentley at utoronto.ca-20071202162059-3ag1cy5qx26ours9
parent: aaron.bentley at utoronto.ca-20071202155634-sxcukjjy6b02tg7u
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 11:20:59 -0500
message:
Style cleanup
modified:
bzrlib/plan_merge.py plan_merge.py-20071201150758-662g2gkq8nlz483v-1
------------------------------------------------------------
revno: 3062.1.4
revision-id:aaron.bentley at utoronto.ca-20071202155634-sxcukjjy6b02tg7u
parent: aaron.bentley at utoronto.ca-20071202154909-tu7o1l5eqmu45vna
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 10:56:34 -0500
message:
Add GPL+copyright notices
modified:
bzrlib/plan_merge.py plan_merge.py-20071201150758-662g2gkq8nlz483v-1
bzrlib/tests/test_plan_merge.py test_plan_merge.py-20071201150758-662g2gkq8nlz483v-2
------------------------------------------------------------
revno: 3062.1.3
revision-id:aaron.bentley at utoronto.ca-20071202154909-tu7o1l5eqmu45vna
parent: aaron.bentley at utoronto.ca-20071201160031-p5u6ed26r675t58f
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sun 2007-12-02 10:49:09 -0500
message:
Correctly determine file revisions
modified:
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
------------------------------------------------------------
revno: 3062.1.2
revision-id:aaron.bentley at utoronto.ca-20071201160031-p5u6ed26r675t58f
parent: aaron.bentley at utoronto.ca-20071201150801-5o6hhw3qe9sle4ol
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sat 2007-12-01 11:00:31 -0500
message:
Use fake versionedfiles for planning the merge
modified:
bzrlib/plan_merge.py plan_merge.py-20071201150758-662g2gkq8nlz483v-1
bzrlib/tests/test_plan_merge.py test_plan_merge.py-20071201150758-662g2gkq8nlz483v-2
------------------------------------------------------------
revno: 3062.1.1
revision-id:aaron.bentley at utoronto.ca-20071201150801-5o6hhw3qe9sle4ol
parent: pqm at pqm.ubuntu.com-20071130233349-86c0lwztw5vt2r17
committer: Aaron Bentley <aaron.bentley at utoronto.ca>
branch nick: fast-plan-merge
timestamp: Sat 2007-12-01 10:08:01 -0500
message:
Get plan-merge working on versionedfile
added:
bzrlib/plan_merge.py plan_merge.py-20071201150758-662g2gkq8nlz483v-1
bzrlib/tests/test_plan_merge.py test_plan_merge.py-20071201150758-662g2gkq8nlz483v-2
modified:
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
=== modified file 'NEWS'
--- a/NEWS 2007-12-03 17:03:10 +0000
+++ b/NEWS 2007-12-04 00:42:43 +0000
@@ -124,6 +124,10 @@
* Diff does not require an inventory to be generated on dirstate trees.
(Aaron Bentley, #149254)
+ * New annotate merge (--merge-type=weave) implementation is fast on
+ versionedfiles withough cached annotations, e.g. pack-0.92.
+ (Aaron Bentley)
+
IMPROVEMENTS:
* ``bzr send`` now doesn't require the target e-mail address to be
=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py 2007-11-30 14:52:30 +0000
+++ b/bzrlib/merge.py 2007-12-03 21:38:07 +0000
@@ -1135,3 +1135,120 @@
for text_a, text_b in zip(plain_a[ai:a_cur], plain_b[bi:b_cur]):
assert text_a == text_b
yield "unchanged", text_a
+
+
+class _PlanMerge(object):
+ """Plan an annotate merge using on-the-fly annotation"""
+
+ def __init__(self, a_rev, b_rev, vf):
+ """Contructor.
+
+ :param a_rev: Revision-id of one revision to merge
+ :param b_rev: Revision-id of the other revision to merge
+ :param vf: A versionedfile containing both revisions
+ """
+ self.a_rev = a_rev
+ self.b_rev = b_rev
+ self.lines_a = vf.get_lines(a_rev)
+ self.lines_b = vf.get_lines(b_rev)
+ self.vf = vf
+ a_ancestry = set(vf.get_ancestry(a_rev, topo_sorted=False))
+ b_ancestry = set(vf.get_ancestry(b_rev, topo_sorted=False))
+ self.uncommon = a_ancestry.symmetric_difference(b_ancestry)
+ self._last_lines = None
+ self._last_lines_revision_id = None
+
+ def plan_merge(self):
+ """Generate a 'plan' for merging the two revisions.
+
+ This involves comparing their texts and determining the cause of
+ differences. If text A has a line and text B does not, then either the
+ line was added to text A, or it was deleted from B. Once the causes
+ are combined, they are written out in the format described in
+ VersionedFile.plan_merge
+ """
+ blocks = self._get_matching_blocks(self.a_rev, self.b_rev)
+ new_a = self._find_new(self.a_rev)
+ new_b = self._find_new(self.b_rev)
+ last_i = 0
+ last_j = 0
+ a_lines = self.vf.get_lines(self.a_rev)
+ b_lines = self.vf.get_lines(self.b_rev)
+ for i, j, n in blocks:
+ # determine why lines aren't common
+ for a_index in range(last_i, i):
+ if a_index in new_a:
+ cause = 'new-a'
+ else:
+ cause = 'killed-b'
+ yield cause, a_lines[a_index]
+ for b_index in range(last_j, j):
+ if b_index in new_b:
+ cause = 'new-b'
+ else:
+ cause = 'killed-a'
+ yield cause, b_lines[b_index]
+ # handle common lines
+ for a_index in range(i, i+n):
+ yield 'unchanged', a_lines[a_index]
+ last_i = i+n
+ last_j = j+n
+
+ def _get_matching_blocks(self, left_revision, right_revision):
+ """Return a description of which sections of two revisions match.
+
+ See SequenceMatcher.get_matching_blocks
+ """
+ if self._last_lines_revision_id == left_revision:
+ left_lines = self._last_lines
+ else:
+ left_lines = self.vf.get_lines(left_revision)
+ right_lines = self.vf.get_lines(right_revision)
+ self._last_lines = right_lines
+ self._last_lines_revision_id = right_revision
+ matcher = patiencediff.PatienceSequenceMatcher(None, left_lines,
+ right_lines)
+ return matcher.get_matching_blocks()
+
+ def _unique_lines(self, matching_blocks):
+ """Analyse matching_blocks to determine which lines are unique
+
+ :return: a tuple of (unique_left, unique_right), where the values are
+ sets of line numbers of unique lines.
+ """
+ last_i = 0
+ last_j = 0
+ unique_left = []
+ unique_right = []
+ for i, j, n in matching_blocks:
+ unique_left.extend(range(last_i, i))
+ unique_right.extend(range(last_j, j))
+ last_i = i + n
+ last_j = j + n
+ return unique_left, unique_right
+
+ def _find_new(self, version_id):
+ """Determine which lines are new in the ancestry of this version.
+
+ If a lines is present in this version, and not present in any
+ common ancestor, it is considered new.
+ """
+ if version_id not in self.uncommon:
+ return set()
+ parents = self.vf.get_parents(version_id)
+ if len(parents) == 0:
+ return set(range(len(self.vf.get_lines(version_id))))
+ new = None
+ for parent in parents:
+ blocks = self._get_matching_blocks(version_id, parent)
+ result, unused = self._unique_lines(blocks)
+ parent_new = self._find_new(parent)
+ for i, j, n in blocks:
+ for ii, jj in [(i+r, j+r) for r in range(n)]:
+ if jj in parent_new:
+ result.append(ii)
+ if new is None:
+ new = set(result)
+ else:
+ new.intersection_update(result)
+ return new
=== modified file 'bzrlib/tests/test_merge.py'
--- a/bzrlib/tests/test_merge.py 2007-09-14 09:51:38 +0000
+++ b/bzrlib/tests/test_merge.py 2007-12-03 21:33:36 +0000
@@ -20,17 +20,19 @@
from bzrlib import (
conflicts,
errors,
+ knit,
merge as _mod_merge,
option,
progress,
+ versionedfile,
)
from bzrlib.branch import Branch
from bzrlib.conflicts import ConflictList, TextConflict
from bzrlib.errors import UnrelatedBranches, NoCommits, BzrCommandError
-from bzrlib.merge import transform_tree, merge_inner
+from bzrlib.merge import transform_tree, merge_inner, _PlanMerge
from bzrlib.osutils import pathjoin, file_kind
from bzrlib.revision import common_ancestor
-from bzrlib.tests import TestCaseWithTransport
+from bzrlib.tests import TestCaseWithTransport, TestCaseWithMemoryTransport
from bzrlib.trace import (enable_test_log, disable_test_log)
from bzrlib.workingtree import WorkingTree
@@ -285,3 +287,84 @@
merger.merge_type = _mod_merge.Merge3Merger
merger.do_merge()
self.assertEqual(tree_a.get_parent_ids(), [tree_b.last_revision()])
+
+
+class TestPlanMerge(TestCaseWithMemoryTransport):
+
+ def setUp(self):
+ TestCaseWithMemoryTransport.setUp(self)
+ self.vf = knit.KnitVersionedFile('root', self.get_transport(),
+ create=True)
+ self.plan_merge_vf = versionedfile._PlanMergeVersionedFile('root',
+ [self.vf])
+
+ def add_version(self, version_id, parents, text):
+ self.vf.add_lines(version_id, parents, [c+'\n' for c in text])
+
+ def add_uncommitted_version(self, version_id, parents, text):
+ self.plan_merge_vf.add_lines(version_id, parents,
+ [c+'\n' for c in text])
+
+ def setup_plan_merge(self):
+ self.add_version('A', [], 'abc')
+ self.add_version('B', ['A'], 'acehg')
+ self.add_version('C', ['A'], 'fabg')
+ return _PlanMerge('B', 'C', self.plan_merge_vf)
+
+ def setup_plan_merge_uncommitted(self):
+ self.add_version('A', [], 'abc')
+ self.add_uncommitted_version('B:', ['A'], 'acehg')
+ self.add_uncommitted_version('C:', ['A'], 'fabg')
+ return _PlanMerge('B:', 'C:', self.plan_merge_vf)
+
+ def test_unique_lines(self):
+ plan = self.setup_plan_merge()
+ self.assertEqual(plan._unique_lines(
+ plan._get_matching_blocks('B', 'C')),
+ ([1, 2, 3], [0, 2]))
+
+ def test_find_new(self):
+ plan = self.setup_plan_merge()
+ self.assertEqual(set([2, 3, 4]), plan._find_new('B'))
+ self.assertEqual(set([0, 3]), plan._find_new('C'))
+
+ def test_find_new2(self):
+ self.add_version('A', [], 'abc')
+ self.add_version('B', ['A'], 'abcde')
+ self.add_version('C', ['A'], 'abcefg')
+ self.add_version('D', ['A', 'B', 'C'], 'abcdegh')
+ my_plan = _PlanMerge('B', 'D', self.plan_merge_vf)
+ self.assertEqual(set([5, 6]), my_plan._find_new('D'))
+ self.assertEqual(set(), my_plan._find_new('A'))
+
+ def test_find_new_no_ancestors(self):
+ self.add_version('A', [], 'abc')
+ self.add_version('B', [], 'xyz')
+ my_plan = _PlanMerge('A', 'B', self.vf)
+ self.assertEqual(set([0, 1, 2]), my_plan._find_new('A'))
+
+ def test_plan_merge(self):
+ self.setup_plan_merge()
+ plan = self.plan_merge_vf.plan_merge('B', 'C')
+ self.assertEqual([
+ ('new-b', 'f\n'),
+ ('unchanged', 'a\n'),
+ ('killed-b', 'c\n'),
+ ('new-a', 'e\n'),
+ ('new-a', 'h\n'),
+ ('killed-a', 'b\n'),
+ ('unchanged', 'g\n')],
+ list(plan))
+
+ def test_plan_merge_uncommitted_files(self):
+ self.setup_plan_merge_uncommitted()
+ plan = self.plan_merge_vf.plan_merge('B:', 'C:')
+ self.assertEqual([
+ ('new-b', 'f\n'),
+ ('unchanged', 'a\n'),
+ ('killed-b', 'c\n'),
+ ('new-a', 'e\n'),
+ ('new-a', 'h\n'),
+ ('killed-a', 'b\n'),
+ ('unchanged', 'g\n')],
+ list(plan))
=== modified file 'bzrlib/tests/test_versionedfile.py'
--- a/bzrlib/tests/test_versionedfile.py 2007-11-09 17:50:31 +0000
+++ b/bzrlib/tests/test_versionedfile.py 2007-12-03 21:38:07 +0000
@@ -797,6 +797,68 @@
return self._factory
+class TestPlanMergeVersionedFile(TestCaseWithMemoryTransport):
+
+ def setUp(self):
+ TestCaseWithMemoryTransport.setUp(self)
+ self.vf1 = KnitVersionedFile('root', self.get_transport(), create=True)
+ self.vf2 = KnitVersionedFile('root', self.get_transport(), create=True)
+ self.plan_merge_vf = versionedfile._PlanMergeVersionedFile('root',
+ [self.vf1, self.vf2])
+
+ def test_add_lines(self):
+ self.plan_merge_vf.add_lines('a:', [], [])
+ self.assertRaises(ValueError, self.plan_merge_vf.add_lines, 'a', [],
+ [])
+ self.assertRaises(ValueError, self.plan_merge_vf.add_lines, 'a:', None,
+ [])
+ self.assertRaises(ValueError, self.plan_merge_vf.add_lines, 'a:', [],
+ None)
+
+ def test_ancestry(self):
+ self.vf1.add_lines('A', [], [])
+ self.vf1.add_lines('B', ['A'], [])
+ self.plan_merge_vf.add_lines('C:', ['B'], [])
+ self.plan_merge_vf.add_lines('D:', ['C:'], [])
+ self.assertEqual(set(['A', 'B', 'C:', 'D:']),
+ self.plan_merge_vf.get_ancestry('D:', topo_sorted=False))
+
+ def setup_abcde(self):
+ self.vf1.add_lines('A', [], ['a'])
+ self.vf1.add_lines('B', ['A'], ['b'])
+ self.vf2.add_lines('C', [], ['c'])
+ self.vf2.add_lines('D', ['C'], ['d'])
+ self.plan_merge_vf.add_lines('E:', ['B', 'D'], ['e'])
+
+ def test_ancestry_uses_all_versionedfiles(self):
+ self.setup_abcde()
+ self.assertEqual(set(['A', 'B', 'C', 'D', 'E:']),
+ self.plan_merge_vf.get_ancestry('E:', topo_sorted=False))
+
+ def test_ancestry_raises_revision_not_present(self):
+ error = self.assertRaises(errors.RevisionNotPresent,
+ self.plan_merge_vf.get_ancestry, 'E:', False)
+ self.assertContainsRe(str(error), '{E:} not present in "root"')
+
+ 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"')
+
+ def test_get_lines(self):
+ self.setup_abcde()
+ self.assertEqual(['a'], self.plan_merge_vf.get_lines('A'))
+ self.assertEqual(['c'], self.plan_merge_vf.get_lines('C'))
+ self.assertEqual(['e'], self.plan_merge_vf.get_lines('E:'))
+ error = self.assertRaises(errors.RevisionNotPresent,
+ self.plan_merge_vf.get_lines, 'F')
+ self.assertContainsRe(str(error), '{F} not present in "root"')
+
+
class InterString(versionedfile.InterVersionedFile):
"""An inter-versionedfile optimised code path for strings.
=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py 2007-11-21 13:21:38 +0000
+++ b/bzrlib/tree.py 2007-12-03 21:33:36 +0000
@@ -299,14 +299,40 @@
uncommitted changes in the other tree, they will be assigned to the
'other:' pseudo-revision.
"""
- from bzrlib import merge
- annotated_a = list(self.annotate_iter(file_id,
- _mod_revision.CURRENT_REVISION))
- annotated_b = list(other.annotate_iter(file_id, 'other:'))
- ancestors_a = self._get_ancestors(_mod_revision.CURRENT_REVISION)
- ancestors_b = other._get_ancestors('other:')
- return merge._plan_annotate_merge(annotated_a, annotated_b,
- ancestors_a, ancestors_b)
+ from bzrlib import merge, versionedfile
+ vf = versionedfile._PlanMergeVersionedFile(file_id)
+ last_revision_a = self._get_file_revision(file_id, vf, 'this:')
+ last_revision_b = other._get_file_revision(file_id, vf, 'other:')
+ return vf.plan_merge(last_revision_a, last_revision_b)
+
+ def _get_file_revision(self, file_id, vf, tree_revision):
+ def file_revision(revision_tree):
+ revision_tree.lock_read()
+ try:
+ return revision_tree.inventory[file_id].revision
+ finally:
+ revision_tree.unlock()
+
+ def iter_parent_trees():
+ for revision_id in self.get_parent_ids():
+ try:
+ yield self.revision_tree(revision_id)
+ except:
+ yield self.repository.revision_tree(revision_id)
+
+ if getattr(self, '_get_weave', None) is None:
+ last_revision = tree_revision
+ parent_revisions = [file_revision(t) for t in iter_parent_trees()]
+ vf.add_lines(last_revision, parent_revisions,
+ self.get_file(file_id).readlines())
+ repo = self.branch.repository
+ transaction = repo.get_transaction()
+ base_vf = repo.weave_store.get_weave(file_id, transaction)
+ else:
+ last_revision = file_revision(self)
+ base_vf = self._get_weave(file_id)
+ vf.fallback_versionedfiles.append(base_vf)
+ return last_revision
inventory = property(_get_inventory,
doc="Inventory of this Tree")
=== modified file 'bzrlib/versionedfile.py'
--- a/bzrlib/versionedfile.py 2007-11-13 21:39:37 +0000
+++ b/bzrlib/versionedfile.py 2007-12-03 21:38:07 +0000
@@ -492,6 +492,105 @@
return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
+class _PlanMergeVersionedFile(object):
+ """A VersionedFile for uncommitted and committed texts.
+
+ It is intended to allow merges to be planned with working tree texts.
+ It implements only the small part of the VersionedFile interface used by
+ PlanMerge. It falls back to multiple versionedfiles for data not stored in
+ _PlanMergeVersionedFile itself.
+ """
+
+ def __init__(self, file_id, fallback_versionedfiles=None):
+ """Constuctor
+
+ :param file_id: Used when raising exceptions.
+ :param fallback_versionedfiles: If supplied, the set of fallbacks to
+ use. Otherwise, _PlanMergeVersionedFile.fallback_versionedfiles
+ can be appended to later.
+ """
+ self._file_id = file_id
+ if fallback_versionedfiles is None:
+ self.fallback_versionedfiles = []
+ else:
+ self.fallback_versionedfiles = fallback_versionedfiles
+ self._parents = {}
+ self._lines = {}
+
+ def plan_merge(self, ver_a, ver_b):
+ """See VersionedFile.plan_merge"""
+ from merge import _PlanMerge
+ return _PlanMerge(ver_a, ver_b, self).plan_merge()
+
+ def add_lines(self, version_id, parents, lines):
+ """See VersionedFile.add_lines
+
+ Lines are added locally, not fallback versionedfiles. Also, ghosts are
+ permitted. Only reserved ids are permitted.
+ """
+ if not revision.is_reserved_id(version_id):
+ raise ValueError('Only reserved ids may be used')
+ if parents is None:
+ raise ValueError('Parents may not be None')
+ if lines is None:
+ raise ValueError('Lines may not be None')
+ self._parents[version_id] = parents
+ self._lines[version_id] = lines
+
+ def get_lines(self, version_id):
+ """See VersionedFile.get_ancestry"""
+ lines = self._lines.get(version_id)
+ if lines is not None:
+ return lines
+ for versionedfile in self.fallback_versionedfiles:
+ try:
+ return versionedfile.get_lines(version_id)
+ except errors.RevisionNotPresent:
+ continue
+ else:
+ raise errors.RevisionNotPresent(version_id, self._file_id)
+
+ def get_ancestry(self, version_id, topo_sorted=False):
+ """See VersionedFile.get_ancestry.
+
+ Note that this implementation assumes that if a VersionedFile can
+ answer get_ancestry at all, it can give an authoritative answer. In
+ fact, ghosts can invalidate this assumption. But it's good enough
+ 99% of the time, and far cheaper/simpler.
+
+ Also note that the results of this version are never topologically
+ sorted, and are a set.
+ """
+ if topo_sorted:
+ raise ValueError('This implementation does not provide sorting')
+ parents = self._parents.get(version_id)
+ if parents is None:
+ for vf in self.fallback_versionedfiles:
+ try:
+ return vf.get_ancestry(version_id, topo_sorted=False)
+ except errors.RevisionNotPresent:
+ continue
+ else:
+ raise errors.RevisionNotPresent(version_id, self._file_id)
+ ancestry = set([version_id])
+ for parent in parents:
+ 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
+ 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)
+
+
class PlanWeaveMerge(TextMerge):
"""Weave merge that takes a plan as its input.
More information about the bazaar-commits
mailing list