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.
This commit is contained in:
FUJIWARA Katsunori 2015-10-17 01:15:34 +09:00
parent 36207b46bf
commit 8f93d72f88
7 changed files with 328 additions and 9 deletions

View File

@ -407,15 +407,13 @@ class cg1unpacker(object):
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()

View File

@ -118,6 +118,13 @@ def _exthook(ui, repo, name, cmd, args, throw):
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()

View File

@ -994,8 +994,7 @@ class localrepository(object):
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 @@ class localrepository(object):
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 @@ class localrepository(object):
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

View File

@ -259,6 +259,60 @@ check line 1 is back
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

View File

@ -533,6 +533,110 @@ external process
$ 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

View File

@ -242,3 +242,85 @@ dropping status of "file2")
====
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

View File

@ -1011,6 +1011,84 @@ with general delta
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