diff --git a/hgext/keyword.py b/hgext/keyword.py index de496752d9364cbc55e14900ccffa851c20d8b41_aGdleHQva2V5d29yZC5weQ==..59e8bc22506ec40f74ce192b6f0c36fb9e42d2c6_aGdleHQva2V5d29yZC5weQ== 100644 --- a/hgext/keyword.py +++ b/hgext/keyword.py @@ -590,8 +590,8 @@ kwt.restrict = restrict return n - def rollback(self, dryrun=False): + def rollback(self, dryrun=False, force=False): wlock = self.wlock() try: if not dryrun: changed = self['.'].files() @@ -594,8 +594,8 @@ wlock = self.wlock() try: if not dryrun: changed = self['.'].files() - ret = super(kwrepo, self).rollback(dryrun) + ret = super(kwrepo, self).rollback(dryrun, force) if not dryrun: ctx = self['.'] modified, added = _preselect(self[None].status(), changed) diff --git a/mercurial/commands.py b/mercurial/commands.py index de496752d9364cbc55e14900ccffa851c20d8b41_bWVyY3VyaWFsL2NvbW1hbmRzLnB5..59e8bc22506ec40f74ce192b6f0c36fb9e42d2c6_bWVyY3VyaWFsL2NvbW1hbmRzLnB5 100644 --- 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): diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py index de496752d9364cbc55e14900ccffa851c20d8b41_bWVyY3VyaWFsL2xvY2FscmVwby5weQ==..59e8bc22506ec40f74ce192b6f0c36fb9e42d2c6_bWVyY3VyaWFsL2xvY2FscmVwby5weQ== 100644 --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -754,9 +754,9 @@ finally: lock.release() - def rollback(self, dryrun=False): + def rollback(self, dryrun=False, force=False): wlock = lock = None try: wlock = self.wlock() lock = self.lock() if os.path.exists(self.sjoin("undo")): @@ -758,12 +758,12 @@ wlock = lock = None try: wlock = self.wlock() lock = self.lock() if os.path.exists(self.sjoin("undo")): - return self._rollback(dryrun) + return self._rollback(dryrun, force) else: self.ui.warn(_("no rollback information available\n")) return 1 finally: release(lock, wlock) @@ -764,10 +764,10 @@ else: self.ui.warn(_("no rollback information available\n")) return 1 finally: release(lock, wlock) - def _rollback(self, dryrun): + def _rollback(self, dryrun, force): ui = self.ui try: args = self.opener.read('undo.desc').splitlines() @@ -786,6 +786,13 @@ % (oldtip, desc)) except IOError: msg = _('rolling back unknown transaction\n') + desc = None + + if not force and self['.'] != self['tip'] and desc == 'commit': + raise util.Abort( + _('rollback of last commit while not checked out ' + 'may lose data (use -f to force)')) + ui.status(msg) if dryrun: return 0 diff --git a/tests/test-debugcomplete.t b/tests/test-debugcomplete.t index de496752d9364cbc55e14900ccffa851c20d8b41_dGVzdHMvdGVzdC1kZWJ1Z2NvbXBsZXRlLnQ=..59e8bc22506ec40f74ce192b6f0c36fb9e42d2c6_dGVzdHMvdGVzdC1kZWJ1Z2NvbXBsZXRlLnQ= 100644 --- a/tests/test-debugcomplete.t +++ b/tests/test-debugcomplete.t @@ -257,7 +257,7 @@ rename: after, force, include, exclude, dry-run resolve: all, list, mark, unmark, no-status, tool, include, exclude revert: all, date, rev, no-backup, include, exclude, dry-run - rollback: dry-run + rollback: dry-run, force root: showconfig: untrusted tag: force, local, rev, remove, edit, message, date, user diff --git a/tests/test-import-bypass.t b/tests/test-import-bypass.t index de496752d9364cbc55e14900ccffa851c20d8b41_dGVzdHMvdGVzdC1pbXBvcnQtYnlwYXNzLnQ=..59e8bc22506ec40f74ce192b6f0c36fb9e42d2c6_dGVzdHMvdGVzdC1pbXBvcnQtYnlwYXNzLnQ= 100644 --- a/tests/test-import-bypass.t +++ b/tests/test-import-bypass.t @@ -60,7 +60,7 @@ |/ @ 0:07f494440405 test 0 0 - default - adda - $ hg rollback + $ hg rollback -f repository tip rolled back to revision 1 (undo commit) Test --import-branch @@ -72,7 +72,7 @@ | @ 0:07f494440405 test 0 0 - default - adda - $ hg rollback + $ hg rollback -f repository tip rolled back to revision 1 (undo commit) Test --strip @@ -94,7 +94,7 @@ > +a > EOF applying patch from stdin - $ hg rollback + $ hg rollback -f repository tip rolled back to revision 1 (undo commit) Test unsupported combinations diff --git a/tests/test-rollback.t b/tests/test-rollback.t index de496752d9364cbc55e14900ccffa851c20d8b41_dGVzdHMvdGVzdC1yb2xsYmFjay50..59e8bc22506ec40f74ce192b6f0c36fb9e42d2c6_dGVzdHMvdGVzdC1yb2xsYmFjay50 100644 --- a/tests/test-rollback.t +++ b/tests/test-rollback.t @@ -83,7 +83,7 @@ $ hg bookmark bar $ cat .hg/undo.branch ; echo test - $ hg rollback + $ hg rollback -f repository tip rolled back to revision 1 (undo commit) $ hg id -n 0 @@ -146,3 +146,37 @@ working directory now based on revision 0 $ hg id default 791dd2169706 + +update to older changeset and then refuse rollback, because +that would lose data (issue2998) + $ cd ../t + $ hg -q update + $ rm `hg status -un` + $ template='{rev}:{node|short} [{branch}] {desc|firstline}\n' + $ echo 'valuable new file' > b + $ echo 'valuable modification' >> a + $ hg commit -A -m'a valuable change' + adding b + $ hg update 0 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg rollback + abort: rollback of last commit while not checked out may lose data (use -f to force) + [255] + $ hg tip -q + 2:4d9cd3795eea + $ hg rollback -f + repository tip rolled back to revision 1 (undo commit) + $ hg status + $ hg log --removed b # yep, it's gone + +same again, but emulate an old client that doesn't write undo.desc + $ hg -q update + $ echo 'valuable modification redux' >> a + $ hg commit -m'a valuable change redux' + $ rm .hg/undo.desc + $ hg update 0 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg rollback + rolling back unknown transaction + $ cat a + a