Rev 2790: (Nam Nguyen) Pre-commit hook in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Mon Sep 3 14:07:31 BST 2007


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

------------------------------------------------------------
revno: 2790
revision-id: pqm at pqm.ubuntu.com-20070903130729-qdcrag0a7vcpzfgm
parent: pqm at pqm.ubuntu.com-20070903110923-pj5pw0wsf1u922j9
parent: ian.clatworthy at internode.on.net-20070903123214-kgrq7hubbtkmkn7i
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2007-09-03 14:07:29 +0100
message:
  (Nam Nguyen) Pre-commit hook
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
  bzrlib/commit.py               commit.py-20050511101309-79ec1a0168e0e825
  bzrlib/tests/branch_implementations/test_commit.py test_commit.py-20070206022134-117z1i5b644p63r0-1
  bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
  bzrlib/tests/workingtree_implementations/test_commit.py test_commit.py-20060421013633-1610ec2331c8190f
  doc/en/user-reference/hooks.txt hooks.txt-20070830033044-xxu2rced13f72dka-1
    ------------------------------------------------------------
    revno: 2789.1.1
    merged: ian.clatworthy at internode.on.net-20070903123214-kgrq7hubbtkmkn7i
    parent: pqm at pqm.ubuntu.com-20070903110923-pj5pw0wsf1u922j9
    parent: nguyen at guy-20070902053708-hjup9c46zj1jxml1
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: ianc-integration
    timestamp: Mon 2007-09-03 22:32:14 +1000
    message:
      (Nam Nguyen) Pre-commit hook
    ------------------------------------------------------------
    revno: 2659.3.10
    merged: nguyen at guy-20070902053708-hjup9c46zj1jxml1
    parent: namnguyen-20070827083837-t6a5oewzvdct6j8i
    parent: pqm at pqm.ubuntu.com-20070901160444-hcr66zejwyy0jezc
    committer: Nam Nguyen <Nam Nguyen at GUY>
    branch nick: precommit
    timestamp: Sun 2007-09-02 13:37:08 +0800
    message:
      merge with bzr.dev and update hooks.txt to list pre_commit hook.
      
      doc/en/user-reference/hooks.txt:
      
         * added a section for pre_commit_hook
    ------------------------------------------------------------
    revno: 2659.3.9
    merged: namnguyen-20070827083837-t6a5oewzvdct6j8i
    parent: namnguyen-20070827032116-11p0vjw01wn4l53y
    committer: NamNguyen
    branch nick: precommit
    timestamp: Mon 2007-08-27 16:38:37 +0800
    message:
      branch.py:
      
         * ``pre_commit`` hook's signature is changed to::
      
         hook(local, master, old_revno, old_revid, future_revno, future_revid,
              tree_delta, future_tree)
      
         * made it clear that pre_commit hooks must not modify neither
           the delta nor future_tree
      
      commit.py:
      
         * replaced ``affected_ids`` with ``tree_delta``
      
         * only compute ``future_tree`` and ``tree_delta`` once
    ------------------------------------------------------------
    revno: 2659.3.8
    merged: namnguyen-20070827032116-11p0vjw01wn4l53y
    parent: namnguyen-20070815091606-2gqalm977ys9e21w
    parent: pqm at pqm.ubuntu.com-20070825182243-a3w20rpadbfz8euc
    committer: NamNguyen
    branch nick: precommit
    timestamp: Mon 2007-08-27 11:21:16 +0800
    message:
      merge with bzr.dev
    ------------------------------------------------------------
    revno: 2659.3.7
    merged: namnguyen-20070815091606-2gqalm977ys9e21w
    parent: namnguyen-20070815090632-w0xcl6t5if19m2oj
    committer: NamNguyen
    branch nick: precommit
    timestamp: Wed 2007-08-15 17:16:06 +0800
    message:
      NEWS:
      
        * Pull the NEWS entry to IN DEVELOPMENT
    ------------------------------------------------------------
    revno: 2659.3.6
    merged: namnguyen-20070815090632-w0xcl6t5if19m2oj
    parent: namnguyen-20070815074942-rd91twf3wm3enomx
    committer: NamNguyen
    branch nick: precommit
    timestamp: Wed 2007-08-15 17:06:32 +0800
    message:
      branch_implementations/test_commit.py:
      
        * renamed test_pre_commit_paths to test_pre_commit_ids
        * added cases for ``added``, ``deleted``, ``renamed``, ``modified``, and
          ``modified and renamed``
    ------------------------------------------------------------
    revno: 2659.3.5
    merged: namnguyen-20070815074942-rd91twf3wm3enomx
    parent: namnguyen-20070807082534-7xw955iabtlxuayj
    parent: pqm at pqm.ubuntu.com-20070815055603-t0fwzxv6if6sr7c6
    committer: NamNguyen
    branch nick: precommit
    timestamp: Wed 2007-08-15 15:49:42 +0800
    message:
      merge with bzr.dev
    ------------------------------------------------------------
    revno: 2659.3.4
    merged: namnguyen-20070807082534-7xw955iabtlxuayj
    parent: namnguyen-20070807082328-6i934n9gdj5fd17a
    parent: pqm at pqm.ubuntu.com-20070807061316-b32atzzop4r4y21g
    committer: NamNguyen
    branch nick: precommit
    timestamp: Tue 2007-08-07 16:25:34 +0800
    message:
      merged with bzr.dev
    ------------------------------------------------------------
    revno: 2659.3.3
    merged: namnguyen-20070807082328-6i934n9gdj5fd17a
    parent: namnguyen-20070801063025-pb139zkmbuwg5ga6
    parent: pqm at pqm.ubuntu.com-20070801000518-wyeivv59zhkh9cbp
    committer: NamNguyen
    branch nick: precommit
    timestamp: Tue 2007-08-07 16:23:28 +0800
    message:
      Changed ``pre_commit`` hook signature.
      
      The hook's signature is now
      
        ::
      
          hook(local, master, old_revno, old_revid, new_revno, new_revid,
               affected_ids, future_revision_tree)
      
      ``affected_ids`` is a dictionary of (change_type, ids). change_type is
      a string describing the change (e.g. 'added', 'deleted', 'modified'), and
      ids is a list of inventory entry ids.
      
      Hooks can get an added file easier than before:
      
        ::
      
          for id in affected_ids.get('added', []):
              if future_revision_tree.kind(id) == 'file':
                  file = future_revision_tree.get_file(id)
                  ...
    ------------------------------------------------------------
    revno: 2659.3.2
    merged: namnguyen-20070801063025-pb139zkmbuwg5ga6
    parent: namnguyen-20070801061414-u0tzrfgcz6z604lz
    committer: NamNguyen
    branch nick: precommit
    timestamp: Wed 2007-08-01 14:30:25 +0800
    message:
      Fix NEWS entry of the last commit.
    ------------------------------------------------------------
    revno: 2659.3.1
    merged: namnguyen-20070801061414-u0tzrfgcz6z604lz
    parent: pqm at pqm.ubuntu.com-20070730051419-0jdj7g8fm4iuoz7h
    committer: NamNguyen
    branch nick: precommit
    timestamp: Wed 2007-08-01 14:14:14 +0800
    message:
      ``Branch.hooks`` now supports ``pre_commit`` hook.
      
      The hook's signature is
      
      ::
      
        hook(local, master, old_revno, old_revid, new_revno, new_revid,
             deleted_paths, added_paths, future_revision_tree)
      
      ``deleted_paths`` and ``added_paths`` are lists of paths. Renamed paths are
      recorded in both ``deleted_paths`` and ``added_paths`` (i.e. deleted then
      added).
      
      ``future_revision_tree`` is obtained from ``CommitBuilder.revision_tree``
      to save hooks from getting it from the branch again.
      
      For example, hooks can get a file:
      
      ::
      
        for path in added_paths:
            id = future_revision_tree.path2id(path)
            if future_revision_tree.kind(id) == 'file':
                file = future_revision_tree.get_file(id)
                ...
      
      or export a tree and do ``make check`` or similar
      
      ::
      
        import bzrlib.export
        bzrlib.export.export(future_revision_tree, 'tmp_space')
      
      
      If the commit is to be rejected, hooks should raise an ``Exception``.
=== modified file 'NEWS'
--- a/NEWS	2007-09-03 10:17:35 +0000
+++ b/NEWS	2007-09-03 12:32:14 +0000
@@ -48,6 +48,9 @@
      verbosity level but do respect *quiet vs normal vs verbose* when
      generating output. (Ian Clatworthy)
 
+   * ``Branch.hooks`` now supports ``pre_commit`` hook. The hook's signature
+     is documented in BranchHooks constructor. (Nam T. Nguyen, #102747)
+
   BUG FIXES:
 
    * ``bzr plugins`` now lists the version number for each plugin in square

=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py	2007-08-20 13:07:12 +0000
+++ b/bzrlib/branch.py	2007-08-27 08:38:37 +0000
@@ -999,6 +999,16 @@
         # is read locked and the target branches write locked. The local
         # branch is the low-latency branch.
         self['post_pull'] = []
+        # invoked before a commit operation takes place.
+        # the api signature is
+        # (local, master, old_revno, old_revid, future_revno, future_revid,
+        #  tree_delta, future_tree).
+        # old_revid is NULL_REVISION for the first commit to a branch
+        # tree_delta is a TreeDelta object describing changes from the basis
+        # revision, hooks MUST NOT modify this delta
+        # future_tree is an in-memory tree obtained from
+        # CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
+        self['pre_commit'] = []
         # invoked after a commit operation completes.
         # the api signature is 
         # (local, master, old_revno, old_revid, new_revno, new_revid)

=== modified file 'bzrlib/commit.py'
--- a/bzrlib/commit.py	2007-08-28 01:58:42 +0000
+++ b/bzrlib/commit.py	2007-09-02 05:37:08 +0000
@@ -254,7 +254,7 @@
             self._check_bound_branch()
 
             # Check that the working tree is up to date
-            old_revno,new_revno = self._check_out_of_date_tree()
+            old_revno, new_revno = self._check_out_of_date_tree()
 
             if self.config is None:
                 self.config = self.branch.get_config()
@@ -275,7 +275,7 @@
             # information in the progress bar during the relevant stages.
             self.pb_stage_name = ""
             self.pb_stage_count = 0
-            self.pb_stage_total = 4
+            self.pb_stage_total = 5
             if self.bound_branch:
                 self.pb_stage_total += 1
             self.pb.show_pct = False
@@ -296,6 +296,7 @@
                     entries_title="Directory")
             self.builder = self.branch.get_commit_builder(self.parents,
                 self.config, timestamp, timezone, committer, revprops, rev_id)
+            
             try:
                 self._update_builder_with_changes()
                 self._check_pointless()
@@ -315,10 +316,13 @@
 
                 # Add revision data to the local branch
                 self.rev_id = self.builder.commit(self.message)
+
             except:
                 self.builder.abort()
                 raise
 
+            self._process_pre_hooks(old_revno, new_revno)
+
             # Upload revision data to the master.
             # this will propagate merged revisions too if needed.
             if self.bound_branch:
@@ -339,7 +343,7 @@
             rev_tree = self.builder.revision_tree()
             self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
             self.reporter.completed(new_revno, self.rev_id)
-            self._process_hooks(old_revno, new_revno)
+            self._process_post_hooks(old_revno, new_revno)
         finally:
             self._cleanup()
         return self.rev_id
@@ -465,10 +469,15 @@
             new_revno = 1
         return old_revno,new_revno
 
-    def _process_hooks(self, old_revno, new_revno):
-        """Process any registered commit hooks."""
+    def _process_pre_hooks(self, old_revno, new_revno):
+        """Process any registered pre commit hooks."""
+        self._set_progress_stage("Running pre_commit hooks")
+        self._process_hooks("pre_commit", old_revno, new_revno)
+
+    def _process_post_hooks(self, old_revno, new_revno):
+        """Process any registered post commit hooks."""
         # Process the post commit hooks, if any
-        self._set_progress_stage("Running post commit hooks")
+        self._set_progress_stage("Running post_commit hooks")
         # old style commit hooks - should be deprecated ? (obsoleted in
         # 0.15)
         if self.config.post_commit() is not None:
@@ -479,6 +488,13 @@
                               {'branch':self.branch,
                                'bzrlib':bzrlib,
                                'rev_id':self.rev_id})
+        # process new style post commit hooks
+        self._process_hooks("post_commit", old_revno, new_revno)
+
+    def _process_hooks(self, hook_name, old_revno, new_revno):
+        if not Branch.hooks[hook_name]:
+            return
+        
         # new style commit hooks:
         if not self.bound_branch:
             hook_master = self.branch
@@ -493,19 +509,30 @@
             old_revid = self.parents[0]
         else:
             old_revid = bzrlib.revision.NULL_REVISION
-        for hook in Branch.hooks['post_commit']:
+        
+        if hook_name == "pre_commit":
+            future_tree = self.builder.revision_tree()
+            tree_delta = future_tree.changes_from(self.basis_tree,
+                                             include_root=True)
+        
+        for hook in Branch.hooks[hook_name]:
             # show the running hook in the progress bar. As hooks may
             # end up doing nothing (e.g. because they are not configured by
             # the user) this is still showing progress, not showing overall
             # actions - its up to each plugin to show a UI if it want's to
             # (such as 'Emailing diff to foo at example.com').
-            self.pb_stage_name = "Running post commit hooks [%s]" % \
-                Branch.hooks.get_hook_name(hook)
+            self.pb_stage_name = "Running %s hooks [%s]" % \
+                (hook_name, Branch.hooks.get_hook_name(hook))
             self._emit_progress()
             if 'hooks' in debug.debug_flags:
                 mutter("Invoking commit hook: %r", hook)
-            hook(hook_local, hook_master, old_revno, old_revid, new_revno,
-                self.rev_id)
+            if hook_name == "post_commit":
+                hook(hook_local, hook_master, old_revno, old_revid, new_revno,
+                     self.rev_id)
+            elif hook_name == "pre_commit":
+                hook(hook_local, hook_master,
+                     old_revno, old_revid, new_revno, self.rev_id,
+                     tree_delta, future_tree)
 
     def _cleanup(self):
         """Cleanup any open locks, progress bars etc."""

=== modified file 'bzrlib/tests/branch_implementations/test_commit.py'
--- a/bzrlib/tests/branch_implementations/test_commit.py	2007-02-06 02:33:42 +0000
+++ b/bzrlib/tests/branch_implementations/test_commit.py	2007-09-03 12:32:14 +0000
@@ -21,6 +21,7 @@
 from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
 from bzrlib.revision import NULL_REVISION
 from bzrlib.transport import get_transport
+from bzrlib.delta import TreeDelta
 
 
 class TestCommit(TestCaseWithBranch):
@@ -59,6 +60,12 @@
             ('post_commit', local_base, master.base, old_revno, old_revid,
              new_revno, new_revid, local_locked, master.is_locked()))
 
+    def capture_pre_commit_hook(self, local, master, old_revno, old_revid,
+                                new_revno, new_revid,
+                                tree_delta, future_tree):
+        self.hook_calls.append(('pre_commit', old_revno, old_revid,
+                                new_revno, new_revid, tree_delta))
+
     def test_post_commit_to_origin(self):
         tree = self.make_branch_and_memory_tree('branch')
         Branch.hooks.install_hook('post_commit',
@@ -112,3 +119,98 @@
             ],
             self.hook_calls)
         tree.unlock()
+    
+    def test_pre_commit_passes(self):
+        empty_delta = TreeDelta()
+        root_delta = TreeDelta()
+        root_delta.added = [('', '', 'directory')]
+        tree = self.make_branch_and_memory_tree('branch')
+        tree.lock_write()
+        tree.add('', '')
+        Branch.hooks.install_hook("pre_commit", self.capture_pre_commit_hook)
+        revid1 = tree.commit('first revision')
+        revid2 = tree.commit('second revision')
+        self.assertEqual([
+            ('pre_commit', 0, NULL_REVISION, 1, revid1, root_delta),
+            ('pre_commit', 1, revid1, 2, revid2, empty_delta)
+            ],
+            self.hook_calls)
+        tree.unlock()
+
+    def test_pre_commit_fails(self):
+        empty_delta = TreeDelta()
+        root_delta = TreeDelta()
+        root_delta.added = [('', '', 'directory')]
+        tree = self.make_branch_and_memory_tree('branch')
+        tree.lock_write()
+        tree.add('', '')
+        class PreCommitException(Exception): pass
+        def hook_func(local, master,
+                      old_revno, old_revid, new_revno, new_revid,
+                      tree_delta, future_tree):
+            raise PreCommitException(new_revid)
+        Branch.hooks.install_hook("pre_commit", self.capture_pre_commit_hook)
+        Branch.hooks.install_hook("pre_commit", hook_func)
+        revids = [None, None, None]
+        # this commit will raise an exception
+        # so the commit is rolled back and revno unchanged
+        err = self.assertRaises(PreCommitException, tree.commit, 'message')
+        # we have to record the revid to use in assertEqual later
+        revids[0] = str(err)
+        # unregister all pre_commit hooks
+        Branch.hooks["pre_commit"] = []
+        # and re-register the capture hook
+        Branch.hooks.install_hook("pre_commit", self.capture_pre_commit_hook)
+        # now these commits should go through
+        for i in range(1, 3):
+            revids[i] = tree.commit('message')
+        self.assertEqual([
+            ('pre_commit', 0, NULL_REVISION, 1, revids[0], root_delta),
+            ('pre_commit', 0, NULL_REVISION, 1, revids[1], root_delta),
+            ('pre_commit', 1, revids[1], 2, revids[2], empty_delta)
+            ],
+            self.hook_calls)
+        tree.unlock()
+
+    def test_pre_commit_delta(self):
+        # This tests the TreeDelta object passed to pre_commit hook.
+        # This does not try to validate data correctness in the delta.
+        self.build_tree(['rootfile', 'dir/', 'dir/subfile'])
+        tree = self.make_branch_and_tree('.')
+        tree.lock_write()
+        try:
+            # setting up a playground
+            tree.set_root_id('root_id')
+            tree.add('rootfile', 'rootfile_id')
+            tree.put_file_bytes_non_atomic('rootfile_id', 'abc')
+            tree.add('dir', 'dir_id')
+            tree.add('dir/subfile', 'dir_subfile_id')
+            tree.mkdir('to_be_unversioned', 'to_be_unversioned_id')
+            tree.put_file_bytes_non_atomic('dir_subfile_id', 'def')
+            revid1 = tree.commit('first revision')
+        finally:
+            tree.unlock()
+        
+        tree.lock_write()
+        try:
+            # making changes
+            tree.put_file_bytes_non_atomic('rootfile_id', 'jkl')
+            tree.rename_one('dir/subfile', 'dir/subfile_renamed')
+            tree.unversion(['to_be_unversioned_id'])
+            tree.mkdir('added_dir', 'added_dir_id')
+            # start to capture pre_commit delta
+            Branch.hooks.install_hook("pre_commit", self.capture_pre_commit_hook)
+            revid2 = tree.commit('second revision')
+        finally:
+            tree.unlock()
+        
+        expected_delta = TreeDelta()
+        expected_delta.added = [('added_dir', 'added_dir_id', 'directory')]
+        expected_delta.removed = [('to_be_unversioned',
+                                   'to_be_unversioned_id', 'directory')]
+        expected_delta.renamed = [('dir/subfile', 'dir/subfile_renamed',
+                                   'dir_subfile_id', 'file', False, False)]
+        expected_delta.modified=[('rootfile', 'rootfile_id', 'file', True,
+                                  False)]
+        self.assertEqual([('pre_commit', 1, revid1, 2, revid2,
+                           expected_delta)], self.hook_calls)

=== modified file 'bzrlib/tests/test_branch.py'
--- a/bzrlib/tests/test_branch.py	2007-08-14 11:53:00 +0000
+++ b/bzrlib/tests/test_branch.py	2007-08-27 03:21:16 +0000
@@ -321,6 +321,7 @@
         self.assertTrue("set_rh" in hooks, "set_rh not in %s" % hooks)
         self.assertTrue("post_push" in hooks, "post_push not in %s" % hooks)
         self.assertTrue("post_commit" in hooks, "post_commit not in %s" % hooks)
+        self.assertTrue("pre_commit" in hooks, "pre_commit not in %s" % hooks)
         self.assertTrue("post_pull" in hooks, "post_pull not in %s" % hooks)
         self.assertTrue("post_uncommit" in hooks, "post_uncommit not in %s" % hooks)
 

=== modified file 'bzrlib/tests/workingtree_implementations/test_commit.py'
--- a/bzrlib/tests/workingtree_implementations/test_commit.py	2007-07-26 00:26:25 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_commit.py	2007-08-27 08:38:37 +0000
@@ -354,17 +354,18 @@
         # into the factory for this test - just make the test ui factory
         # pun as a reporter. Then we can check the ordering is right.
         tree.commit('second post', specific_files=['b'])
-        # 4 steps, the first of which is reported 2 times, once per dir
+        # 5 steps, the first of which is reported 2 times, once per dir
         self.assertEqual(
-            [('update', 1, 4, 'Collecting changes [Directory 0] - Stage'),
-             ('update', 1, 4, 'Collecting changes [Directory 1] - Stage'),
-             ('update', 2, 4, 'Saving data locally - Stage'),
-             ('update', 3, 4, 'Updating the working tree - Stage'),
-             ('update', 4, 4, 'Running post commit hooks - Stage')],
+            [('update', 1, 5, 'Collecting changes [Directory 0] - Stage'),
+             ('update', 1, 5, 'Collecting changes [Directory 1] - Stage'),
+             ('update', 2, 5, 'Saving data locally - Stage'),
+             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
+             ('update', 4, 5, 'Updating the working tree - Stage'),
+             ('update', 5, 5, 'Running post_commit hooks - Stage')],
             factory._calls
            )
 
-    def test_commit_progress_shows_hook_names(self):
+    def test_commit_progress_shows_post_hook_names(self):
         tree = self.make_branch_and_tree('.')
         # set a progress bar that captures the calls so we can see what is 
         # emitted
@@ -378,15 +379,38 @@
         branch.Branch.hooks.name_hook(a_hook, 'hook name')
         tree.commit('first post')
         self.assertEqual(
-            [('update', 1, 4, 'Collecting changes [Directory 0] - Stage'),
-             ('update', 1, 4, 'Collecting changes [Directory 1] - Stage'),
-             ('update', 2, 4, 'Saving data locally - Stage'),
-             ('update', 3, 4, 'Updating the working tree - Stage'),
-             ('update', 4, 4, 'Running post commit hooks - Stage'),
-             ('update', 4, 4, 'Running post commit hooks [hook name] - Stage'),
-             ],
-            factory._calls
-           )
-
-
-
+            [('update', 1, 5, 'Collecting changes [Directory 0] - Stage'),
+             ('update', 1, 5, 'Collecting changes [Directory 1] - Stage'),
+             ('update', 2, 5, 'Saving data locally - Stage'),
+             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
+             ('update', 4, 5, 'Updating the working tree - Stage'),
+             ('update', 5, 5, 'Running post_commit hooks - Stage'),
+             ('update', 5, 5, 'Running post_commit hooks [hook name] - Stage'),
+             ],
+            factory._calls
+           )
+
+    def test_commit_progress_shows_pre_hook_names(self):
+        tree = self.make_branch_and_tree('.')
+        # set a progress bar that captures the calls so we can see what is 
+        # emitted
+        self.old_ui_factory = ui.ui_factory
+        self.addCleanup(self.restoreDefaults)
+        factory = CapturingUIFactory()
+        ui.ui_factory = factory
+        def a_hook(_, _2, _3, _4, _5, _6, _7, _8):
+            pass
+        branch.Branch.hooks.install_hook('pre_commit', a_hook)
+        branch.Branch.hooks.name_hook(a_hook, 'hook name')
+        tree.commit('first post')
+        self.assertEqual(
+            [('update', 1, 5, 'Collecting changes [Directory 0] - Stage'),
+             ('update', 1, 5, 'Collecting changes [Directory 1] - Stage'),
+             ('update', 2, 5, 'Saving data locally - Stage'),
+             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
+             ('update', 3, 5, 'Running pre_commit hooks [hook name] - Stage'),
+             ('update', 4, 5, 'Updating the working tree - Stage'),
+             ('update', 5, 5, 'Running post_commit hooks - Stage'),
+             ],
+            factory._calls
+           )

=== modified file 'doc/en/user-reference/hooks.txt'
--- a/doc/en/user-reference/hooks.txt	2007-08-30 15:55:36 +0000
+++ b/doc/en/user-reference/hooks.txt	2007-09-02 05:37:08 +0000
@@ -26,6 +26,17 @@
 is read-locked and the target branches are write-locked. Source will
 be the local low-latency branch.
 
+pre_commit
+##########
+Run prefore ``commit`` has completed.
+
+The hook signature is (local, master, old_revno, old_revid, future_revno,
+future_revid, tree_delta, future_tree) where old_revno is NULL_REVISION for
+the first commit to a branch, tree_delta is a TreeDelta object describing
+changes from the basis revision, and future_tree is an in-memory tree
+obtained from CommitBuilder.revision_tree(). Hooks MUST NOT modify tree_delta
+and future_tree.
+
 post_commit
 ###########
 Run after ``commit`` has completed.




More information about the bazaar-commits mailing list