diff mercurial/commands.py @ 15183:59e8bc22506e

rollback: avoid unsafe rollback when not at tip (issue2998) You can get into trouble if you commit, update back to an older changeset, and then rollback. The update removes your valuable changes from the working dir, then rollback removes them history. Oops: you've just irretrievably lost data running nothing but core Mercurial commands. (More subtly: rollback from a shared clone that was already at an older changeset -- no update required, just rollback from the wrong directory.) The fix assumes that only "commit" transactions have irreplaceable data, and allows rolling back non-commit transactions as always. But when rolling back a commit, check that the working dir is checked out to tip, i.e. the changeset we're about to destroy. If not, abort. You can get back the old (dangerous) behaviour with --force.
author Greg Ward <greg@gerg.ca>
date Fri, 30 Sep 2011 21:58:54 -0400
parents d3b42e96cfcf
children 3834ca04664a
line wrap: on
line diff
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4612,7 +4612,8 @@
     finally:
         wlock.release()
 
-@command('rollback', dryrunopts)
+@command('rollback', dryrunopts +
+         [('f', 'force', False, _('ignore safety measures'))])
 def rollback(ui, repo, **opts):
     """roll back the last transaction (dangerous)
 
@@ -4633,6 +4634,12 @@
     - push (with this repository as the destination)
     - unbundle
 
+    It's possible to lose data with rollback: commit, update back to
+    an older changeset, and then rollback. The update removes the
+    changes you committed from the working directory, and rollback
+    removes them from history. To avoid data loss, you must pass
+    --force in this case.
+
     This command is not intended for use on public repositories. Once
     changes are visible for pull by other users, rolling a transaction
     back locally is ineffective (someone else may already have pulled
@@ -4642,7 +4649,8 @@
 
     Returns 0 on success, 1 if no rollback data is available.
     """
-    return repo.rollback(opts.get('dry_run'))
+    return repo.rollback(dryrun=opts.get('dry_run'),
+                         force=opts.get('force'))
 
 @command('root', [])
 def root(ui, repo):