Rev 5856: (spiv) Fix ValueError in changelog_merge plugin. (Andrew Bennetts) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Fri May 13 08:29:17 UTC 2011


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 5856 [merge]
revision-id: pqm at pqm.ubuntu.com-20110513082913-2igf61vxl0gqoilw
parent: pqm at pqm.ubuntu.com-20110512235039-pj1gatuvy4jq415y
parent: andrew.bennetts at canonical.com-20110513065020-cpv86qwc8t7qqjta
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2011-05-13 08:29:13 +0000
message:
  (spiv) Fix ValueError in changelog_merge plugin. (Andrew Bennetts)
modified:
  bzrlib/plugins/changelog_merge/changelog_merge.py changelog_merge.py-20100920024628-tdrjysnxjm1q96oq-2
  bzrlib/plugins/changelog_merge/tests/test_changelog_merge.py test_changelog_merge-20110310080015-9c3muqni567c1qux-3
=== modified file 'bzrlib/plugins/changelog_merge/changelog_merge.py'
--- a/bzrlib/plugins/changelog_merge/changelog_merge.py	2011-03-15 07:54:39 +0000
+++ b/bzrlib/plugins/changelog_merge/changelog_merge.py	2011-05-13 06:50:20 +0000
@@ -85,8 +85,8 @@
             result_entries = merge_entries(
                 base_entries, this_entries, other_entries)
         except EntryConflict:
-            return 'not_applicable' # XXX: generating a nice conflict file
-                                    # would be better
+            # XXX: generating a nice conflict file would be better
+            return 'not_applicable', None
         # Transform the merged elements back into real blocks of lines.
         return 'success', entries_to_lines(result_entries)
 
@@ -101,10 +101,8 @@
     This algorithm does O(N^2 * logN) SequenceMatcher.ratio() calls, which is
     pretty bad, but it shouldn't be used very often.
     """
-    deleted_entries_as_strs = [
-        entry_as_str(entry) for entry in deleted_entries]
-    new_entries_as_strs = [
-        entry_as_str(entry) for entry in new_entries]
+    deleted_entries_as_strs = map(entry_as_str, deleted_entries)
+    new_entries_as_strs = map(entry_as_str, new_entries)
     result_new = list(new_entries)
     result_deleted = list(deleted_entries)
     result_edits = []
@@ -112,18 +110,20 @@
     CUTOFF = 0.8
     while True:
         best = None
-        best_score = None
-        for new_entry in new_entries:
-            new_entry_as_str = entry_as_str(new_entry)
+        best_score = CUTOFF
+        # Compare each new entry with each old entry to find the best match
+        for new_entry_as_str in new_entries_as_strs:
             sm.set_seq1(new_entry_as_str)
             for old_entry_as_str in deleted_entries_as_strs:
                 sm.set_seq2(old_entry_as_str)
                 score = sm.ratio()
-                if score > CUTOFF:
-                    if best_score is None or score > best_score:
-                        best = new_entry_as_str, old_entry_as_str
-                        best_score = score
+                if score > best_score:
+                    best = new_entry_as_str, old_entry_as_str
+                    best_score = score
         if best is not None:
+            # Add the best match to the list of edits, and remove it from the
+            # the list of new/old entries.  Also remove it from the new/old
+            # lists for the next round.
             del_index = deleted_entries_as_strs.index(best[1])
             new_index = new_entries_as_strs.index(best[0])
             result_edits.append(
@@ -131,6 +131,8 @@
             del deleted_entries_as_strs[del_index], result_deleted[del_index]
             del new_entries_as_strs[new_index], result_new[new_index]
         else:
+            # No match better than CUTOFF exists in the remaining new and old
+            # entries.
             break
     return result_new, result_deleted, result_edits
 

=== modified file 'bzrlib/plugins/changelog_merge/tests/test_changelog_merge.py'
--- a/bzrlib/plugins/changelog_merge/tests/test_changelog_merge.py	2011-03-11 06:20:40 +0000
+++ b/bzrlib/plugins/changelog_merge/tests/test_changelog_merge.py	2011-05-13 06:34:55 +0000
@@ -15,8 +15,10 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 from bzrlib import (
+    merge,
     tests,
     )
+from bzrlib.tests import test_merge_core
 from bzrlib.plugins.changelog_merge import changelog_merge
 
 
@@ -138,3 +140,80 @@
             ],
             list(result_entries))
 
+    def test_too_hard(self):
+        """A conflict this plugin cannot resolve raises EntryConflict.
+        """
+        # An entry edited in other but deleted in this is a conflict we can't
+        # resolve.  (Ideally perhaps we'd generate a nice conflict file, but
+        # for now we just give up.)
+        self.assertRaises(changelog_merge.EntryConflict,
+            changelog_merge.merge_entries,
+            sample2_base_entries, [], sample2_other_entries)
+
+    def test_default_guess_edits(self):
+        """default_guess_edits matches a new entry only once.
+        
+        (Even when that entry is the best match for multiple old entries.)
+        """
+        new_in_other = [('AAAAA',), ('BBBBB',)]
+        deleted_in_other = [('DDDDD',), ('BBBBBx',), ('BBBBBxx',)]
+        # BBBBB is the best match for both BBBBBx and BBBBBxx
+        result = changelog_merge.default_guess_edits(
+            new_in_other, deleted_in_other)
+        self.assertEqual(
+            ([('AAAAA',)], # new
+             [('DDDDD',), ('BBBBBxx',)], # deleted
+             [(('BBBBBx',), ('BBBBB',))]), # edits
+            result)
+
+
+class TestChangeLogMerger(tests.TestCaseWithTransport):
+    """Tests for ChangeLogMerger class.
+    
+    Most tests should be unit tests for merge_entries (and its helpers).
+    This class is just to cover the handful of lines of code in ChangeLogMerger
+    itself.
+    """
+
+    def make_builder(self):
+        builder = test_merge_core.MergeBuilder(self.test_base_dir)
+        self.addCleanup(builder.cleanup)
+        return builder
+
+    def make_changelog_merger(self, base_text, this_text, other_text):
+        builder = self.make_builder()
+        builder.add_file('clog-id', builder.tree_root, 'ChangeLog',
+            base_text, True)
+        builder.change_contents('clog-id', other=other_text, this=this_text)
+        merger = builder.make_merger(merge.Merge3Merger, ['clog-id'])
+        merger.this_branch.get_config().set_user_option(
+            'changelog_merge_files', 'ChangeLog')
+        merge_hook_params = merge.MergeHookParams(merger, 'clog-id', None,
+            'file', 'file', 'conflict')
+        changelog_merger = changelog_merge.ChangeLogMerger(merger)
+        return changelog_merger, merge_hook_params
+
+    def test_merge_text_returns_not_applicable(self):
+        """A conflict this plugin cannot resolve returns (not_applicable, None).
+        """
+        # Build same example as TestMergeCoreLogic.test_too_hard: edit an entry
+        # in other but delete it in this.
+        def entries_as_str(entries):
+            return ''.join(entry + '\n' for entry in entries)
+        changelog_merger, merge_hook_params = self.make_changelog_merger(
+            entries_as_str(sample2_base_entries),
+            '',
+            entries_as_str(sample2_other_entries))
+        self.assertEqual(
+            ('not_applicable', None),
+            changelog_merger.merge_contents(merge_hook_params))
+
+    def test_merge_text_returns_success(self):
+        """A successful merge returns ('success', lines)."""
+        changelog_merger, merge_hook_params = self.make_changelog_merger(
+            '', 'this text\n', 'other text\n')
+        status, lines = changelog_merger.merge_contents(merge_hook_params)
+        self.assertEqual(
+            ('success', ['other text\n', 'this text\n']),
+            (status, list(lines)))
+




More information about the bazaar-commits mailing list