Making rollback safer

Greg Ward greg at gerg.ca
Tue Sep 20 19:21:51 CDT 2011


On Tue, Sep 13, 2011 at 5:51 PM, Matt Mackall <mpm at selenic.com> wrote:
> Second, rolling back the dirstate should be seen as a side-effect of
> truncating history: we don't want the dirstate to keep pointing at
> truncated history. So if dirstate points at a revision that's not being
> truncated, no action should be taken. Example:
>
> hg co stable
> echo foo > bar
> hg ci -Am "add new file bar"
> hg co default
> hg rollback -> should remove tip commit without jumping to stable

Done, as you no doubt noticed ("if parentgone" patch, 7c26ce9edbd2).

> Similarly, we shouldn't mess with rolling back the dirstate (or even
> storing it!) if the last transaction had nothing to do with the
> dirstate. Example:
>
> hg pull
> hg add foo
> hg rollback -> shouldn't unadd foo

This is tricky because the user might update after pull. Typically
that means updating to a changeset in the pulled changegroup that
would be destroyed by rollback, so we must restore dirstate to avoid
an orphaned working dir. Also, this case is partially addressed by the
"if parentgone" patch above.

I've added some code to test-rollback.t to demonstrate. First test:

"""
rollback a pull does not affect dirstate
  $ cd ../t
  $ hg update 1
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ echo foo >> a
  $ hg commit -m'another modification to a'
  $ hg -q clone -r 0 . ../t2
  $ cd ../t2
  $ hg id -n
  0
  $ hg pull
  pulling from $TESTTMP/t
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 2 changesets with 2 changes to 1 files
  (run 'hg update' to get a working copy)
  $ hg status
  $ touch newfile
  $ hg add newfile
  $ hg status
  A newfile
  $ hg rollback
  repository tip rolled back to revision 0 (undo pull)
  $ hg status
  A newfile
"""

This passes without any code change thanks to the "if parentgone"
patch (7c26ce9edbd2).

Next test:

"""
now what if we update after pull?
  $ hg revert --all --no-backup
  forgetting newfile
  $ rm -f `hg status -un`
  $ hg status
  $ hg id -n
  0
  $ hg pull
  pulling from $TESTTMP/t
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 2 changesets with 2 changes to 1 files
  (run 'hg update' to get a working copy)
  $ hg update 2
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ hg id -n
  2
  $ touch newfile
  $ hg add newfile
  $ hg status
  A newfile
  $ hg rollback
  repository tip rolled back to revision 0 (undo pull)
  working directory now based on revision 0
  $ hg status
  A newfile
"""

This fails as follows:

--- /home/greg/src/hg-crew/tests/test-rollback.t
+++ /home/greg/src/hg-crew/tests/test-rollback.t.err
@@ -203,4 +203,5 @@
   repository tip rolled back to revision 0 (undo pull)
   working directory now based on revision 0
   $ hg status
-  A newfile
+  M a
+  ? newfile

As long as we save and restore dirstate all-or-nothing, I don't see
how to avoid that. Either we forget newfile in this test or we create
orphaned working dirs. Yech.

However... what if we only restore the first 40 bytes of dirstate?
That will set the working dir parents back to existing changesets
while not forgetting about newfile. But it feels weird and risky and
strange. What if you have a pending modification to a file whose
history is completely destroyed by the rollback?

Another crazy idea is for update to interact with the transaction
system somehow, but that feels like a layering violation.

Greg


More information about the Mercurial-devel mailing list