view tests/test-hook.t @ 46582:b0a3ca02d17a

copies-rust: implement PartialEqual manually Now that we know that each (dest, rev) pair has at most a unique CopySource, we can simplify comparison a lot. This "simple" step buy a good share of the previous slowdown back in some case: Repo Case Source-Rev Dest-Rev # of revisions old time new time Difference Factor time per rev --------------------------------------------------------------------------------------------------------------------------------------------------------------- mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 382065 revs, 43.304637 s, 34.443661 s, -8.860976 s, × 0.7954, 90 µs/rev Full benchmark: Repo Case Source-Rev Dest-Rev # of revisions old time new time Difference Factor time per rev --------------------------------------------------------------------------------------------------------------------------------------------------------------- mercurial x_revs_x_added_0_copies ad6b123de1c7 39cfcef4f463 : 1 revs, 0.000043 s, 0.000043 s, +0.000000 s, × 1.0000, 43 µs/rev mercurial x_revs_x_added_x_copies 2b1c78674230 0c1d10351869 : 6 revs, 0.000114 s, 0.000117 s, +0.000003 s, × 1.0263, 19 µs/rev mercurial x000_revs_x000_added_x_copies 81f8ff2a9bf2 dd3267698d84 : 1032 revs, 0.004937 s, 0.004892 s, -0.000045 s, × 0.9909, 4 µs/rev pypy x_revs_x_added_0_copies aed021ee8ae8 099ed31b181b : 9 revs, 0.000339 s, 0.000196 s, -0.000143 s, × 0.5782, 21 µs/rev pypy x_revs_x000_added_0_copies 4aa4e1f8e19a 359343b9ac0e : 1 revs, 0.000049 s, 0.000050 s, +0.000001 s, × 1.0204, 50 µs/rev pypy x_revs_x_added_x_copies ac52eb7bbbb0 72e022663155 : 7 revs, 0.000202 s, 0.000117 s, -0.000085 s, × 0.5792, 16 µs/rev pypy x_revs_x00_added_x_copies c3b14617fbd7 ace7255d9a26 : 1 revs, 0.000409 s, 0.6f1f4a s, -0.000087 s, × 0.7873, 322 µs/rev pypy x_revs_x000_added_x000_copies df6f7a526b60 a83dc6a2d56f : 6 revs, 0.011984 s, 0.011949 s, -0.000035 s, × 0.9971, 1991 µs/rev pypy x000_revs_xx00_added_0_copies 89a76aede314 2f22446ff07e : 4785 revs, 0.050820 s, 0.050802 s, -0.000018 s, × 0.9996, 10 µs/rev pypy x000_revs_x000_added_x_copies 8a3b5bfd266e 2c68e87c3efe : 6780 revs, 0.087953 s, 0.088090 s, +0.000137 s, × 1.0016, 12 µs/rev pypy x000_revs_x000_added_x000_copies 89a76aede314 7b3dda341c84 : 5441 revs, 0.062902 s, 0.062079 s, -0.000823 s, × 0.9869, 11 µs/rev pypy x0000_revs_x_added_0_copies d1defd0dc478 c9cb1334cc78 : 43645 revs, 0.679234 s, 0.635337 s, -0.043897 s, × 0.9354, 14 µs/rev pypy x0000_revs_xx000_added_0_copies bf2c629d0071 4ffed77c095c : 2 revs, 0.013095 s, 0.013262 s, +0.000167 s, × 1.0128, 6631 µs/rev pypy x0000_revs_xx000_added_x000_copies 08ea3258278e d9fa043f30c0 : 11316 revs, 0.120910 s, 0.120085 s, -0.000825 s, × 0.9932, 10 µs/rev netbeans x_revs_x_added_0_copies fb0955ffcbcd a01e9239f9e7 : 2 revs, 0.000087 s, 0.000085 s, -0.000002 s, × 0.9770, 42 µs/rev netbeans x_revs_x000_added_0_copies 6f360122949f 20eb231cc7d0 : 2 revs, 0.000107 s, 0.000110 s, +0.000003 s, × 1.0280, 55 µs/rev netbeans x_revs_x_added_x_copies 1ada3faf6fb6 5a39d12eecf4 : 3 revs, 0.000186 s, 0.000177 s, -0.000009 s, × 0.9516, 59 µs/rev netbeans x_revs_x00_added_x_copies 35be93ba1e2c 9eec5e90c05f : 9 revs, 0.000754 s, 0.000743 s, -0.000011 s, × 0.9854, 82 µs/rev netbeans x000_revs_xx00_added_0_copies eac3045b4fdd 51d4ae7f1290 : 1421 revs, 0.010443 s, 0.010168 s, -0.000275 s, × 0.9737, 7 µs/rev netbeans x000_revs_x000_added_x_copies e2063d266acd 6081d72689dc : 1533 revs, 0.015697 s, 0.015946 s, +0.000249 s, × 1.0159, 10 µs/rev netbeans x000_revs_x000_added_x000_copies ff453e9fee32 411350406ec2 : 5750 revs, 0.063528 s, 0.062712 s, -0.000816 s, × 0.9872, 10 µs/rev netbeans x0000_revs_xx000_added_x000_copies 588c2d1ced70 1aad62e59ddd : 66949 revs, 0.545515 s, 0.523832 s, -0.021683 s, × 0.9603, 7 µs/rev mozilla-central x_revs_x_added_0_copies 3697f962bb7b 7015fcdd43a2 : 2 revs, 0.000089 s, 0.000090 s, +0.000001 s, × 1.0112, 45 µs/rev mozilla-central x_revs_x000_added_0_copies dd390860c6c9 40d0c5bed75d : 8 revs, 0.000265 s, 0.000264 s, -0.000001 s, × 0.9962, 33 µs/rev mozilla-central x_revs_x_added_x_copies 8d198483ae3b 14207ffc2b2f : 9 revs, 0.000381 s, 0.000187 s, -0.000194 s, × 0.4908, 20 µs/rev mozilla-central x_revs_x00_added_x_copies 98cbc58cc6bc 446a150332c3 : 7 revs, 0.000672 s, 0.000665 s, -0.000007 s, × 0.9896, 95 µs/rev mozilla-central x_revs_x000_added_x000_copies 3c684b4b8f68 0a5e72d1b479 : 3 revs, 0.003497 s, 0.003556 s, +0.000059 s, × 1.0169, 1185 µs/rev mozilla-central x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.073204 s, 0.071345 s, -0.001859 s, × 0.9746, 11890 µs/rev mozilla-central x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.006482 s, 0.006551 s, +0.000069 s, × 1.0106, 4 µs/rev mozilla-central x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.005066 s, 0.005078 s, +0.000012 s, × 1.0024, 123 µs/rev mozilla-central x000_revs_x000_added_x000_copies 7c97034feb78 4407bd0c6330 : 7839 revs, 0.065707 s, 0.065823 s, +0.000116 s, × 1.0018, 8 µs/rev mozilla-central x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.026800 s, 0.027050 s, +0.000250 s, × 1.0093, 43 µs/rev mozilla-central x0000_revs_xx000_added_x000_copies f78c615a656c 96a38b690156 : 30263 revs, 0.203856 s, 0.202443 s, -0.001413 s, × 0.9931, 6 µs/rev mozilla-central x00000_revs_x0000_added_x0000_copies 6832ae71433c 4c222a1d9a00 : 153721 revs, 1.293394 s, 1.261583 s, -0.031811 s, × 0.9754, 8 µs/rev mozilla-central x00000_revs_x00000_added_x000_copies 76caed42cf7c 1daa622bbe42 : 204976 revs, 1.698239 s, 1.643869 s, -0.054370 s, × 0.9680, 8 µs/rev mozilla-try x_revs_x_added_0_copies aaf6dde0deb8 9790f499805a : 2 revs, 0.000875 s, 0.000868 s, -0.000007 s, × 0.9920, 434 µs/rev mozilla-try x_revs_x000_added_0_copies d8d0222927b4 5bb8ce8c7450 : 2 revs, 0.000891 s, 0.000887 s, -0.000004 s, × 0.9955, 443 µs/rev mozilla-try x_revs_x_added_x_copies 092fcca11bdb 936255a0384a : 4 revs, 0.000292 s, 0.000168 s, -0.000124 s, × 0.5753, 42 µs/rev mozilla-try x_revs_x00_added_x_copies b53d2fadbdb5 017afae788ec : 2 revs, 0.003939 s, 0.001160 s, -0.002779 s, × 0.2945, 580 µs/rev mozilla-try x_revs_x000_added_x000_copies 20408ad61ce5 6f0ee96e21ad : 1 revs, 0.033027 s, 0.033016 s, -0.000011 s, × 0.9997, 33016 µs/rev mozilla-try x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.073703 s, 0.073312 s, -0.39ae31 s, × 0.9947, 12218 µs/rev mozilla-try x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.006469 s, 0.006485 s, +0.000016 s, × 1.0025, 4 µs/rev mozilla-try x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.005278 s, 0.005494 s, +0.000216 s, × 1.0409, 134 µs/rev mozilla-try x000_revs_x000_added_x000_copies 1346fd0130e4 4c65cbdabc1f : 6657 revs, 0.064995 s, 0.064879 s, -0.000116 s, × 0.9982, 9 µs/rev mozilla-try x0000_revs_x_added_0_copies 63519bfd42ee a36a2a865d92 : 40314 revs, 0.301041 s, 0.301469 s, +0.000428 s, × 1.0014, 7 µs/rev mozilla-try x0000_revs_x_added_x_copies 9fe69ff0762d bcabf2a78927 : 38690 revs, 0.285575 s, 0.297113 s, +0.011538 s, × 1.0404, 7 µs/rev mozilla-try x0000_revs_xx000_added_x_copies 156f6e2674f2 4d0f2c178e66 : 8598 revs, 0.085597 s, 0.085890 s, +0.000293 s, × 1.0034, 9 µs/rev mozilla-try x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.027118 s, 0.027718 s, +0.000600 s, × 1.0221, 45 µs/rev mozilla-try x0000_revs_xx000_added_x000_copies 89294cd501d9 7ccb2fc7ccb5 : 97052 revs, 2.119204 s, 2.048949 s, -0.070255 s, × 0.9668, 21 µs/rev mozilla-try x0000_revs_x0000_added_x0000_copies e928c65095ed e951f4ad123a : 52031 revs, 0.701479 s, 0.685924 s, -0.015555 s, × 0.9778, 13 µs/rev mozilla-try x00000_revs_x_added_0_copies 6a320851d377 1ebb79acd503 : 363753 revs, 4.482399 s, 4.482891 s, +0.000492 s, × 1.0001, 12 µs/rev mozilla-try x00000_revs_x00000_added_0_copies dc8a3ca7010e d16fde900c9c : 34414 revs, 0.574082 s, 0.577633 s, +0.003551 s, × 1.0062, 16 µs/rev mozilla-try x00000_revs_x_added_x_copies 5173c4b6f97c 95d83ee7242d : 362229 revs, 4.480366 s, 4.397816 s, -0.082550 s, × 0.9816, 12 µs/rev mozilla-try x00000_revs_x000_added_x_copies 9126823d0e9c ca82787bb23c : 359344 revs, 4.369070 s, 4.370538 s, +0.001468 s, × 1.0003, 12 µs/rev mozilla-try x00000_revs_x0000_added_x0000_copies 8d3fafa80d4b eb884023b810 : 192665 revs, 1.592506 s, 1.570439 s, -0.022067 s, × 0.9861, 8 µs/rev mozilla-try x00000_revs_x00000_added_x0000_copies 1b661134e2ca 1ae03d022d6d : 228985 revs, 87.824489 s, 88.388512 s, +0.564023 s, × 1.0064, 386 µs/rev mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 382065 revs, 43.304637 s, 34.443661 s, -8.860976 s, × 0.7954, 90 µs/rev private : 459513 revs, 33.853687 s, 27.370148 s, -6.483539 s, × 0.8085, 59 µs/rev Differential Revision: https://phab.mercurial-scm.org/D9653
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 16 Dec 2020 11:11:05 +0100
parents d67732a4b58a
children 802ba3c81507
line wrap: on
line source

commit hooks can see env vars
(and post-transaction one are run unlocked)


  $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
  > from mercurial import pycompat
  > def showargs(ui, repo, hooktype, **kwargs):
  >     kwargs = pycompat.byteskwargs(kwargs)
  >     ui.write(b'%s Python hook: %s\n' % (hooktype,
  >                                         b','.join(sorted(kwargs))))
  > EOF

  $ hg init a
  $ cd a
  $ cat > .hg/hgrc <<EOF
  > [hooks]
  > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit"
  > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit.b"
  > precommit = sh -c  "HG_LOCAL= HG_NODE= HG_TAG= printenv.py --line precommit"
  > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxncommit"
  > pretxncommit.tip = hg -q tip
  > pre-identify = sh -c "printenv.py --line pre-identify 1"
  > pre-cat = sh -c "printenv.py --line pre-cat"
  > post-cat = sh -c "printenv.py --line post-cat"
  > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnopen"
  > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnclose"
  > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnclose"
  > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
  > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnabort"
  > txnclose.checklock = sh -c "hg debuglock > /dev/null"
  > EOF
  $ echo a > a
  $ hg add a
  $ hg commit -m a
  precommit hook: HG_HOOKNAME=precommit
  HG_HOOKTYPE=precommit
  HG_PARENT1=0000000000000000000000000000000000000000
  
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  pretxncommit hook: HG_HOOKNAME=pretxncommit
  HG_HOOKTYPE=pretxncommit
  HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  HG_PARENT1=0000000000000000000000000000000000000000
  HG_PENDING=$TESTTMP/a
  
  0:cb9a9f314b8b
  pretxnclose hook: HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_PHASES_MOVED=1
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  txnclose hook: HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_PHASES_MOVED=1
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  commit hook: HG_HOOKNAME=commit
  HG_HOOKTYPE=commit
  HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  HG_PARENT1=0000000000000000000000000000000000000000
  
  commit.b hook: HG_HOOKNAME=commit.b
  HG_HOOKTYPE=commit
  HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  HG_PARENT1=0000000000000000000000000000000000000000
  

  $ hg clone . ../b
  updating to branch default
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ cd ../b

changegroup hooks can see env vars

  $ cat > .hg/hgrc <<EOF
  > [hooks]
  > prechangegroup = sh -c "printenv.py --line prechangegroup"
  > changegroup = sh -c "printenv.py --line changegroup"
  > incoming = sh -c "printenv.py --line incoming"
  > EOF

pretxncommit and commit hooks can see both parents of merge

  $ cd ../a
  $ echo b >> a
  $ hg commit -m a1 -d "1 0"
  precommit hook: HG_HOOKNAME=precommit
  HG_HOOKTYPE=precommit
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  pretxncommit hook: HG_HOOKNAME=pretxncommit
  HG_HOOKTYPE=pretxncommit
  HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  HG_PENDING=$TESTTMP/a
  
  1:ab228980c14d
  pretxnclose hook: HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  txnclose hook: HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  commit hook: HG_HOOKNAME=commit
  HG_HOOKTYPE=commit
  HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  
  commit.b hook: HG_HOOKNAME=commit.b
  HG_HOOKTYPE=commit
  HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  
  $ hg update -C 0
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ echo b > b
  $ hg add b
  $ hg commit -m b -d '1 0'
  precommit hook: HG_HOOKNAME=precommit
  HG_HOOKTYPE=precommit
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  pretxncommit hook: HG_HOOKNAME=pretxncommit
  HG_HOOKTYPE=pretxncommit
  HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  HG_PENDING=$TESTTMP/a
  
  2:ee9deb46ab31
  pretxnclose hook: HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  created new head
  txnclose hook: HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  commit hook: HG_HOOKNAME=commit
  HG_HOOKTYPE=commit
  HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  
  commit.b hook: HG_HOOKNAME=commit.b
  HG_HOOKTYPE=commit
  HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
  
  $ hg merge 1
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  (branch merge, don't forget to commit)
  $ hg commit -m merge -d '2 0'
  precommit hook: HG_HOOKNAME=precommit
  HG_HOOKTYPE=precommit
  HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
  
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  pretxncommit hook: HG_HOOKNAME=pretxncommit
  HG_HOOKTYPE=pretxncommit
  HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
  HG_PENDING=$TESTTMP/a
  
  3:07f3376c1e65
  pretxnclose hook: HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  txnclose hook: HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  commit hook: HG_HOOKNAME=commit
  HG_HOOKTYPE=commit
  HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
  
  commit.b hook: HG_HOOKNAME=commit.b
  HG_HOOKTYPE=commit
  HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
  

test generic hooks

  $ hg id
  pre-identify hook: HG_ARGS=id
  HG_HOOKNAME=pre-identify
  HG_HOOKTYPE=pre-identify
  HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None, 'template': ''}
  HG_PATS=[]
  
  abort: pre-identify hook exited with status 1
  [40]
  $ hg cat b
  pre-cat hook: HG_ARGS=cat b
  HG_HOOKNAME=pre-cat
  HG_HOOKTYPE=pre-cat
  HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
  HG_PATS=['b']
  
  b
  post-cat hook: HG_ARGS=cat b
  HG_HOOKNAME=post-cat
  HG_HOOKTYPE=post-cat
  HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
  HG_PATS=['b']
  HG_RESULT=0
  

  $ cd ../b
  $ hg pull ../a
  pulling from ../a
  searching for changes
  prechangegroup hook: HG_HOOKNAME=prechangegroup
  HG_HOOKTYPE=prechangegroup
  HG_SOURCE=pull
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=pull
  file:/*/$TESTTMP/a (glob)
  HG_URL=file:$TESTTMP/a
  
  adding changesets
  adding manifests
  adding file changes
  added 3 changesets with 2 changes to 2 files
  new changesets ab228980c14d:07f3376c1e65
  changegroup hook: HG_HOOKNAME=changegroup
  HG_HOOKTYPE=changegroup
  HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
  HG_NODE_LAST=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_SOURCE=pull
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=pull
  file:/*/$TESTTMP/a (glob)
  HG_URL=file:$TESTTMP/a
  
  incoming hook: HG_HOOKNAME=incoming
  HG_HOOKTYPE=incoming
  HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
  HG_SOURCE=pull
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=pull
  file:/*/$TESTTMP/a (glob)
  HG_URL=file:$TESTTMP/a
  
  incoming hook: HG_HOOKNAME=incoming
  HG_HOOKTYPE=incoming
  HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
  HG_SOURCE=pull
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=pull
  file:/*/$TESTTMP/a (glob)
  HG_URL=file:$TESTTMP/a
  
  incoming hook: HG_HOOKNAME=incoming
  HG_HOOKTYPE=incoming
  HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_SOURCE=pull
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=pull
  file:/*/$TESTTMP/a (glob)
  HG_URL=file:$TESTTMP/a
  
  (run 'hg update' to get a working copy)

tag hooks can see env vars

  $ cd ../a
  $ cat >> .hg/hgrc <<EOF
  > pretag = sh -c "printenv.py --line pretag"
  > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py --line tag"
  > EOF
  $ hg tag -d '3 0' a
  pretag hook: HG_HOOKNAME=pretag
  HG_HOOKTYPE=pretag
  HG_LOCAL=0
  HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_TAG=a
  
  precommit hook: HG_HOOKNAME=precommit
  HG_HOOKTYPE=precommit
  HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
  
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  pretxncommit hook: HG_HOOKNAME=pretxncommit
  HG_HOOKTYPE=pretxncommit
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_PENDING=$TESTTMP/a
  
  4:539e4b31b6dc
  pretxnclose hook: HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  tag hook: HG_HOOKNAME=tag
  HG_HOOKTYPE=tag
  HG_LOCAL=0
  HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
  HG_TAG=a
  
  txnclose hook: HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  commit hook: HG_HOOKNAME=commit
  HG_HOOKTYPE=commit
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
  
  commit.b hook: HG_HOOKNAME=commit.b
  HG_HOOKTYPE=commit
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
  
  $ hg tag -l la
  pretag hook: HG_HOOKNAME=pretag
  HG_HOOKTYPE=pretag
  HG_LOCAL=1
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_TAG=la
  
  tag hook: HG_HOOKNAME=tag
  HG_HOOKTYPE=tag
  HG_LOCAL=1
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_TAG=la
  

pretag hook can forbid tagging

  $ cat >> .hg/hgrc <<EOF
  > pretag.forbid = sh -c "printenv.py --line pretag.forbid 1"
  > EOF
  $ hg tag -d '4 0' fa
  pretag hook: HG_HOOKNAME=pretag
  HG_HOOKTYPE=pretag
  HG_LOCAL=0
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_TAG=fa
  
  pretag.forbid hook: HG_HOOKNAME=pretag.forbid
  HG_HOOKTYPE=pretag
  HG_LOCAL=0
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_TAG=fa
  
  abort: pretag.forbid hook exited with status 1
  [40]
  $ hg tag -l fla
  pretag hook: HG_HOOKNAME=pretag
  HG_HOOKTYPE=pretag
  HG_LOCAL=1
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_TAG=fla
  
  pretag.forbid hook: HG_HOOKNAME=pretag.forbid
  HG_HOOKTYPE=pretag
  HG_LOCAL=1
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_TAG=fla
  
  abort: pretag.forbid hook exited with status 1
  [40]

pretxncommit hook can see changeset, can roll back txn, changeset no
more there after

  $ cat >> .hg/hgrc <<EOF
  > pretxncommit.forbid0 = sh -c "hg tip -q"
  > pretxncommit.forbid1 = sh -c "printenv.py --line pretxncommit.forbid 1"
  > EOF
  $ echo z > z
  $ hg add z
  $ hg -q tip
  4:539e4b31b6dc
  $ hg commit -m 'fail' -d '4 0'
  precommit hook: HG_HOOKNAME=precommit
  HG_HOOKTYPE=precommit
  HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  pretxncommit hook: HG_HOOKNAME=pretxncommit
  HG_HOOKTYPE=pretxncommit
  HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
  HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_PENDING=$TESTTMP/a
  
  5:6f611f8018c1
  5:6f611f8018c1
  pretxncommit.forbid hook: HG_HOOKNAME=pretxncommit.forbid1
  HG_HOOKTYPE=pretxncommit
  HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
  HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_PENDING=$TESTTMP/a
  
  transaction abort!
  txnabort Python hook: changes,txnid,txnname
  txnabort hook: HG_HOOKNAME=txnabort.1
  HG_HOOKTYPE=txnabort
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=commit
  
  rollback completed
  abort: pretxncommit.forbid1 hook exited with status 1
  [40]
  $ hg -q tip
  4:539e4b31b6dc

(Check that no 'changelog.i.a' file were left behind)

  $ ls -1 .hg/store/
  00changelog.i
  00manifest.i
  data
  fncache (repofncache !)
  journal.phaseroots
  phaseroots
  undo
  undo.backup.fncache (repofncache !)
  undo.backupfiles
  undo.phaseroots


precommit hook can prevent commit

  $ cat >> .hg/hgrc <<EOF
  > precommit.forbid = sh -c "printenv.py --line precommit.forbid 1"
  > EOF
  $ hg commit -m 'fail' -d '4 0'
  precommit hook: HG_HOOKNAME=precommit
  HG_HOOKTYPE=precommit
  HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  
  precommit.forbid hook: HG_HOOKNAME=precommit.forbid
  HG_HOOKTYPE=precommit
  HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  
  abort: precommit.forbid hook exited with status 1
  [40]
  $ hg -q tip
  4:539e4b31b6dc

preupdate hook can prevent update

  $ cat >> .hg/hgrc <<EOF
  > preupdate = sh -c "printenv.py --line preupdate"
  > EOF
  $ hg update 1
  preupdate hook: HG_HOOKNAME=preupdate
  HG_HOOKTYPE=preupdate
  HG_PARENT1=ab228980c14d
  
  0 files updated, 0 files merged, 2 files removed, 0 files unresolved

update hook

  $ cat >> .hg/hgrc <<EOF
  > update = sh -c "printenv.py --line update"
  > EOF
  $ hg update
  preupdate hook: HG_HOOKNAME=preupdate
  HG_HOOKTYPE=preupdate
  HG_PARENT1=539e4b31b6dc
  
  update hook: HG_ERROR=0
  HG_HOOKNAME=update
  HG_HOOKTYPE=update
  HG_PARENT1=539e4b31b6dc
  
  2 files updated, 0 files merged, 0 files removed, 0 files unresolved

pushkey hook

  $ cat >> .hg/hgrc <<EOF
  > pushkey = sh -c "printenv.py --line pushkey"
  > EOF
  $ cd ../b
  $ hg bookmark -r null foo
  $ hg push -B foo ../a
  pushing to ../a
  searching for changes
  no changes found
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=push
  
  pretxnclose hook: HG_BOOKMARK_MOVED=1
  HG_BUNDLE2=1
  HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_SOURCE=push
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=push
  HG_URL=file:$TESTTMP/a
  
  pushkey hook: HG_BUNDLE2=1
  HG_HOOKNAME=pushkey
  HG_HOOKTYPE=pushkey
  HG_KEY=foo
  HG_NAMESPACE=bookmarks
  HG_NEW=0000000000000000000000000000000000000000
  HG_PUSHKEYCOMPAT=1
  HG_SOURCE=push
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=push
  HG_URL=file:$TESTTMP/a
  
  txnclose hook: HG_BOOKMARK_MOVED=1
  HG_BUNDLE2=1
  HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_SOURCE=push
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=push
  HG_URL=file:$TESTTMP/a
  
  exporting bookmark foo
  [1]
  $ cd ../a

listkeys hook

  $ cat >> .hg/hgrc <<EOF
  > listkeys = sh -c "printenv.py --line listkeys"
  > EOF
  $ hg bookmark -r null bar
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=bookmark
  
  pretxnclose hook: HG_BOOKMARK_MOVED=1
  HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=bookmark
  
  txnclose hook: HG_BOOKMARK_MOVED=1
  HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=bookmark
  
  $ cd ../b
  $ hg pull -B bar ../a
  pulling from ../a
  listkeys hook: HG_HOOKNAME=listkeys
  HG_HOOKTYPE=listkeys
  HG_NAMESPACE=bookmarks
  HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
  
  no changes found
  adding remote bookmark bar
  $ cd ../a

test that prepushkey can prevent incoming keys

  $ cat >> .hg/hgrc <<EOF
  > prepushkey = sh -c "printenv.py --line prepushkey.forbid 1"
  > EOF
  $ cd ../b
  $ hg bookmark -r null baz
  $ hg push -B baz ../a
  pushing to ../a
  searching for changes
  listkeys hook: HG_HOOKNAME=listkeys
  HG_HOOKTYPE=listkeys
  HG_NAMESPACE=phases
  HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
  
  listkeys hook: HG_HOOKNAME=listkeys
  HG_HOOKTYPE=listkeys
  HG_NAMESPACE=bookmarks
  HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
  
  no changes found
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=push
  
  prepushkey.forbid hook: HG_BUNDLE2=1
  HG_HOOKNAME=prepushkey
  HG_HOOKTYPE=prepushkey
  HG_KEY=baz
  HG_NAMESPACE=bookmarks
  HG_NEW=0000000000000000000000000000000000000000
  HG_PUSHKEYCOMPAT=1
  HG_SOURCE=push
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=push
  HG_URL=file:$TESTTMP/a
  
  abort: prepushkey hook exited with status 1
  [40]
  $ cd ../a

test that prelistkeys can prevent listing keys

  $ cat >> .hg/hgrc <<EOF
  > prelistkeys = sh -c "printenv.py --line prelistkeys.forbid 1"
  > EOF
  $ hg bookmark -r null quux
  pretxnopen hook: HG_HOOKNAME=pretxnopen
  HG_HOOKTYPE=pretxnopen
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=bookmark
  
  pretxnclose hook: HG_BOOKMARK_MOVED=1
  HG_HOOKNAME=pretxnclose
  HG_HOOKTYPE=pretxnclose
  HG_PENDING=$TESTTMP/a
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=bookmark
  
  txnclose hook: HG_BOOKMARK_MOVED=1
  HG_HOOKNAME=txnclose
  HG_HOOKTYPE=txnclose
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=bookmark
  
  $ cd ../b
  $ hg pull -B quux ../a
  pulling from ../a
  prelistkeys.forbid hook: HG_HOOKNAME=prelistkeys
  HG_HOOKTYPE=prelistkeys
  HG_NAMESPACE=bookmarks
  
  abort: prelistkeys hook exited with status 1
  [40]
  $ cd ../a
  $ rm .hg/hgrc

prechangegroup hook can prevent incoming changes

  $ cd ../b
  $ hg -q tip
  3:07f3376c1e65
  $ cat > .hg/hgrc <<EOF
  > [hooks]
  > prechangegroup.forbid = sh -c "printenv.py --line prechangegroup.forbid 1"
  > EOF
  $ hg pull ../a
  pulling from ../a
  searching for changes
  prechangegroup.forbid hook: HG_HOOKNAME=prechangegroup.forbid
  HG_HOOKTYPE=prechangegroup
  HG_SOURCE=pull
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=pull
  file:/*/$TESTTMP/a (glob)
  HG_URL=file:$TESTTMP/a
  
  abort: prechangegroup.forbid hook exited with status 1
  [40]

pretxnchangegroup hook can see incoming changes, can roll back txn,
incoming changes no longer there after

  $ cat > .hg/hgrc <<EOF
  > [hooks]
  > pretxnchangegroup.forbid0 = hg tip -q
  > pretxnchangegroup.forbid1 = sh -c "printenv.py --line pretxnchangegroup.forbid 1"
  > EOF
  $ hg pull ../a
  pulling from ../a
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  4:539e4b31b6dc
  pretxnchangegroup.forbid hook: HG_HOOKNAME=pretxnchangegroup.forbid1
  HG_HOOKTYPE=pretxnchangegroup
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_NODE_LAST=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_PENDING=$TESTTMP/b
  HG_SOURCE=pull
  HG_TXNID=TXN:$ID$
  HG_TXNNAME=pull
  file:/*/$TESTTMP/a (glob)
  HG_URL=file:$TESTTMP/a
  
  transaction abort!
  rollback completed
  abort: pretxnchangegroup.forbid1 hook exited with status 1
  [40]
  $ hg -q tip
  3:07f3376c1e65

outgoing hooks can see env vars

  $ rm .hg/hgrc
  $ cat > ../a/.hg/hgrc <<EOF
  > [hooks]
  > preoutgoing = sh -c "printenv.py --line preoutgoing"
  > outgoing = sh -c "printenv.py --line outgoing"
  > EOF
  $ hg pull ../a
  pulling from ../a
  searching for changes
  preoutgoing hook: HG_HOOKNAME=preoutgoing
  HG_HOOKTYPE=preoutgoing
  HG_SOURCE=pull
  
  outgoing hook: HG_HOOKNAME=outgoing
  HG_HOOKTYPE=outgoing
  HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
  HG_SOURCE=pull
  
  adding changesets
  adding manifests
  adding file changes
  adding remote bookmark quux
  added 1 changesets with 1 changes to 1 files
  new changesets 539e4b31b6dc
  (run 'hg update' to get a working copy)
  $ hg rollback
  repository tip rolled back to revision 3 (undo pull)

preoutgoing hook can prevent outgoing changes

  $ cat >> ../a/.hg/hgrc <<EOF
  > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
  > EOF
  $ hg pull ../a
  pulling from ../a
  searching for changes
  preoutgoing hook: HG_HOOKNAME=preoutgoing
  HG_HOOKTYPE=preoutgoing
  HG_SOURCE=pull
  
  preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
  HG_HOOKTYPE=preoutgoing
  HG_SOURCE=pull
  
  abort: preoutgoing.forbid hook exited with status 1
  [40]

outgoing hooks work for local clones

  $ cd ..
  $ cat > a/.hg/hgrc <<EOF
  > [hooks]
  > preoutgoing = sh -c "printenv.py --line preoutgoing"
  > outgoing = sh -c "printenv.py --line outgoing"
  > EOF
  $ hg clone a c
  preoutgoing hook: HG_HOOKNAME=preoutgoing
  HG_HOOKTYPE=preoutgoing
  HG_SOURCE=clone
  
  outgoing hook: HG_HOOKNAME=outgoing
  HG_HOOKTYPE=outgoing
  HG_NODE=0000000000000000000000000000000000000000
  HG_SOURCE=clone
  
  updating to branch default
  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ rm -rf c

preoutgoing hook can prevent outgoing changes for local clones

  $ cat >> a/.hg/hgrc <<EOF
  > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
  > EOF
  $ hg clone a zzz
  preoutgoing hook: HG_HOOKNAME=preoutgoing
  HG_HOOKTYPE=preoutgoing
  HG_SOURCE=clone
  
  preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
  HG_HOOKTYPE=preoutgoing
  HG_SOURCE=clone
  
  abort: preoutgoing.forbid hook exited with status 1
  [40]

  $ cd "$TESTTMP/b"

  $ cat > hooktests.py <<EOF
  > from __future__ import print_function
  > from mercurial import (
  >     error,
  >     pycompat,
  > )
  > 
  > uncallable = 0
  > 
  > def printargs(ui, args):
  >     a = list(pycompat.byteskwargs(args).items())
  >     a.sort()
  >     ui.write(b'hook args:\n')
  >     for k, v in a:
  >        ui.write(b'  %s %s\n' % (k, v))
  > 
  > def passhook(ui, repo, **args):
  >     printargs(ui, args)
  > 
  > def failhook(ui, repo, **args):
  >     printargs(ui, args)
  >     return True
  > 
  > class LocalException(Exception):
  >     pass
  > 
  > def raisehook(**args):
  >     raise LocalException('exception from hook')
  > 
  > def aborthook(**args):
  >     raise error.Abort(b'raise abort from hook')
  > 
  > def brokenhook(**args):
  >     return 1 + {}
  > 
  > def verbosehook(ui, **args):
  >     ui.note(b'verbose output from hook\n')
  > 
  > def printtags(ui, repo, **args):
  >     ui.write(b'[%s]\n' % b', '.join(sorted(repo.tags())))
  > 
  > class container(object):
  >     unreachable = 1
  > EOF

  $ cat > syntaxerror.py << NO_CHECK_EOF
  > (foo
  > NO_CHECK_EOF

test python hooks

#if windows
  $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
#else
  $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
#endif
  $ export PYTHONPATH

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
  $ hg pull ../a 2>&1 | grep 'raised an exception'
  error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
  $ hg pull ../a 2>&1 | grep 'raised an exception'
  error: preoutgoing.raise hook raised an exception: exception from hook

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  error: preoutgoing.abort hook failed: raise abort from hook
  abort: raise abort from hook
  [255]

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  hook args:
    hooktype preoutgoing
    source pull
  abort: preoutgoing.fail hook failed
  [40]

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  abort: preoutgoing.uncallable hook is invalid: "hooktests.uncallable" is not callable
  [255]

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  abort: preoutgoing.nohook hook is invalid: "hooktests.nohook" is not defined
  [255]

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  abort: preoutgoing.nomodule hook is invalid: "nomodule" not in a module
  [255]

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  abort: preoutgoing.badmodule hook is invalid: import of "nomodule" failed
  (run with --traceback for stack trace)
  [255]

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  abort: preoutgoing.unreachable hook is invalid: import of "hooktests.container" failed
  (run with --traceback for stack trace)
  [255]

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.syntaxerror = python:syntaxerror.syntaxerror' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
  (run with --traceback for stack trace)
  [255]

  $ hg pull ../a --traceback 2>&1 | egrep 'pulling|searching|^exception|Traceback|SyntaxError|ImportError|ModuleNotFoundError|HookLoadError|abort'
  pulling from ../a
  searching for changes
  exception from first failed import attempt:
  Traceback (most recent call last):
  SyntaxError: * (glob)
  exception from second failed import attempt:
  Traceback (most recent call last): (py3 !)
  SyntaxError: * (glob) (py3 !)
  Traceback (most recent call last):
  ImportError: No module named hgext_syntaxerror (no-py3 !)
  ImportError: No module named 'hgext_syntaxerror' (py3 no-py36 !)
  ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
  Traceback (most recent call last):
  SyntaxError: * (glob) (py3 !)
  Traceback (most recent call last): (py3 !)
  ImportError: No module named 'hgext_syntaxerror' (py3 no-py36 !)
  ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
  Traceback (most recent call last): (py3 !)
  HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed (no-py3 !)
      raise error.HookLoadError( (py38 !)
  mercurial.error.HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed (py3 !)
  abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed

  $ echo '[hooks]' > ../a/.hg/hgrc
  $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  hook args:
    hooktype preoutgoing
    source pull
  adding changesets
  adding manifests
  adding file changes
  adding remote bookmark quux
  added 1 changesets with 1 changes to 1 files
  new changesets 539e4b31b6dc
  (run 'hg update' to get a working copy)

post- python hooks that fail to *run* don't cause an abort
  $ rm ../a/.hg/hgrc
  $ echo '[hooks]' > .hg/hgrc
  $ echo 'post-pull.broken = python:hooktests.brokenhook' >> .hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  no changes found
  error: post-pull.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
  (run with --traceback for stack trace)

but post- python hooks that fail to *load* do
  $ echo '[hooks]' > .hg/hgrc
  $ echo 'post-pull.nomodule = python:nomodule' >> .hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  no changes found
  abort: post-pull.nomodule hook is invalid: "nomodule" not in a module
  [255]

  $ echo '[hooks]' > .hg/hgrc
  $ echo 'post-pull.badmodule = python:nomodule.nowhere' >> .hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  no changes found
  abort: post-pull.badmodule hook is invalid: import of "nomodule" failed
  (run with --traceback for stack trace)
  [255]

  $ echo '[hooks]' > .hg/hgrc
  $ echo 'post-pull.nohook = python:hooktests.nohook' >> .hg/hgrc
  $ hg pull ../a
  pulling from ../a
  searching for changes
  no changes found
  abort: post-pull.nohook hook is invalid: "hooktests.nohook" is not defined
  [255]

make sure --traceback works

  $ echo '[hooks]' > .hg/hgrc
  $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc

  $ echo aa > a
  $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
  Traceback (most recent call last):

  $ cd ..
  $ hg init c
  $ cd c

  $ cat > hookext.py <<EOF
  > def autohook(ui, **args):
  >     ui.write(b'Automatically installed hook\n')
  > 
  > def reposetup(ui, repo):
  >     repo.ui.setconfig(b"hooks", b"commit.auto", autohook)
  > EOF
  $ echo '[extensions]' >> .hg/hgrc
  $ echo 'hookext = hookext.py' >> .hg/hgrc

  $ touch foo
  $ hg add foo
  $ hg ci -d '0 0' -m 'add foo'
  Automatically installed hook
  $ echo >> foo
  $ hg ci --debug -d '0 0' -m 'change foo'
  committing files:
  foo
  committing manifest
  committing changelog
  updating the branch cache
  committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
  calling hook commit.auto: hgext_hookext.autohook
  Automatically installed hook

  $ hg showconfig hooks
  hooks.commit.auto=<function autohook at *> (glob)

test python hook configured with python:[file]:[hook] syntax

  $ cd ..
  $ mkdir d
  $ cd d
  $ hg init repo
  $ mkdir hooks

  $ cd hooks
  $ cat > testhooks.py <<EOF
  > def testhook(ui, **args):
  >     ui.write(b'hook works\n')
  > EOF
  $ echo '[hooks]' > ../repo/.hg/hgrc
  $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc

  $ cd ../repo
  $ hg commit -d '0 0'
  hook works
  nothing changed
  [1]

  $ echo '[hooks]' > .hg/hgrc
  $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
  $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc

  $ hg up null
  loading update.ne hook failed:
  abort: $ENOENT$: '$TESTTMP/d/repo/nonexistent.py'
  [255]

  $ hg id
  loading pre-identify.npmd hook failed:
  abort: No module named repo (no-py3 !)
  abort: No module named 'repo' (py3 !)
  [255]

  $ cd ../../b

make sure --traceback works on hook import failure

  $ cat > importfail.py <<EOF
  > import somebogusmodule
  > # dereference something in the module to force demandimport to load it
  > somebogusmodule.whatever
  > EOF

  $ echo '[hooks]' > .hg/hgrc
  $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc

  $ echo a >> a
  $ hg --traceback commit -ma 2>&1 | egrep '^exception|ImportError|ModuleNotFoundError|Traceback|HookLoadError|abort'
  exception from first failed import attempt:
  Traceback (most recent call last):
  ImportError: No module named somebogusmodule (no-py3 !)
  ImportError: No module named 'somebogusmodule' (py3 no-py36 !)
  ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
  exception from second failed import attempt:
  Traceback (most recent call last): (py3 !)
  ImportError: No module named 'somebogusmodule' (py3 no-py36 !)
  ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
  Traceback (most recent call last): (py3 !)
  ImportError: No module named 'hgext_importfail' (py3 no-py36 !)
  ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
  Traceback (most recent call last): (py3 !)
  ImportError: No module named 'somebogusmodule' (py3 no-py36 !)
  ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
  Traceback (most recent call last):
  ImportError: No module named hgext_importfail (no-py3 !)
  ImportError: No module named 'hgext_importfail' (py3 no-py36 !)
  ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
  Traceback (most recent call last):
  HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed (no-py3 !)
      raise error.HookLoadError( (py38 !)
  mercurial.error.HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed (py3 !)
  abort: precommit.importfail hook is invalid: import of "importfail" failed

Issue1827: Hooks Update & Commit not completely post operation

commit and update hooks should run after command completion.  The largefiles
use demonstrates a recursive wlock, showing the hook doesn't run until the
final release (and dirstate flush).

  $ echo '[hooks]' > .hg/hgrc
  $ echo 'commit = hg id' >> .hg/hgrc
  $ echo 'update = hg id' >> .hg/hgrc
  $ echo bb > a
  $ hg ci -ma
  223eafe2750c tip
  $ hg up 0 --config extensions.largefiles=
  The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
  cb9a9f314b8b
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved

make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
that is passed to pre/post hooks

  $ echo '[hooks]' > .hg/hgrc
  $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
  $ hg id
  cb9a9f314b8b
  $ hg id --verbose
  calling hook pre-identify: hooktests.verbosehook
  verbose output from hook
  cb9a9f314b8b

Ensure hooks can be prioritized

  $ echo '[hooks]' > .hg/hgrc
  $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
  $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
  $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
  $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
  $ hg id --verbose
  calling hook pre-identify.b: hooktests.verbosehook
  verbose output from hook
  calling hook pre-identify.a: hooktests.verbosehook
  verbose output from hook
  calling hook pre-identify.c: hooktests.verbosehook
  verbose output from hook
  cb9a9f314b8b

new tags must be visible in pretxncommit (issue3210)

  $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
  $ hg tag -f foo
  [a, foo, tip]

post-init hooks must not crash (issue4983)
This also creates the `to` repo for the next test block.

  $ cd ..
  $ cat << EOF >> hgrc-with-post-init-hook
  > [hooks]
  > post-init = sh -c "printenv.py --line post-init"
  > EOF
  $ HGRCPATH=hgrc-with-post-init-hook hg init to
  post-init hook: HG_ARGS=init to
  HG_HOOKNAME=post-init
  HG_HOOKTYPE=post-init
  HG_OPTS={'insecure': None, 'remotecmd': '', 'ssh': ''}
  HG_PATS=['to']
  HG_RESULT=0
  

new commits must be visible in pretxnchangegroup (issue3428)

  $ echo '[hooks]' >> to/.hg/hgrc
  $ echo 'prechangegroup = hg --traceback tip' >> to/.hg/hgrc
  $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
  $ echo a >> to/a
  $ hg --cwd to ci -Ama
  adding a
  $ hg clone to from
  updating to branch default
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ echo aa >> from/a
  $ hg --cwd from ci -mb
  $ hg --cwd from push
  pushing to $TESTTMP/to
  searching for changes
  changeset:   0:cb9a9f314b8b
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     a
  
  adding changesets
  adding manifests
  adding file changes
  changeset:   1:9836a07b9b9d
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     b
  
  added 1 changesets with 1 changes to 1 files

pretxnclose hook failure should abort the transaction

  $ hg init txnfailure
  $ cd txnfailure
  $ touch a && hg commit -Aqm a
  $ cat >> .hg/hgrc <<EOF
  > [hooks]
  > pretxnclose.error = exit 1
  > EOF
  $ hg strip -r 0 --config extensions.strip=
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  saved backup bundle to * (glob)
  transaction abort!
  rollback completed
  strip failed, backup bundle stored in * (glob)
  abort: pretxnclose.error hook exited with status 1
  [40]
  $ hg recover
  no interrupted transaction available
  [1]
  $ cd ..

check whether HG_PENDING makes pending changes only in related
repositories visible to an external hook.

(emulate a transaction running concurrently by copied
.hg/store/00changelog.i.a in subsequent test)

  $ cat > $TESTTMP/savepending.sh <<EOF
  > cp .hg/store/00changelog.i.a  .hg/store/00changelog.i.a.saved
  > exit 1 # to avoid adding new revision for subsequent tests
  > EOF
  $ cd a
  $ hg tip -q
  4:539e4b31b6dc
  $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" commit -m "invisible"
  transaction abort!
  rollback completed
  abort: pretxnclose hook exited with status 1
  [40]
  $ cp .hg/store/00changelog.i.a.saved .hg/store/00changelog.i.a

(check (in)visibility of new changeset while transaction running in
repo)

  $ cat > $TESTTMP/checkpending.sh <<EOF
  > echo '@a'
  > hg -R "$TESTTMP/a" tip -q
  > echo '@a/nested'
  > hg -R "$TESTTMP/a/nested" tip -q
  > exit 1 # to avoid adding new revision for subsequent tests
  > EOF
  $ hg init nested
  $ cd nested
  $ echo a > a
  $ hg add a
  $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" commit -m '#0'
  @a
  4:539e4b31b6dc
  @a/nested
  0:bf5e395ced2c
  transaction abort!
  rollback completed
  abort: pretxnclose hook exited with status 1
  [40]

Hook from untrusted hgrc are reported as failure
================================================

  $ cat << EOF > $TESTTMP/untrusted.py
  > from mercurial import scmutil, util
  > def uisetup(ui):
  >     class untrustedui(ui.__class__):
  >         def _trusted(self, fp, f):
  >             if util.normpath(fp.name).endswith(b'untrusted/.hg/hgrc'):
  >                 return False
  >             return super(untrustedui, self)._trusted(fp, f)
  >     ui.__class__ = untrustedui
  > EOF
  $ cat << EOF >> $HGRCPATH
  > [extensions]
  > untrusted=$TESTTMP/untrusted.py
  > EOF
  $ hg init untrusted
  $ cd untrusted

Non-blocking hook
-----------------

  $ cat << EOF >> .hg/hgrc
  > [hooks]
  > txnclose.testing=echo txnclose hook called
  > EOF
  $ touch a && hg commit -Aqm a
  warning: untrusted hook txnclose.testing not executed
  $ hg log
  changeset:   0:3903775176ed
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     a
  

Non-blocking hook
-----------------

  $ cat << EOF >> .hg/hgrc
  > [hooks]
  > pretxnclose.testing=echo pre-txnclose hook called
  > EOF
  $ touch b && hg commit -Aqm a
  transaction abort!
  rollback completed
  abort: untrusted hook pretxnclose.testing not executed
  (see 'hg help config.trusted')
  [40]
  $ hg log
  changeset:   0:3903775176ed
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     a
  

unsetup the test
----------------

# touch the file to unconfuse chg with a diffrent mtime
  $ sleep 1
  $ touch $TESTTMP/untrusted.py
  $ cat << EOF >> $HGRCPATH
  > [extensions]
  > untrusted=!
  > EOF

HGPLAIN setting in hooks
========================

  $ cat << EOF >> .hg/hgrc
  > [hooks]
  > pre-version.testing-default=echo '### default ###' plain: \${HGPLAIN:-'<unset>'}
  > pre-version.testing-yes=echo '### yes #######' plain: \${HGPLAIN:-'<unset>'}
  > pre-version.testing-yes:run-with-plain=yes
  > pre-version.testing-no=echo '### no ########' plain: \${HGPLAIN:-'<unset>'}
  > pre-version.testing-no:run-with-plain=no
  > pre-version.testing-auto=echo '### auto ######' plain: \${HGPLAIN:-'<unset>'}
  > pre-version.testing-auto:run-with-plain=auto
  > EOF

  $ (unset HGPLAIN; hg version --quiet)
  ### default ### plain: 1
  ### yes ####### plain: 1
  ### no ######## plain: <unset>
  ### auto ###### plain: <unset>
  Mercurial Distributed SCM (*) (glob)

  $ HGPLAIN=1 hg version --quiet
  ### default ### plain: 1
  ### yes ####### plain: 1
  ### no ######## plain: <unset>
  ### auto ###### plain: 1
  Mercurial Distributed SCM (*) (glob)