Mercurial > hg
comparison hgext/notify.py @ 42904:d26a6706b070
notify: add option for deterministic message-id generation
Copied from email by durin42:
> Pierre-Yves asked offline why I asked a new option for this and why it
> is not the default. Message-Id is supposed to be unique world-wide and
> while it is desirable to have a deterministic mechanism for them for
> creating follow-up emails, it needs organisational control for ensuring
> the uniqueness.
Differential Revision: https://phab.mercurial-scm.org/D6824
author | Joerg Sonnenberger <joerg@bec.de> |
---|---|
date | Sat, 07 Sep 2019 12:49:33 +0200 |
parents | 43f9b8c0574b |
children | 2372284d9457 |
comparison
equal
deleted
inserted
replaced
42903:66048f6b5d0d | 42904:d26a6706b070 |
---|---|
80 turn them into relative paths. For example, ``notify.strip=3`` will change | 80 turn them into relative paths. For example, ``notify.strip=3`` will change |
81 ``/long/path/repository`` into ``repository``. Default: 0. | 81 ``/long/path/repository`` into ``repository``. Default: 0. |
82 | 82 |
83 notify.domain | 83 notify.domain |
84 Default email domain for sender or recipients with no explicit domain. | 84 Default email domain for sender or recipients with no explicit domain. |
85 It is also used for the domain part of the ``Message-Id`` when using | |
86 ``notify.messageidseed``. | |
87 | |
88 notify.messageidseed | |
89 Create deterministic ``Message-Id`` headers for the mails based on the seed | |
90 and the revision identifier of the first commit in the changeset. | |
85 | 91 |
86 notify.style | 92 notify.style |
87 Style file to use when formatting emails. | 93 Style file to use when formatting emails. |
88 | 94 |
89 notify.template | 95 notify.template |
142 from __future__ import absolute_import | 148 from __future__ import absolute_import |
143 | 149 |
144 import email.errors as emailerrors | 150 import email.errors as emailerrors |
145 import email.parser as emailparser | 151 import email.parser as emailparser |
146 import fnmatch | 152 import fnmatch |
153 import hashlib | |
147 import socket | 154 import socket |
148 import time | 155 import time |
149 | 156 |
150 from mercurial.i18n import _ | 157 from mercurial.i18n import _ |
151 from mercurial import ( | 158 from mercurial import ( |
179 ) | 186 ) |
180 configitem('notify', 'diffstat', | 187 configitem('notify', 'diffstat', |
181 default=True, | 188 default=True, |
182 ) | 189 ) |
183 configitem('notify', 'domain', | 190 configitem('notify', 'domain', |
191 default=None, | |
192 ) | |
193 configitem('notify', 'messageidseed', | |
184 default=None, | 194 default=None, |
185 ) | 195 ) |
186 configitem('notify', 'fromauthor', | 196 configitem('notify', 'fromauthor', |
187 default=None, | 197 default=None, |
188 ) | 198 ) |
266 self.test = self.ui.configbool('notify', 'test') | 276 self.test = self.ui.configbool('notify', 'test') |
267 self.charsets = mail._charsets(self.ui) | 277 self.charsets = mail._charsets(self.ui) |
268 self.subs = self.subscribers() | 278 self.subs = self.subscribers() |
269 self.merge = self.ui.configbool('notify', 'merge') | 279 self.merge = self.ui.configbool('notify', 'merge') |
270 self.showfunc = self.ui.configbool('notify', 'showfunc') | 280 self.showfunc = self.ui.configbool('notify', 'showfunc') |
281 self.messageidseed = self.ui.config('notify', 'messageidseed') | |
271 if self.showfunc is None: | 282 if self.showfunc is None: |
272 self.showfunc = self.ui.configbool('diff', 'showfunc') | 283 self.showfunc = self.ui.configbool('diff', 'showfunc') |
273 | 284 |
274 mapfile = None | 285 mapfile = None |
275 template = (self.ui.config('notify', hooktype) or | 286 template = (self.ui.config('notify', hooktype) or |
410 msg[r'From'] = encoding.strfromlocal( | 421 msg[r'From'] = encoding.strfromlocal( |
411 mail.addressencode(self.ui, sender, self.charsets, self.test)) | 422 mail.addressencode(self.ui, sender, self.charsets, self.test)) |
412 | 423 |
413 msg[r'X-Hg-Notification'] = r'changeset %s' % ctx | 424 msg[r'X-Hg-Notification'] = r'changeset %s' % ctx |
414 if not msg[r'Message-Id']: | 425 if not msg[r'Message-Id']: |
415 msg[r'Message-Id'] = encoding.strfromlocal( | 426 msg[r'Message-Id'] = messageid(ctx, self.domain, self.messageidseed) |
416 '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()), | |
417 hash(self.repo.root), | |
418 encoding.strtolocal(socket.getfqdn()))) | |
419 msg[r'To'] = encoding.strfromlocal(', '.join(sorted(subs))) | 427 msg[r'To'] = encoding.strfromlocal(', '.join(sorted(subs))) |
420 | 428 |
421 msgtext = encoding.strtolocal(msg.as_string()) | 429 msgtext = encoding.strtolocal(msg.as_string()) |
422 if self.test: | 430 if self.test: |
423 self.ui.write(msgtext) | 431 self.ui.write(msgtext) |
515 if author and fromauthor: | 523 if author and fromauthor: |
516 data = '\n'.join(['From: %s' % author, data]) | 524 data = '\n'.join(['From: %s' % author, data]) |
517 | 525 |
518 if count: | 526 if count: |
519 n.send(ctx, count, data) | 527 n.send(ctx, count, data) |
528 | |
529 def messageid(ctx, domain, messageidseed): | |
530 if domain and messageidseed: | |
531 host = domain | |
532 else: | |
533 host = encoding.strtolocal(socket.getfqdn()) | |
534 if messageidseed: | |
535 messagehash = hashlib.sha512(ctx.hex() + messageidseed) | |
536 messageid = '<hg.%s@%s>' % (messagehash.hexdigest()[:64], host) | |
537 else: | |
538 messageid = '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()), | |
539 hash(ctx.repo().root), host) | |
540 return encoding.strfromlocal(messageid) |