Introduce HG_PREPEND to solve pretxn races
- add writepending to flush delayed writes to separate file
- add support in hooks for lazy evaluation of callable parameters
- add HG_PENDING to pretxn hooks
- call writepending if hook is used
- pass repo root to hook environment
- if HG_PENDING = repo root, we're in pretxn hook
- read pending data to make pending changesets visible
- filter HG_PENDING in tests/printenv.py
--- a/mercurial/changelog.py Sun Jan 18 19:59:51 2009 +0100
+++ b/mercurial/changelog.py Mon Feb 16 19:35:07 2009 -0600
@@ -96,7 +96,7 @@
fp = self.opener(self.indexfile, 'a')
fp.write("".join(self._delaybuf))
fp.close()
- del self._delaybuf
+ self._delaybuf = []
# split when we're done
self.checkinlinesize(tr)
@@ -115,6 +115,31 @@
# otherwise, divert to memory
return appender(fp, self._delaybuf)
+ def readpending(self, file):
+ r = revlog.revlog(self.opener, file)
+ self.index = r.index
+ self.nodemap = r.nodemap
+ self._chunkcache = r._chunkcache
+
+ def writepending(self):
+ "create a file containing the unfinalized state for pretxnchangegroup"
+ if self._delaybuf:
+ # make a temporary copy of the index
+ fp1 = self._realopener(self.indexfile)
+ fp2 = self._realopener(self.indexfile + ".a", "w")
+ fp2.write(fp1.read())
+ # add pending data
+ fp2.write("".join(self._delaybuf))
+ fp2.close()
+ # switch modes so finalize can simply rename
+ self._delaybuf = []
+ self._delayname = fp1.name
+
+ if self._delayname:
+ return True
+
+ return False
+
def checkinlinesize(self, tr, fp=None):
if self.opener == self._delayopener:
return
--- a/mercurial/hook.py Sun Jan 18 19:59:51 2009 +0100
+++ b/mercurial/hook.py Mon Feb 16 19:35:07 2009 -0600
@@ -70,7 +70,13 @@
def _exthook(ui, repo, name, cmd, args, throw):
ui.note(_("running hook %s: %s\n") % (name, cmd))
- env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
+
+ env = {}
+ for k, v in args.iteritems():
+ if callable(v):
+ v = v()
+ env['HG_' + k.upper()] = v
+
if repo:
cwd = repo.root
else:
--- a/mercurial/localrepo.py Sun Jan 18 19:59:51 2009 +0100
+++ b/mercurial/localrepo.py Mon Feb 16 19:35:07 2009 -0600
@@ -88,6 +88,10 @@
def __getattr__(self, name):
if name == 'changelog':
self.changelog = changelog.changelog(self.sopener)
+ if 'HG_PENDING' in os.environ:
+ p = os.environ['HG_PENDING']
+ if p.startswith(self.root):
+ self.changelog.readpending('00changelog.i.a')
self.sopener.defversion = self.changelog.version
return self.changelog
if name == 'manifest':
@@ -955,10 +959,13 @@
raise util.Abort(_("empty commit message"))
text = '\n'.join(lines)
+ self.changelog.delayupdate()
n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
user, wctx.date(), extra)
+ p = lambda: self.changelog.writepending() and self.root or ""
self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
- parent2=xp2)
+ parent2=xp2, pending=p)
+ self.changelog.finalize(trp)
tr.close()
if self.branchcache:
@@ -2034,9 +2041,6 @@
revisions += len(fl) - o
files += 1
- # make changelog see real files again
- cl.finalize(trp)
-
newheads = len(self.changelog.heads())
heads = ""
if oldheads and newheads != oldheads:
@@ -2047,9 +2051,13 @@
% (changesets, revisions, files, heads))
if changesets > 0:
+ p = lambda: self.changelog.writepending() and self.root or ""
self.hook('pretxnchangegroup', throw=True,
node=hex(self.changelog.node(cor+1)), source=srctype,
- url=url)
+ url=url, pending=p)
+
+ # make changelog see real files again
+ cl.finalize(trp)
tr.close()
finally:
--- a/tests/printenv.py Sun Jan 18 19:59:51 2009 +0100
+++ b/tests/printenv.py Mon Feb 16 19:35:07 2009 -0600
@@ -46,6 +46,9 @@
elif url.startswith("remote:http"):
os.environ["HG_URL"] = "remote:http"
+if "HG_PENDING" in os.environ:
+ os.environ["HG_PENDING"] = os.environ["HG_PENDING"] and "true"
+
out.write("%s hook: " % name)
for v in env:
out.write("%s=%s " % (v, os.environ[v]))
--- a/tests/test-hook.out Sun Jan 18 19:59:51 2009 +0100
+++ b/tests/test-hook.out Mon Feb 16 19:35:07 2009 -0600
@@ -1,18 +1,18 @@
precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
-pretxncommit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
+pretxncommit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=true
0:29b62aeb769f
commit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
commit.b hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
updating working directory
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
-pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
+pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=true
1:b702efe96888
commit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
commit.b hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
-pretxncommit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
+pretxncommit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=true
2:1324a5531bac
commit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
commit.b hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
@@ -20,7 +20,7 @@
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
precommit hook: HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
-pretxncommit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
+pretxncommit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PENDING=true
3:4c52fb2e4022
commit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
commit.b hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
@@ -43,7 +43,7 @@
(run 'hg update' to get a working copy)
pretag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a
precommit hook: HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
-pretxncommit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
+pretxncommit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321 HG_PENDING=true
4:8ea2ef7ad3e8
commit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
commit.b hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
@@ -58,9 +58,9 @@
abort: pretag.forbid hook exited with status 1
4:8ea2ef7ad3e8
precommit hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
-pretxncommit hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
+pretxncommit hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=true
5:fad284daf8c0
-pretxncommit.forbid hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
+pretxncommit.forbid hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=true
transaction abort!
rollback completed
abort: pretxncommit.forbid1 hook exited with status 1
@@ -80,7 +80,7 @@
searching for changes
abort: prechangegroup.forbid hook exited with status 1
4:8ea2ef7ad3e8
-pretxnchangegroup.forbid hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_SOURCE=pull HG_URL=file:
+pretxnchangegroup.forbid hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=true HG_SOURCE=pull HG_URL=file:
pulling from ../a
searching for changes
adding changesets