D5940: uncommit: add -f/--force when possibly hiding data (issue5977)

navaneeth.suresh (Navaneeth Suresh) phabricator at mercurial-scm.org
Mon Feb 11 19:02:07 UTC 2019


navaneeth.suresh created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The behaviour of uncommit may confuse a new user. Although it never
  destroys the data, it can hide instead. I added a `-f/--force` flag
  when the working copy is dirty. The data can be visible on `--hidden`
  flag.
  
  Some cases in `test-uncommit.t` changes output. I'll be working on
  this further based on review.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D5940

AFFECTED FILES
  hgext/uncommit.py
  mercurial/rewriteutil.py
  tests/test-uncommit.t

CHANGE DETAILS

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,119 +229,85 @@
   +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
 
   $ 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
+  
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/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:



To: navaneeth.suresh, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list