# HG changeset patch # User Arseniy Alekseyev # Date 1723803139 -3600 # Node ID 95cdc01f313d3580afe569166262415af5931637 # Parent 0d7ccb163b4f91d9750ba7193ebc6c36aeeacfe5 sparse: reliably avoid writing to store without a lock With the code as written before this patch we can still end up writing to store in `debugsparse`. Obviously we'll write to it if by accident a store requirement is modified, but more importantly we write to it if another concurrent transaction modifies the requirements file on disk. We can't rule this out since we're not holding the store lock, so it's better to explicitly pass a permission to write instead of inferring it based on file contents. diff -r 0d7ccb163b4f -r 95cdc01f313d mercurial/scmutil.py --- a/mercurial/scmutil.py Thu Aug 15 13:52:14 2024 +0100 +++ b/mercurial/scmutil.py Fri Aug 16 11:12:19 2024 +0100 @@ -1668,7 +1668,7 @@ return requirementsmod.TREEMANIFEST_REQUIREMENT in repo.requirements -def writereporequirements(repo, requirements=None) -> None: +def writereporequirements(repo, requirements=None, maywritestore=True) -> None: """writes requirements for the repo Requirements are written to .hg/requires and .hg/store/requires based @@ -1681,10 +1681,11 @@ if wcreq is not None: writerequires(repo.vfs, wcreq) if storereq is not None: - writerequires(repo.svfs, storereq) + writerequires(repo.svfs, storereq, maywrite=maywritestore) elif repo.ui.configbool(b'format', b'usestore'): # only remove store requires if we are using store - repo.svfs.tryunlink(b'requires') + if maywritestore: + repo.svfs.tryunlink(b'requires') def readrequires(vfs, allowmissing): @@ -1701,9 +1702,11 @@ return set(read(b'requires').splitlines()) -def writerequires(opener, requirements) -> None: +def writerequires(opener, requirements, maywrite=True) -> None: on_disk = readrequires(opener, True) if not (on_disk == set(requirements)): + if not maywrite: + raise error.Abort(_(b"store requirements are not as expected")) with opener(b'requires', b'w', atomictemp=True) as fp: for r in sorted(requirements): fp.write(b"%s\n" % r) diff -r 0d7ccb163b4f -r 95cdc01f313d mercurial/sparse.py --- a/mercurial/sparse.py Thu Aug 15 13:52:14 2024 +0100 +++ b/mercurial/sparse.py Fri Aug 16 11:12:19 2024 +0100 @@ -632,10 +632,10 @@ if requirements.SPARSE_REQUIREMENT in oldrequires and removing: repo.requirements.discard(requirements.SPARSE_REQUIREMENT) - scmutil.writereporequirements(repo) + scmutil.writereporequirements(repo, maywritestore=False) elif requirements.SPARSE_REQUIREMENT not in oldrequires: repo.requirements.add(requirements.SPARSE_REQUIREMENT) - scmutil.writereporequirements(repo) + scmutil.writereporequirements(repo, maywritestore=False) try: writeconfig(repo, includes, excludes, profiles) @@ -644,7 +644,7 @@ if repo.requirements != oldrequires: repo.requirements.clear() repo.requirements |= oldrequires - scmutil.writereporequirements(repo) + scmutil.writereporequirements(repo, maywritestore=False) writeconfig(repo, oldincludes, oldexcludes, oldprofiles) raise