Rev 5053: Merge 2.1 into 2.2 including fixes for #262450, #373898, #498409 in http://bazaar.launchpad.net/~vila/bzr/2.2-integration/

Vincent Ladeuil v.ladeuil+lp at free.fr
Thu Apr 15 15:50:25 BST 2010


At http://bazaar.launchpad.net/~vila/bzr/2.2-integration/

------------------------------------------------------------
revno: 5053 [merge]
revision-id: v.ladeuil+lp at free.fr-20100415145017-3jhbks4pqlm8r3tj
parent: pqm at pqm.ubuntu.com-20100414084133-ka1xi7y837y85x85
parent: v.ladeuil+lp at free.fr-20100415120211-1uqa9ss6dflw4bvg
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: 2.2
timestamp: Thu 2010-04-15 16:50:17 +0200
message:
  Merge 2.1 into 2.2 including fixes for #262450, #373898, #498409
added:
  bzrlib/tests/commands/test_revert.py test_revert.py-20100309150314-zqzie40mnu7vcfhg-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/lock.py                 lock.py-20050527050856-ec090bb51bc03349
  bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/commands/__init__.py __init__.py-20070520095518-ecfl8531fxgjeycj-1
  bzrlib/tests/per_workingtree/test_merge_from_branch.py test_merge_from_bran-20060904034200-12jxyk2zlhpufxe1-1
  bzrlib/transport/__init__.py   transport.py-20050711165921-4978aa7ce1285ad5
  doc/developers/testing.txt     testing.txt-20080812140359-i70zzh6v2z7grqex-1
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS	2010-04-14 04:26:53 +0000
+++ b/NEWS	2010-04-15 14:50:17 +0000
@@ -792,12 +792,34 @@
   tests that 'failed' - they're all just failures.
   (Martin Pool)
 
+bzr 2.0.6
+#########
+
+:2.0.6: NOT RELEASED YET
+
+Bug Fixes
+*********
+
+* ``bzr revert`` now only takes write lock on working tree, instead of on 
+  both working tree and branch.
+  (Danny van Heumen, #498409)
+
+* ``bzr upgrade`` now creates the ``backup.bzr`` directory with the same
+  permissions as ``.bzr`` directory on a POSIX OS.
+  (Parth Malwankar, #262450)
+
+* Additional merges after an unrelated branch has been merged with its
+  history no longer crash when deleted files are involved.
+  (Vincent Ladeuil, John Arbash Meinel, #375898)
 
 bzr 2.0.5
 #########
 
-:Codename:
-:2.0.5: NOT RELEASED YET
+:2.0.5: 2010-03-23
+
+This fifth release in our 2.0 series addresses several user-inconvenience
+bugs.  None are critical, but upgrading is recommended for all users on
+earlier 2.0 releases.
 
 Bug Fixes
 *********
@@ -826,9 +848,6 @@
 * Handle renames correctly when there are files or directories that 
   differ only in case.  (Chris Jones, Martin Pool, #368931)
 
-* Fixed CHM generation by moving the NEWS section template into
-  a separate file. (Ian Clatworthy, #524184)
-
 * If ``bzr push --create-prefix`` triggers an unexpected ``NoSuchFile``
   error, report that error rather than failing with an unhelpful
   ``UnboundLocalError``.
@@ -848,6 +867,10 @@
 * Added ``location-alias`` help topic.
   (Andrew Bennetts, #337834)
 
+* Fixed CHM generation by moving the NEWS section template into
+  a separate file. (Ian Clatworthy, #524184)
+
+
 bzr 2.0.4
 #########
 

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2010-04-13 09:19:53 +0000
+++ b/bzrlib/builtins.py	2010-04-15 14:50:17 +0000
@@ -4193,7 +4193,7 @@
     def run(self, revision=None, no_backup=False, file_list=None,
             forget_merges=None):
         tree, file_list = tree_files(file_list)
-        tree.lock_write()
+        tree.lock_tree_write()
         self.add_cleanup(tree.unlock)
         if forget_merges:
             tree.set_parent_ids(tree.get_parent_ids()[:1])

=== modified file 'bzrlib/lock.py'
--- a/bzrlib/lock.py	2010-02-10 17:52:08 +0000
+++ b/bzrlib/lock.py	2010-04-15 14:50:17 +0000
@@ -84,7 +84,7 @@
         return self.lock_url == other.lock_url and self.details == other.details
 
     def __repr__(self):
-        return '%s(%s%s)' % (self.__class__.__name__,
+        return '%s(%s, %s)' % (self.__class__.__name__,
                              self.lock_url, self.details)
 
 

=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2010-04-06 11:29:06 +0000
+++ b/bzrlib/merge.py	2010-04-15 14:50:17 +0000
@@ -1042,21 +1042,38 @@
         other_root = self.tt.trans_id_file_id(other_root_file_id)
         if other_root == self.tt.root:
             return
+        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
+            # the other tree's root is a non-root in the current tree (as when
+            # a previously unrelated branch is merged into another)
+            return
         try:
             self.tt.final_kind(other_root)
+            other_root_is_present = True
         except errors.NoSuchFile:
-            return
-        if self.this_tree.has_id(self.other_tree.inventory.root.file_id):
-            # the other tree's root is a non-root in the current tree
-            return
-        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
-        self.tt.cancel_creation(other_root)
-        self.tt.cancel_versioning(other_root)
-
-    def reparent_children(self, ie, target):
-        for thing, child in ie.children.iteritems():
+            # other_root doesn't have a physical representation. We still need
+            # to move any references to the actual root of the tree.
+            other_root_is_present = False
+        # 'other_tree.inventory.root' is not present in this tree. We are
+        # calling adjust_path for children which *want* to be present with a
+        # correct place to go.
+        for thing, child in self.other_tree.inventory.root.children.iteritems():
             trans_id = self.tt.trans_id_file_id(child.file_id)
-            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
+            if not other_root_is_present:
+                # FIXME: Make final_kind returns None instead of raising
+                # NoSuchFile to avoid the ugly construct below -- vila 20100402
+                try:
+                    self.tt.final_kind(trans_id)
+                    # The item exist in the final tree and has a defined place
+                    # to go already.
+                    continue
+                except errors.NoSuchFile, e:
+                    pass
+            # Move the item into the root
+            self.tt.adjust_path(self.tt.final_name(trans_id),
+                                self.tt.root, trans_id)
+        if other_root_is_present:
+            self.tt.cancel_creation(other_root)
+            self.tt.cancel_versioning(other_root)
 
     def write_modified(self, results):
         modified_hashes = {}

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2010-04-11 19:40:23 +0000
+++ b/bzrlib/tests/__init__.py	2010-04-15 14:50:17 +0000
@@ -4435,3 +4435,27 @@
             return result
 except ImportError:
     pass
+
+class _PosixPermissionsFeature(Feature):
+
+    def _probe(self):
+        def has_perms():
+            # create temporary file and check if specified perms are maintained.
+            import tempfile
+
+            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
+            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
+            fd, name = f
+            os.close(fd)
+            os.chmod(name, write_perms)
+
+            read_perms = os.stat(name).st_mode & 0777
+            os.unlink(name)
+            return (write_perms == read_perms)
+
+        return (os.name == 'posix') and has_perms()
+
+    def feature_name(self):
+        return 'POSIX permissions support'
+
+posix_permissions_feature = _PosixPermissionsFeature()

=== modified file 'bzrlib/tests/commands/__init__.py'
--- a/bzrlib/tests/commands/__init__.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/commands/__init__.py	2010-04-15 08:50:40 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2007 Canonical Ltd
+# Copyright (C) 2007-2010 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -41,6 +41,7 @@
         'bzrlib.tests.commands.test_pull',
         'bzrlib.tests.commands.test_push',
         'bzrlib.tests.commands.test_update',
+        'bzrlib.tests.commands.test_revert',
         ]
     # add the tests for the sub modules
     suite.addTests(loader.loadTestsFromModuleNames(testmod_names))

=== added file 'bzrlib/tests/commands/test_revert.py'
--- a/bzrlib/tests/commands/test_revert.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/commands/test_revert.py	2010-04-15 14:50:17 +0000
@@ -0,0 +1,61 @@
+# Copyright (C) 2010 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+from bzrlib import (
+    branch,
+    builtins,
+    errors,
+    lock,
+    )
+from bzrlib.tests import (
+    transport_util,
+    TestCaseInTempDir,
+    )
+
+
+class TestRevert(TestCaseInTempDir):
+
+    def setUp(self):
+        super(TestRevert, self).setUp()
+
+    def test_revert_tree_write_lock_and_branch_read_lock(self):
+
+        # install lock hooks to find out about cmd_revert's locking actions
+        locks_acquired = []
+        locks_released = []
+        lock.Lock.hooks.install_named_hook('lock_acquired',
+            locks_acquired.append, None)
+        lock.Lock.hooks.install_named_hook('lock_released',
+            locks_released.append, None)
+
+        # execute the revert command (There is nothing to actually revert,
+        # but locks are acquired either way.)
+        revert = builtins.cmd_revert()
+        revert.run()
+
+        # make sure that only one lock is acquired and released.
+        self.assertLength(1, locks_acquired)
+        self.assertLength(1, locks_released)
+
+        # make sure that the nonces are the same, since otherwise
+        # this would not be the same lock.
+        self.assertEqual(locks_acquired[0].details, locks_released[0].details)
+
+        # make sure that the locks are checkout locks.
+        self.assertEndsWith(locks_acquired[0].lock_url, "/checkout/lock")
+        self.assertEndsWith(locks_released[0].lock_url, "/checkout/lock")
+

=== modified file 'bzrlib/tests/per_workingtree/test_merge_from_branch.py'
--- a/bzrlib/tests/per_workingtree/test_merge_from_branch.py	2009-07-10 07:14:02 +0000
+++ b/bzrlib/tests/per_workingtree/test_merge_from_branch.py	2010-04-01 14:06:48 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006-2010 Canonical Ltd
 # Authors:  Robert Collins <robert.collins at canonical.com>
 #
 # This program is free software; you can redistribute it and/or modify
@@ -20,13 +20,14 @@
 import os
 
 from bzrlib import (
+    branchbuilder,
     errors,
     merge
     )
-from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
-
-
-class TestMergeFromBranch(TestCaseWithWorkingTree):
+from bzrlib.tests import per_workingtree
+
+
+class TestMergeFromBranch(per_workingtree.TestCaseWithWorkingTree):
 
     def create_two_trees_for_merging(self):
         """Create two trees that can be merged from.
@@ -114,3 +115,103 @@
                 self.tt.create_file('qux', trans_id)
         this.merge_from_branch(other.branch, merge_type=QuxMerge)
         self.assertEqual('qux', this.get_file_text('foo-id'))
+
+
+class TestMergedBranch(per_workingtree.TestCaseWithWorkingTree):
+
+    def make_branch_builder(self, relpath, format=None):
+        if format is None:
+            format = self.bzrdir_format
+        builder = branchbuilder.BranchBuilder(self.get_transport(),
+                                              format=format)
+        return builder
+
+    def make_inner_branch(self):
+        bld_inner = self.make_branch_builder('inner')
+        bld_inner.start_series()
+        bld_inner.build_snapshot(
+            '1', None,
+            [('add', ('', 'inner-root-id', 'directory', '')),
+             ('add', ('dir', 'dir-id', 'directory', '')),
+             ('add', ('dir/file1', 'file1-id', 'file', 'file1 content\n')),
+             ('add', ('file3', 'file3-id', 'file', 'file3 content\n')),
+             ])
+        bld_inner.build_snapshot(
+            '3', ['1'], [('modify', ('file3-id', 'new file3 contents\n')),])
+        bld_inner.build_snapshot(
+            '2', ['1'],
+            [('add', ('dir/file2', 'file2-id', 'file', 'file2 content\n')),
+             ])
+        bld_inner.finish_series()
+        br = bld_inner.get_branch()
+        return br
+
+    def assertTreeLayout(self, expected, tree):
+        tree.lock_read()
+        try:
+            actual = [e[0] for e in tree.list_files()]
+            # list_files doesn't guarantee order
+            actual = sorted(actual)
+            self.assertEqual(expected, actual)
+        finally:
+            tree.unlock()
+
+    def make_outer_tree(self):
+        outer = self.make_branch_and_tree('outer')
+        self.build_tree_contents([('outer/foo', 'foo')])
+        outer.add('foo', 'foo-id')
+        outer.commit('added foo')
+        inner = self.make_inner_branch()
+        outer.merge_from_branch(inner, to_revision='1', from_revision='null:')
+        outer.commit('merge inner branch')
+        outer.mkdir('dir-outer', 'dir-outer-id')
+        outer.move(['dir', 'file3'], to_dir='dir-outer')
+        outer.commit('rename imported dir and file3 to dir-outer')
+        return outer, inner
+
+    def test_file1_deleted_in_dir(self):
+        outer, inner = self.make_outer_tree()
+        outer.remove(['dir-outer/dir/file1'], keep_files=False)
+        outer.commit('delete file1')
+        outer.merge_from_branch(inner)
+        outer.commit('merge the rest')
+        self.assertTreeLayout(['dir-outer',
+                               'dir-outer/dir',
+                               'dir-outer/dir/file2',
+                               'dir-outer/file3',
+                               'foo'],
+                              outer)
+
+    def test_file3_deleted_in_root(self):
+        # Reproduce bug #375898
+        outer, inner = self.make_outer_tree()
+        outer.remove(['dir-outer/file3'], keep_files=False)
+        outer.commit('delete file3')
+        outer.merge_from_branch(inner)
+        outer.commit('merge the rest')
+        self.assertTreeLayout(['dir-outer',
+                               'dir-outer/dir',
+                               'dir-outer/dir/file1',
+                               'dir-outer/dir/file2',
+                               'foo'],
+                              outer)
+
+
+    def test_file3_in_root_conflicted(self):
+        outer, inner = self.make_outer_tree()
+        outer.remove(['dir-outer/file3'], keep_files=False)
+        outer.commit('delete file3')
+        nb_conflicts = outer.merge_from_branch(inner, to_revision='3')
+        self.assertEqual(4, nb_conflicts)
+        self.assertTreeLayout(['dir-outer',
+                               'dir-outer/dir',
+                               'dir-outer/dir/file1',
+                               # Ideally th conflict helpers should be in
+                               # dir-outer/dir but since we can't easily find
+                               # back the file3 -> outer-dir/dir rename, root
+                               # is good enough -- vila 20100401
+                               'file3.BASE',
+                               'file3.OTHER',
+                               'foo'],
+                              outer)
+

=== modified file 'bzrlib/transport/__init__.py'
--- a/bzrlib/transport/__init__.py	2010-03-17 05:36:11 +0000
+++ b/bzrlib/transport/__init__.py	2010-04-15 14:50:17 +0000
@@ -1065,7 +1065,6 @@
         # use mask to ensure that bits other than rwx are ignored.
         stat = self.stat(from_relpath)
         target.mkdir('.', stat.st_mode & 0777)
-
         source.copy_tree_to_transport(target)
 
     def copy_tree_to_transport(self, to_transport):

=== modified file 'doc/developers/testing.txt'
--- a/doc/developers/testing.txt	2010-03-18 06:25:23 +0000
+++ b/doc/developers/testing.txt	2010-04-15 14:50:17 +0000
@@ -458,25 +458,40 @@
 Testing locking behaviour
 -------------------------
 
-You may want to write tests that particular objects are or aren't locked
-during particular operations: see for example `bug 498409`__.  
-
- __ https://launchpad.net/bugs/498409
-
-The `TestCase` base class registers hooks that record lock actions into 
-``._lock_actions`` in this format::
-
-  [
-    ('acquired', LockResult(file:///tmp/testbzr-J2pcy2.tmp/.bzr/branch-lockc4au55ppz8wdym11z1aq)),
-    ('released', LockResult(file:///tmp/testbzr-J2pcy2.tmp/.bzr/branch-lockc4au55ppz8wdym11z1aq)),
-    ('acquired', LockResult(file:///tmp/testbzr-J2pcy2.tmp/.bzr/repository/lockyxb3rn4sw1oyx1jzkt45)),
-    ('released', LockResult(file:///tmp/testbzr-J2pcy2.tmp/.bzr/repository/lockyxb3rn4sw1oyx1jzkt45)),
-    ('acquired', LockResult(file:///tmp/testbzr-J2pcy2.tmp/.bzr/branch/lockh8c6t28rcjdkgxtndbje)),
-    ('released', LockResult(file:///tmp/testbzr-J2pcy2.tmp/.bzr/branch/lockh8c6t28rcjdkgxtndbje)),
-    ...
-
-Alternatively you can register your own hooks to make custom assertions:
-see `TestCase._check_locks` for an example.
+In order to test the locking behaviour of commands, it is possible to install
+a hook that is called when a write lock is: acquired, released or broken.
+(Read locks also exist, they cannot be discovered in this way.)
+
+A hook can be installed by calling bzrlib.lock.Lock.hooks.install_named_hook.
+The three valid hooks are: `lock_acquired`, `lock_released` and `lock_broken`.
+
+Example::
+
+    locks_acquired = []
+    locks_released = []
+
+    lock.Lock.hooks.install_named_hook('lock_acquired',
+        locks_acquired.append, None)
+    lock.Lock.hooks.install_named_hook('lock_released',
+        locks_released.append, None)
+
+`locks_acquired` will now receive a LockResult instance for all locks acquired
+since the time the hook is installed.
+
+The last part of the `lock_url` allows you to identify the type of object that is locked.
+
+- BzrDir: `/branch-lock`
+- Working tree: `/checkout/lock`
+- Branch: `/branch/lock`
+- Repository: `/repository/lock`
+
+To test if a lock is a write lock on a working tree, one can do the following::
+
+    self.assertEndsWith(locks_acquired[0].lock_url, "/checkout/lock")
+
+See bzrlib/tests/commands/test_revert.py for an example of how to use this for
+testing locks.
+
 
 Skipping tests
 --------------



More information about the bazaar-commits mailing list