thunderbird-patch-review

Easy-to-use patch review interface for Thunderbird

Contribute: ~marcc/thunderbird-review-plugin@lists.sr.ht

git clone git://mccd.space/thunderbird-patch-review

commit b8ab32ae75bafc8224bcd1317f468a16d1103467
parent ac9de41f8a07dfe3a659abfc604305ba4c9f3281
Author: Pi Agent <agent@pi.local>
Date:   Sat, 18 Jul 2026 13:06:49 +0200

diff-parse: strip git format-patch mbox header block from commit message

git format-patch prepends an mbox-style header to the body (the
'From <sha> Mon Sep 17 00:00:00 2001' From_ line followed by From:,
Date:, Subject:, then a blank). The review UI now renders author and
subject in its own header, so keeping that block in the parsed
commitMessage duplicated the From:/Subject: lines above the real
message text.

Detect the mbox From_ line by its fixed 'Mon Sep 17 00:00:00 2001'
timestamp and drop the header block up to the first blank line, leaving
only the real commit message. formatReview does not use commitMessage,
so stripping is safe.

Diffstat:
Mextension/modules/diff-parse.js | 13++++++++++++-
Mtests/run.mjs | 31+++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/extension/modules/diff-parse.js b/extension/modules/diff-parse.js
@@ -45,7 +45,18 @@ export function parsePatchEmail(text) {
     }
   }
   const messageEnd = scissors === -1 ? firstDiff : scissors;
-  result.commitMessage = bodyLines.slice(0, messageEnd).join("\n").trim();
+  // git format-patch prepends an mbox-style header block to the body
+  // ("From <sha> Mon Sep 17 ...", From:, Date:, Subject:, then a blank
+  // line). The UI shows author and subject separately, so drop that
+  // block to keep only the real commit message text. reply-format does
+  // not use commitMessage, so stripping is safe.
+  let messageStart = 0;
+  if (messageEnd > 0 && /^From .*Mon Sep 17 00:00:00 2001$/.test(bodyLines[0])) {
+    let blank = 1;
+    while (blank < messageEnd && bodyLines[blank] !== "") blank++;
+    messageStart = Math.min(blank + 1, messageEnd);
+  }
+  result.commitMessage = bodyLines.slice(messageStart, messageEnd).join("\n").trim();
   if (scissors !== -1) {
     result.diffstat = bodyLines.slice(scissors + 1, firstDiff).join("\n").trim();
   }
diff --git a/tests/run.mjs b/tests/run.mjs
@@ -142,6 +142,37 @@ function check(name, cond, extra = "") {
   check("parse: scissors-first diffstat", p.diffstat.includes("1 file changed"));
 }
 
+{
+  // git format-patch prepends an mbox-style header block to the body; the
+  // UI renders author/subject separately, so the parser must drop that
+  // block and keep only the real commit message.
+  const body = [
+    "From 0123456789abcdef0123456789abcdef01234567 Mon Sep 17 00:00:00 2001",
+    "From: Author Name <author@example.com>",
+    "Date: Thu, 1 Jan 2026 00:00:00 +0000",
+    "Subject: [PATCH] fix a bug",
+    "",
+    "This is the actual commit message.",
+    "",
+    "---",
+    " file.txt | 1 +",
+    " 1 file changed, 1 insertion(+)",
+    "",
+    "diff --git a/file.txt b/file.txt",
+    "index e69de29..8baef1b 100644",
+    "--- a/file.txt",
+    "+++ b/file.txt",
+    "@@ -0,0 +1 @@",
+    "+hello",
+    "-- ",
+    "2.45.0",
+    "",
+  ].join("\n");
+  const p = parsePatchEmail(body);
+  check("parse: format-patch header stripped", p.commitMessage === "This is the actual commit message.", JSON.stringify(p.commitMessage));
+  check("parse: format-patch diff still parsed", p.files.length === 1);
+}
+
 // --- sourcehut detection ----------------------------------------------------
 
 {