From 6e6961544e370fcdd27fcd9a60476658158df86e Mon Sep 17 00:00:00 2001 From: Buddy Date: Tue, 14 Jul 2026 12:59:25 -0700 Subject: [PATCH] fix: prepare commit message hook no longer fires on merge/squash/etc --- .../hooks/executable_prepare-commit-msg.tmpl | 78 ++++++++----------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/dot_config/git/template/hooks/executable_prepare-commit-msg.tmpl b/dot_config/git/template/hooks/executable_prepare-commit-msg.tmpl index e225f53..cf8deec 100644 --- a/dot_config/git/template/hooks/executable_prepare-commit-msg.tmpl +++ b/dot_config/git/template/hooks/executable_prepare-commit-msg.tmpl @@ -4,52 +4,38 @@ /*{{ if lookPath "node" -}}*/ #!/usr/bin/env node -const fs = require("fs"); -const childProcess = require("child_process"); -const { promisify } = require("util"); - -const exec = promisify(childProcess.exec); -const readFile = promisify(fs.readFile); -const writeFile = promisify(fs.writeFile); - -const [, , message, mode] = process.argv; - -async function main() { - const file = await readFile(message, { encoding: "utf8" }); - let result; - try { - result = await exec("git rev-parse --abbrev-ref HEAD"); - } catch (err) { - return - } - - if (message.match(/^#/)) { - return - } - - const tags = result.stdout - .split("\n") - .map(branch => branch.match(/[A-Z]+-\d+/g)) - .filter(x => !!x) - .reduce((allIDs, matchedIDs) => [...allIDs, ...matchedIDs], []) - .map(id => `[${id}]`); - - if (tags.length === 0) { - return; - } - if (tags.find(tag => file.includes(tag))) { - return; - } - - const newFile = mode === 'message' - ? file + '\n\n' + tags.join(" ") - : `\n\n# ` + tags.join(" ") + "\n" + file; - - await writeFile(message, newFile); +// Add a "# [TICKET-ID]" comment hint to the editor when running a plain +// `git commit` on a branch containing a ticket ID (e.g. buddy/BE-1234/foo, +// fix/BE-1234/bar). The hint is a comment, so git strips it unless you +// uncomment or retype it. +// +// Every other way a commit message gets made is left alone: -m/-F +// ("message"), merge, squash, amend/-c ("commit"), and commit templates +// ("template") all arrive with a mode argument, and we exit immediately. + +import * as fs from "node:fs"; +import { execSync } from "node:child_process"; + +const [, , messagePath, mode] = process.argv; + +if (mode !== undefined) process.exit(0); + +let branch; +try { + branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }).trim(); +} catch { + process.exit(0); // detached HEAD, rebase, etc. } -main().catch(err => { - console.error(err); - process.exit(1); -}); +const ids = branch.match(/[A-Z]+-\d+/g); +if (!ids) process.exit(0); + +const tags = [...new Set(ids)].map(id => `[${id}]`); +const file = fs.readFileSync(messagePath, "utf8"); +if (tags.some(tag => file.includes(tag))) process.exit(0); + +fs.writeFileSync(messagePath, `\n\n# ${tags.join(" ")}\n${file}`); /*{{- end }}*/