Rev 4725: Merge bzr/2.0 at 4725, resolve NEWS in http://bazaar.launchpad.net/~jameinel/bzr/bdist_rpm

John Arbash Meinel john at arbash-meinel.com
Wed Jan 13 16:27:48 GMT 2010


At http://bazaar.launchpad.net/~jameinel/bzr/bdist_rpm

------------------------------------------------------------
revno: 4725 [merge]
revision-id: john at arbash-meinel.com-20100113162722-c177orxwb4fo19xe
parent: john at arbash-meinel.com-20100113162457-dlhtnmy57mfmddo5
parent: pqm at pqm.ubuntu.com-20100113074324-g4mbj7sdxktz602t
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: bdist_rpm
timestamp: Wed 2010-01-13 10:27:22 -0600
message:
  Merge bzr/2.0 at 4725, resolve NEWS
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
  bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
  bzrlib/tests/blackbox/test_pull.py test_pull.py-20051201144907-64959364f629947f
  bzrlib/tests/per_workingtree/test_pull.py test_pull.py-20060222044334-43594dd8e143b708
  bzrlib/tests/per_workingtree/test_set_root_id.py test_set_root_id.py-20061004073850-0r1c7qikmnkb8m9k-1
  bzrlib/tests/per_workingtree/test_workingtree.py test_workingtree.py-20060203003124-817757d3e31444fb
  bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
  bzrlib/tests/test_revert.py    test_revert.py-20060828180832-fqb1v6ecpyvnlitj-1
  bzrlib/tests/test_shelf.py     test_prepare_shelf.p-20081005181341-n74qe6gu1e65ad4v-2
  bzrlib/tests/test_switch.py    test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
  bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
  bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
  bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
  setup.py                       setup.py-20050314065409-02f8a0a6e3f9bc70
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS	2010-01-13 16:24:57 +0000
+++ b/NEWS	2010-01-13 16:27:22 +0000
@@ -41,12 +41,21 @@
 * Give a clearer message if the lockdir disappears after being apparently
   successfully taken.  (Martin Pool, #498378)
 
+* Give a warning when fetching between local repositories with
+  sufficiently different formats that the content will need to be
+  serialized (ie ``InterDifferingSerializer``) so the user has a clue that
+  upgrading could make it faster.
+  (Martin Pool, #456077)
+
 * If we fail to open ``~/.bzr.log`` write a clear message to stderr rather
   than using ``warning()``. The log file is opened before logging is set
   up, and it leads to very confusing: 'no handlers for "bzr"' messages for
   users, rather than something nicer.
   (John Arbash Meinel, Barry Warsaw, #503886)
 
+* Refuse to build with any Pyrex 0.9.4 release, as they have known bugs.
+  (Martin Pool, John Arbash Meinel, #449372)
+
 * ``setup.py bdist_rpm`` now properly finds extra files needed for the
   build. (Joe Julian)
 
@@ -54,6 +63,12 @@
   changed underneath it (like another autopack). Now concurrent
   autopackers will properly succeed. (John Arbash Meinel, #495000)
 
+* ``TreeTransform`` can now handle when a delta says that the file id for
+  the tree root changes. Rather than trying to rename your working
+  directory, or failing early saying that you can't have multiple
+  tree roots. This also fixes revert, update, and pull when the root id
+  changes.  (John Arbash Meinel, #494269, #504390)
+
 * ``_update_current_block`` no longer suppresses exceptions, so ^C at just
   the right time will get propagated, rather than silently failing to move
   the block pointer. (John Arbash Meinel, Gareth White, #495023)

=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py	2009-10-01 00:56:56 +0000
+++ b/bzrlib/dirstate.py	2010-01-12 22:32:34 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007, 2008 Canonical Ltd
+# Copyright (C) 2006-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
@@ -1997,6 +1997,8 @@
                 entry_index, present = self._find_entry_index(key, block)
                 if present:
                     entry = self._dirblocks[block_index][1][entry_index]
+                    # TODO: We might want to assert that entry[0][2] ==
+                    #       fileid_utf8.
                     if entry[1][tree_index][0] in 'fdlt':
                         # this is the result we are looking for: the
                         # real home of this file_id in this tree.
@@ -2354,8 +2356,6 @@
         self.update_minimal(('', '', new_id), 'd',
             path_utf8='', packed_stat=entry[1][0][4])
         self._dirblock_state = DirState.IN_MEMORY_MODIFIED
-        if self._id_index is not None:
-            self._id_index.setdefault(new_id, set()).add(entry[0])
 
     def set_parent_trees(self, trees, ghosts):
         """Set the parent trees for the dirstate.
@@ -3013,6 +3013,13 @@
             if absent_positions == tree_count:
                 raise AssertionError(
                     "entry %r has no data for any tree." % (entry,))
+        if self._id_index is not None:
+            for file_id, entry_keys in self._id_index.iteritems():
+                for entry_key in entry_keys:
+                    if entry_key[2] != file_id:
+                        raise AssertionError(
+                            'file_id %r did not match entry key %s'
+                            % (file_id, entry_key))
 
     def _wipe_state(self):
         """Forget all state information about the dirstate."""

=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2009-09-03 02:04:04 +0000
+++ b/bzrlib/repository.py	2010-01-12 06:12:31 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
+# Copyright (C) 2005-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
@@ -39,6 +39,7 @@
     osutils,
     revision as _mod_revision,
     symbol_versioning,
+    trace,
     tsort,
     ui,
     versionedfile,
@@ -3979,6 +3980,15 @@
         """See InterRepository.fetch()."""
         if fetch_spec is not None:
             raise AssertionError("Not implemented yet...")
+        # See <https://launchpad.net/bugs/456077> asking for a warning here
+        #
+        # nb this is only active for local-local fetches; other things using
+        # streaming.
+        trace.warning("Fetching between repositories with different formats\n"
+            "from %s to %s.\n"
+            "This may take some time. Upgrade the branches to the same format \n"
+            "for better results.\n"
+            % (self.source._format, self.target._format))
         if (not self.source.supports_rich_root()
             and self.target.supports_rich_root()):
             self._converting_to_rich_root = True

=== modified file 'bzrlib/tests/blackbox/test_pull.py'
--- a/bzrlib/tests/blackbox/test_pull.py	2009-06-15 06:47:14 +0000
+++ b/bzrlib/tests/blackbox/test_pull.py	2010-01-13 16:27:22 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006 Canonical Ltd
+# Copyright (C) 2005-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
@@ -390,4 +390,14 @@
         self.assertLength(18, self.hpss_calls)
         remote = Branch.open('stacked')
         self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
+    
+    def test_pull_cross_format_warning(self):
+        """You get a warning for probably slow cross-format pulls.
+        """
 
+        from_tree = self.make_branch_and_tree('from', format='2a')
+        to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
+        from_tree.commit(message='first commit')
+        out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
+        self.assertContainsRe(err,
+            "(?m)Fetching between repositories with different formats.*")

=== modified file 'bzrlib/tests/per_workingtree/test_pull.py'
--- a/bzrlib/tests/per_workingtree/test_pull.py	2009-07-10 07:14:02 +0000
+++ b/bzrlib/tests/per_workingtree/test_pull.py	2010-01-11 23:02:32 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2007, 2009, 2010 Canonical Ltd
 # Authors:  Robert Collins <robert.collins at canonical.com>
 #
 # This program is free software; you can redistribute it and/or modify
@@ -56,3 +56,15 @@
         tree_b.pull(tree_a.branch)
         self.assertFileEqual('contents of from/file\n', 'to/file')
 
+    def test_pull_changes_root_id(self):
+        tree = self.make_branch_and_tree('from')
+        tree.set_root_id('first_root_id')
+        self.build_tree(['from/file'])
+        tree.add(['file'])
+        tree.commit('first')
+        to_tree = tree.bzrdir.sprout('to').open_workingtree()
+        self.assertEqual('first_root_id', to_tree.get_root_id())
+        tree.set_root_id('second_root_id')
+        tree.commit('second')
+        to_tree.pull(tree.branch)
+        self.assertEqual('second_root_id', to_tree.get_root_id())

=== modified file 'bzrlib/tests/per_workingtree/test_set_root_id.py'
--- a/bzrlib/tests/per_workingtree/test_set_root_id.py	2009-09-10 06:32:42 +0000
+++ b/bzrlib/tests/per_workingtree/test_set_root_id.py	2010-01-12 22:34:03 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006-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
@@ -16,7 +16,7 @@
 
 """Tests for WorkingTree.set_root_id"""
 
-from bzrlib import inventory
+from bzrlib import errors, inventory
 from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
 
 
@@ -48,3 +48,19 @@
         # should still be retained
         tree = tree.bzrdir.open_workingtree()
         self.assertEqual(root_id, tree.get_root_id())
+        tree._validate()
+
+    def test_set_root_id(self):
+        tree = self.make_branch_and_tree('.')
+        tree.lock_write()
+        self.addCleanup(tree.unlock)
+        orig_root_id = tree.get_root_id()
+        self.assertNotEqual('custom-root-id', orig_root_id)
+        self.assertEqual('', tree.id2path(orig_root_id))
+        self.assertRaises(errors.NoSuchId, tree.id2path, 'custom-root-id')
+        tree.set_root_id('custom-root-id')
+        self.assertEqual('custom-root-id', tree.get_root_id())
+        self.assertEqual('custom-root-id', tree.path2id(''))
+        self.assertEqual('', tree.id2path('custom-root-id'))
+        self.assertRaises(errors.NoSuchId, tree.id2path, orig_root_id)
+        tree._validate()

=== modified file 'bzrlib/tests/per_workingtree/test_workingtree.py'
--- a/bzrlib/tests/per_workingtree/test_workingtree.py	2009-08-20 04:09:58 +0000
+++ b/bzrlib/tests/per_workingtree/test_workingtree.py	2010-01-13 16:27:22 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
+# Copyright (C) 2006-2010 Canonical Ltd
 # Authors:  Robert Collins <robert.collins at canonical.com>
 #           and others
 #
@@ -478,6 +478,20 @@
         self.assertEqual(wt.get_root_id(), checkout.get_root_id())
         self.assertNotEqual(None, wt.get_root_id())
 
+    def test_update_sets_updated_root_id(self):
+        wt = self.make_branch_and_tree('tree')
+        wt.set_root_id('first_root_id')
+        self.assertEqual('first_root_id', wt.get_root_id())
+        self.build_tree(['tree/file'])
+        wt.add(['file'])
+        wt.commit('first')
+        co = wt.branch.create_checkout('checkout')
+        wt.set_root_id('second_root_id')
+        wt.commit('second')
+        self.assertEqual('second_root_id', wt.get_root_id())
+        self.assertEqual(0, co.update())
+        self.assertEqual('second_root_id', co.get_root_id())
+
     def test_update_returns_conflict_count(self):
         # working tree formats from the meta-dir format and newer support
         # setting the last revision on a tree independently of that on the

=== modified file 'bzrlib/tests/test_dirstate.py'
--- a/bzrlib/tests/test_dirstate.py	2009-08-17 03:33:56 +0000
+++ b/bzrlib/tests/test_dirstate.py	2010-01-12 20:51:58 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007 Canonical Ltd
+# Copyright (C) 2006-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
@@ -873,15 +873,23 @@
         state = dirstate.DirState.initialize('dirstate')
         try:
             # check precondition to be sure the state does change appropriately.
-            self.assertEqual(
-                [(('', '', 'TREE_ROOT'), [('d', '', 0, False,
-                   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])],
-                list(state._iter_entries()))
-            state.set_path_id('', 'foobarbaz')
-            expected_rows = [
-                (('', '', 'foobarbaz'), [('d', '', 0, False,
-                   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])]
+            root_entry = (('', '', 'TREE_ROOT'), [('d', '', 0, False, 'x'*32)])
+            self.assertEqual([root_entry], list(state._iter_entries()))
+            self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
+            self.assertEqual(root_entry,
+                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
+            self.assertEqual((None, None),
+                             state._get_entry(0, fileid_utf8='second-root-id'))
+            state.set_path_id('', 'second-root-id')
+            new_root_entry = (('', '', 'second-root-id'),
+                              [('d', '', 0, False, 'x'*32)])
+            expected_rows = [new_root_entry]
             self.assertEqual(expected_rows, list(state._iter_entries()))
+            self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
+            self.assertEqual(new_root_entry, 
+                             state._get_entry(0, fileid_utf8='second-root-id'))
+            self.assertEqual((None, None),
+                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
             # should work across save too
             state.save()
         finally:
@@ -905,21 +913,36 @@
         state._validate()
         try:
             state.set_parent_trees([('parent-revid', rt)], ghosts=[])
-            state.set_path_id('', 'foobarbaz')
+            root_entry = (('', '', 'TREE_ROOT'),
+                          [('d', '', 0, False, 'x'*32),
+                           ('d', '', 0, False, 'parent-revid')])
+            self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
+            self.assertEqual(root_entry,
+                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
+            self.assertEqual((None, None),
+                             state._get_entry(0, fileid_utf8='Asecond-root-id'))
+            state.set_path_id('', 'Asecond-root-id')
             state._validate()
             # now see that it is what we expected
-            expected_rows = [
-                (('', '', 'TREE_ROOT'),
-                    [('a', '', 0, False, ''),
-                     ('d', '', 0, False, 'parent-revid'),
-                     ]),
-                (('', '', 'foobarbaz'),
-                    [('d', '', 0, False, ''),
-                     ('a', '', 0, False, ''),
-                     ]),
-                ]
+            old_root_entry = (('', '', 'TREE_ROOT'),
+                              [('a', '', 0, False, ''),
+                               ('d', '', 0, False, 'parent-revid')])
+            new_root_entry = (('', '', 'Asecond-root-id'),
+                              [('d', '', 0, False, ''),
+                               ('a', '', 0, False, '')])
+            expected_rows = [new_root_entry, old_root_entry]
             state._validate()
             self.assertEqual(expected_rows, list(state._iter_entries()))
+            self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
+            self.assertEqual(old_root_entry, state._get_entry(1, path_utf8=''))
+            self.assertEqual((None, None),
+                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
+            self.assertEqual(old_root_entry,
+                             state._get_entry(1, fileid_utf8='TREE_ROOT'))
+            self.assertEqual(new_root_entry,
+                             state._get_entry(0, fileid_utf8='Asecond-root-id'))
+            self.assertEqual((None, None),
+                             state._get_entry(1, fileid_utf8='Asecond-root-id'))
             # should work across save too
             state.save()
         finally:

=== modified file 'bzrlib/tests/test_revert.py'
--- a/bzrlib/tests/test_revert.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/test_revert.py	2010-01-11 22:27:17 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2007, 2009, 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
@@ -157,3 +157,14 @@
         self.failUnlessExists('dir/file1')
         self.failIfExists('dir/file2')
         self.assertEqual('dir-id', tree.path2id('dir'))
+
+    def test_revert_root_id_change(self):
+        tree = self.make_branch_and_tree('.')
+        tree.set_root_id('initial-root-id')
+        self.build_tree(['file1'])
+        tree.add(['file1'])
+        tree.commit('first')
+        tree.set_root_id('temp-root-id')
+        self.assertEqual('temp-root-id', tree.get_root_id())
+        tree.revert()
+        self.assertEqual('initial-root-id', tree.get_root_id())

=== modified file 'bzrlib/tests/test_shelf.py'
--- a/bzrlib/tests/test_shelf.py	2009-09-10 06:32:42 +0000
+++ b/bzrlib/tests/test_shelf.py	2010-01-12 18:05:31 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2008 Canonical Ltd
+# Copyright (C) 2008, 2009, 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
@@ -108,6 +108,29 @@
         creator.shelve_change(('rename', 'baz-id', 'foo/baz', 'bar/baz'))
         self.check_shelve_move(creator, tree)
 
+    def test_shelve_changed_root_id(self):
+        tree = self.make_branch_and_tree('.')
+        self.build_tree(['foo'])
+        tree.set_root_id('first-root-id')
+        tree.add(['foo'], ['foo-id'])
+        tree.commit('foo')
+        tree.set_root_id('second-root-id')
+        tree.lock_tree_write()
+        self.addCleanup(tree.unlock)
+        creator = shelf.ShelfCreator(tree, tree.basis_tree())
+        self.addCleanup(creator.finalize)
+        self.expectFailure('shelf doesn\'t support shelving root changes yet',
+            self.assertEqual, [
+                ('delete file', 'first-root-id', 'directory', ''),
+                ('add file', 'second-root-id', 'directory', ''),
+                ('rename', 'foo-id', u'foo', u'foo'),
+                ], list(creator.iter_shelvable()))
+
+        self.assertEqual([('delete file', 'first-root-id', 'directory', ''),
+                          ('add file', 'second-root-id', 'directory', ''),
+                          ('rename', 'foo-id', u'foo', u'foo'),
+                         ], list(creator.iter_shelvable()))
+
     def assertShelvedFileEqual(self, expected_content, creator, file_id):
         s_trans_id = creator.shelf_transform.trans_id_file_id(file_id)
         shelf_file = creator.shelf_transform._limbo_name(s_trans_id)

=== modified file 'bzrlib/tests/test_switch.py'
--- a/bzrlib/tests/test_switch.py	2009-05-07 05:08:46 +0000
+++ b/bzrlib/tests/test_switch.py	2010-01-12 18:10:23 +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
@@ -100,6 +100,18 @@
         self.assertContainsRe(str(err),
             "Pending merges must be committed or reverted before using switch")
 
+    def test_switch_changing_root_id(self):
+        tree = self._setup_tree()
+        tree2 = self.make_branch_and_tree('tree-2')
+        tree2.set_root_id('custom-root-id')
+        self.build_tree(['tree-2/file-2'])
+        tree2.add(['file-2'])
+        tree2.commit('rev1b')
+        checkout = tree.branch.create_checkout('checkout',
+            lightweight=self.lightweight)
+        switch.switch(checkout.bzrdir, tree2.branch)
+        self.assertEqual('custom-root-id', tree2.get_root_id())
+
 
 class TestSwitchHeavyweight(TestSwitch):
 

=== modified file 'bzrlib/tests/test_transform.py'
--- a/bzrlib/tests/test_transform.py	2009-10-14 18:37:13 +0000
+++ b/bzrlib/tests/test_transform.py	2010-01-13 16:27:22 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007, 2008 Canonical Ltd
+# Copyright (C) 2006-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
@@ -136,6 +136,36 @@
         transform.finalize()
         transform.finalize()
 
+    def test_change_root_id(self):
+        transform, root = self.get_transform()
+        self.assertNotEqual('new-root-id', self.wt.get_root_id())
+        transform.new_directory('', ROOT_PARENT, 'new-root-id')
+        transform.delete_contents(root)
+        transform.unversion_file(root)
+        transform.fixup_new_roots()
+        transform.apply()
+        self.assertEqual('new-root-id', self.wt.get_root_id())
+
+    def test_change_root_id_add_files(self):
+        transform, root = self.get_transform()
+        self.assertNotEqual('new-root-id', self.wt.get_root_id())
+        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
+        transform.new_file('file', new_trans_id, ['new-contents\n'],
+                           'new-file-id')
+        transform.delete_contents(root)
+        transform.unversion_file(root)
+        transform.fixup_new_roots()
+        transform.apply()
+        self.assertEqual('new-root-id', self.wt.get_root_id())
+        self.assertEqual('new-file-id', self.wt.path2id('file'))
+        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
+
+    def test_add_two_roots(self):
+        transform, root = self.get_transform()
+        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
+        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
+        self.assertRaises(ValueError, transform.fixup_new_roots)
+
     def test_hardlink(self):
         self.requireFeature(HardlinkFeature)
         transform, root = self.get_transform()
@@ -746,7 +776,7 @@
         create.apply()
         transform, root = self.get_transform()
         transform.adjust_root_path('oldroot', fun)
-        new_root=transform.trans_id_tree_path('')
+        new_root = transform.trans_id_tree_path('')
         transform.version_file('new-root', new_root)
         transform.apply()
 
@@ -2428,7 +2458,7 @@
 
     def test_file_content_summary_executable(self):
         if not osutils.supports_executable():
-            raise TestNotApplicable()
+            raise tests.TestNotApplicable()
         preview = self.get_empty_preview()
         path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
         preview.set_executability(True, path_id)
@@ -2443,8 +2473,6 @@
         self.assertIs(None, summary[3])
 
     def test_change_executability(self):
-        if not osutils.supports_executable():
-            raise TestNotApplicable()
         tree = self.make_branch_and_tree('tree')
         self.build_tree(['tree/path'])
         tree.add('path')
@@ -2616,7 +2644,7 @@
         preview = self.get_empty_preview()
         root = preview.new_directory('', ROOT_PARENT, 'tree-root')
         # FIXME: new_directory should mark root.
-        preview.adjust_path('', ROOT_PARENT, root)
+        preview.fixup_new_roots()
         preview_tree = preview.get_preview_tree()
         file_trans_id = preview.new_file('a', preview.root, 'contents',
                                          'a-id')

=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py	2009-10-28 07:34:55 +0000
+++ b/bzrlib/transform.py	2010-01-13 16:27:22 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
+# Copyright (C) 2006-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
@@ -161,14 +161,12 @@
 
     def adjust_path(self, name, parent, trans_id):
         """Change the path that is assigned to a transaction id."""
+        if parent is None:
+            raise ValueError("Parent trans-id may not be None")
         if trans_id == self._new_root:
             raise CantMoveRoot
         self._new_name[trans_id] = name
         self._new_parent[trans_id] = parent
-        if parent == ROOT_PARENT:
-            if self._new_root is not None:
-                raise ValueError("Cannot have multiple roots.")
-            self._new_root = trans_id
 
     def adjust_root_path(self, name, parent):
         """Emulate moving the root by moving all children, instead.
@@ -202,6 +200,67 @@
         self.version_file(old_root_file_id, old_root)
         self.unversion_file(self._new_root)
 
+    def fixup_new_roots(self):
+        """Reinterpret requests to change the root directory
+
+        Instead of creating a root directory, or moving an existing directory,
+        all the attributes and children of the new root are applied to the
+        existing root directory.
+
+        This means that the old root trans-id becomes obsolete, so it is
+        recommended only to invoke this after the root trans-id has become
+        irrelevant.
+        """
+        new_roots = [k for k, v in self._new_parent.iteritems() if v is
+                     ROOT_PARENT]
+        if len(new_roots) < 1:
+            return
+        if len(new_roots) != 1:
+            raise ValueError('A tree cannot have two roots!')
+        if self._new_root is None:
+            self._new_root = new_roots[0]
+            return
+        old_new_root = new_roots[0]
+        # TODO: What to do if a old_new_root is present, but self._new_root is
+        #       not listed as being removed? This code explicitly unversions
+        #       the old root and versions it with the new file_id. Though that
+        #       seems like an incomplete delta
+
+        # unversion the new root's directory.
+        file_id = self.final_file_id(old_new_root)
+        if old_new_root in self._new_id:
+            self.cancel_versioning(old_new_root)
+        else:
+            self.unversion_file(old_new_root)
+        # if, at this stage, root still has an old file_id, zap it so we can
+        # stick a new one in.
+        if (self.tree_file_id(self._new_root) is not None and
+            self._new_root not in self._removed_id):
+            self.unversion_file(self._new_root)
+        self.version_file(file_id, self._new_root)
+
+        # Now move children of new root into old root directory.
+        # Ensure all children are registered with the transaction, but don't
+        # use directly-- some tree children have new parents
+        list(self.iter_tree_children(old_new_root))
+        # Move all children of new root into old root directory.
+        for child in self.by_parent().get(old_new_root, []):
+            self.adjust_path(self.final_name(child), self._new_root, child)
+
+        # Ensure old_new_root has no directory.
+        if old_new_root in self._new_contents:
+            self.cancel_creation(old_new_root)
+        else:
+            self.delete_contents(old_new_root)
+
+        # prevent deletion of root directory.
+        if self._new_root in self._removed_contents:
+            self.cancel_deletion(self._new_root)
+
+        # destroy path info for old_new_root.
+        del self._new_parent[old_new_root]
+        del self._new_name[old_new_root]
+
     def trans_id_tree_file_id(self, inventory_id):
         """Determine the transaction id of a working tree file.
 
@@ -253,6 +312,8 @@
 
     def delete_contents(self, trans_id):
         """Schedule the contents of a path entry for deletion"""
+        # Ensure that the object exists in the WorkingTree, this will raise an
+        # exception if there is a problem
         self.tree_kind(trans_id)
         self._removed_contents.add(trans_id)
 
@@ -1075,8 +1136,10 @@
         if (trans_id in self._limbo_files and
             trans_id not in self._needs_rename):
             self._rename_in_limbo([trans_id])
-            self._limbo_children[previous_parent].remove(trans_id)
-            del self._limbo_children_names[previous_parent][previous_name]
+            if previous_parent != parent:
+                self._limbo_children[previous_parent].remove(trans_id)
+            if previous_parent != parent or previous_name != name:
+                del self._limbo_children_names[previous_parent][previous_name]
 
     def _rename_in_limbo(self, trans_ids):
         """Fix limbo names so that the right final path is produced.
@@ -1542,10 +1605,10 @@
                 child_pb.update('removing file', num, len(tree_paths))
                 full_path = self._tree.abspath(path)
                 if trans_id in self._removed_contents:
-                    mover.pre_delete(full_path, os.path.join(self._deletiondir,
-                                     trans_id))
-                elif trans_id in self._new_name or trans_id in \
-                    self._new_parent:
+                    delete_path = os.path.join(self._deletiondir, trans_id)
+                    mover.pre_delete(full_path, delete_path)
+                elif (trans_id in self._new_name
+                      or trans_id in self._new_parent):
                     try:
                         mover.rename(full_path, self._limbo_name(trans_id))
                     except OSError, e:
@@ -2636,7 +2699,10 @@
                     parent_trans = ROOT_PARENT
                 else:
                     parent_trans = tt.trans_id_file_id(parent[1])
-                tt.adjust_path(name[1], parent_trans, trans_id)
+                if parent[0] is None and versioned[0]:
+                    tt.adjust_root_path(name[1], parent_trans)
+                else:
+                    tt.adjust_path(name[1], parent_trans, trans_id)
             if executable[0] != executable[1] and kind[1] == "file":
                 tt.set_executability(executable[1], trans_id)
         if working_tree.supports_content_filtering():
@@ -2655,6 +2721,7 @@
             for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
                 deferred_files):
                 tt.create_file(bytes, trans_id, mode_id)
+        tt.fixup_new_roots()
     finally:
         if basis_tree is not None:
             basis_tree.unlock()

=== modified file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py	2009-08-26 05:38:16 +0000
+++ b/bzrlib/workingtree.py	2010-01-13 16:27:22 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
+# Copyright (C) 2005-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
@@ -1624,9 +1624,10 @@
                                 this_tree=self,
                                 pb=pb,
                                 change_reporter=change_reporter)
-                    if (basis_tree.inventory.root is None and
-                        new_basis_tree.inventory.root is not None):
-                        self.set_root_id(new_basis_tree.get_root_id())
+                    basis_root_id = basis_tree.get_root_id()
+                    new_root_id = new_basis_tree.get_root_id()
+                    if basis_root_id != new_root_id:
+                        self.set_root_id(new_root_id)
                 finally:
                     pb.finished()
                     basis_tree.unlock()
@@ -2245,8 +2246,10 @@
             basis.lock_read()
             try:
                 to_tree = self.branch.basis_tree()
-                if basis.inventory.root is None:
-                    self.set_root_id(to_tree.get_root_id())
+                to_root_id = to_tree.get_root_id()
+                if (basis.inventory.root is None
+                    or basis.inventory.root.file_id != to_root_id):
+                    self.set_root_id(to_root_id)
                     self.flush()
                 result += merge.merge_inner(
                                       self.branch,

=== modified file 'setup.py'
--- a/setup.py	2009-10-30 14:07:31 +0000
+++ b/setup.py	2010-01-12 01:44:13 +0000
@@ -278,11 +278,13 @@
     add_pyrex_extension('bzrlib._walkdirs_win32')
     z_lib = 'zdll'
 else:
-    if have_pyrex and pyrex_version == '0.9.4.1':
+    if have_pyrex and pyrex_version.startswith('0.9.4'):
         # Pyrex 0.9.4.1 fails to compile this extension correctly
         # The code it generates re-uses a "local" pointer and
         # calls "PY_DECREF" after having set it to NULL. (It mixes PY_XDECREF
         # which is NULL safe with PY_DECREF which is not.)
+        # <https://bugs.edge.launchpad.net/bzr/+bug/449372>
+        # <https://bugs.edge.launchpad.net/bzr/+bug/276868>
         print 'Cannot build extension "bzrlib._dirstate_helpers_pyx" using'
         print 'your version of pyrex "%s". Please upgrade your pyrex' % (
             pyrex_version,)



More information about the bazaar-commits mailing list