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 + 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/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