diff hgext/notify.py @ 42910: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
line wrap: on
line diff
--- a/hgext/notify.py	Sat Sep 07 23:20:11 2019 -0400
+++ b/hgext/notify.py	Sat Sep 07 12:49:33 2019 +0200
@@ -82,6 +82,12 @@
 
 notify.domain
   Default email domain for sender or recipients with no explicit domain.
+  It is also used for the domain part of the ``Message-Id`` when using
+  ``notify.messageidseed``.
+
+notify.messageidseed
+  Create deterministic ``Message-Id`` headers for the mails based on the seed
+  and the revision identifier of the first commit in the changeset.
 
 notify.style
   Style file to use when formatting emails.
@@ -144,6 +150,7 @@
 import email.errors as emailerrors
 import email.parser as emailparser
 import fnmatch
+import hashlib
 import socket
 import time
 
@@ -183,6 +190,9 @@
 configitem('notify', 'domain',
     default=None,
 )
+configitem('notify', 'messageidseed',
+    default=None,
+)
 configitem('notify', 'fromauthor',
     default=None,
 )
@@ -268,6 +278,7 @@
         self.subs = self.subscribers()
         self.merge = self.ui.configbool('notify', 'merge')
         self.showfunc = self.ui.configbool('notify', 'showfunc')
+        self.messageidseed = self.ui.config('notify', 'messageidseed')
         if self.showfunc is None:
             self.showfunc = self.ui.configbool('diff', 'showfunc')
 
@@ -412,10 +423,7 @@
 
         msg[r'X-Hg-Notification'] = r'changeset %s' % ctx
         if not msg[r'Message-Id']:
-            msg[r'Message-Id'] = encoding.strfromlocal(
-                '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()),
-                                      hash(self.repo.root),
-                                      encoding.strtolocal(socket.getfqdn())))
+            msg[r'Message-Id'] = messageid(ctx, self.domain, self.messageidseed)
         msg[r'To'] = encoding.strfromlocal(', '.join(sorted(subs)))
 
         msgtext = encoding.strtolocal(msg.as_string())
@@ -517,3 +525,16 @@
 
     if count:
         n.send(ctx, count, data)
+
+def messageid(ctx, domain, messageidseed):
+    if domain and messageidseed:
+        host = domain
+    else:
+        host = encoding.strtolocal(socket.getfqdn())
+    if messageidseed:
+        messagehash = hashlib.sha512(ctx.hex() + messageidseed)
+        messageid = '<hg.%s@%s>' % (messagehash.hexdigest()[:64], host)
+    else:
+        messageid = '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()),
+                                          hash(ctx.repo().root), host)
+    return encoding.strfromlocal(messageid)