Anonymous avatar Anonymous committed 5f761b0

patchmessage: add the Message-Id field to the commit log

Add a new option that will automatically add a Message-Id: line (that
ressembles a SoB-line) to the commit log of the patch. For example:

Message-Id: <1234.567890@abcdef>

This line can be used by projects that want to automatically send
acknowledge messages to the author when his/her patches finally land
in a repository (using some incoming hook), and want such message to
appear as replies to the original email with the patch (for proper
mail threading).

Signed-off-by: "Yann E. MORIN" <yann.morin.1998@free.fr>;

Comments (0)

Files changed (7)

 re_mailheaderlinebreak = re.compile('\r?\n[ \t]', re.M)
 re_patchsubject = re.compile(r'^.*\[PATCH(\s.+?)?\]\s*(.+)$')
 re_patchnum = re.compile(r'^\s+(\d+)\s+of\s+(\d+)')
+# attempt to detect the start of a patch (this heuristic is borrowed
+# from Hg, which itself borrowed it from quilt)
+re_startdiff = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
+                          r'retrieving revision [0-9]+(\.[0-9]+)*$|'
+                          r'---[ \t].*?^\+\+\+[ \t]|'
+                          r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
 
 def parsesubject(s):
     """Return unbroken subject, title, index and total count from
 
 class patchmessage:
     """A patch extracted from an email message"""
-    def __init__(self, msg):
-        """Initialize from msg, raising an error in case of problems"""
+    def __init__(self, msg, messageid = False):
+        """Initialize from msg, raising an error in case of problems;
+        if messageid is True, store the Message-Id in the commit log
+        """
         s = parsequopri(msg.get('Subject', ''))
         self.subject, self.title, self.index, self.count = parsesubject(s)
 
             patch = getpayload(msg)
             if patch is None:
                 raise parseerror("no patch found in content: %s" % self.subject)
-            self.patch = patch.replace('\r\n', '\n')
+            self.patch = self.finalisepatch(patch, messageid)
 
     def __cmp__(self, other):
         """Compare by Message-ID"""
         """Hash on Message-ID"""
         return hash(self.id)
 
+    def finalisepatch(self, patch, messageid):
+        """Apply some (potentially conditional) transformations on the patch"""
+        patch = patch.replace('\r\n', '\n')
+
+        if messageid:
+            m = re_startdiff.search(patch)
+            if m:
+                # Insert a Message-Id line
+                patch = patch[:m.start(0)-1]            + \
+                        'Message-Id: ' + self.id + '\n' + \
+                        patch[m.start(0)-1:]
+        return patch
+
 class mboxsource(object):
     def __init__(self, name, path):
         self.name = name
             name = s.split('"', 3)[3].rsplit('"', 1)[0]
             ui.write(' ' + name.replace(r'\"', '"') + '\n')
 
-def getpatchmessages(ui, sources):
+def getpatchmessages(ui, sources, messageid = False):
     """Yield all patchbomb messages in the specified mailboxes"""
     for source in sources:
         for m in source.getmessages(ui):
             try:
-                msg = patchmessage(m)
+                msg = patchmessage(m, messageid)
             except parseerror, (errmsg,):
                 ui.debug(errmsg + '\n')
                 continue
     sources = getsources(ui, **opts)
     if not sources:
         raise util.Abort(_('no mailboxes configured'))
-    patchmessages = getpatchmessages(ui, sources)
+    patchmessages = getpatchmessages(ui, sources, opts.get('message_id'))
     matcher = makematcher(patterns)
     selecteds = []
     stop = False
         [('a', 'all', False, _('show all patches, including orphaned ones')),
          ('d', 'date', '', _('show patches matching date spec')),
          ('m', 'mbox', '', _('path to an mbox to parse')),
+         ('i', 'message-id', False, _('add a Message-Id: line to the commit log')),
          ],
         _('hg mimport PATTERN...'))
     cmdtable['debugmbox'] = (
 n
 EOF
 
+echo % add Message-Id
+hg mimport --message-id --mbox $TESTDIR/tests/test4.mbx <<EOF
+y
+y
+EOF
+cat .hg/patches/patch_with_sob_block
+cat .hg/patches/patch_without_sob_block
+
 echo % test all option
 hg mimport -a --mbox $TESTDIR/tests/test2.mbx <<EOF
 n
     [PATCH 2 of 2] patch 2 of 2
 import this group? [Nydq?]  
 0 patches imported
+% add Message-Id
+[PATCH] patch with sob-block
+import this group? [Nydq?]  
+[PATCH] patch without sob-block
+import this group? [Nydq?]  
+adding patch_with_sob_block to series file
+adding patch_without_sob_block to series file
+2 patches imported
+test with SoB-block
+
+Signed-off-by: Bar Baz <barbaz@gmail.com>
+Message-Id: <5ab3c4562bd574dbd782.1230599538@localhost>
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ b/a
+@@ -0,0 +1,1 @@
++a
+
+_______________________________________________
+Mercurial-devel mailing list
+Mercurial-devel@selenic.com
+http://selenic.com/mailman/listinfo/mercurial-devel
+test no SoB-block
+Message-Id: <5ab3c4562bd574dbd782.1230599538@localhost>
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ b/a
+@@ -0,0 +1,1 @@
++a
+
+_______________________________________________
+Mercurial-devel mailing list
+Mercurial-devel@selenic.com
+http://selenic.com/mailman/listinfo/mercurial-devel
 % test all option
 [PATCH] standalone
 import this group? [Nydq?]  

tests/makembox4.py

+#!/usr/bin/env python
+#
+# Run this script to generate the mbox test sample
+#
+import email, mailbox, os
+
+# Fill the test mailbox
+if os.path.isfile('test4.mbx'):
+    os.remove('test4.mbx')
+mbox = mailbox.mbox('test4.mbx')
+
+# Add a patch with a SoB-block, and one without
+mbox.add(email.message_from_file(file('msg4.0')))
+mbox.add(email.message_from_file(file('msg4.1')))
+
+mbox.close()
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:13:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH] patch with sob-block
+Message-Id: <5ab3c4562bd574dbd782.1230599538@localhost>
+Date: Tue, 16 Dec 2008 02:12:18 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+test with SoB-block
+
+Signed-off-by: Bar Baz <barbaz@gmail.com>
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ b/a
+@@ -0,0 +1,1 @@
++a
+
+_______________________________________________
+Mercurial-devel mailing list
+Mercurial-devel@selenic.com
+http://selenic.com/mailman/listinfo/mercurial-devel
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:13:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH] patch without sob-block
+Message-Id: <5ab3c4562bd574dbd782.1230599538@localhost>
+Date: Tue, 16 Dec 2008 02:12:18 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+test no SoB-block
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ b/a
+@@ -0,0 +1,1 @@
++a
+
+_______________________________________________
+Mercurial-devel mailing list
+Mercurial-devel@selenic.com
+http://selenic.com/mailman/listinfo/mercurial-devel
+From MAILER-DAEMON Wed Jun 20 21:04:25 2012
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:13:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH] patch with sob-block
+Message-Id: <5ab3c4562bd574dbd782.1230599538@localhost>
+Date: Tue, 16 Dec 2008 02:12:18 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+test with SoB-block
+
+Signed-off-by: Bar Baz <barbaz@gmail.com>
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ b/a
+@@ -0,0 +1,1 @@
++a
+
+_______________________________________________
+Mercurial-devel mailing list
+Mercurial-devel@selenic.com
+http://selenic.com/mailman/listinfo/mercurial-devel
+
+From MAILER-DAEMON Wed Jun 20 21:04:25 2012
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:13:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH] patch without sob-block
+Message-Id: <5ab3c4562bd574dbd782.1230599538@localhost>
+Date: Tue, 16 Dec 2008 02:12:18 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+test no SoB-block
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ b/a
+@@ -0,0 +1,1 @@
++a
+
+_______________________________________________
+Mercurial-devel mailing list
+Mercurial-devel@selenic.com
+http://selenic.com/mailman/listinfo/mercurial-devel
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.