test-dirstate-race.t
250 lines
| 6.4 KiB
| text/troff
|
Tads3Lexer
/ tests / test-dirstate-race.t
|
r48056 | #testcases dirstate-v1 dirstate-v1-tree dirstate-v2 | ||
|
r47900 | |||
#if dirstate-v1-tree | ||||
#require rust | ||||
$ echo '[experimental]' >> $HGRCPATH | ||||
$ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH | ||||
#endif | ||||
|
r48056 | #if dirstate-v2 | ||
#require rust | ||||
$ echo '[format]' >> $HGRCPATH | ||||
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH | ||||
#endif | ||||
|
r32651 | $ hg init repo | ||
$ cd repo | ||||
|
r12279 | $ echo a > a | ||
$ hg add a | ||||
$ hg commit -m test | ||||
Do we ever miss a sub-second change?: | ||||
$ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do | ||||
> hg co -qC 0 | ||||
> echo b > a | ||||
> hg st | ||||
> done | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
M a | ||||
|
r32651 | $ echo test > b | ||
$ mkdir dir1 | ||||
$ echo test > dir1/c | ||||
$ echo test > d | ||||
$ echo test > e | ||||
#if execbit | ||||
A directory will typically have the execute bit -- make sure it doesn't get | ||||
confused with a file with the exec bit set | ||||
$ chmod +x e | ||||
#endif | ||||
$ hg add b dir1 d e | ||||
|
r35394 | adding dir1/c | ||
|
r32651 | $ hg commit -m test2 | ||
$ cat >> $TESTTMP/dirstaterace.py << EOF | ||||
> from mercurial import ( | ||||
> context, | ||||
> extensions, | ||||
> ) | ||||
|
r41098 | > def extsetup(ui): | ||
|
r32651 | > extensions.wrapfunction(context.workingctx, '_checklookup', overridechecklookup) | ||
> def overridechecklookup(orig, self, files): | ||||
> # make an update that changes the dirstate from underneath | ||||
|
r40332 | > self._repo.ui.system(br"sh '$TESTTMP/dirstaterace.sh'", | ||
|
r32751 | > cwd=self._repo.root) | ||
|
r32651 | > return orig(self, files) | ||
> EOF | ||||
$ hg debugrebuilddirstate | ||||
$ hg debugdirstate | ||||
n 0 -1 unset a | ||||
n 0 -1 unset b | ||||
n 0 -1 unset d | ||||
n 0 -1 unset dir1/c | ||||
n 0 -1 unset e | ||||
XXX Note that this returns M for files that got replaced by directories. This is | ||||
definitely a bug, but the fix for that is hard and the next status run is fine | ||||
anyway. | ||||
|
r32751 | $ cat > $TESTTMP/dirstaterace.sh <<EOF | ||
> rm b && rm -r dir1 && rm d && mkdir d && rm e && mkdir e | ||||
> EOF | ||||
$ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py | ||||
|
r32651 | M d | ||
M e | ||||
! b | ||||
! dir1/c | ||||
$ hg debugdirstate | ||||
n 644 2 * a (glob) | ||||
n 0 -1 unset b | ||||
n 0 -1 unset d | ||||
n 0 -1 unset dir1/c | ||||
n 0 -1 unset e | ||||
$ hg status | ||||
! b | ||||
! d | ||||
! dir1/c | ||||
! e | ||||
|
r32752 | |||
$ rmdir d e | ||||
$ hg update -C -q . | ||||
Test that dirstate changes aren't written out at the end of "hg | ||||
status", if .hg/dirstate is already changed simultaneously before | ||||
|
r32812 | acquisition of wlock in workingctx._poststatusfixup(). | ||
|
r32752 | |||
This avoidance is important to keep consistency of dirstate in race | ||||
condition (see issue5584 for detail). | ||||
$ hg parents -q | ||||
1:* (glob) | ||||
$ hg debugrebuilddirstate | ||||
$ hg debugdirstate | ||||
n 0 -1 unset a | ||||
n 0 -1 unset b | ||||
n 0 -1 unset d | ||||
n 0 -1 unset dir1/c | ||||
n 0 -1 unset e | ||||
$ cat > $TESTTMP/dirstaterace.sh <<EOF | ||||
> # This script assumes timetable of typical issue5584 case below: | ||||
> # | ||||
> # 1. "hg status" loads .hg/dirstate | ||||
> # 2. "hg status" confirms clean-ness of FILE | ||||
> # 3. "hg update -C 0" updates the working directory simultaneously | ||||
> # (FILE is removed, and FILE is dropped from .hg/dirstate) | ||||
> # 4. "hg status" acquires wlock | ||||
> # (.hg/dirstate is re-loaded = no FILE entry in dirstate) | ||||
> # 5. "hg status" marks FILE in dirstate as clean | ||||
> # (FILE entry is added to in-memory dirstate) | ||||
> # 6. "hg status" writes dirstate changes into .hg/dirstate | ||||
> # (FILE entry is written into .hg/dirstate) | ||||
> # | ||||
> # To reproduce similar situation easily and certainly, #2 and #3 | ||||
> # are swapped. "hg cat" below ensures #2 on "hg status" side. | ||||
> | ||||
> hg update -q -C 0 | ||||
> hg cat -r 1 b > b | ||||
> EOF | ||||
"hg status" below should excludes "e", of which exec flag is set, for | ||||
portability of test scenario, because unsure but missing "e" is | ||||
treated differently in _checklookup() according to runtime platform. | ||||
- "missing(!)" on POSIX, "pctx[f].cmp(self[f])" raises ENOENT | ||||
- "modified(M)" on Windows, "self.flags(f) != pctx.flags(f)" is True | ||||
$ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py --debug -X path:e | ||||
skip updating dirstate: identity mismatch | ||||
M a | ||||
! d | ||||
! dir1/c | ||||
$ hg parents -q | ||||
0:* (glob) | ||||
$ hg files | ||||
a | ||||
$ hg debugdirstate | ||||
|
r32801 | n * * * a (glob) | ||
|
r32815 | |||
$ rm b | ||||
|
r32816 | #if fsmonitor | ||
Create fsmonitor state. | ||||
$ hg status | ||||
$ f --type .hg/fsmonitor.state | ||||
.hg/fsmonitor.state: file | ||||
Test that invalidating fsmonitor state in the middle (which doesn't require the | ||||
wlock) causes the fsmonitor update to be skipped. | ||||
hg debugrebuilddirstate ensures that the dirstaterace hook will be called, but | ||||
it also invalidates the fsmonitor state. So back it up and restore it. | ||||
$ mv .hg/fsmonitor.state .hg/fsmonitor.state.tmp | ||||
$ hg debugrebuilddirstate | ||||
$ mv .hg/fsmonitor.state.tmp .hg/fsmonitor.state | ||||
$ cat > $TESTTMP/dirstaterace.sh <<EOF | ||||
> rm .hg/fsmonitor.state | ||||
> EOF | ||||
$ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py --debug | ||||
skip updating fsmonitor.state: identity mismatch | ||||
$ f .hg/fsmonitor.state | ||||
.hg/fsmonitor.state: file not found | ||||
#endif | ||||
|
r32815 | Set up a rebase situation for issue5581. | ||
$ echo c2 > a | ||||
$ echo c2 > b | ||||
$ hg add b | ||||
$ hg commit -m c2 | ||||
created new head | ||||
$ echo c3 >> a | ||||
$ hg commit -m c3 | ||||
$ hg update 2 | ||||
1 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
$ echo c4 >> a | ||||
$ echo c4 >> b | ||||
$ hg commit -m c4 | ||||
created new head | ||||
|
r33578 | Configure a merge tool that runs status in the middle of the rebase. The goal of | ||
the status call is to trigger a potential bug if fsmonitor's state is written | ||||
even though the wlock is held by another process. The output of 'hg status' in | ||||
the merge tool goes to /dev/null because we're more interested in the results of | ||||
'hg status' run after the rebase. | ||||
|
r32815 | |||
$ cat >> $TESTTMP/mergetool-race.sh << EOF | ||||
> echo "custom merge tool" | ||||
> printf "c2\nc3\nc4\n" > \$1 | ||||
|
r33578 | > hg --cwd "$TESTTMP/repo" status > /dev/null | ||
|
r32815 | > echo "custom merge tool end" | ||
> EOF | ||||
$ cat >> $HGRCPATH << EOF | ||||
> [extensions] | ||||
> rebase = | ||||
> [merge-tools] | ||||
> test.executable=sh | ||||
> test.args=$TESTTMP/mergetool-race.sh \$output | ||||
> EOF | ||||
$ hg rebase -s . -d 3 --tool test | ||||
|
r46356 | rebasing 4:b08445fd6b2a tip "c4" | ||
|
r32815 | merging a | ||
custom merge tool | ||||
custom merge tool end | ||||
saved backup bundle to $TESTTMP/repo/.hg/strip-backup/* (glob) | ||||
This hg status should be empty, whether or not fsmonitor is enabled (issue5581). | ||||
$ hg status | ||||