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