Patchwork D3557: extensions: new closehead module for closing arbitrary heads

login
register
mail settings
Submitter phabricator
Date July 1, 2018, 10:15 p.m.
Message ID <89c0b93d70fcb2acb670dfa83496df2d@localhost.localdomain>
Download mbox | patch
Permalink /patch/32557/
State Not Applicable
Headers show

Comments

phabricator - July 1, 2018, 10:15 p.m.
joerg.sonnenberger updated this revision to Diff 9399.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D3557?vs=8963&id=9399

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

AFFECTED FILES
  hgext/closehead.py
  tests/test-close-head.t
  tests/test-help.t

CHANGE DETAILS




To: joerg.sonnenberger, #hg-reviewers
Cc: indygreg, pulkit, mercurial-devel

Patch

diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -262,6 +262,7 @@ 
        censor        erase file content at a given revision
        churn         command to display statistics about repository history
        clonebundles  advertise pre-generated bundles to seed clones
+       closehead     close arbitrary heads without checking them out first
        convert       import revisions from foreign VCS repositories into
                      Mercurial
        eol           automatically manage newlines in repository files
diff --git a/tests/test-close-head.t b/tests/test-close-head.t
new file mode 100644
--- /dev/null
+++ b/tests/test-close-head.t
@@ -0,0 +1,55 @@ 
+  $ hg init test-content
+  $ cd test-content
+  $ hg debugbuilddag '+2*2*3*4'
+  $ hg log -G --template '{rev}:{node|short}'
+  o  4:e7bd5218ca15
+  |
+  | o  3:6100d3090acf
+  |/
+  | o  2:fa942426a6fd
+  |/
+  | o  1:66f7d451a68b
+  |/
+  o  0:1ea73414a91b
+  
+  $ hg --config extensions.closehead= close-head -m 'Not a head' 0 1
+  abort: revision is not an open head: 0
+  [255]
+  $ hg --config extensions.closehead= close-head -m 'Close old heads' -r 1 2
+  $ hg heads
+  changeset:   4:e7bd5218ca15
+  parent:      0:1ea73414a91b
+  user:        debugbuilddag
+  date:        Thu Jan 01 00:00:04 1970 +0000
+  summary:     r4
+  
+  changeset:   3:6100d3090acf
+  parent:      0:1ea73414a91b
+  user:        debugbuilddag
+  date:        Thu Jan 01 00:00:03 1970 +0000
+  summary:     r3
+  
+  $ hg --config extensions.closehead= close-head -m 'Close more old heads' 4
+  $ hg heads
+  changeset:   3:6100d3090acf
+  parent:      0:1ea73414a91b
+  user:        debugbuilddag
+  date:        Thu Jan 01 00:00:03 1970 +0000
+  summary:     r3
+  
+  $ hg --config extensions.closehead= close-head -m 'Not a head' 0
+  abort: revision is not an open head: 0
+  [255]
+  $ hg --config extensions.closehead= close-head -m 'Already closed head' 1
+  abort: revision is not an open head: 1
+  [255]
+
+  $ hg init ../test-empty
+  $ cd ../test-empty
+  $ hg debugbuilddag '+1'
+  $ hg log -G --template '{rev}:{node|short}'
+  o  0:1ea73414a91b
+  
+  $ hg --config extensions.closehead= close-head -m 'Close initial revision' 0
+  $ hg heads
+  [1]
diff --git a/hgext/closehead.py b/hgext/closehead.py
new file mode 100644
--- /dev/null
+++ b/hgext/closehead.py
@@ -0,0 +1,86 @@ 
+# closehead.py - Close arbitrary heads without checking them out first
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''close arbitrary heads without checking them out first'''
+
+from __future__ import absolute_import
+
+from mercurial.i18n import _
+from mercurial import (
+    bookmarks,
+    cmdutil,
+    context,
+    error,
+    lock,
+    pycompat,
+    registrar,
+    scmutil,
+)
+
+cmdtable = {}
+command = registrar.command(cmdtable)
+# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
+# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
+# be specifying the version(s) of Mercurial they are tested with, or
+# leave the attribute unspecified.
+testedwith = 'ships-with-hg-core'
+
+commitopts = cmdutil.commitopts
+commitopts2 = cmdutil.commitopts2
+commitopts3 = [('r', 'rev', '',
+               _('revision to check'), _('REV'))]
+
+@command('close-head|close-heads', commitopts + commitopts2 + commitopts3,
+    _('[OPTION]... [REV]...'), inferrepo=True)
+def close_branch(ui, repo, *revs, **opts):
+    """close the given head revisions
+
+    This is equivalent to checking out each revision in a clean tree and running
+    ``hg commit --close-branch``, except that it doesn't change the working
+    directory.
+
+    The commit message must be specified with -l or -m.
+    """
+    def docommit(rev):
+        cctx = context.memctx(repo, parents=[rev, None], text=message,
+                              files=[], filectxfn=None, user=opts.get('user'),
+                              date=opts.get('date'), extra=extra)
+        tr = repo.transaction('commit')
+        ret = repo.commitctx(cctx, True)
+        bookmarks.update(repo, [rev, None], ret)
+        cctx.markcommitted(ret)
+        tr.close()
+
+    if not revs:
+        raise error.Abort(_('no revisions specified'))
+
+    opts = pycompat.byteskwargs(opts)
+
+    revs += tuple(opts.get('rev', []))
+    revs = scmutil.revrange(repo, revs)
+
+    heads = []
+    for branch in repo.branchmap():
+        heads.extend(repo.branchheads(branch))
+    heads = set(repo[h].rev() for h in heads)
+    for rev in revs:
+        if rev not in heads:
+            raise error.Abort(_('revision is not an open head: %s') % rev)
+
+    message = cmdutil.logmessage(ui, opts)
+    if not message:
+        raise error.Abort(_("no commit message specified with -l or -m"))
+    extra = { 'close': '1' }
+
+    wlock = repo.wlock()
+    lock = repo.lock()
+    with wlock, lock:
+        for rev in revs:
+            r = repo[rev]
+            branch = r.branch()
+            extra['branch'] = branch
+            docommit(r)
+    return 0
+