Rev 5068: (vila) Resolve --take-this or --take-other correctly rename kept file in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Tue Mar 2 08:49:08 GMT 2010


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

------------------------------------------------------------
revno: 5068 [merge]
revision-id: pqm at pqm.ubuntu.com-20100302084907-z4r0yoa4ldspjz82
parent: pqm at pqm.ubuntu.com-20100302064136-g8xdfe3r16ck5p6q
parent: v.ladeuil+lp at free.fr-20100302081739-pjqlb2hqw1lgnybn
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2010-03-02 08:49:07 +0000
message:
  (vila) Resolve --take-this or --take-other correctly rename kept file
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/conflicts.py            conflicts.py-20051001061850-78ef952ba63d2b42
  bzrlib/tests/test_conflicts.py test_conflicts.py-20051006031059-e2dad9bbeaa5891f
=== modified file 'NEWS'
--- a/NEWS	2010-03-02 06:41:36 +0000
+++ b/NEWS	2010-03-02 08:17:39 +0000
@@ -79,6 +79,10 @@
 * ``bzr remove-tree`` can now remove multiple working trees.
   (Jared Hance, Andrew Bennetts, #253137)
 
+* ``bzr resolve --take-this`` and ``--take-other`` now correctly renames
+  the kept file on content conflicts where one side deleted the file.
+  (Vincent Ladeuil, #529968)
+
 * ``bzr upgrade`` now names backup directory as ``backup.bzr.~N~`` instead
   of ``backup.bzr``. This directory is ignored by bzr commands such as
   ``add``.

=== modified file 'bzrlib/conflicts.py'
--- a/bzrlib/conflicts.py	2010-02-23 07:43:11 +0000
+++ b/bzrlib/conflicts.py	2010-03-02 07:58:53 +0000
@@ -26,6 +26,7 @@
 
 from bzrlib import (
     builtins,
+    cleanup,
     commands,
     errors,
     osutils,
@@ -479,16 +480,43 @@
     def associated_filenames(self):
         return [self.path + suffix for suffix in ('.BASE', '.OTHER')]
 
-    # FIXME: I smell something weird here and it seems we should be able to be
-    # more coherent with some other conflict ? bzr *did* a choice there but
-    # neither action_take_this nor action_take_other reflect that...
-    # -- vila 20091224
+    def _take_it(self, tt, suffix_to_remove):
+        """Resolve the conflict.
+
+        :param tt: The TreeTransform where the conflict is resolved.
+        :param suffix_to_remove: Either 'THIS' or 'OTHER'
+
+        The resolution is symmetric, when taking THIS, OTHER is deleted and
+        item.THIS is renamed into item and vice-versa.
+        """
+        try:
+            # Delete 'item.THIS' or 'item.OTHER' depending on
+            # suffix_to_remove
+            tt.delete_contents(
+                tt.trans_id_tree_path(self.path + '.' + suffix_to_remove))
+        except errors.NoSuchFile:
+            # There are valid cases where 'item.suffix_to_remove' either
+            # never existed or was already deleted (including the case
+            # where the user deleted it)
+            pass
+        # Rename 'item.suffix_to_remove' (note that if
+        # 'item.suffix_to_remove' has been deleted, this is a no-op)
+        this_tid = tt.trans_id_file_id(self.file_id)
+        parent_tid = tt.get_tree_parent(this_tid)
+        tt.adjust_path(self.path, parent_tid, this_tid)
+        tt.apply()
+
+    def _take_it_with_cleanups(self, tree, suffix_to_remove):
+        tt = transform.TreeTransform(tree)
+        op = cleanup.OperationWithCleanups(self._take_it)
+        op.add_cleanup(tt.finalize)
+        op.run_simple(tt, suffix_to_remove)
+
     def action_take_this(self, tree):
-        tree.remove([self.path + '.OTHER'], force=True, keep_files=False)
+        self._take_it_with_cleanups(tree, 'OTHER')
 
     def action_take_other(self, tree):
-        tree.remove([self.path], force=True, keep_files=False)
-
+        self._take_it_with_cleanups(tree, 'THIS')
 
 
 # FIXME: TextConflict is about a single file-id, there never is a conflict_path

=== modified file 'bzrlib/tests/test_conflicts.py'
--- a/bzrlib/tests/test_conflicts.py	2010-02-23 07:43:11 +0000
+++ b/bzrlib/tests/test_conflicts.py	2010-03-02 07:38:15 +0000
@@ -18,15 +18,32 @@
 import os
 
 from bzrlib import (
+    branchbuilder,
     bzrdir,
     conflicts,
     errors,
     option,
     tests,
+    workingtree,
     )
 from bzrlib.tests import script
 
 
+def load_tests(standard_tests, module, loader):
+    result = loader.suiteClass()
+
+    sp_tests, remaining_tests = tests.split_suite_by_condition(
+        standard_tests, tests.condition_isinstance((
+                TestResolveContentConflicts,
+                )))
+    tests.multiply_tests(sp_tests, content_conflict_scenarios(), result)
+
+    # No parametrization for the remaining tests
+    result.addTests(remaining_tests)
+
+    return result
+
+
 # TODO: Test commit with some added, and added-but-missing files
 # RBC 20060124 is that not tested in test_commit.py ?
 
@@ -69,15 +86,15 @@
                                   ('hello.sploo.OTHER', 'yellowworld2'),
                                   ])
         tree.lock_read()
-        self.assertEqual(6, len(list(tree.list_files())))
+        self.assertLength(6, list(tree.list_files()))
         tree.unlock()
         tree_conflicts = tree.conflicts()
-        self.assertEqual(2, len(tree_conflicts))
+        self.assertLength(2, tree_conflicts)
         self.assertTrue('hello' in tree_conflicts[0].path)
         self.assertTrue('hello.sploo' in tree_conflicts[1].path)
         conflicts.restore('hello')
         conflicts.restore('hello.sploo')
-        self.assertEqual(0, len(tree.conflicts()))
+        self.assertLength(0, tree.conflicts())
         self.assertFileEqual('hello world2', 'hello')
         self.assertFalse(os.path.lexists('hello.sploo'))
         self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
@@ -192,59 +209,99 @@
     pass
 
 
-class TestResolveContentConflicts(TestResolveConflicts):
-
-    # FIXME: We need to add the reverse case (delete in trunk, modify in
-    # branch) but that could wait until the resolution mechanism is implemented.
-
-    preamble = """
-$ bzr init trunk
-$ cd trunk
-$ echo 'trunk content' >file
-$ bzr add file
-$ bzr commit -m 'Create trunk'
-
-$ bzr branch . ../branch
-$ cd ../branch
-$ bzr rm file
-$ bzr commit -m 'Delete file'
-
-$ cd ../trunk
-$ echo 'more content' >>file
-$ bzr commit -m 'Modify file'
-
-$ cd ../branch
-$ bzr merge ../trunk
-2>+N  file.OTHER
-2>Contents conflict in file
-2>1 conflicts encountered.
-"""
-
-    def test_take_this(self):
-        self.run_script("""
-$ bzr rm file.OTHER --force # a simple rm file.OTHER is valid too
-$ bzr resolve file
-$ bzr commit --strict -m 'No more conflicts nor unknown files'
-""")
-
-    def test_take_other(self):
-        self.run_script("""
-$ bzr mv file.OTHER file
-$ bzr resolve file
-$ bzr commit --strict -m 'No more conflicts nor unknown files'
-""")
+def content_conflict_scenarios():
+    return [('file,None', dict(_this_actions='modify_file',
+                               _check_this='file_has_more_content',
+                               _other_actions='delete_file',
+                               _check_other='file_doesnt_exist',
+                               )),
+            ('None,file', dict(_this_actions='delete_file',
+                               _check_this='file_doesnt_exist',
+                               _other_actions='modify_file',
+                               _check_other='file_has_more_content',
+                               )),
+            ]
+
+
+class TestResolveContentConflicts(tests.TestCaseWithTransport):
+
+    # Set by load_tests
+    this_actions = None
+    other_actions = None
+
+    def setUp(self):
+        super(TestResolveContentConflicts, self).setUp()
+        builder = self.make_branch_builder('trunk')
+        builder.start_series()
+        # Create an empty trunk
+        builder.build_snapshot('start', None, [
+                ('add', ('', 'root-id', 'directory', ''))])
+        # Add a minimal base content
+        builder.build_snapshot('base', ['start'], [
+                ('add', ('file', 'file-id', 'file', 'trunk content\n'))])
+        # Modify the base content in branch
+        other_actions = self._get_actions(self._other_actions)
+        builder.build_snapshot('other', ['base'], other_actions())
+        # Modify the base content in trunk
+        this_actions = self._get_actions(self._this_actions)
+        builder.build_snapshot('this', ['base'], this_actions())
+        builder.finish_series()
+        self.builder = builder
+
+    def _get_actions(self, name):
+        return getattr(self, 'do_%s' % name)
+
+    def _get_check(self, name):
+        return getattr(self, 'check_%s' % name)
+
+    def do_modify_file(self):
+        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
+
+    def check_file_has_more_content(self):
+        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
+
+    def do_delete_file(self):
+        return [('unversion', 'file-id')]
+
+    def check_file_doesnt_exist(self):
+        self.failIfExists('branch/file')
+
+    def _merge_other_into_this(self):
+        b = self.builder.get_branch()
+        wt = b.bzrdir.sprout('branch').open_workingtree()
+        wt.merge_from_branch(b, 'other')
+        return wt
+
+    def assertConflict(self, wt, ctype, **kwargs):
+        confs = wt.conflicts()
+        self.assertLength(1, confs)
+        c = confs[0]
+        self.assertIsInstance(c, ctype)
+        sentinel = object() # An impossible value
+        for k, v in kwargs.iteritems():
+            self.assertEqual(v, getattr(c, k, sentinel))
+
+    def check_resolved(self, wt, item, action):
+        conflicts.resolve(wt, [item], action=action)
+        # Check that we don't have any conflicts nor unknown left
+        self.assertLength(0, wt.conflicts())
+        self.assertLength(0, list(wt.unknowns()))
 
     def test_resolve_taking_this(self):
-        self.run_script("""
-$ bzr resolve --take-this file
-$ bzr commit --strict -m 'No more conflicts nor unknown files'
-""")
+        wt = self._merge_other_into_this()
+        self.assertConflict(wt, conflicts.ContentsConflict,
+                            path='file', file_id='file-id',)
+        self.check_resolved(wt, 'file', 'take_this')
+        check_this = self._get_check(self._check_this)
+        check_this()
 
     def test_resolve_taking_other(self):
-        self.run_script("""
-$ bzr resolve --take-other file
-$ bzr commit --strict -m 'No more conflicts nor unknown files'
-""")
+        wt = self._merge_other_into_this()
+        self.assertConflict(wt, conflicts.ContentsConflict,
+                            path='file', file_id='file-id',)
+        self.check_resolved(wt, 'file', 'take_other')
+        check_other = self._get_check(self._check_other)
+        check_other()
 
 
 class TestResolveDuplicateEntry(TestResolveConflicts):
@@ -255,6 +312,7 @@
 $ echo 'trunk content' >file
 $ bzr add file
 $ bzr commit -m 'Create trunk'
+
 $ echo 'trunk content too' >file2
 $ bzr add file2
 $ bzr commit -m 'Add file2 in trunk'
@@ -314,6 +372,7 @@
 $ mkdir dir
 $ bzr add dir
 $ bzr commit -m 'Create trunk'
+
 $ echo 'trunk content' >dir/file
 $ bzr add dir/file
 $ bzr commit -m 'Add dir/file in trunk'
@@ -354,6 +413,7 @@
 $ echo 'trunk content' >dir/file
 $ bzr add
 $ bzr commit -m 'Create trunk'
+
 $ echo 'trunk content' >dir/file2
 $ bzr add dir/file2
 $ bzr commit -m 'Add dir/file2 in branch'
@@ -415,6 +475,7 @@
 $ echo 'trunk content' >dir/file
 $ bzr add
 $ bzr commit -m 'Create trunk'
+
 $ bzr rm dir/file --force
 $ bzr rm dir --force
 $ bzr commit -m 'Remove dir/file'
@@ -474,6 +535,7 @@
 $ echo 'Boo!' >file
 $ bzr add
 $ bzr commit -m 'Create trunk'
+
 $ bzr mv file file-in-trunk
 $ bzr commit -m 'Renamed to file-in-trunk'
 
@@ -522,6 +584,7 @@
 $ bzr mkdir dir1
 $ bzr mkdir dir2
 $ bzr commit -m 'Create trunk'
+
 $ bzr mv dir2 dir1
 $ bzr commit -m 'Moved dir2 into dir1'
 




More information about the bazaar-commits mailing list