hook: centralize passing HG_PENDING to external hook process
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Sat, 17 Oct 2015 01:15:34 +0900
changeset 26751 520defbc0335
parent 26750 9f9ec4abe700
child 26752 949e8c626d19
hook: centralize passing HG_PENDING to external hook process This patch centralizes passing HG_PENDING to external hook process into '_exthook()'. To make in-memory changes visible to external hook process, this patch does: - write (or schedule to write) in-memory dirstate changes, and - set HG_PENDING environment variable, if: - a transaction is running, and - there are in-memory changes to be visible This patch tests some commands with some hooks, because transaction activity of a same hook differs from each other ("---": "not tested"). ======== ========= ========= ============ command preupdate precommit pretxncommit ======== ========= ========= ============ unshelve o --- --- backout x --- --- import --- o o qrefresh --- x o ======== ========= ========= ============ Each hooks are examined separately to prevent in-memory changes from being visible to external process accidentally by side effect of hooks previously invoked.
mercurial/changegroup.py
mercurial/hook.py
mercurial/localrepo.py
tests/test-backout.t
tests/test-import.t
tests/test-mq-qrefresh-replace-log-message.t
tests/test-shelve.t
--- a/mercurial/changegroup.py	Sat Oct 17 01:15:34 2015 +0900
+++ b/mercurial/changegroup.py	Sat Oct 17 01:15:34 2015 +0900
@@ -407,15 +407,13 @@
             repo.invalidatevolatilesets()
 
             if changesets > 0:
-                p = lambda: tr.writepending() and repo.root or ""
                 if 'node' not in tr.hookargs:
                     tr.hookargs['node'] = hex(cl.node(clstart))
                     hookargs = dict(tr.hookargs)
                 else:
                     hookargs = dict(tr.hookargs)
                     hookargs['node'] = hex(cl.node(clstart))
-                repo.hook('pretxnchangegroup', throw=True, pending=p,
-                          **hookargs)
+                repo.hook('pretxnchangegroup', throw=True, **hookargs)
 
             added = [cl.node(r) for r in xrange(clstart, clend)]
             publishing = repo.publishing()
--- a/mercurial/hook.py	Sat Oct 17 01:15:34 2015 +0900
+++ b/mercurial/hook.py	Sat Oct 17 01:15:34 2015 +0900
@@ -118,6 +118,13 @@
 
     starttime = time.time()
     env = {}
+
+    # make in-memory changes visible to external process
+    tr = repo.currenttransaction()
+    repo.dirstate.write(tr)
+    if tr and tr.writepending():
+        env['HG_PENDING'] = repo.root
+
     for k, v in args.iteritems():
         if callable(v):
             v = v()
--- a/mercurial/localrepo.py	Sat Oct 17 01:15:34 2015 +0900
+++ b/mercurial/localrepo.py	Sat Oct 17 01:15:34 2015 +0900
@@ -994,8 +994,7 @@
         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,
+            reporef().hook('pretxnclose', throw=True,
                            txnname=desc, **tr.hookargs)
         def releasefn(tr, success):
             repo = reporef()
@@ -1682,10 +1681,9 @@
             n = self.changelog.add(mn, files, ctx.description(),
                                    trp, p1.node(), p2.node(),
                                    user, ctx.date(), ctx.extra().copy())
-            p = lambda: tr.writepending() and self.root or ""
             xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
             self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
-                      parent2=xp2, pending=p)
+                      parent2=xp2)
             # set the new commit is proper phase
             targetphase = subrepo.newcommitphase(self.ui, ctx)
             if targetphase:
@@ -1865,8 +1863,6 @@
             hookargs = {}
             if tr is not None:
                 hookargs.update(tr.hookargs)
-                pending = lambda: tr.writepending() and self.root or ""
-                hookargs['pending'] = pending
             hookargs['namespace'] = namespace
             hookargs['key'] = key
             hookargs['old'] = old
--- a/tests/test-backout.t	Sat Oct 17 01:15:34 2015 +0900
+++ b/tests/test-backout.t	Sat Oct 17 01:15:34 2015 +0900
@@ -259,6 +259,60 @@
   line 2
   line 3
 
+Test visibility of in-memory dirstate changes outside transaction to
+external hook process
+
+  $ cat > $TESTTMP/checkvisibility.sh <<EOF
+  > echo "==== \$1:"
+  > hg parents --template "{rev}:{node|short}\n"
+  > echo "===="
+  > EOF
+
+"hg backout --merge REV1" at REV2 below implies steps below:
+
+(1) update to REV1 (REV2 => REV1)
+(2) revert by REV1^1
+(3) commit backnig out revision (REV3)
+(4) update to REV2 (REV3 => REV2)
+(5) merge with REV3 (REV2 => REV2, REV3)
+
+== test visibility to external preupdate hook
+
+  $ hg update -q -C 2
+  $ hg --config extensions.strip= strip 3
+  saved backup bundle to * (glob)
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
+  > EOF
+
+("-m" is needed to avoid writing dirstte changes out at other than
+invocation of the hook to be examined)
+
+  $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
+  ==== preupdate:
+  2:6ea3f2a197a2
+  ====
+  reverting a
+  created new head
+  changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
+  ==== preupdate:
+  3:d92a3f57f067
+  ====
+  merging with changeset 3:d92a3f57f067
+  ==== preupdate:
+  2:6ea3f2a197a2
+  ====
+  merging a
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > preupdate.visibility =
+  > EOF
+
   $ cd ..
 
 backout should not back out subsequent changesets
--- a/tests/test-import.t	Sat Oct 17 01:15:34 2015 +0900
+++ b/tests/test-import.t	Sat Oct 17 01:15:34 2015 +0900
@@ -533,6 +533,110 @@
   $ hg --cwd b revert --no-backup a
   $ rm -f b/foo
 
+== test visibility to precommit external hook
+
+  $ cat >> b/.hg/hgrc <<EOF
+  > [hooks]
+  > precommit.visibility = sh $TESTTMP/checkvisibility.sh
+  > EOF
+
+  $ (cd b && sh "$TESTTMP/checkvisibility.sh")
+  ====
+  VISIBLE 0:80971e65b431
+  ACTUAL  0:80971e65b431
+  ====
+
+  $ hg --cwd b import ../patch1 ../patch2 ../patch3
+  applying ../patch1
+  ====
+  VISIBLE 0:80971e65b431
+  M a
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+  applying ../patch2
+  ====
+  VISIBLE 1:1d4bd90af0e4
+  M a
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+  applying ../patch3
+  ====
+  VISIBLE 2:6d019af21222
+  A foo
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+
+  $ hg --cwd b rollback -q
+  $ (cd b && sh "$TESTTMP/checkvisibility.sh")
+  ====
+  VISIBLE 0:80971e65b431
+  M a
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+  $ hg --cwd b revert --no-backup a
+  $ rm -f b/foo
+
+  $ cat >> b/.hg/hgrc <<EOF
+  > [hooks]
+  > precommit.visibility =
+  > EOF
+
+== test visibility to pretxncommit external hook
+
+  $ cat >> b/.hg/hgrc <<EOF
+  > [hooks]
+  > pretxncommit.visibility = sh $TESTTMP/checkvisibility.sh
+  > EOF
+
+  $ (cd b && sh "$TESTTMP/checkvisibility.sh")
+  ====
+  VISIBLE 0:80971e65b431
+  ACTUAL  0:80971e65b431
+  ====
+
+  $ hg --cwd b import ../patch1 ../patch2 ../patch3
+  applying ../patch1
+  ====
+  VISIBLE 0:80971e65b431
+  M a
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+  applying ../patch2
+  ====
+  VISIBLE 1:1d4bd90af0e4
+  M a
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+  applying ../patch3
+  ====
+  VISIBLE 2:6d019af21222
+  A foo
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+
+  $ hg --cwd b rollback -q
+  $ (cd b && sh "$TESTTMP/checkvisibility.sh")
+  ====
+  VISIBLE 0:80971e65b431
+  M a
+  ACTUAL  0:80971e65b431
+  M a
+  ====
+  $ hg --cwd b revert --no-backup a
+  $ rm -f b/foo
+
+  $ cat >> b/.hg/hgrc <<EOF
+  > [hooks]
+  > pretxncommit.visibility =
+  > EOF
+
   $ rm -r b
 
 
--- a/tests/test-mq-qrefresh-replace-log-message.t	Sat Oct 17 01:15:34 2015 +0900
+++ b/tests/test-mq-qrefresh-replace-log-message.t	Sat Oct 17 01:15:34 2015 +0900
@@ -242,3 +242,85 @@
   ====
   0:25e397dabed2
   ====
+
+== test visibility to precommit external hook
+
+  $ hg update -C -q
+  $ rm -f file2
+  $ hg qpush -q second-patch --config hooks.pretxncommit.unexpectedabort=
+  now at: second-patch
+  $ echo bbbb >> file2
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > precommit.checkvisibility = sh "$TESTTMP/checkvisibility.sh"
+  > EOF
+
+  $ sh "$TESTTMP/checkvisibility.sh"
+  ====
+  1:e30108269082
+  M file2
+  ====
+
+  $ hg qrefresh
+  ====
+  0:25e397dabed2
+  A file2
+  ====
+  transaction abort!
+  rollback completed
+  refresh interrupted while patch was popped! (revert --all, qpush to recover)
+  abort: pretxncommit.unexpectedabort hook exited with status 1
+  [255]
+
+  $ sh "$TESTTMP/checkvisibility.sh"
+  ====
+  0:25e397dabed2
+  ====
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > precommit.checkvisibility =
+  > EOF
+
+== test visibility to pretxncommit external hook
+
+  $ hg update -C -q
+  $ rm -f file2
+  $ hg qpush -q second-patch --config hooks.pretxncommit.unexpectedabort=
+  now at: second-patch
+  $ echo bbbb >> file2
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > pretxncommit.checkvisibility = sh "$TESTTMP/checkvisibility.sh"
+  > # make checkvisibility run before unexpectedabort
+  > priority.pretxncommit.checkvisibility = 10
+  > EOF
+
+  $ sh "$TESTTMP/checkvisibility.sh"
+  ====
+  1:e30108269082
+  M file2
+  ====
+
+  $ hg qrefresh
+  ====
+  0:25e397dabed2
+  A file2
+  ====
+  transaction abort!
+  rollback completed
+  refresh interrupted while patch was popped! (revert --all, qpush to recover)
+  abort: pretxncommit.unexpectedabort hook exited with status 1
+  [255]
+
+  $ sh "$TESTTMP/checkvisibility.sh"
+  ====
+  0:25e397dabed2
+  ====
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > pretxncommit.checkvisibility =
+  > EOF
--- a/tests/test-shelve.t	Sat Oct 17 01:15:34 2015 +0900
+++ b/tests/test-shelve.t	Sat Oct 17 01:15:34 2015 +0900
@@ -1011,6 +1011,84 @@
       7e30d8ac6f23cfc84330fd7e698730374615d21a
   $ cd ..
 
+Test visibility of in-memory changes inside transaction to external hook
+------------------------------------------------------------------------
+
+  $ cd repo
+
+  $ echo xxxx >> x
+  $ hg commit -m "#5: changes to invoke rebase"
+
+  $ cat > $TESTTMP/checkvisibility.sh <<EOF
+  > echo "==== \$1:"
+  > hg parents --template "VISIBLE {rev}:{node|short}\n"
+  > # test that pending changes are hidden
+  > unset HG_PENDING
+  > hg parents --template "ACTUAL  {rev}:{node|short}\n"
+  > echo "===="
+  > EOF
+
+  $ cat >> .hg/hgrc <<EOF
+  > [defaults]
+  > # to fix hash id of temporary revisions
+  > unshelve = --date '0 0'
+  > EOF
+
+"hg unshelve" at REV5 implies steps below:
+
+(1) commit changes in the working directory (REV6)
+(2) unbundle shelved revision (REV7)
+(3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
+(4) rebase: commit merged revision (REV8)
+(5) rebase: update to REV6 (REV8 => REV6)
+(6) update to REV5 (REV6 => REV5)
+(7) abort transaction
+
+== test visibility to external preupdate hook
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
+  > EOF
+
+  $ echo nnnn >> n
+
+  $ sh $TESTTMP/checkvisibility.sh before-unshelving
+  ==== before-unshelving:
+  VISIBLE 5:703117a2acfb
+  ACTUAL  5:703117a2acfb
+  ====
+
+  $ hg unshelve --keep default
+  temporarily committing pending changes (restore with 'hg unshelve --abort')
+  rebasing shelved changes
+  rebasing 7:fcbb97608399 "changes to 'create conflict'" (tip)
+  ==== preupdate:
+  VISIBLE 6:66b86db80ee4
+  ACTUAL  5:703117a2acfb
+  ====
+  ==== preupdate:
+  VISIBLE 8:cb2a4e59c2d5
+  ACTUAL  5:703117a2acfb
+  ====
+  ==== preupdate:
+  VISIBLE 6:66b86db80ee4
+  ACTUAL  5:703117a2acfb
+  ====
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > preupdate.visibility =
+  > EOF
+
+  $ sh $TESTTMP/checkvisibility.sh after-unshelving
+  ==== after-unshelving:
+  VISIBLE 5:703117a2acfb
+  ACTUAL  5:703117a2acfb
+  ====
+
+  $ cd ..
+
 test Abort unshelve always gets user out of the unshelved state
 ---------------------------------------------------------------
   $ hg init salvage