tests/test-merge-local.t
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Wed, 02 Dec 2015 03:12:07 +0900
changeset 27192 a01d3d32b53a
parent 26941 454deda24315
child 27316 777f668eca70
permissions -rw-r--r--
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

  $ hg init

Revision 0:

  $ echo "unchanged" > unchanged
  $ echo "remove me" > remove
  $ echo "copy me" > copy
  $ echo "move me" > move
  $ for i in 1 2 3 4 5 6 7 8 9; do
  >     echo "merge ok $i" >> zzz1_merge_ok
  > done
  $ echo "merge bad" > zzz2_merge_bad
  $ hg ci -Am "revision 0"
  adding copy
  adding move
  adding remove
  adding unchanged
  adding zzz1_merge_ok
  adding zzz2_merge_bad

Revision 1:

  $ hg rm remove
  $ hg mv move moved
  $ hg cp copy copied
  $ echo "added" > added
  $ hg add added
  $ echo "new first line" > zzz1_merge_ok
  $ hg cat zzz1_merge_ok >> zzz1_merge_ok
  $ echo "new last line" >> zzz2_merge_bad
  $ hg ci -m "revision 1"

Local changes to revision 0:

  $ hg co 0
  4 files updated, 0 files merged, 3 files removed, 0 files unresolved
  $ echo "new last line" >> zzz1_merge_ok
  $ echo "another last line" >> zzz2_merge_bad

  $ hg diff --nodates | grep "^[+-][^<>]"
  --- a/zzz1_merge_ok
  +++ b/zzz1_merge_ok
  +new last line
  --- a/zzz2_merge_bad
  +++ b/zzz2_merge_bad
  +another last line

  $ hg st
  M zzz1_merge_ok
  M zzz2_merge_bad

Local merge with bad merge tool:

  $ HGMERGE=false hg co
  merging zzz1_merge_ok
  merging zzz2_merge_bad
  merging zzz2_merge_bad failed!
  3 files updated, 1 files merged, 2 files removed, 1 files unresolved
  use 'hg resolve' to retry unresolved file merges
  [1]

  $ hg co 0
  merging zzz1_merge_ok
  merging zzz2_merge_bad
  warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
  2 files updated, 1 files merged, 3 files removed, 1 files unresolved
  use 'hg resolve' to retry unresolved file merges
  [1]

  $ hg diff --nodates | grep "^[+-][^<>]"
  --- a/zzz1_merge_ok
  +++ b/zzz1_merge_ok
  +new last line
  --- a/zzz2_merge_bad
  +++ b/zzz2_merge_bad
  +another last line
  +=======

  $ hg st
  M zzz1_merge_ok
  M zzz2_merge_bad
  ? zzz2_merge_bad.orig

Local merge with conflicts:

  $ hg co
  merging zzz1_merge_ok
  merging zzz2_merge_bad
  warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
  3 files updated, 1 files merged, 2 files removed, 1 files unresolved
  use 'hg resolve' to retry unresolved file merges
  [1]

  $ hg co 0 --config 'ui.origbackuppath=.hg/origbackups'
  merging zzz1_merge_ok
  merging zzz2_merge_bad
  warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
  2 files updated, 1 files merged, 3 files removed, 1 files unresolved
  use 'hg resolve' to retry unresolved file merges
  [1]

Are orig files from the last commit where we want them?
  $ ls .hg/origbackups
  zzz2_merge_bad.orig

  $ hg diff --nodates | grep "^[+-][^<>]"
  --- a/zzz1_merge_ok
  +++ b/zzz1_merge_ok
  +new last line
  --- a/zzz2_merge_bad
  +++ b/zzz2_merge_bad
  +another last line
  +=======
  +=======
  +new last line
  +=======

  $ hg st
  M zzz1_merge_ok
  M zzz2_merge_bad
  ? zzz2_merge_bad.orig

Local merge without conflicts:

  $ hg revert zzz2_merge_bad

  $ hg co
  merging zzz1_merge_ok
  4 files updated, 1 files merged, 2 files removed, 0 files unresolved

  $ hg diff --nodates | grep "^[+-][^<>]"
  --- a/zzz1_merge_ok
  +++ b/zzz1_merge_ok
  +new last line

  $ hg st
  M zzz1_merge_ok
  ? zzz2_merge_bad.orig