editcommitmsgs /

# - enables editing commit messages for all patches in on go
# Copyright 2010, 2010 Erik Zielke <>
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
This extension do only have an effect if Mercurial Queues (mq) is enables.
Then it will allow you to edit commit messages of all patches in a single file.
import os

from mercurial import extensions
from mercurial import cmdutil
from mercurial.i18n import _

_patchlinestarter = "HG: Patch: "

def editcommitmsgs(ui, repo, **opts):
    """"Edit commit message of all applied patches"""
        mq = extensions.find('mq')
    except KeyError:
    if repo[None].dirty():
        ui.warn(_('abort: local changes found, refresh first\n'))

    if not
        ui.warn(_('No patches applied'))

    allmessages = introductiontext()
    patchmap = {}
    appliedbefore =[:]

    #temporarily disable writing
    oldwrite = ui.write
    def nowrite(*_args, **_kwargs):
        """Noop write function"""
    ui.write = nowrite

    for appliedpatch in
        patchheader = mq.patchheader(
        patchmap[] = patchheader
        allmessages += _patchlinestarter + + '\n'
        if opts.get('showstatus'):
            allmessages += ('\n').join(
                fullstatus(repo, appliedpatch.node)) + '\n'
        allmessages += ('\n').join(patchheader.message) + '\n'
        allmessages += "HG: --\n"

    editedmessages = ui.edit(allmessages, ui.username)
    commitmessages = parsemessage(editedmessages)
    savecommitcomments(ui, repo, mq, appliedbefore, commitmessages)
    ui.write = oldwrite

def fullstatus(repo, node1):
    """Gets status between ode1 and its first parent."""
    ctx = repo[node1]
    node2 = ctx.p1().node()
    modified, added, removed = repo.status(node2, node1,

    fulltext = []
    fulltext.append(_("HG: user: %s") % ctx.user())
    if ctx.p2():
        fulltext.append(_("HG: branch merge"))
    if ctx.branch():
        fulltext.append(_("HG: branch '%s'") % ctx.branch())
    fulltext.extend([_("HG: added %s") % f for f in added])
    fulltext.extend([_("HG: changed %s") % f for f in modified])
    fulltext.extend([_("HG: removed %s") % f for f in removed])
    if not added and not modified and not removed:
        fulltext.append(_("HG: no files changed"))
    return fulltext

def introductiontext():
    """"Returns an introduction text for the edit file"""
    return _("HG: Enter commit messages for the different patches.\n"
       "HG: Lines beginning with 'HG:' is used to apply the\n"
       "HG: messages to the right patch, and gives some status on the patch\n"
       "HG: Which can help you write the commit messages, so please do not\n"
       "HG: edit them\n"
       "HG: --\n"
       "HG: Applied patches:\n"
       "HG: --\n")

def parsemessage(text):
    """Parses the edited text.

    It parses it to a map from patch name to lines of the commit messages
    currentpatch = None
    commitcomments = {}
    for line in text.splitlines():
        if line.startswith(_patchlinestarter):
            currentpatch = line.replace(_patchlinestarter, "")
        elif line.startswith("HG: "):
            if currentpatch != None:
                if not currentpatch in commitcomments:
                    commitcomments[currentpatch] = []
    return commitcomments

def savecommitcomments(ui, repo, mq, appliedbefore, commitmessages):
    """Saves commit messages.

    It does so by first popping all applied patches, then pushing them back
    one by one, while updating the commit messages.
    mq.pop(ui, repo, all=True)
    for appliedpatch in appliedbefore:
        mq.push(ui, repo,
        message = '\n'.join(commitmessages[])
        mq.refresh(ui, repo, message=message)

def uisetup(ui):
    """Adds the command to command table if mq is found."""
    except KeyError:
        ui.warn(_('could not find mq extensions'))

    ccmdtable = {"qeditcommitmsgs":
       [('s', 'showstatus', None, _('show status of changed files')),])}

cmdtable = {}