diff -r edc8504bc26b -r 4cabeea6d214 hgext/hooklib/changeset_obsoleted.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/hooklib/changeset_obsoleted.py Sat Sep 07 14:50:39 2019 +0200 @@ -0,0 +1,131 @@ +# Copyright 2020 Joerg Sonnenberger +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. +"""changeset_obsoleted is a hook to send a mail when an +existing draft changeset is obsoleted by an obsmarker without successor. + +Correct message threading requires the same messageidseed to be used for both +the original notification and the new mail. + +Usage: + [notify] + messageidseed = myseed + + [hooks] + pretxnclose.changeset_obsoleted = \ + python:hgext.hooklib.changeset_obsoleted.hook +""" + +from __future__ import absolute_import + +import email.errors as emailerrors +import email.utils as emailutils + +from mercurial.i18n import _ +from mercurial import ( + encoding, + error, + logcmdutil, + mail, + obsutil, + pycompat, + registrar, +) +from mercurial.utils import dateutil +from .. import notify + +configtable = {} +configitem = registrar.configitem(configtable) + +configitem( + b'notify_obsoleted', b'domain', default=None, +) +configitem( + b'notify_obsoleted', b'messageidseed', default=None, +) +configitem( + b'notify_obsoleted', + b'template', + default=b'''Subject: changeset abandoned + +This changeset has been abandoned. +''', +) + + +def _report_commit(ui, repo, ctx): + domain = ui.config(b'notify_obsoleted', b'domain') or ui.config( + b'notify', b'domain' + ) + messageidseed = ui.config( + b'notify_obsoleted', b'messageidseed' + ) or ui.config(b'notify', b'messageidseed') + template = ui.config(b'notify_obsoleted', b'template') + spec = logcmdutil.templatespec(template, None) + templater = logcmdutil.changesettemplater(ui, repo, spec) + ui.pushbuffer() + n = notify.notifier(ui, repo, b'incoming') + + subs = set() + for sub, spec in n.subs: + if spec is None: + subs.add(sub) + continue + revs = repo.revs(b'%r and %d:', spec, ctx.rev()) + if len(revs): + subs.add(sub) + continue + if len(subs) == 0: + ui.debug( + b'notify_obsoleted: no subscribers to selected repo and revset\n' + ) + return + + templater.show( + ctx, + changes=ctx.changeset(), + baseurl=ui.config(b'web', b'baseurl'), + root=repo.root, + webroot=n.root, + ) + data = ui.popbuffer() + + try: + msg = mail.parsebytes(data) + except emailerrors.MessageParseError as inst: + raise error.Abort(inst) + + msg['In-reply-to'] = notify.messageid(ctx, domain, messageidseed) + msg['Message-Id'] = notify.messageid( + ctx, domain, messageidseed + b'-obsoleted' + ) + msg['Date'] = encoding.strfromlocal( + dateutil.datestr(format=b"%a, %d %b %Y %H:%M:%S %1%2") + ) + if not msg['From']: + sender = ui.config(b'email', b'from') or ui.username() + if b'@' not in sender or b'@localhost' in sender: + sender = n.fixmail(sender) + msg['From'] = mail.addressencode(ui, sender, n.charsets, n.test) + msg['To'] = ', '.join(sorted(subs)) + + msgtext = msg.as_bytes() if pycompat.ispy3 else msg.as_string() + if ui.configbool(b'notify', b'test'): + ui.write(msgtext) + if not msgtext.endswith(b'\n'): + ui.write(b'\n') + else: + ui.status(_(b'notify_obsoleted: sending mail for %d\n') % ctx.rev()) + mail.sendmail( + ui, emailutils.parseaddr(msg['From'])[1], subs, msgtext, mbox=n.mbox + ) + + +def hook(ui, repo, hooktype, node=None, **kwargs): + if hooktype != b"pretxnclose": + raise error.Abort( + _(b'Unsupported hook type %r') % pycompat.bytestr(hooktype) + ) + for rev in obsutil.getobsoleted(repo, repo.currenttransaction()): + _report_commit(ui, repo, repo.unfiltered()[rev])