localrepo: "blindly" do a dirstate backup at the end of the transaction
Having the file backup mechanism dealing with file backup as benefit. So lets
move closer to that.
The fact `hg rollback` even needs this is sad. I hope to have the time to
implement one of the alternative soon.
--- a/mercurial/dirstate.py Thu Feb 16 17:12:21 2023 +0100
+++ b/mercurial/dirstate.py Thu Feb 16 11:42:43 2023 +0100
@@ -1615,6 +1615,22 @@
else:
return self._filename
+ def all_file_names(self):
+ """list all filename currently used by this dirstate
+
+ This is only used to do `hg rollback` related backup in the transaction
+ """
+ if not self._opener.exists(self._filename):
+ # no data every written to disk yet
+ return ()
+ elif self._use_dirstate_v2:
+ return (
+ self._filename,
+ self._map.docket.data_filename(),
+ )
+ else:
+ return (self._filename,)
+
def data_backup_filename(self, backupname):
if not self._use_dirstate_v2:
return None
--- a/mercurial/localrepo.py Thu Feb 16 17:12:21 2023 +0100
+++ b/mercurial/localrepo.py Thu Feb 16 11:42:43 2023 +0100
@@ -2647,6 +2647,32 @@
tr.addpostclose(b'refresh-filecachestats', self._refreshfilecachestats)
self._transref = weakref.ref(tr)
scmutil.registersummarycallback(self, tr, desc)
+ # This only exist to deal with the need of rollback to have viable
+ # parents at the end of the operation. So backup viable parents at the
+ # time of this operation.
+ #
+ # We only do it when the `wlock` is taken, otherwise other might be
+ # altering the dirstate under us.
+ #
+ # This is really not a great way to do this (first, because we cannot
+ # always do it). There are more viable alternative that exists
+ #
+ # - backing only the working copy parent in a dedicated files and doing
+ # a clean "keep-update" to them on `hg rollback`.
+ #
+ # - slightly changing the behavior an applying a logic similar to "hg
+ # strip" to pick a working copy destination on `hg rollback`
+ if self.currentwlock() is not None:
+ ds = self.dirstate
+
+ def backup_dirstate(tr):
+ for f in ds.all_file_names():
+ # hardlink backup is okay because `dirstate` is always
+ # atomically written and possible data file are append only
+ # and resistant to trailing data.
+ tr.addbackup(f, hardlink=True, location=b'plain')
+
+ tr.addvalidator(b'dirstate-backup', backup_dirstate)
return tr
def _journalfiles(self):