changeset 24284:ff14b26fe5f4

hook: add a generic hook right before we commit a transaction We are adding a 'txnclose' hook that will be run right before a transaction is closed. Hooks running at that time will have access to the full transaction content through both 'hookargs' content and on-disk reading. They will be able to abort the transaction.
author Pierre-Yves David <pierre-yves.david@fb.com>
date Mon, 09 Mar 2015 22:50:49 -0700
parents ef22cfff7052
children 8e13cc0825f1
files mercurial/help/config.txt mercurial/localrepo.py tests/test-hook.t
diffstat 3 files changed, 32 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/help/config.txt	Mon Mar 09 22:43:36 2015 -0700
+++ b/mercurial/help/config.txt	Mon Mar 09 22:50:49 2015 -0700
@@ -813,15 +813,23 @@
   transaction will be in ``$HG_TXNNAME``. A non-zero status will
   prevent the transaction from being opened.
 
+``pretxnclose``
+  Run right before the transaction is actually finalized. Any
+  repository change will be visible to the hook program. This lets you
+  validate the transaction content or change it. Exit status 0 allows
+  the commit to proceed. Non-zero status will cause the transaction to
+  be rolled back. The reason for the transaction opening will be in
+  ``$HG_TXNNAME``. The rest of the available data will vary according
+  the transaction type. New changesets will add
+  ``$HG_NODE`` (id of the first added changeset), ``$HG_URL`` and
+  ``$HG_SOURCE`` variables, bookmarks and phases changes will set
+  ``HG_BOOKMARK_MOVED`` and ``HG_PHASES_MOVED`` to ``1``, etc.
+
 ``txnclose``
   Run after any repository transaction has been commited. At this
   point, the transaction can no longer be rolled back. The hook will run
-  after the lock is released. The reason for the transaction will
-  be in ``$HG_TXNNAME``. The rest of the available data will vary
-  according the event that happened during the transaction. New changesets
-  will add ``$HG_NODE`` (id of the first added changeset), ``$HG_URL``
-  and ``$HG_SOURCE`` variables, bookmarks and phases changes will set
-  ``HG_BOOKMARK_MOVED`` and ``HG_PHASES_MOVED`` to ``1``, etc.
+  after the lock is released. see ``pretxnclose`` docs for details about
+  available variables.
 
 ``pretxnchangegroup``
   Run after a changegroup has been added via push, pull or unbundle,
--- a/mercurial/localrepo.py	Mon Mar 09 22:43:36 2015 -0700
+++ b/mercurial/localrepo.py	Mon Mar 09 22:50:49 2015 -0700
@@ -915,17 +915,24 @@
         renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
         rp = report and report or self.ui.warn
         vfsmap = {'plain': self.vfs} # root of .hg/
-        tr = transaction.transaction(rp, self.svfs, vfsmap,
+        # we must avoid cyclic reference between repo and transaction.
+        reporef = weakref.ref(self)
+        def validate(tr):
+            """will run pre-closing hooks"""
+            pending = lambda: tr.writepending() and self.root or ""
+            reporef().hook('pretxnclose', throw=True, pending=pending,
+                           xnname=desc)
+
+        tr = transaction.transaction(rp, self.sopener, vfsmap,
                                      "journal",
                                      "undo",
                                      aftertrans(renames),
-                                     self.store.createmode)
+                                     self.store.createmode,
+                                     validator=validate)
         # note: writing the fncache only during finalize mean that the file is
         # outdated when running hooks. As fncache is used for streaming clone,
         # this is not expected to break anything that happen during the hooks.
         tr.addfinalize('flush-fncache', self.store.write)
-        # we must avoid cyclic reference between repo and transaction.
-        reporef = weakref.ref(self)
         def txnclosehook(tr2):
             """To be run if transaction is successful, will schedule a hook run
             """
--- a/tests/test-hook.t	Mon Mar 09 22:43:36 2015 -0700
+++ b/tests/test-hook.t	Mon Mar 09 22:50:49 2015 -0700
@@ -13,6 +13,7 @@
   > pre-cat = python "$TESTDIR/printenv.py" pre-cat
   > post-cat = python "$TESTDIR/printenv.py" post-cat
   > pretxnopen = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" pretxnopen"
+  > pretxnclose = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" pretxnclose"
   > txnclose = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" txnclose"
   > EOF
   $ echo a > a
@@ -22,6 +23,7 @@
   pretxnopen hook: HG_TXNNAME=commit
   pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
   0:cb9a9f314b8b
+  pretxnclose hook: HG_PENDING=$TESTTMP/a HG_XNNAME=commit
   txnclose hook: HG_PHASES_MOVED=1 HG_TXNNAME=commit
   commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
   commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
@@ -49,6 +51,7 @@
   pretxnopen hook: HG_TXNNAME=commit
   pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
   1:ab228980c14d
+  pretxnclose hook: HG_PENDING=$TESTTMP/a HG_XNNAME=commit
   txnclose hook: HG_TXNNAME=commit
   commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
   commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
@@ -61,6 +64,7 @@
   pretxnopen hook: HG_TXNNAME=commit
   pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
   2:ee9deb46ab31
+  pretxnclose hook: HG_PENDING=$TESTTMP/a HG_XNNAME=commit
   txnclose hook: HG_TXNNAME=commit
   commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
   commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
@@ -73,6 +77,7 @@
   pretxnopen hook: HG_TXNNAME=commit
   pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
   3:07f3376c1e65
+  pretxnclose hook: HG_PENDING=$TESTTMP/a HG_XNNAME=commit
   txnclose hook: HG_TXNNAME=commit
   commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
   commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
@@ -116,6 +121,7 @@
   pretxnopen hook: HG_TXNNAME=commit
   pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
   4:539e4b31b6dc
+  pretxnclose hook: HG_PENDING=$TESTTMP/a HG_XNNAME=commit
   tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
   txnclose hook: HG_TXNNAME=commit
   commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
@@ -212,6 +218,7 @@
   searching for changes
   no changes found
   pretxnopen hook: HG_TXNNAME=bookmarks
+  pretxnclose hook: HG_PENDING=$TESTTMP/a HG_XNNAME=bookmarks
   txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNNAME=bookmarks
   pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
   exporting bookmark foo