comparison mercurial/commands.py @ 27192:a01d3d32b53a

commands: make commit acquire locks before processing (issue4368) Before this patch, "hg commit" (process A) executes steps below: 1. get current branch heads via 'repo.branchheads()' - cache 'repo.changelog' 2. invoke 'repo.commit()' 3. acquire wlock - invalidate 'repo.dirstate' 4. access 'repo.dirstate' - re-read '.hg/dirstate' - check validity of parent revisions with 'repo.changelog' 5. invoke 'repo.commitctx()' 6. acquire store lock (slock) - invalidate 'repo.changelog' 7. do committing 8. release slock 9. release wlock 10. check new branch head (via 'cmdutil.commitstatus()') If acquisition of wlock at (3) above waits for another "hg commit" (process B) or so running parallelly to release wlock, process A causes creating orphan revision, because: - '.hg/dirstate' refers the revision, which is newly added by process B, as its parent - but already cached 'repo.changelog' doesn't contain such revision - therefore, validating parents of '.hg/dirstate' at (4) above replaces such revision with 'nullid' Then, process A creates "orphan" revision, of which parent is "null" revision. In addition to it, "created new head" may be shown at the end of process A unintentionally, if store is updated parallelly, because both getting branch heads (1) and checking new branch head (10) are executed outside slock scope. To avoid this issue, this patch makes "hg commit" acquire wlock and slock before processing. This patch resolves the issue between "hg commit" processes, but not one between "hg commit" and other commands. Subsequent patches resolve the latter. Even after this patch, there are still corner case problems below: - filecache may overlook changes of '.hg/dirstate', and it causes similar issue (see below for detail) https://bz.mercurial-scm.org/show_bug.cgi?id=4368#c10 - 3rd party extension may cause similar issue, if it directly uses 'repo.commit()' without acquisition of wlock and slock This can be fixed by acquisition of slock at the beginning of 'repo.commit()', but it seems suitable for "default" branch In fact, acquisition of slock itself is already introduced at "default" branch by 4414d500604f, but acquisition is not at the beginning of 'repo.commit()'. This patch also changes some tests: - test-fncache.t needs this tricky wrapping, to release (= forced failure of) wlock certainly - order of "hg commit" output is changed by widening scope of locks, because some hooks are fired after releasing wlock
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Wed, 02 Dec 2015 03:12:07 +0900
parents 54ace3372f84
children c7217f1458bf
comparison
equal deleted inserted replaced
27191:20a9226bdc8a 27192:a01d3d32b53a
1574 1574
1575 See :hg:`help dates` for a list of formats valid for -d/--date. 1575 See :hg:`help dates` for a list of formats valid for -d/--date.
1576 1576
1577 Returns 0 on success, 1 if nothing changed. 1577 Returns 0 on success, 1 if nothing changed.
1578 """ 1578 """
1579 wlock = lock = None
1580 try:
1581 wlock = repo.wlock()
1582 lock = repo.lock()
1583 return _docommit(ui, repo, *pats, **opts)
1584 finally:
1585 release(lock, wlock)
1586
1587 def _docommit(ui, repo, *pats, **opts):
1579 if opts.get('interactive'): 1588 if opts.get('interactive'):
1580 opts.pop('interactive') 1589 opts.pop('interactive')
1581 cmdutil.dorecord(ui, repo, commit, None, False, 1590 cmdutil.dorecord(ui, repo, commit, None, False,
1582 cmdutil.recordfilter, *pats, **opts) 1591 cmdutil.recordfilter, *pats, **opts)
1583 return 1592 return