Rev 6441: (jelmer) Add pre_command and post_command hooks. (Jelmer Vernooij) in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

Patch Queue Manager pqm at pqm.ubuntu.com
Wed Jan 18 20:08:49 UTC 2012


At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 6441 [merge]
revision-id: pqm at pqm.ubuntu.com-20120118200849-i93mg9cvcwxpvth3
parent: pqm at pqm.ubuntu.com-20120118162331-md4sf1tw6hyuw344
parent: jelmer at samba.org-20120118194010-whqj4i18csb3x848
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2012-01-18 20:08:49 +0000
message:
  (jelmer) Add pre_command and post_command hooks. (Jelmer Vernooij)
modified:
  bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
  bzrlib/commands.py             bzr.py-20050309040720-d10f4714595cf8c3
  bzrlib/controldir.py           controldir.py-20100802102926-hvtvh0uae5epuibp-1
  bzrlib/mutabletree.py          mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
  bzrlib/plugins/weave_fmt/branch.py branch_weave.py-20110303112759-greg4a9dt5pent0m-1
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/smart/bzrdir.py         bzrdir.py-20061122024551-ol0l0o0oofsu9b3t-1
  bzrlib/tests/per_bzrdir/test_bzrdir.py test_bzrdir.py-20100829143338-2uachgod1c3liktl-1
  bzrlib/tests/per_controldir/test_controldir.py test_bzrdir.py-20060131065642-0ebeca5e30e30866
  bzrlib/tests/per_controldir_colo/test_unsupported.py test_unsupported.py-20100411192232-kawv9qu1t42gv89k-4
  bzrlib/tests/test_commands.py  test_command.py-20051019190109-3b17be0f52eaa7a8
  bzrlib/tests/test_foreign.py   test_foreign.py-20081125004048-ywb901edgp9lluxo-1
  bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
  bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
  doc/en/admin-guide/migration.txt migration.txt-20091205144603-lgpl0e0z6lzk2rdw-8
  doc/en/release-notes/bzr-2.5.txt bzr2.5.txt-20110708125756-587p0hpw7oke4h05-1
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py	2012-01-04 17:12:42 +0000
+++ b/bzrlib/branch.py	2012-01-07 01:45:31 +0000
@@ -2038,6 +2038,8 @@
         :param name: Name of colocated branch to create, if any
         :return: a branch in this format
         """
+        if name is None:
+            name = a_bzrdir._get_selected_branch()
         mutter('creating branch %r in %s', self, a_bzrdir.user_url)
         branch_transport = a_bzrdir.get_branch_transport(self, name=name)
         control_files = lockable_files.LockableFiles(branch_transport,
@@ -2060,6 +2062,8 @@
     def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
             found_repository=None, possible_transports=None):
         """See BranchFormat.open()."""
+        if name is None:
+            name = a_bzrdir._get_selected_branch()
         if not _found:
             format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
             if format.__class__ != self.__class__:
@@ -2305,12 +2309,13 @@
         mutter('creating branch reference in %s', a_bzrdir.user_url)
         if a_bzrdir._format.fixed_components:
             raise errors.IncompatibleFormat(self, a_bzrdir._format)
+        if name is None:
+            name = a_bzrdir._get_selected_branch()
         branch_transport = a_bzrdir.get_branch_transport(self, name=name)
         branch_transport.put_bytes('location',
             target_branch.user_url)
         branch_transport.put_bytes('format', self.as_string())
-        branch = self.open(
-            a_bzrdir, name, _found=True,
+        branch = self.open(a_bzrdir, name, _found=True,
             possible_transports=[target_branch.bzrdir.root_transport])
         self._run_post_branch_init_hooks(a_bzrdir, name, branch)
         return branch
@@ -2342,6 +2347,8 @@
             a_bzrdir.
         :param possible_transports: An optional reusable transports list.
         """
+        if name is None:
+            name = a_bzrdir._get_selected_branch()
         if not _found:
             format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
             if format.__class__ != self.__class__:
@@ -2351,8 +2358,7 @@
             location = self.get_reference(a_bzrdir, name)
         real_bzrdir = controldir.ControlDir.open(
             location, possible_transports=possible_transports)
-        result = real_bzrdir.open_branch(name=name, 
-            ignore_fallbacks=ignore_fallbacks,
+        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
             possible_transports=possible_transports)
         # this changes the behaviour of result.clone to create a new reference
         # rather than a copy of the content of the branch.
@@ -2445,10 +2451,11 @@
         """Create new branch object at a particular location."""
         if a_bzrdir is None:
             raise ValueError('a_bzrdir must be supplied')
-        else:
-            self.bzrdir = a_bzrdir
+        if name is None:
+            raise ValueError('name must be supplied')
+        self.bzrdir = a_bzrdir
         self._user_transport = self.bzrdir.transport.clone('..')
-        if name is not None:
+        if name != "":
             self._user_transport.set_segment_parameter(
                 "branch", urlutils.escape(name))
         self._base = self._user_transport.base

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2012-01-16 14:45:48 +0000
+++ b/bzrlib/builtins.py	2012-01-17 10:30:33 +0000
@@ -1444,13 +1444,13 @@
         else:
             dir = controldir.ControlDir.open_containing(location)[0]
             try:
-                active_branch = dir.open_branch(name=None)
+                active_branch = dir.open_branch(name="")
             except errors.NotBranchError:
                 active_branch = None
             branches = dir.get_branches()
             names = {}
             for name, branch in branches.iteritems():
-                if name is None:
+                if name == "":
                     continue
                 active = (active_branch is not None and
                           active_branch.base == branch.base)

=== modified file 'bzrlib/bzrdir.py'
--- a/bzrlib/bzrdir.py	2011-12-22 19:54:56 +0000
+++ b/bzrlib/bzrdir.py	2012-01-07 01:06:26 +0000
@@ -829,7 +829,9 @@
 
     def destroy_branch(self, name=None):
         """See BzrDir.create_branch."""
-        if name is not None:
+        if name is None:
+            name = self._get_selected_branch()
+        if name != "":
             raise errors.NoColocatedBranchSupport(self)
         self.transport.delete_tree('branch')
 
@@ -887,7 +889,9 @@
 
     def get_branch_transport(self, branch_format, name=None):
         """See BzrDir.get_branch_transport()."""
-        if name is not None:
+        if name is None:
+            name = self._get_selected_branch()
+        if name != "":
             raise errors.NoColocatedBranchSupport(self)
         # XXX: this shouldn't implicitly create the directory if it's just
         # promising to get a transport -- mbp 20090727
@@ -1021,7 +1025,7 @@
         :param name: Optional branch name to use
         :return: Relative path to branch
         """
-        if name is None:
+        if name == "":
             return 'branch'
         return urlutils.join('branches', name.encode("utf-8"))
 
@@ -1056,7 +1060,7 @@
         if name is None:
             name = self._get_selected_branch()
         path = self._get_branch_path(name)
-        if name is not None:
+        if name != "":
             self.control_files.lock_write()
             try:
                 branches = self._read_branch_list()
@@ -1073,17 +1077,19 @@
         """See ControlDir.get_branches."""
         ret = {}
         try:
-            ret[None] = self.open_branch()
+            ret[""] = self.open_branch(name="")
         except (errors.NotBranchError, errors.NoRepositoryPresent):
             pass
 
         for name in self._read_branch_list():
-            ret[name] = self.open_branch(name.decode('utf-8'))
+            ret[name] = self.open_branch(name=name.decode('utf-8'))
 
         return ret
 
     def get_branch_transport(self, branch_format, name=None):
         """See BzrDir.get_branch_transport()."""
+        if name is None:
+            name = self._get_selected_branch()
         path = self._get_branch_path(name)
         # XXX: this shouldn't implicitly create the directory if it's just
         # promising to get a transport -- mbp 20090727
@@ -1093,7 +1099,7 @@
             branch_format.get_format_string()
         except NotImplementedError:
             raise errors.IncompatibleFormat(branch_format, self._format)
-        if name is not None:
+        if name != "":
             try:
                 self.transport.mkdir('branches', mode=self._get_mkdir_mode())
             except errors.FileExists:

=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py	2011-12-18 12:46:49 +0000
+++ b/bzrlib/commands.py	2012-01-02 14:41:49 +0000
@@ -689,11 +689,15 @@
         """
         class_run = self.run
         def run(*args, **kwargs):
+            for hook in Command.hooks['pre_command']:
+                hook(self)
             self._operation = cleanup.OperationWithCleanups(class_run)
             try:
                 return self._operation.run_simple(*args, **kwargs)
             finally:
                 del self._operation
+                for hook in Command.hooks['post_command']:
+                    hook(self)
         self.run = run
 
     def run(self):
@@ -787,6 +791,12 @@
             " is safe to mutate - e.g. to remove a command. "
             "list_commands should return the updated set of command names.",
             (1, 17))
+        self.add_hook('pre_command',
+            "Called prior to executing a command. Called with the command "
+            "object.", (2, 5))
+        self.add_hook('post_command',
+            "Called after executing a command. Called with the command "
+            "object.", (2, 5))
 
 Command.hooks = CommandHooks()
 

=== modified file 'bzrlib/controldir.py'
--- a/bzrlib/controldir.py	2012-01-03 13:47:01 +0000
+++ b/bzrlib/controldir.py	2012-01-06 23:29:07 +0000
@@ -116,7 +116,7 @@
         :return: Dictionary mapping branch names to instances.
         """
         try:
-           return { None: self.open_branch() }
+           return { "": self.open_branch() }
         except (errors.NotBranchError, errors.NoRepositoryPresent):
            return {}
 
@@ -294,9 +294,9 @@
         :return: Name of the branch selected by the user, or None.
         """
         branch = self.root_transport.get_segment_parameters().get("branch")
-        if branch is not None:
-            branch = urlutils.unescape(branch)
-        return branch
+        if branch is None:
+            branch = ""
+        return urlutils.unescape(branch)
 
     def has_workingtree(self):
         """Tell if this controldir contains a working tree.

=== modified file 'bzrlib/mutabletree.py'
--- a/bzrlib/mutabletree.py	2012-01-06 14:09:04 +0000
+++ b/bzrlib/mutabletree.py	2012-01-18 10:42:07 +0000
@@ -520,10 +520,18 @@
             "called with a bzrlib.mutabletree.PostCommitHookParams object. "
             "The mutable tree the commit was performed on is available via "
             "the mutable_tree attribute of that object.", (2, 0))
+        self.add_hook('pre_transform',
+            "Called before a tree transform on this tree. The hook is called "
+            "with the tree that is being transformed and the transform.",
+            (2, 5))
         self.add_hook('post_build_tree',
             "Called after a completely new tree is built. The hook is "
             "called with the tree as its only argument.", (2, 5))
-
+        self.add_hook('post_transform',
+            "Called after a tree transform has been performed on a tree. "
+            "The hook is called with the tree that is being transformed and "
+            "the transform.",
+            (2, 5))
 
 # install the default hooks into the MutableTree class.
 MutableTree.hooks = MutableTreeHooks()

=== modified file 'bzrlib/plugins/weave_fmt/branch.py'
--- a/bzrlib/plugins/weave_fmt/branch.py	2011-12-19 13:23:58 +0000
+++ b/bzrlib/plugins/weave_fmt/branch.py	2012-01-07 01:06:26 +0000
@@ -138,7 +138,9 @@
     def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
             found_repository=None, possible_transports=None):
         """See BranchFormat.open()."""
-        if name is not None:
+        if name is None:
+            name = a_bzrdir._get_selected_branch()
+        if name != "":
             raise errors.NoColocatedBranchSupport(self)
         if not _found:
             # we are being called directly and must probe.

=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2012-01-04 16:02:14 +0000
+++ b/bzrlib/remote.py	2012-01-07 01:06:26 +0000
@@ -625,6 +625,8 @@
 
     def create_branch(self, name=None, repository=None,
                       append_revisions_only=None):
+        if name is None:
+            name = self._get_selected_branch()
         # as per meta1 formats - just delegate to the format object which may
         # be parameterised.
         real_branch = self._format.get_branch_format().initialize(self,
@@ -724,6 +726,8 @@
 
     def open_branch(self, name=None, unsupported=False,
                     ignore_fallbacks=False, possible_transports=None):
+        if name is None:
+            name = self._get_selected_branch()
         if unsupported:
             raise NotImplementedError('unsupported flag support not implemented yet.')
         if self._next_open_branch_result is not None:
@@ -3102,6 +3106,8 @@
 
     def initialize(self, a_bzrdir, name=None, repository=None,
                    append_revisions_only=None):
+        if name is None:
+            name = a_bzrdir._get_selected_branch()
         # 1) get the network name to use.
         if self._custom_format:
             network_name = self._custom_format.network_name()
@@ -3122,7 +3128,7 @@
         # Creating on a remote bzr dir.
         # 2) try direct creation via RPC
         path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
-        if name is not None:
+        if name != "":
             # XXX JRV20100304: Support creating colocated branches
             raise errors.NoColocatedBranchSupport(self)
         verb = 'BzrDir.create_branch'

=== modified file 'bzrlib/smart/bzrdir.py'
--- a/bzrlib/smart/bzrdir.py	2011-12-19 13:23:58 +0000
+++ b/bzrlib/smart/bzrdir.py	2012-01-07 01:06:26 +0000
@@ -268,7 +268,7 @@
             self.transport_from_client_path(path))
         format = branch.network_format_registry.get(network_name)
         bzrdir.branch_format = format
-        result = format.initialize(bzrdir)
+        result = format.initialize(bzrdir, name="")
         rich_root, tree_ref, external_lookup = self._format_to_capabilities(
             result.repository._format)
         branch_format = result._format.network_name()

=== modified file 'bzrlib/tests/per_bzrdir/test_bzrdir.py'
--- a/bzrlib/tests/per_bzrdir/test_bzrdir.py	2011-12-12 12:09:50 +0000
+++ b/bzrlib/tests/per_bzrdir/test_bzrdir.py	2012-01-07 01:45:31 +0000
@@ -693,5 +693,5 @@
             raise TestNotApplicable('Format does not support colocation')
         reference = branch.BranchReferenceFormat().initialize(
             repo.bzrdir, target_branch=target_branch)
-        self.assertEqual(set([None, 'foo']),
+        self.assertEqual(set(["", 'foo']),
                          set(repo.bzrdir.get_branches().keys()))

=== modified file 'bzrlib/tests/per_controldir/test_controldir.py'
--- a/bzrlib/tests/per_controldir/test_controldir.py	2011-12-27 12:18:36 +0000
+++ b/bzrlib/tests/per_controldir/test_controldir.py	2012-01-06 23:29:07 +0000
@@ -1195,7 +1195,7 @@
     def test_get_branches(self):
         repo = self.make_repository('branch-1')
         target_branch = repo.bzrdir.create_branch()
-        self.assertEqual([None], repo.bzrdir.get_branches().keys())
+        self.assertEqual([""], repo.bzrdir.get_branches().keys())
 
     def test_create_repository(self):
         # a bzrdir can construct a repository for itself.
@@ -1342,7 +1342,7 @@
             raise TestSkipped("Can't initialize %r on transport %r"
                               % (self.bzrdir_format, t))
         dir = bzrdir.BzrDir.open(t.base)
-        self.assertIs(None, dir._get_selected_branch())
+        self.assertEqual(u"", dir._get_selected_branch())
 
     def test_root_transport(self):
         dir = self.make_bzrdir('.')

=== modified file 'bzrlib/tests/per_controldir_colo/test_unsupported.py'
--- a/bzrlib/tests/per_controldir_colo/test_unsupported.py	2011-12-11 04:07:08 +0000
+++ b/bzrlib/tests/per_controldir_colo/test_unsupported.py	2012-01-06 23:29:07 +0000
@@ -73,4 +73,4 @@
         made_control = self.make_bzrdir_with_repo()
         made_control.create_branch()
         self.assertEqual(made_control.get_branches().keys(),
-                         [None])
+                         [""])

=== modified file 'bzrlib/tests/test_commands.py'
--- a/bzrlib/tests/test_commands.py	2011-12-09 14:24:21 +0000
+++ b/bzrlib/tests/test_commands.py	2012-01-02 14:41:49 +0000
@@ -25,6 +25,7 @@
     errors,
     option,
     tests,
+    trace,
     )
 from bzrlib.commands import display_command
 from bzrlib.tests import TestSkipped
@@ -370,3 +371,81 @@
         cmds = list(commands.all_command_names())
         self.assertEqual(['called'], hook_calls)
         self.assertSubset(['foo', 'bar'], cmds)
+
+class TestPreAndPostCommandHooks(tests.TestCase):
+    class TestError(StandardError):
+        __doc__ = """A test exception."""
+
+    def test_pre_and_post_hooks(self):
+        hook_calls = []
+
+        def pre_command(cmd):
+            self.assertEqual([], hook_calls)
+            hook_calls.append('pre')
+
+        def post_command(cmd):
+            self.assertEqual(['pre', 'run'], hook_calls)
+            hook_calls.append('post')
+
+        def run(cmd):
+            self.assertEqual(['pre'], hook_calls)
+            hook_calls.append('run')
+
+        self.overrideAttr(builtins.cmd_rocks, 'run', run)
+        commands.install_bzr_command_hooks()
+        commands.Command.hooks.install_named_hook(
+            "pre_command", pre_command, None)
+        commands.Command.hooks.install_named_hook(
+            "post_command", post_command, None)
+
+        self.assertEqual([], hook_calls)
+        self.run_bzr(['rocks', '-Oxx=12', '-Oyy=foo'])
+        self.assertEqual(['pre', 'run', 'post'], hook_calls)
+
+    def test_post_hook_provided_exception(self):
+        hook_calls = []
+
+        def post_command(cmd):
+            hook_calls.append('post')
+
+        def run(cmd):
+            hook_calls.append('run')
+            raise self.TestError()
+
+        self.overrideAttr(builtins.cmd_rocks, 'run', run)
+        commands.install_bzr_command_hooks()
+        commands.Command.hooks.install_named_hook(
+            "post_command", post_command, None)
+
+        self.assertEqual([], hook_calls)
+        self.assertRaises(self.TestError, commands.run_bzr, [u'rocks'])
+        self.assertEqual(['run', 'post'], hook_calls)
+
+    def test_pre_command_error(self):
+        """Ensure an BzrCommandError in pre_command aborts the command"""
+
+        hook_calls = []
+
+        def pre_command(cmd):
+            hook_calls.append('pre')
+            # verify that all subclasses of BzrCommandError caught too
+            raise errors.BzrOptionError()
+
+        def post_command(cmd, e):
+            self.fail('post_command should not be called')
+
+        def run(cmd):
+            self.fail('command should not be called')
+
+        self.overrideAttr(builtins.cmd_rocks, 'run', run)
+        commands.install_bzr_command_hooks()
+        commands.Command.hooks.install_named_hook(
+            "pre_command", pre_command, None)
+        commands.Command.hooks.install_named_hook(
+            "post_command", post_command, None)
+
+        self.assertEqual([], hook_calls)
+        self.assertRaises(errors.BzrCommandError,
+                          commands.run_bzr, [u'rocks'])
+        self.assertEqual(['pre'], hook_calls)
+

=== modified file 'bzrlib/tests/test_foreign.py'
--- a/bzrlib/tests/test_foreign.py	2011-12-19 19:15:58 +0000
+++ b/bzrlib/tests/test_foreign.py	2012-01-07 01:06:26 +0000
@@ -240,6 +240,8 @@
 
     def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
             found_repository=None):
+        if name is None:
+            name = a_bzrdir._get_selected_branch()
         if not _found:
             raise NotImplementedError
         try:
@@ -251,7 +253,8 @@
             return DummyForeignVcsBranch(_format=self,
                               _control_files=control_files,
                               a_bzrdir=a_bzrdir,
-                              _repository=found_repository)
+                              _repository=found_repository,
+                              name=name)
         except errors.NoSuchFile:
             raise errors.NotBranchError(path=transport.base)
 
@@ -317,7 +320,9 @@
 
     def open_branch(self, name=None, unsupported=False, ignore_fallbacks=True,
             possible_transports=None):
-        if name is not None:
+        if name is None:
+            name = self._get_selected_branch()
+        if name != "":
             raise errors.NoColocatedBranchSupport(self)
         return self._format.get_branch_format().open(self, _found=True)
 

=== modified file 'bzrlib/tests/test_transform.py'
--- a/bzrlib/tests/test_transform.py	2011-11-17 13:45:49 +0000
+++ b/bzrlib/tests/test_transform.py	2012-01-06 01:58:27 +0000
@@ -60,6 +60,7 @@
     pathjoin,
 )
 from bzrlib.merge import Merge3Merger, Merger
+from bzrlib.mutabletree import MutableTree
 from bzrlib.tests import (
     features,
     TestCaseInTempDir,
@@ -3717,3 +3718,40 @@
                          remaining_conflicts.pop())
         self.assertLength(1, warnings)
         self.assertStartsWith(warnings[0], 'donttouchmypreciouuus')
+
+
+class TestTransformHooks(tests.TestCaseWithTransport):
+
+    def setUp(self):
+        super(TestTransformHooks, self).setUp()
+        self.wt = self.make_branch_and_tree('.')
+        os.chdir('..')
+
+    def get_transform(self):
+        transform = TreeTransform(self.wt)
+        self.addCleanup(transform.finalize)
+        return transform, transform.root
+
+    def test_pre_commit_hooks(self):
+        calls = []
+        def record_pre_transform(tree, tt):
+            calls.append((tree, tt))
+        MutableTree.hooks.install_named_hook('pre_transform',
+            record_pre_transform, "Pre transform")
+        transform, root = self.get_transform()
+        old_root_id = transform.tree_file_id(root)
+        transform.apply()
+        self.assertEqual(old_root_id, self.wt.get_root_id())
+        self.assertEquals([(self.wt, transform)], calls)
+
+    def test_post_commit_hooks(self):
+        calls = []
+        def record_post_transform(tree, tt):
+            calls.append((tree, tt))
+        MutableTree.hooks.install_named_hook('post_transform',
+            record_post_transform, "Post transform")
+        transform, root = self.get_transform()
+        old_root_id = transform.tree_file_id(root)
+        transform.apply()
+        self.assertEqual(old_root_id, self.wt.get_root_id())
+        self.assertEquals([(self.wt, transform)], calls)

=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py	2011-12-19 17:39:35 +0000
+++ b/bzrlib/transform.py	2012-01-18 10:42:07 +0000
@@ -50,6 +50,7 @@
                            ExistingLimbo, ImmortalLimbo, NoFinalPath,
                            UnableCreateSymlink)
 from bzrlib.filters import filtered_output_bytes, ContentFilterContext
+from bzrlib.mutabletree import MutableTree
 from bzrlib.osutils import (
     delete_any,
     file_kind,
@@ -57,7 +58,6 @@
     pathjoin,
     sha_file,
     splitpath,
-    supports_executable,
     )
 from bzrlib.progress import ProgressPhase
 from bzrlib.symbol_versioning import (
@@ -156,6 +156,8 @@
         """
         if self._tree is None:
             return
+        for hook in MutableTree.hooks['post_transform']:
+            hook(self._tree, self)
         self._tree.unlock()
         self._tree = None
 
@@ -230,7 +232,7 @@
         irrelevant.
 
         """
-        new_roots = [k for k, v in self._new_parent.iteritems() if v is
+        new_roots = [k for k, v in self._new_parent.iteritems() if v ==
                      ROOT_PARENT]
         if len(new_roots) < 1:
             return
@@ -626,7 +628,7 @@
         for trans_id in self._new_parent:
             seen = set()
             parent_id = trans_id
-            while parent_id is not ROOT_PARENT:
+            while parent_id != ROOT_PARENT:
                 seen.add(parent_id)
                 try:
                     parent_id = self.final_parent(parent_id)
@@ -642,7 +644,7 @@
         """If parent directories are versioned, children must be versioned."""
         conflicts = []
         for parent_id, children in by_parent.iteritems():
-            if parent_id is ROOT_PARENT:
+            if parent_id == ROOT_PARENT:
                 continue
             if self.final_file_id(parent_id) is not None:
                 continue
@@ -741,7 +743,7 @@
         """Children must have a directory parent"""
         conflicts = []
         for parent_id, children in by_parent.iteritems():
-            if parent_id is ROOT_PARENT:
+            if parent_id == ROOT_PARENT:
                 continue
             no_children = True
             for child_id in children:
@@ -1722,6 +1724,8 @@
             calculating one.
         :param _mover: Supply an alternate FileMover, for testing
         """
+        for hook in MutableTree.hooks['pre_transform']:
+            hook(self._tree, self)
         if not no_conflicts:
             self._check_malformed()
         child_pb = ui.ui_factory.nested_progress_bar()

=== modified file 'doc/en/admin-guide/migration.txt'
--- a/doc/en/admin-guide/migration.txt	2010-08-13 19:08:57 +0000
+++ b/doc/en/admin-guide/migration.txt	2012-01-17 17:22:36 +0000
@@ -47,7 +47,7 @@
 Bazaar's `svn`_ plugin provides tools for interaction with Subversion
 projects.  In fact, Bazaar can be used transparently with projects stored in
 Subversion, but that is beyond the scope of this document.  (See
-http://doc.bazaar.canonical.com/en/migration/foreign/bzr-on-svn-projects.html for
+http://doc.bazaar.canonical.com/migration/en/foreign/bzr-on-svn-projects.html for
 more on that subject.)  What is relevant here is the ``svn-import`` command
 provided by that plugin.  This can import an entire subversion repository
 including tags and branches, particularly if they are stored in Subversion's

=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt	2012-01-16 17:24:42 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt	2012-01-18 19:40:10 +0000
@@ -26,12 +26,19 @@
 .. Improvements to existing commands, especially improved performance 
    or memory usage, or better results.
 
+* Two new command hooks, ``pre_command`` and ``post_command``,
+  provide notification before and after a command has been run.
+  (Brian de Alwis, Jelmer Vernooij)
+
 Bug Fixes
 *********
 
 .. Fixes for situations where bzr would previously crash or give incorrect
    or undesirable results.
 
+* Test for equality instead of object identity where ROOT_PARENT is concerned.
+  (Wouter van Heyst, #881142)
+
 Documentation
 *************
 
@@ -49,6 +56,10 @@
 .. Major internal changes, unlikely to be visible to users or plugin 
    developers, but interesting for bzr developers.
 
+* ``MutableTree`` has two new hooks ``pre_transform`` and
+  ``post_transform`` that are called for tree transform operations.
+  (Jelmer Vernooij, #912084)
+
 Testing
 *******
 




More information about the bazaar-commits mailing list