diff --git a/hgext/uncommit.py b/hgext/uncommit.py --- a/hgext/uncommit.py +++ b/hgext/uncommit.py @@ -137,6 +137,7 @@ @command('uncommit', [('', 'keep', False, _('allow an empty commit after uncommiting')), + ('f', 'force', False, _('allow uncommit with outstanding changes')), ] + commands.walkopts, _('[OPTION]... [FILE]...'), helpcategory=command.CATEGORY_CHANGE_MANAGEMENT) @@ -159,12 +160,16 @@ 'uncommitondirtywdir'): cmdutil.bailifchanged(repo) old = repo['.'] - rewriteutil.precheck(repo, [old.rev()], 'uncommit') - if len(old.parents()) > 1: - raise error.Abort(_("cannot uncommit merge changeset")) + rewriteutil.precheck(repo, [old.rev()], 'uncommit', merge=True) + + match = scmutil.match(old, pats, opts) + # explicitly check for a merge, so it cannot be overridden + if old.p2(): + raise error.Abort(_("outstanding uncommitted merge")) + if not opts.get('force'): + cmdutil.bailifchanged(repo, hint=_('use -f to force')) with repo.transaction('uncommit'): - match = scmutil.match(old, pats, opts) keepcommit = opts.get('keep') or pats newid = _commitfiltered(repo, old, match, keepcommit) if newid is None: diff --git a/mercurial/rewriteutil.py b/mercurial/rewriteutil.py --- a/mercurial/rewriteutil.py +++ b/mercurial/rewriteutil.py @@ -16,9 +16,10 @@ revset, ) -def precheck(repo, revs, action='rewrite'): +def precheck(repo, revs, action='rewrite', merge=False): """check if revs can be rewritten action is used to control the error message. + action is used to true the reject merges. Make sure this function is called after taking the lock. """ @@ -39,6 +40,9 @@ newunstable = disallowednewunstable(repo, revs) if newunstable: raise error.Abort(_("cannot %s changeset with children") % action) + if merge and any(repo[r].p2() for r in revs): + msg = _("cannot %s merge changesets") % (action,) + raise error.Abort(msg) def disallowednewunstable(repo, revs): """Checks whether editing the revs will create new unstable changesets and diff --git a/tests/test-uncommit.t b/tests/test-uncommit.t --- a/tests/test-uncommit.t +++ b/tests/test-uncommit.t @@ -35,6 +35,7 @@ options ([+] can be repeated): --keep allow an empty commit after uncommiting + -f --force allow uncommit with outstanding changes -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns @@ -158,6 +159,9 @@ abort: uncommitted changes [255] $ hg uncommit files + abort: uncommitted changes + (use -f to force) + [255] $ cat files abcde foo @@ -170,14 +174,48 @@ abort: uncommitted changes [255] $ hg uncommit --config experimental.uncommitondirtywdir=True + abort: uncommitted changes + (use -f to force) + [255] $ hg commit -m "files abcde + foo" Uncommit in the middle of a stack, does not move bookmark $ hg checkout '.^^^' - 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (leaving bookmark foo) $ hg log -r . -p -T '{rev}:{node} {desc}' + 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcddiff -r abf2df566fc1 -r 6db330d65db4 file-abcd + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/file-abcd Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +abcd + diff -r abf2df566fc1 -r 6db330d65db4 files + --- a/files Thu Jan 01 00:00:00 1970 +0000 + +++ b/files Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,1 @@ + -abc + +abcd + + $ hg bookmark + foo 9:ad3773de7293 + $ hg uncommit + 3 new orphan changesets + $ hg status + M files + A file-abcd + $ hg heads -T '{rev}:{node} {desc}' + 9:ad3773de72930be60f1ebb39fe89115b81630a8a files abcde + foo (no-eol) + $ hg bookmark + foo 9:ad3773de7293 + $ hg commit -m 'new abc' + created new head + +Partial uncommit in the middle, does not move bookmark + + $ hg checkout '.^' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg log -r . -p -T '{rev}:{node} {desc}' 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abcdiff -r 69a232e754b0 -r abf2df566fc1 file-abc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/file-abc Thu Jan 01 00:00:00 1970 +0000 @@ -191,98 +229,63 @@ +abc $ hg bookmark - foo 10:48e5bd7cd583 - $ hg uncommit - 3 new orphan changesets + foo 9:ad3773de7293 + $ hg uncommit file-ab + nothing to uncommit + [1] $ hg status - M files - A file-abc - $ hg heads -T '{rev}:{node} {desc}' - 10:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo (no-eol) - $ hg bookmark - foo 10:48e5bd7cd583 - $ hg commit -m 'new abc' - created new head - -Partial uncommit in the middle, does not move bookmark - - $ hg checkout '.^' - 1 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg log -r . -p -T '{rev}:{node} {desc}' - 1:69a232e754b08d568c4899475faf2eb44b857802 added file-abdiff -r 3004d2d9b508 -r 69a232e754b0 file-ab - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/file-ab Thu Jan 01 00:00:00 1970 +0000 - @@ -0,0 +1,1 @@ - +ab - diff -r 3004d2d9b508 -r 69a232e754b0 files - --- a/files Thu Jan 01 00:00:00 1970 +0000 - +++ b/files Thu Jan 01 00:00:00 1970 +0000 - @@ -1,1 +1,1 @@ - -a - +ab - - $ hg bookmark - foo 10:48e5bd7cd583 - $ hg uncommit file-ab - 1 new orphan changesets - $ hg status - A file-ab $ hg heads -T '{rev}:{node} {desc}\n' - 12:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab - 11:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc - 10:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo + 10:25798b2f714d0937797be0f9fde55aaf5472c052 new abc + 9:ad3773de72930be60f1ebb39fe89115b81630a8a files abcde + foo $ hg bookmark - foo 10:48e5bd7cd583 + foo 9:ad3773de7293 $ hg commit -m 'update ab' + nothing changed + [1] $ hg status $ hg heads -T '{rev}:{node} {desc}\n' - 13:f21039c59242b085491bb58f591afc4ed1c04c09 update ab - 11:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc - 10:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo + 10:25798b2f714d0937797be0f9fde55aaf5472c052 new abc + 9:ad3773de72930be60f1ebb39fe89115b81630a8a files abcde + foo $ hg log -G -T '{rev}:{node} {desc}' --hidden - @ 13:f21039c59242b085491bb58f591afc4ed1c04c09 update ab + o 10:25798b2f714d0937797be0f9fde55aaf5472c052 new abc | - o 12:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab - | - | * 11:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc + | * 9:ad3773de72930be60f1ebb39fe89115b81630a8a files abcde + foo + | | + | * 8:84beeba0ac30e19521c036e4d2dd3a5fa02586ff files abcde + foo + | | + | | x 7:0977fa602c2fd7d8427ed4e7ee15ea13b84c9173 update files for abcde + | |/ + | * 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde | | - | | * 10:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo - | | | - | | | x 9:8a6b58c173ca6a2e3745d8bd86698718d664bc6c files abcde + foo - | | |/ - | | | x 8:39ad452c7f684a55d161c574340c5766c4569278 update files for abcde - | | |/ - | | | x 7:0977fa602c2fd7d8427ed4e7ee15ea13b84c9173 update files for abcde - | | |/ - | | * 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde - | | | - | | | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde - | | |/ - | | | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde - | | |/ - | | * 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd - | | | - | | x 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc + | | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde + | |/ + | | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde | |/ - | x 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab + | x 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd |/ + @ 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc + | + o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab + | o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a Uncommit with draft parent $ hg uncommit + 1 new orphan changesets $ hg phase -r . - 12: draft + 1: draft $ hg commit -m 'update ab again' + created new head Phase is preserved $ hg uncommit --keep --config phases.new-commit=secret $ hg phase -r . - 15: draft + 12: draft $ hg commit --amend -m 'update ab again' Uncommit with public parent @@ -290,20 +293,21 @@ $ hg phase -p "::.^" $ hg uncommit $ hg phase -r . - 12: public + 1: public Partial uncommit with public parent $ echo xyz > xyz $ hg add xyz $ hg commit -m "update ab and add xyz" + created new head $ hg uncommit xyz $ hg status A xyz $ hg phase -r . - 18: draft + 15: draft $ hg phase -r ".^" - 12: public + 1: public Uncommit leaving an empty changeset @@ -379,7 +383,7 @@ $ hg commit -m 'merge a and b' $ hg uncommit - abort: cannot uncommit merge changeset + abort: cannot uncommit merge changesets [255] $ hg status @@ -398,3 +402,35 @@ |/ o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1 + $ cd .. + +Uncommit should require -f/--force when possibly hiding data + + $ hg init issue5977 + $ cd issue5977 + $ echo 'super critical info!' > a + $ hg ci -Am 'add a' + adding a + $ echo 'foo' > a + $ hg unc a + abort: uncommitted changes + (use -f to force) + [255] + $ cat a + foo + $ hg log + changeset: 0:e37b99831f6e + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a + + $ hg unc -f a + $ hg log + changeset: 1:656ba143d384 + tag: tip + parent: -1:000000000000 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a +