Rev 4458: New work on how to resolve conflict lines. in http://bazaar.launchpad.net/~jameinel/bzr/1.17-rework-annotate
John Arbash Meinel
john at arbash-meinel.com
Wed Jun 17 21:33:00 BST 2009
At http://bazaar.launchpad.net/~jameinel/bzr/1.17-rework-annotate
------------------------------------------------------------
revno: 4458
revision-id: john at arbash-meinel.com-20090617203250-x0v6nnrrk7j4z173
parent: john at arbash-meinel.com-20090617195744-vugtpm7w05sfdmxm
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 1.17-rework-annotate
timestamp: Wed 2009-06-17 15:32:50 -0500
message:
New work on how to resolve conflict lines.
The new code doesn't try to do much resolution. All it does is
claim that both parents introduced the text. The conflict resolution
can be done just before displaying, rather than while building
up the annotations.
At least, that is the current idea.
-------------- next part --------------
=== modified file 'bzrlib/_annotator_py.py'
--- a/bzrlib/_annotator_py.py 2009-06-17 19:57:44 +0000
+++ b/bzrlib/_annotator_py.py 2009-06-17 20:32:50 +0000
@@ -53,20 +53,55 @@
self._heads_provider = _mod_graph.KnownGraph(self._parent_map)
return self._heads_provider
- def _reannotate_one_parent(self, annotations, lines, key, parent_key):
- """Reannotate this text relative to its first parent."""
+ def _get_parent_annotations_and_matches(self, lines, parent_key):
parent_lines = self._lines_cache[parent_key]
parent_annotations = self._annotations_cache[parent_key]
# PatienceSequenceMatcher should probably be part of Policy
matcher = patiencediff.PatienceSequenceMatcher(None,
parent_lines, lines)
matching_blocks = matcher.get_matching_blocks()
+ return parent_annotations, matching_blocks
+
+ def _reannotate_one_parent(self, annotations, lines, key, parent_key):
+ """Reannotate this text relative to its first parent."""
+ parent_annotations, matching_blocks = self._get_parent_annotations_and_matches(
+ lines, parent_key)
for parent_idx, lines_idx, match_len in matching_blocks:
# For all matching regions we copy across the parent annotations
annotations[lines_idx:lines_idx + match_len] = \
parent_annotations[parent_idx:parent_idx + match_len]
+ def _reannotate_other_parents(self, annotations, lines, key, parent_key):
+ """Reannotate this text relative to a second (or more) parent."""
+ parent_annotations, matching_blocks = self._get_parent_annotations_and_matches(
+ lines, parent_key)
+
+ simple_key_ann = (key,)
+ for parent_idx, lines_idx, match_len in matching_blocks:
+ # For lines which match this parent, we will now resolve whether
+ # this parent wins over the current annotation
+ for idx in xrange(match_len):
+ ann_idx = lines_idx + idx
+ ann = annotations[ann_idx]
+ par_ann = parent_annotations[parent_idx + idx]
+ if ann == par_ann:
+ # Nothing to change
+ continue
+ if ann == simple_key_ann:
+ # Originally claimed 'this', but it was really in this
+ # parent
+ annotations[ann_idx] = par_ann
+ continue
+ # Now we have a conflict, both sides claim to have introduced
+ # this line
+ new_ann = set(ann)
+ assert key not in new_ann
+ # new_ann.discard(key)
+ new_ann.update(par_ann)
+ new_ann = tuple(sorted(new_ann))
+ annotations[ann_idx] = new_ann
+
def annotate(self, key):
"""Return annotated fulltext for the given key."""
keys = self._get_needed_texts(key)
@@ -82,6 +117,8 @@
if not parents:
continue
self._reannotate_one_parent(annotations, lines, key, parents[0])
+ for parent in parents[1:]:
+ self._reannotate_other_parents(annotations, lines, key, parent)
try:
annotations = self._annotations_cache[key]
except KeyError:
=== modified file 'bzrlib/tests/test__annotator.py'
--- a/bzrlib/tests/test__annotator.py 2009-06-17 19:46:24 +0000
+++ b/bzrlib/tests/test__annotator.py 2009-06-17 20:32:50 +0000
@@ -61,36 +61,109 @@
module = None # Set by load_tests
+ fa_key = ('f-id', 'a-id')
+ fb_key = ('f-id', 'b-id')
+ fc_key = ('f-id', 'c-id')
+ fd_key = ('f-id', 'd-id')
+
def make_simple_text(self):
- repo = self.make_repository('repo')
- repo.lock_write()
- self.addCleanup(repo.unlock)
- vf = repo.texts
- repo.start_write_group()
- try:
- fa_key = ('f-id', 'a-id')
- fb_key = ('f-id', 'b-id')
- vf.add_lines(fa_key, (), ['simple\n', 'content\n'])
- vf.add_lines(fb_key, (fa_key,), ['simple\n', 'new content\n'])
- except:
- repo.abort_write_group()
- raise
- else:
- repo.commit_write_group()
- return vf
+ self.repo = self.make_repository('repo')
+ self.repo.lock_write()
+ self.addCleanup(self.repo.unlock)
+ vf = self.repo.texts
+ self.vf = vf
+ self.repo.start_write_group()
+ try:
+ self.vf.add_lines(self.fa_key, [], ['simple\n', 'content\n'])
+ self.vf.add_lines(self.fb_key, [self.fa_key],
+ ['simple\n', 'new content\n'])
+ except:
+ self.repo.abort_write_group()
+ raise
+ else:
+ self.repo.commit_write_group()
+
+ def make_merge_text(self):
+ self.make_simple_text()
+ self.repo.start_write_group()
+ try:
+ self.vf.add_lines(self.fc_key, [self.fa_key],
+ ['simple\n', 'from c\n', 'content\n'])
+ self.vf.add_lines(self.fd_key, [self.fb_key, self.fc_key],
+ ['simple\n', 'from c\n', 'new content\n',
+ 'introduced in merge\n'])
+ except:
+ self.repo.abort_write_group()
+ raise
+ else:
+ self.repo.commit_write_group()
+
+ def make_common_merge_text(self):
+ """Both sides of the merge will have introduced a line."""
+ self.make_simple_text()
+ self.repo.start_write_group()
+ try:
+ self.vf.add_lines(self.fc_key, [self.fa_key],
+ ['simple\n', 'new content\n'])
+ self.vf.add_lines(self.fd_key, [self.fb_key, self.fc_key],
+ ['simple\n', 'new content\n'])
+ except:
+ self.repo.abort_write_group()
+ raise
+ else:
+ self.repo.commit_write_group()
+
+ def make_merge_and_restored_text(self):
+ self.make_simple_text()
+ self.repo.start_write_group()
+ try:
+ # c reverts back to 'a' for the new content line
+ self.vf.add_lines(self.fc_key, [self.fb_key],
+ ['simple\n', 'content\n'])
+ # d merges 'a' and 'c', to find both claim last modified
+ self.vf.add_lines(self.fd_key, [self.fa_key, self.fc_key],
+ ['simple\n', 'content\n'])
+ except:
+ self.repo.abort_write_group()
+ raise
+ else:
+ self.repo.commit_write_group()
+
+ def assertAnnotateEqual(self, expected_annotation, annotator, key):
+ annotation, lines = annotator.annotate(key)
+ self.assertEqual(expected_annotation, annotation)
+ record = self.vf.get_record_stream([key], 'unordered', True).next()
+ exp_text = record.get_bytes_as('fulltext')
+ self.assertEqualDiff(exp_text, ''.join(lines))
def test_annotate_missing(self):
- vf = self.make_simple_text()
- ann = self.module.Annotator(vf)
+ self.make_simple_text()
+ ann = self.module.Annotator(self.vf)
self.assertRaises(errors.RevisionNotPresent,
ann.annotate, ('not', 'present'))
def test_annotate_simple(self):
- vf = self.make_simple_text()
- ann = self.module.Annotator(vf)
- f_key = ('f-id', 'a-id')
- self.assertEqual(([(f_key,)]*2, ['simple\n', 'content\n']),
- ann.annotate(f_key))
- fb_key = ('f-id', 'b-id')
- self.assertEqual(([(f_key,), (fb_key,)], ['simple\n', 'new content\n']),
- ann.annotate(fb_key))
+ self.make_simple_text()
+ ann = self.module.Annotator(self.vf)
+ self.assertAnnotateEqual([(self.fa_key,)]*2, ann, self.fa_key)
+ self.assertAnnotateEqual([(self.fa_key,), (self.fb_key,)],
+ ann, self.fb_key)
+
+ def test_annotate_merge_text(self):
+ self.make_merge_text()
+ ann = self.module.Annotator(self.vf)
+ self.assertAnnotateEqual([(self.fa_key,), (self.fc_key,),
+ (self.fb_key,), (self.fd_key,)],
+ ann, self.fd_key)
+
+ def test_annotate_common_merge_text(self):
+ self.make_common_merge_text()
+ ann = self.module.Annotator(self.vf)
+ self.assertAnnotateEqual([(self.fa_key,), (self.fb_key, self.fc_key)],
+ ann, self.fd_key)
+
+ def test_annotate_merge_and_restored(self):
+ self.make_merge_and_restored_text()
+ ann = self.module.Annotator(self.vf)
+ self.assertAnnotateEqual([(self.fa_key,), (self.fa_key, self.fc_key)],
+ ann, self.fd_key)
More information about the bazaar-commits
mailing list