[PATCH] commit: add --allow-empty flag

Durham Goode durham at fb.com
Thu May 7 17:10:14 CDT 2015


# HG changeset patch
# User Durham Goode <durham at fb.com>
# Date 1431034538 25200
#      Thu May 07 14:35:38 2015 -0700
# Node ID 9f90e53f8e810db2d6b0e4cf66433166c4d42934
# Parent  c5d4f9cc8da7bb2068457e96e4f74ff694514ced
commit: add --allow-empty flag

This adds a flag that enables a user to make empty commits. This is useful in a
number of cases.

For instance, automation that creates release branches via
bookmarks may want to make empty commits to that release bookmark so that it
can't be fast forwarded and so it can record information about the release
bookmark's creation. This is already possible with named branches, so making it
possible for bookmarks makes sense.

Another case we've wanted it is for mirroring repositories into Mercurial. We
have automation that syncs commits into hg by running things from the command
line. The ability to produce empty commits is useful for syncing unusual commits
from other VCS's.

In general, allowing the user to create the DAG as they see fit seems useful,
and when I mentioned this in IRC more than one person piped up and said they
were already hacking around this limitation by using mq, import, and
commit-dummy-change-then-amend-the-content-away style solutions.

diff --git a/hgext/largefiles/reposetup.py b/hgext/largefiles/reposetup.py
--- a/hgext/largefiles/reposetup.py
+++ b/hgext/largefiles/reposetup.py
@@ -260,7 +260,7 @@ def reposetup(ui, repo):
         # contents updated to reflect the hash of their largefile.
         # Do that here.
         def commit(self, text="", user=None, date=None, match=None,
-                force=False, editor=False, extra={}):
+                force=False, editor=False, extra={}, **kwargs):
             orig = super(lfilesrepo, self).commit
 
             wlock = self.wlock()
@@ -268,7 +268,8 @@ def reposetup(ui, repo):
                 lfcommithook = self._lfcommithooks[-1]
                 match = lfcommithook(self, match)
                 result = orig(text=text, user=user, date=date, match=match,
-                                force=force, editor=editor, extra=extra)
+                                force=force, editor=editor, extra=extra,
+                                **kwargs)
                 return result
             finally:
                 wlock.release()
diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -3399,13 +3399,13 @@ def reposetup(ui, repo):
                     raise util.Abort(errmsg)
 
         def commit(self, text="", user=None, date=None, match=None,
-                   force=False, editor=False, extra={}):
+                   force=False, editor=False, extra={}, **kwargs):
             self.abortifwdirpatched(
                 _('cannot commit over an applied mq patch'),
                 force)
 
             return super(mqrepo, self).commit(text, user, date, match, force,
-                                              editor, extra)
+                                              editor, extra, **kwargs)
 
         def checkpush(self, pushop):
             if self.mq.applied and self.mq.checkapplied and not pushop.force:
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1418,6 +1418,7 @@ def clone(ui, source, dest=None, **opts)
     ('s', 'secret', None, _('use the secret phase for committing')),
     ('e', 'edit', None, _('invoke editor on commit messages')),
     ('i', 'interactive', None, _('use interactive mode')),
+    ('', 'allow-empty', None, _('allow commiting without pending changes')),
     ] + walkopts + commitopts + commitopts2 + subrepoopts,
     _('[OPTION]... [FILE]...'),
     inferrepo=True)
@@ -1537,7 +1538,8 @@ def commit(ui, repo, *pats, **opts):
                 return repo.commit(message, opts.get('user'), opts.get('date'),
                                    match,
                                    editor=editor,
-                                   extra=extra)
+                                   extra=extra,
+                                   allowempty=opts.get('allow_empty'))
             finally:
                 ui.restoreconfig(backup)
                 repo.baseui.restoreconfig(basebackup)
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1356,7 +1356,7 @@ class localrepository(object):
 
     @unfilteredmethod
     def commit(self, text="", user=None, date=None, match=None, force=False,
-               editor=False, extra={}):
+               editor=False, extra={}, allowempty=False):
         """Add a new revision to current repository.
 
         Revision information is gathered from the working directory,
@@ -1465,8 +1465,8 @@ class localrepository(object):
             cctx = context.workingcommitctx(self, status,
                                             text, user, date, extra)
 
-            if (not force and not extra.get("close") and not merge
-                and not cctx.files()
+            if (not force and not extra.get("close") and not merge and not
+                allowempty and not cctx.files()
                 and wctx.branch() == wctx.p1().branch()):
                 return None
 
diff --git a/tests/test-commit.t b/tests/test-commit.t
--- a/tests/test-commit.t
+++ b/tests/test-commit.t
@@ -544,6 +544,18 @@ commit copy
        0         0       6  .....       0 26d3ca0dfd18 000000000000 000000000000 (re)
        1         6       7  .....       1 d267bddd54f7 26d3ca0dfd18 000000000000 (re)
 
+Test making empty commits
+  $ hg commit --allow-empty -m "empty commit"
+  $ hg log -r . -v --stat
+  changeset:   2:d809f3644287
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  description:
+  empty commit
+  
+  
+  
 verify pathauditor blocks evil filepaths
   $ cat > evil-commit.py <<EOF
   > from mercurial import ui, hg, context, node
@@ -568,7 +580,7 @@ verify pathauditor blocks evil filepaths
 #endif
 
   $ hg rollback -f
-  repository tip rolled back to revision 1 (undo commit)
+  repository tip rolled back to revision 2 (undo commit)
   $ cat > evil-commit.py <<EOF
   > from mercurial import ui, hg, context, node
   > notrc = "HG~1/hgrc"
@@ -586,7 +598,7 @@ verify pathauditor blocks evil filepaths
   [255]
 
   $ hg rollback -f
-  repository tip rolled back to revision 1 (undo commit)
+  repository tip rolled back to revision 2 (undo commit)
   $ cat > evil-commit.py <<EOF
   > from mercurial import ui, hg, context, node
   > notrc = "HG8B6C~2/hgrc"
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -202,7 +202,7 @@ Show all commands + options
   add: include, exclude, subrepos, dry-run
   annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude, template
   clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
-  commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
+  commit: addremove, close-branch, amend, secret, edit, interactive, allow-empty, include, exclude, message, logfile, date, user, subrepos
   diff: rev, change, text, git, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, root, include, exclude, subrepos
   export: output, switch-parent, rev, text, git, nodates
   forget: include, exclude
diff --git a/tests/test-qrecord.t b/tests/test-qrecord.t
--- a/tests/test-qrecord.t
+++ b/tests/test-qrecord.t
@@ -63,6 +63,7 @@ help record (record)
       --amend               amend the parent of the working directory
    -s --secret              use the secret phase for committing
    -e --edit                invoke editor on commit messages
+      --allow-empty         allow commiting without pending changes
    -I --include PATTERN [+] include names matching the given patterns
    -X --exclude PATTERN [+] exclude names matching the given patterns
    -m --message TEXT        use text as commit message
diff --git a/tests/test-record.t b/tests/test-record.t
--- a/tests/test-record.t
+++ b/tests/test-record.t
@@ -50,6 +50,7 @@ Record help
       --amend               amend the parent of the working directory
    -s --secret              use the secret phase for committing
    -e --edit                invoke editor on commit messages
+      --allow-empty         allow commiting without pending changes
    -I --include PATTERN [+] include names matching the given patterns
    -X --exclude PATTERN [+] exclude names matching the given patterns
    -m --message TEXT        use text as commit message


More information about the Mercurial-devel mailing list