##// END OF EJS Templates
hooklib: update documentation of changeset_obsoletedfor for changed hook type...
Aay Jay Chan -
r45951:8b700e9b default
parent child Browse files
Show More
@@ -1,140 +1,140 b''
1 # Copyright 2020 Joerg Sonnenberger <joerg@bec.de>
1 # Copyright 2020 Joerg Sonnenberger <joerg@bec.de>
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5 """changeset_obsoleted is a hook to send a mail when an
5 """changeset_obsoleted is a hook to send a mail when an
6 existing draft changeset is obsoleted by an obsmarker without successor.
6 existing draft changeset is obsoleted by an obsmarker without successor.
7
7
8 Correct message threading requires the same messageidseed to be used for both
8 Correct message threading requires the same messageidseed to be used for both
9 the original notification and the new mail.
9 the original notification and the new mail.
10
10
11 Usage:
11 Usage:
12 [notify]
12 [notify]
13 messageidseed = myseed
13 messageidseed = myseed
14
14
15 [hooks]
15 [hooks]
16 pretxnclose.changeset_obsoleted = \
16 txnclose.changeset_obsoleted = \
17 python:hgext.hooklib.changeset_obsoleted.hook
17 python:hgext.hooklib.changeset_obsoleted.hook
18 """
18 """
19
19
20 from __future__ import absolute_import
20 from __future__ import absolute_import
21
21
22 import email.errors as emailerrors
22 import email.errors as emailerrors
23 import email.utils as emailutils
23 import email.utils as emailutils
24
24
25 from mercurial.i18n import _
25 from mercurial.i18n import _
26 from mercurial import (
26 from mercurial import (
27 encoding,
27 encoding,
28 error,
28 error,
29 formatter,
29 formatter,
30 logcmdutil,
30 logcmdutil,
31 mail,
31 mail,
32 obsutil,
32 obsutil,
33 pycompat,
33 pycompat,
34 registrar,
34 registrar,
35 )
35 )
36 from mercurial.utils import dateutil
36 from mercurial.utils import dateutil
37 from .. import notify
37 from .. import notify
38
38
39 configtable = {}
39 configtable = {}
40 configitem = registrar.configitem(configtable)
40 configitem = registrar.configitem(configtable)
41
41
42 configitem(
42 configitem(
43 b'notify_obsoleted', b'domain', default=None,
43 b'notify_obsoleted', b'domain', default=None,
44 )
44 )
45 configitem(
45 configitem(
46 b'notify_obsoleted', b'messageidseed', default=None,
46 b'notify_obsoleted', b'messageidseed', default=None,
47 )
47 )
48 configitem(
48 configitem(
49 b'notify_obsoleted',
49 b'notify_obsoleted',
50 b'template',
50 b'template',
51 default=b'''Subject: changeset abandoned
51 default=b'''Subject: changeset abandoned
52
52
53 This changeset has been abandoned.
53 This changeset has been abandoned.
54 ''',
54 ''',
55 )
55 )
56
56
57
57
58 def _report_commit(ui, repo, ctx):
58 def _report_commit(ui, repo, ctx):
59 domain = ui.config(b'notify_obsoleted', b'domain') or ui.config(
59 domain = ui.config(b'notify_obsoleted', b'domain') or ui.config(
60 b'notify', b'domain'
60 b'notify', b'domain'
61 )
61 )
62 messageidseed = ui.config(
62 messageidseed = ui.config(
63 b'notify_obsoleted', b'messageidseed'
63 b'notify_obsoleted', b'messageidseed'
64 ) or ui.config(b'notify', b'messageidseed')
64 ) or ui.config(b'notify', b'messageidseed')
65 template = ui.config(b'notify_obsoleted', b'template')
65 template = ui.config(b'notify_obsoleted', b'template')
66 spec = formatter.literal_templatespec(template)
66 spec = formatter.literal_templatespec(template)
67 templater = logcmdutil.changesettemplater(ui, repo, spec)
67 templater = logcmdutil.changesettemplater(ui, repo, spec)
68 ui.pushbuffer()
68 ui.pushbuffer()
69 n = notify.notifier(ui, repo, b'incoming')
69 n = notify.notifier(ui, repo, b'incoming')
70
70
71 subs = set()
71 subs = set()
72 for sub, spec in n.subs:
72 for sub, spec in n.subs:
73 if spec is None:
73 if spec is None:
74 subs.add(sub)
74 subs.add(sub)
75 continue
75 continue
76 revs = repo.revs(b'%r and %d:', spec, ctx.rev())
76 revs = repo.revs(b'%r and %d:', spec, ctx.rev())
77 if len(revs):
77 if len(revs):
78 subs.add(sub)
78 subs.add(sub)
79 continue
79 continue
80 if len(subs) == 0:
80 if len(subs) == 0:
81 ui.debug(
81 ui.debug(
82 b'notify_obsoleted: no subscribers to selected repo and revset\n'
82 b'notify_obsoleted: no subscribers to selected repo and revset\n'
83 )
83 )
84 return
84 return
85
85
86 templater.show(
86 templater.show(
87 ctx,
87 ctx,
88 changes=ctx.changeset(),
88 changes=ctx.changeset(),
89 baseurl=ui.config(b'web', b'baseurl'),
89 baseurl=ui.config(b'web', b'baseurl'),
90 root=repo.root,
90 root=repo.root,
91 webroot=n.root,
91 webroot=n.root,
92 )
92 )
93 data = ui.popbuffer()
93 data = ui.popbuffer()
94
94
95 try:
95 try:
96 msg = mail.parsebytes(data)
96 msg = mail.parsebytes(data)
97 except emailerrors.MessageParseError as inst:
97 except emailerrors.MessageParseError as inst:
98 raise error.Abort(inst)
98 raise error.Abort(inst)
99
99
100 msg['In-reply-to'] = notify.messageid(ctx, domain, messageidseed)
100 msg['In-reply-to'] = notify.messageid(ctx, domain, messageidseed)
101 msg['Message-Id'] = notify.messageid(
101 msg['Message-Id'] = notify.messageid(
102 ctx, domain, messageidseed + b'-obsoleted'
102 ctx, domain, messageidseed + b'-obsoleted'
103 )
103 )
104 msg['Date'] = encoding.strfromlocal(
104 msg['Date'] = encoding.strfromlocal(
105 dateutil.datestr(format=b"%a, %d %b %Y %H:%M:%S %1%2")
105 dateutil.datestr(format=b"%a, %d %b %Y %H:%M:%S %1%2")
106 )
106 )
107 if not msg['From']:
107 if not msg['From']:
108 sender = ui.config(b'email', b'from') or ui.username()
108 sender = ui.config(b'email', b'from') or ui.username()
109 if b'@' not in sender or b'@localhost' in sender:
109 if b'@' not in sender or b'@localhost' in sender:
110 sender = n.fixmail(sender)
110 sender = n.fixmail(sender)
111 msg['From'] = mail.addressencode(ui, sender, n.charsets, n.test)
111 msg['From'] = mail.addressencode(ui, sender, n.charsets, n.test)
112 msg['To'] = ', '.join(sorted(subs))
112 msg['To'] = ', '.join(sorted(subs))
113
113
114 msgtext = msg.as_bytes() if pycompat.ispy3 else msg.as_string()
114 msgtext = msg.as_bytes() if pycompat.ispy3 else msg.as_string()
115 if ui.configbool(b'notify', b'test'):
115 if ui.configbool(b'notify', b'test'):
116 ui.write(msgtext)
116 ui.write(msgtext)
117 if not msgtext.endswith(b'\n'):
117 if not msgtext.endswith(b'\n'):
118 ui.write(b'\n')
118 ui.write(b'\n')
119 else:
119 else:
120 ui.status(_(b'notify_obsoleted: sending mail for %d\n') % ctx.rev())
120 ui.status(_(b'notify_obsoleted: sending mail for %d\n') % ctx.rev())
121 mail.sendmail(
121 mail.sendmail(
122 ui, emailutils.parseaddr(msg['From'])[1], subs, msgtext, mbox=n.mbox
122 ui, emailutils.parseaddr(msg['From'])[1], subs, msgtext, mbox=n.mbox
123 )
123 )
124
124
125
125
126 def has_successor(repo, rev):
126 def has_successor(repo, rev):
127 return any(
127 return any(
128 r for r in obsutil.allsuccessors(repo.obsstore, [rev]) if r != rev
128 r for r in obsutil.allsuccessors(repo.obsstore, [rev]) if r != rev
129 )
129 )
130
130
131
131
132 def hook(ui, repo, hooktype, node=None, **kwargs):
132 def hook(ui, repo, hooktype, node=None, **kwargs):
133 if hooktype != b"txnclose":
133 if hooktype != b"txnclose":
134 raise error.Abort(
134 raise error.Abort(
135 _(b'Unsupported hook type %r') % pycompat.bytestr(hooktype)
135 _(b'Unsupported hook type %r') % pycompat.bytestr(hooktype)
136 )
136 )
137 for rev in obsutil.getobsoleted(repo, changes=kwargs['changes']):
137 for rev in obsutil.getobsoleted(repo, changes=kwargs['changes']):
138 ctx = repo.unfiltered()[rev]
138 ctx = repo.unfiltered()[rev]
139 if not has_successor(repo, ctx.node()):
139 if not has_successor(repo, ctx.node()):
140 _report_commit(ui, repo, ctx)
140 _report_commit(ui, repo, ctx)
General Comments 0
You need to be logged in to leave comments. Login now