From 880fbd963a616148df6aa169c82563763b958e60 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Mon, 13 Jul 2026 13:04:16 -0400 Subject: [PATCH] fix(#2206): strip trailing slashes in isGitIgnored to avoid CRLF check-ignore quirk (#2235) * fix(#2206): strip trailing slashes in isGitIgnored to avoid CRLF check-ignore quirk isGitIgnored was called with a trailing slash (`.planning/`) in config-loader. git check-ignore has a longstanding quirk: a CRLF .gitignore with blank lines falsely reports any path WITH a trailing slash as ignored. This silently set commit_docs=false on Windows repos (where CRLF .gitignore is the norm), skipping all planning-doc commits. Normalize trailing slashes inside isGitIgnored so every call site is protected. Closes #2206 * docs(#2206): add changeset fragment * docs(#2206): backfill PR number --- .changeset/tidy-voles-glide.md | 5 +++++ src/config-loader.cts | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .changeset/tidy-voles-glide.md diff --git a/.changeset/tidy-voles-glide.md b/.changeset/tidy-voles-glide.md new file mode 100644 index 000000000..92bd662ce --- /dev/null +++ b/.changeset/tidy-voles-glide.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 2235 +--- +**`commit_docs` no longer silently disables on CRLF `.gitignore` repos** — git check-ignore falsely reports a trailing-slash path (e.g. `.planning/`) as ignored when the .gitignore has CRLF line endings with blank lines. isGitIgnored now strips trailing slashes before querying, so the false positive cannot occur. (#2206) diff --git a/src/config-loader.cts b/src/config-loader.cts index 2384f60a2..c5216dfb5 100644 --- a/src/config-loader.cts +++ b/src/config-loader.cts @@ -175,10 +175,14 @@ interface ParsedConfig { const _gitIgnoredCache = new Map(); function isGitIgnored(cwd: string, targetPath: string): boolean { - const key = cwd + '::' + targetPath; + // #2206: strip trailing slashes — `git check-ignore` has a quirk where a + // CRLF .gitignore with blank lines falsely reports a trailing-slash path + // (e.g. `.planning/`) as ignored. Normalizing here protects every call site. + const normalized = targetPath.replace(/\/+$/, ''); + const key = cwd + '::' + normalized; if (_gitIgnoredCache.has(key)) return _gitIgnoredCache.get(key)!; // --no-index checks .gitignore rules regardless of whether the file is tracked. - const result = execGit(['check-ignore', '-q', '--no-index', '--', targetPath], { cwd }); + const result = execGit(['check-ignore', '-q', '--no-index', '--', normalized], { cwd }); const ignored = result.exitCode === 0; _gitIgnoredCache.set(key, ignored); return ignored;