diff --git a/contrib/automation/hgautomation/linux.py b/contrib/automation/hgautomation/linux.py --- a/contrib/automation/hgautomation/linux.py +++ b/contrib/automation/hgautomation/linux.py @@ -485,7 +485,9 @@ def synchronize_hg(source_path: pathlib. 'python2.7', str(hg_bin), '--config', 'ui.ssh=ssh -F %s' % ssh_config, '--config', 'ui.remotecmd=/hgdev/venv-bootstrap/bin/hg', - 'push', '-f', '-r', full_revision, + # Also ensure .hgtags changes are present so auto version + # calculation works. + 'push', '-f', '-r', full_revision, '-r', 'file(.hgtags)', 'ssh://%s//hgwork/src' % public_ip, ] diff --git a/contrib/automation/hgautomation/windows.py b/contrib/automation/hgautomation/windows.py --- a/contrib/automation/hgautomation/windows.py +++ b/contrib/automation/hgautomation/windows.py @@ -176,7 +176,9 @@ def synchronize_hg(hg_repo: pathlib.Path 'python2.7', hg_bin, '--config', 'ui.ssh=ssh -F %s' % ssh_config, '--config', 'ui.remotecmd=c:/hgdev/venv-bootstrap/Scripts/hg.exe', - 'push', '-f', '-r', full_revision, + # Also ensure .hgtags changes are present so auto version + # calculation works. + 'push', '-f', '-r', full_revision, '-r', 'file(.hgtags)', 'ssh://%s/c:/hgdev/src' % public_ip, ] diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1227,6 +1227,55 @@ class localrepository(object): @mixedrepostorecache(('bookmarks', 'plain'), ('bookmarks.current', 'plain'), ('bookmarks', ''), ('00changelog.i', '')) def _bookmarks(self): + # Since the multiple files involved in the transaction cannot be + # written atomically (with current repository format), there is a race + # condition here. + # + # 1) changelog content A is read + # 2) outside transaction update changelog to content B + # 3) outside transaction update bookmark file referring to content B + # 4) bookmarks file content is read and filtered against changelog-A + # + # When this happens, bookmarks against nodes missing from A are dropped. + # + # Having this happening during read is not great, but it become worse + # when this happen during write because the bookmarks to the "unknown" + # nodes will be dropped for good. However, writes happen within locks. + # This locking makes it possible to have a race free consistent read. + # For this purpose data read from disc before locking are + # "invalidated" right after the locks are taken. This invalidations are + # "light", the `filecache` mechanism keep the data in memory and will + # reuse them if the underlying files did not changed. Not parsing the + # same data multiple times helps performances. + # + # Unfortunately in the case describe above, the files tracked by the + # bookmarks file cache might not have changed, but the in-memory + # content is still "wrong" because we used an older changelog content + # to process the on-disk data. So after locking, the changelog would be + # refreshed but `_bookmarks` would be preserved. + # Adding `00changelog.i` to the list of tracked file is not + # enough, because at the time we build the content for `_bookmarks` in + # (4), the changelog file has already diverged from the content used + # for loading `changelog` in (1) + # + # To prevent the issue, we force the changelog to be explicitly + # reloaded while computing `_bookmarks`. The data race can still happen + # without the lock (with a narrower window), but it would no longer go + # undetected during the lock time refresh. + # + # The new schedule is as follow + # + # 1) filecache logic detect that `_bookmarks` needs to be computed + # 2) cachestat for `bookmarks` and `changelog` are captured (for book) + # 3) We force `changelog` filecache to be tested + # 4) cachestat for `changelog` are captured (for changelog) + # 5) `_bookmarks` is computed and cached + # + # The step in (3) ensure we have a changelog at least as recent as the + # cache stat computed in (1). As a result at locking time: + # * if the changelog did not changed since (1) -> we can reuse the data + # * otherwise -> the bookmarks get refreshed. + self._refreshchangelog() return bookmarks.bmstore(self) def _refreshchangelog(self): diff --git a/mercurial/repair.py b/mercurial/repair.py --- a/mercurial/repair.py +++ b/mercurial/repair.py @@ -109,6 +109,9 @@ def strip(ui, repo, nodelist, backup=Tru repo = repo.unfiltered() repo.destroying() vfs = repo.vfs + # load bookmark before changelog to avoid side effect from outdated + # changelog (see repo._refreshchangelog) + repo._bookmarks cl = repo.changelog # TODO handle undo of merge sets diff --git a/tests/test-bookmarks-corner-case.t b/tests/test-bookmarks-corner-case.t --- a/tests/test-bookmarks-corner-case.t +++ b/tests/test-bookmarks-corner-case.t @@ -200,6 +200,7 @@ Check raced push output. $ cat push-output.txt pushing to ssh://user@dummy/bookrace-server searching for changes + remote has heads on branch 'default' that are not known locally: f26c3b5167d1 remote: setting raced push up remote: adding changesets remote: adding manifests @@ -219,7 +220,7 @@ Check result of the push. | summary: A1 | | o changeset: 3:f26c3b5167d1 - | | bookmark: book-B (false !) + | | bookmark: book-B | | user: test | | date: Thu Jan 01 00:00:00 1970 +0000 | | summary: B1 @@ -242,4 +243,4 @@ Check result of the push. $ hg -R bookrace-server book book-A 4:9ce3b28c16de - book-B 3:f26c3b5167d1 (false !) + book-B 3:f26c3b5167d1 diff --git a/tests/test-strip.t b/tests/test-strip.t --- a/tests/test-strip.t +++ b/tests/test-strip.t @@ -9,10 +9,10 @@ $ teststrip() { > hg up -C $1 > echo % before update $1, strip $2 - > hg parents + > hg log -G -T '{rev}:{node}' > hg --traceback strip $2 > echo % after update $1, strip $2 - > hg parents + > hg log -G -T '{rev}:{node}' > restore > } @@ -70,95 +70,136 @@ $ teststrip 4 4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved % before update 4, strip 4 - changeset: 4:443431ffac4f - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: e + @ 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | + o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + | o 2:264128213d290d868c54642d13aeaa3675551a78 + |/ + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b 1 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) % after update 4, strip 4 - changeset: 3:65bd5f99a4a3 - tag: tip - parent: 1:ef3a871183d7 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: d + @ 3:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + | o 2:264128213d290d868c54642d13aeaa3675551a78 + |/ + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b $ teststrip 4 3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % before update 4, strip 3 - changeset: 4:443431ffac4f - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: e + @ 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | + o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + | o 2:264128213d290d868c54642d13aeaa3675551a78 + |/ + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b 1 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) % after update 4, strip 3 - changeset: 1:ef3a871183d7 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: b + o 2:264128213d290d868c54642d13aeaa3675551a78 + | + @ 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b $ teststrip 1 4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved % before update 1, strip 4 - changeset: 1:ef3a871183d7 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: b + o 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | + o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + | o 2:264128213d290d868c54642d13aeaa3675551a78 + |/ + @ 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) % after update 1, strip 4 - changeset: 1:ef3a871183d7 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: b + o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + | o 2:264128213d290d868c54642d13aeaa3675551a78 + |/ + @ 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b $ teststrip 4 2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % before update 4, strip 2 - changeset: 4:443431ffac4f - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: e + @ 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | + o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + | o 2:264128213d290d868c54642d13aeaa3675551a78 + |/ + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) % after update 4, strip 2 - changeset: 3:443431ffac4f - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: e + @ 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | + o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b $ teststrip 4 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % before update 4, strip 1 - changeset: 4:264128213d29 - tag: tip - parent: 1:ef3a871183d7 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: c + @ 4:264128213d290d868c54642d13aeaa3675551a78 + | + | o 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | | + | o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64 + |/ + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b 1 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) % after update 4, strip 1 - changeset: 0:9ab35a2d17cb - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: a + @ 0:9ab35a2d17cb64271241ea881efcc19dd953215b $ teststrip null 4 0 files updated, 0 files merged, 1 files removed, 0 files unresolved % before update null, strip 4 + o 4:264128213d290d868c54642d13aeaa3675551a78 + | + | o 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | | + | o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64 + |/ + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b + saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) % after update null, strip 4 + o 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce + | + o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64 + | + o 1:ef3a871183d7199c541cc140218298bbfcc6c28a + | + o 0:9ab35a2d17cb64271241ea881efcc19dd953215b + $ hg log changeset: 4:264128213d29