From 71cf7045e8d3d3a2eda0894784a61044ca3166ed Mon Sep 17 00:00:00 2001 From: L4XB Date: Fri, 23 Oct 2026 03:59:10 +0200 Subject: [PATCH] fix(hooks): decode the escapes that Bash removes in a double-quoted commit message In double quotes, Bash drops the backslash before $, `, ", \ and a newline, so git receives fix: say "hello" for -m "fix: say \"hello\"". The check kept the backslashes, so an escaped backslash before a newline counted twice. These escapes are now decoded, and a backslash before any other character stays. The test that pinned the escaped quotes now expects the message git records. --- scripts/hooks/pre-bash-commit-quality.js | 5 +++-- tests/hooks/pre-bash-commit-quality.test.js | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/hooks/pre-bash-commit-quality.js b/scripts/hooks/pre-bash-commit-quality.js index 90c281609..ac06203d1 100644 --- a/scripts/hooks/pre-bash-commit-quality.js +++ b/scripts/hooks/pre-bash-commit-quality.js @@ -204,8 +204,9 @@ function validateCommitMessage(command) { const heredoc = messageMatch[1]?.startsWith('$(') ? heredocBody(command.slice(messageMatch.index + messageMatch[0].indexOf('"') + 1)) : null; - // In double quotes, Bash drops a backslash-newline, a line continuation - const doubleQuoted = messageMatch[1]?.replace(/\\(\r?\n|[\s\S])/g, (escape, next) => (next.endsWith('\n') ? '' : escape)); + // In double quotes, Bash drops the backslash before $ ` " \ and drops a backslash-newline, + // a line continuation, altogether. Before any other character the backslash stays. + const doubleQuoted = messageMatch[1]?.replace(/\\(\r?\n|[$`"\\])/g, (escape, next) => (next.endsWith('\n') ? '' : next)); const message = heredoc ?? doubleQuoted ?? messageMatch[2] ?? messageMatch[3]; // The format rules apply to the subject: git drops leading blank lines and trailing // whitespace, and the first line left is the subject. diff --git a/tests/hooks/pre-bash-commit-quality.test.js b/tests/hooks/pre-bash-commit-quality.test.js index 695c19a0c..c5653e57d 100644 --- a/tests/hooks/pre-bash-commit-quality.test.js +++ b/tests/hooks/pre-bash-commit-quality.test.js @@ -495,7 +495,7 @@ if (test('captures full single-quoted -m message containing a double quote', () if (test('captures full double-quoted -m message with escaped inner quotes (not truncated)', () => { const res = hook.validateCommitMessage('git commit -m "fix: say \\"hello\\" to the user"'); assert.ok(res, 'expected a validation result'); - assert.strictEqual(res.message, 'fix: say \\"hello\\" to the user'); + assert.strictEqual(res.message, 'fix: say "hello" to the user'); })) passed++; else failed++; if (test('measures length of the full message past an apostrophe (not the truncated prefix)', () => { @@ -529,8 +529,10 @@ if (test('drops a line continuation from a double-quoted message, as Bash does', const res = hook.validateCommitMessage('git commit -m "\\\nfix: handle empty input"'); assert.strictEqual(res.message, 'fix: handle empty input'); assert.deepStrictEqual(res.issues, []); - // An escaped backslash before a newline is a backslash, and the newline stays - assert.strictEqual(hook.validateCommitMessage('git commit -m "fix: path C:\\\\\nbody"').message, 'fix: path C:\\\\\nbody'); + // An escaped backslash before a newline is one backslash, and the newline stays + assert.strictEqual(hook.validateCommitMessage('git commit -m "fix: path C:\\\\\nbody"').message, 'fix: path C:\\\nbody'); + // Bash removes the backslash only before $ ` " \ and a newline + assert.strictEqual(hook.validateCommitMessage('git commit -m "fix: cost \\$5 in \\d+ steps"').message, 'fix: cost $5 in \\d+ steps'); })) passed++; else failed++; if (test('reads a here-document whose body has a backslash at the end of a line', () => {