thunderbird-patch-review

Simple email patch review tool for Thunderbird

git clone git://mccd.space/thunderbird-patch-review
commit 311a4a6314711fdd8d6f64210ad8c013ebe23e30
parent 0d97836750f6d9f92ecf9f97a266cfe943dd226d
Author: Marc <marc@coquand.email>
Date:   Sun, 19 Jul 2026 11:41:57 +0200

bugfix with patch apply

Diffstat:
Mextension/api/patchHost/implementation.js | 12++++++++++--
Mtests/run.mjs | 7+++++++
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/extension/api/patchHost/implementation.js b/extension/api/patchHost/implementation.js
@@ -144,7 +144,11 @@ async function apply(repo, strategy, patches) {
 // maps the target to the new name and the old name is recorded deleted.
 function collectFinalBlobs(text) {
   const finals = new Map();
-  const lines = text.split("\n");
+  // Mail transport commonly delivers CRLF (Thunderbird's getRaw does); a
+  // trailing \r would defeat the $-anchored path regexes below, since in
+  // JS `.` matches neither \r nor \n. A path genuinely ending in \r is
+  // git-quoted and hits the quoted-path bail, so stripping is safe.
+  const lines = text.split("\n").map((l) => (l.endsWith("\r") ? l.slice(0, -1) : l));
   let block = null;
   let unsupported = false;
   const flush = (b) => {
@@ -280,7 +284,11 @@ async function check(repo, strategy, patches) {
     PathUtils.tempDir,
     `patch-review.check.${Date.now()}.${Math.floor(Math.random() * 1e9)}.patch`
   );
-  await IOUtils.writeUTF8(probe, patches.join("\n"));
+  // Unlike git am (whose mailinfo strips mail-transport CRLF), git apply
+  // takes the probe bytes literally, and a \r on every context line
+  // matches nothing. Fold transport CRLF to LF; a genuine content \r in
+  // a CRLF-file patch arrives as \r\r\n and keeps its single \r.
+  await IOUtils.writeUTF8(probe, patches.join("\n").replace(/\r\n/g, "\n"));
   let fwd;
   try {
     fwd = await run(git, ["-C", repo, "-c", "advice.mergeConflict=false", "-c", "advice.amWorkDir=false", "apply", "--check", probe]);
diff --git a/tests/run.mjs b/tests/run.mjs
@@ -461,6 +461,13 @@ function check(name, cond, extra = "") {
       "",
     ].join("\n");
     check("check: quoted path bails to null", collect(quoted) === null);
+
+    // Thunderbird's getRaw delivers CRLF line endings; the parser must
+    // read them identically to LF instead of bailing to null.
+    check(
+      "check: collectFinalBlobs tolerates CRLF line endings",
+      eq(collect(two.replace(/\n/g, "\r\n")), new Map([["f", { blob: "ccccccc" }]]))
+    );
   }
 }