Rev 3518: Hack in a Weave merger that includes all possibly relevant texts. in http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/merge3_per_file
John Arbash Meinel
john at arbash-meinel.com
Wed Jun 25 23:41:51 BST 2008
At http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/merge3_per_file
------------------------------------------------------------
revno: 3518
revision-id: john at arbash-meinel.com-20080625224143-ndnv3fygtime27au
parent: john at arbash-meinel.com-20080625174816-r8mgsvobyaruyj7w
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: merge3_per_file
timestamp: Wed 2008-06-25 17:41:43 -0500
message:
Hack in a Weave merger that includes all possibly relevant texts.
It seems it fixes this problem, so we may need to re-think how we are doing the partial merges.
-------------- next part --------------
=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py 2008-06-25 17:48:16 +0000
+++ b/bzrlib/merge.py 2008-06-25 22:41:43 +0000
@@ -27,6 +27,7 @@
patiencediff,
registry,
revision as _mod_revision,
+ tsort,
)
from bzrlib.branch import Branch
from bzrlib.conflicts import ConflictList, Conflict
@@ -822,6 +823,13 @@
"""
base_first = bases[0]
for base in bases[1:]:
+ # XXX: This is not technically correct. A value does not always
+ # supersede None. Sometimes you get None because someone
+ # deleted an object. The correct resolution would probably be
+ # to recurse back into the LCA of these bases until we get
+ # some sort of convergance. (Eventually you always reach NULL
+ # where everything is None). That would let us know if this
+ # None is a "didn't exist yet" or a "deleted".
if base is not None and base_first != base:
break
else: # All the bases are identical, we can just use 3-way
@@ -1198,6 +1206,7 @@
supports_show_base = False
supports_reverse_cherrypick = False
history_based = True
+ full_texts_merge = False
def _merged_lines(self, file_id):
"""Generate the merged lines.
@@ -1209,7 +1218,8 @@
else:
base = None
plan = self.this_tree.plan_file_merge(file_id, self.other_tree,
- base=base)
+ base=base,
+ full_texts_merge=self.full_texts_merge)
if 'merge' in debug.debug_flags:
plan = list(plan)
trans_id = self.tt.trans_id_file_id(file_id)
@@ -1240,6 +1250,10 @@
file_group.append(trans_id)
+class FullWeaveMerger(WeaveMerger):
+ full_texts_merge = True
+
+
class LCAMerger(WeaveMerger):
def _merged_lines(self, file_id):
@@ -1522,9 +1536,10 @@
class _PlanMerge(_PlanMergeBase):
"""Plan an annotate merge using on-the-fly annotation"""
- def __init__(self, a_rev, b_rev, vf):
+ def __init__(self, a_rev, b_rev, vf, full_texts_merge=False):
# We don't call _PlanMergeBase, because we don't use most of what it
# does
+ self.full_texts_merge = full_texts_merge
self.a_rev = a_rev
self.b_rev = b_rev
self.vf = vf
@@ -1549,26 +1564,41 @@
"""Find all the ancestors back to a unique lca"""
cur_ancestors = (self.a_rev, self.b_rev)
mutter('finding lcas for %s:\n%s', self.vf, cur_ancestors)
- ancestors = [cur_ancestors]
+ ancestors = set(cur_ancestors)
+ parent_map = {}
while True:
next_lcas = self.graph.find_lca(*cur_ancestors)
- ancestors.append(next_lcas)
+ ancestors.update(next_lcas)
+ for rev_id in cur_ancestors:
+ parent_map[rev_id] = next_lcas
if len(next_lcas) == 0:
- ancestors[-1] = (NULL_REVISION,)
+ ancestors.add(NULL_REVISION)
+ parent_map[NULL_REVISION] = ()
self.vf.add_lines(NULL_REVISION, [], [])
break
elif len(next_lcas) == 1:
+ parent_map[next_lcas.pop()] = ()
break
cur_ancestors = next_lcas
- ancestors.reverse()
- return ancestors
-
- def _get_interesting_texts(self, lcas):
- all_revision_ids = set()
- for ancestors in lcas:
- all_revision_ids.update(ancestors)
- all_revision_ids.add(self.a_rev)
- all_revision_ids.add(self.b_rev)
+ return parent_map
+
+ def _find_all_ancestors(self):
+ unique_lca = self.graph.find_unique_lca(self.a_rev, self.b_rev)
+ interesting = self.graph.find_unique_ancestors(self.a_rev, [unique_lca])
+ interesting.update(self.graph.find_unique_ancestors(self.b_rev,
+ [unique_lca]))
+ parent_map = self.graph.get_parent_map(interesting)
+ interesting.add(unique_lca)
+ parent_map[unique_lca] = ()
+ culled_parent_map = {}
+ for rev_id, parent_ids in parent_map.iteritems():
+ real_parent_ids = [p_id for p_id in parent_ids
+ if p_id in parent_map]
+ culled_parent_map[rev_id] = real_parent_ids
+ return culled_parent_map
+
+ def _get_interesting_texts(self, parent_map):
+ all_revision_ids = set(parent_map)
all_texts = dict(zip(all_revision_ids,
self.vf.get_line_list(all_revision_ids)))
@@ -1578,24 +1608,19 @@
from bzrlib import weave
self._weave = weave.Weave(weave_name='in_memory_weave',
allow_reserved=True)
- lcas = self._find_recursive_lcas()
- mutter('Found:\n => %s',
- '\n => '.join(str(sorted(lca)) for lca in lcas))
-
- all_texts = self._get_interesting_texts(lcas)
-
- last_parents = ()
- for ancestors in lcas:
- for ancestor in ancestors:
- if self._weave.has_version(ancestor):
- # Most likely this happened because one node purely
- # dominated another in the per-file graph. That is okay, we
- # already have it in the weave, and the plan should be very
- # straightforward.
- continue
- self._weave.add_lines(ancestor, last_parents,
- all_texts[ancestor])
- last_parents = ancestors
+ if self.full_texts_merge:
+ parent_map = self._find_all_ancestors()
+ else:
+ parent_map = self._find_recursive_lcas()
+ mutter('Found:\n%s', parent_map)
+
+ all_texts = self._get_interesting_texts(parent_map)
+
+
+ for revision_id in tsort.topo_sort(parent_map):
+ parent_ids = parent_map[revision_id]
+ self._weave.add_lines(revision_id, parent_ids,
+ all_texts[revision_id])
def plan_merge(self):
"""Generate a 'plan' for merging the two revisions.
=== modified file 'bzrlib/option.py'
--- a/bzrlib/option.py 2008-04-24 07:22:53 +0000
+++ b/bzrlib/option.py 2008-06-25 22:41:43 +0000
@@ -474,6 +474,8 @@
"Merge using external diff3")
_merge_type_registry.register_lazy('weave', 'bzrlib.merge', 'WeaveMerger',
"Weave-based merge")
+_merge_type_registry.register_lazy('full-weave', 'bzrlib.merge', 'FullWeaveMerger',
+ "Weave-based merge with all texts")
_merge_type_registry.register_lazy('lca', 'bzrlib.merge', 'LCAMerger',
"LCA-newness merge")
=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py 2008-06-20 20:07:26 +0000
+++ b/bzrlib/tree.py 2008-06-25 22:41:43 +0000
@@ -331,7 +331,8 @@
last_revision_base = base._get_file_revision(file_id, vf, 'base:')
return vf, last_revision_a, last_revision_b, last_revision_base
- def plan_file_merge(self, file_id, other, base=None):
+ def plan_file_merge(self, file_id, other, base=None,
+ full_texts_merge=False):
"""Generate a merge plan based on annotations.
If the file contains uncommitted changes in this tree, they will be
@@ -342,7 +343,8 @@
data = self._get_plan_merge_data(file_id, other, base)
vf, last_revision_a, last_revision_b, last_revision_base = data
return vf.plan_merge(last_revision_a, last_revision_b,
- last_revision_base)
+ last_revision_base,
+ full_texts_merge=full_texts_merge)
def plan_file_lca_merge(self, file_id, other, base=None):
"""Generate a merge plan based lca-newness.
=== modified file 'bzrlib/versionedfile.py'
--- a/bzrlib/versionedfile.py 2008-06-21 01:21:08 +0000
+++ b/bzrlib/versionedfile.py 2008-06-25 22:41:43 +0000
@@ -543,11 +543,11 @@
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._file_id)
- def plan_merge(self, ver_a, ver_b, base=None):
+ def plan_merge(self, ver_a, ver_b, base=None, full_texts_merge=False):
"""See VersionedFile.plan_merge"""
from bzrlib.merge import _PlanMerge
if base is None:
- return _PlanMerge(ver_a, ver_b, self).plan_merge()
+ return _PlanMerge(ver_a, ver_b, self, full_texts_merge).plan_merge()
old_plan = list(_PlanMerge(ver_a, base, self).plan_merge())
new_plan = list(_PlanMerge(ver_a, ver_b, self).plan_merge())
return _PlanMerge._subtract_plans(old_plan, new_plan)
More information about the bazaar-commits
mailing list