diff --git a/mercurial/dirstatemap.py b/mercurial/dirstatemap.py --- a/mercurial/dirstatemap.py +++ b/mercurial/dirstatemap.py @@ -37,6 +37,9 @@ WRITE_MODE_FORCE_NEW = 1 WRITE_MODE_FORCE_APPEND = 2 +V2_MAX_READ_ATTEMPTS = 5 + + class _dirstatemapcommon: """ Methods that are identical for both implementations of the dirstatemap @@ -125,6 +128,21 @@ class _dirstatemapcommon: return self._docket def _read_v2_data(self): + data = None + attempts = 0 + while attempts < V2_MAX_READ_ATTEMPTS: + attempts += 1 + try: + data = self._opener.read(self.docket.data_filename()) + except FileNotFoundError: + # read race detected between docket and data file + # reload the docket and retry + self._docket = None + if data is None: + assert attempts >= V2_MAX_READ_ATTEMPTS + msg = b"dirstate read race happened %d times in a row" + msg %= attempts + raise error.Abort(msg) return self._opener.read(self.docket.data_filename()) def write_v2_no_append(self, tr, st, meta, packed): diff --git a/tests/test-dirstate-read-race.t b/tests/test-dirstate-read-race.t --- a/tests/test-dirstate-read-race.t +++ b/tests/test-dirstate-read-race.t @@ -217,8 +217,12 @@ The status process should return a consi #endif #else $ cat $TESTTMP/status-race-lock.out + A dir/n + A dir/o + R dir/nested/m + ? p + ? q $ cat $TESTTMP/status-race-lock.log - abort: $ENOENT$: '$TESTTMP/race-with-add/.hg/dirstate.* (glob) #endif #endif #endif @@ -318,8 +322,12 @@ The status process should return a consi #endif #else $ cat $TESTTMP/status-race-lock.out + M dir/o + ? dir/n + ? p + ? q $ cat $TESTTMP/status-race-lock.log - abort: $ENOENT$: '$TESTTMP/race-with-commit/.hg/dirstate.* (glob) + warning: ignoring unknown working parent 02a67a77ee9b! #endif #endif #endif @@ -452,8 +460,11 @@ The status process should return a consi #endif #else $ cat $TESTTMP/status-race-lock.out + A dir/o + ? dir/n + ? p + ? q $ cat $TESTTMP/status-race-lock.log - abort: $ENOENT$: '$TESTTMP/race-with-update/.hg/dirstate.* (glob) #endif #endif #endif @@ -542,8 +553,12 @@ The status process should return a consi #endif #else $ cat $TESTTMP/status-race-lock.out + A dir/o + R dir/nested/m + ? dir/n + ? p + ? q $ cat $TESTTMP/status-race-lock.log - abort: $ENOENT$: '$TESTTMP/race-with-status/.hg/dirstate.* (glob) #endif #endif #endif