Uh oh!
There was an error while loading. Please reload this page.
fix(git): tolerate noisy git rev-parse output in is_git_project - #1980
fix(git): tolerate noisy git rev-parse output in is_git_project#1980bearomorphism wants to merge 2 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@## master #1980 +/- ##
=======================================
Coverage 98.23% 98.24% =======================================
Files 61 61 Lines 2779 2790 +11 =======================================
+ Hits 2730 2741 +11
Misses 49 49 ☔ View full report in Codecov by Sentry. |
The previous `is_git_project` implementation compared `git rev-parse --is-inside-work-tree` output to the literal string `"true"` after stripping. Shell wrappers that prepend ANSI colour codes or extra whitespace (reported on git-bash + `uv tool install` on Windows, commitizen-tools#1497) flipped the check to `False` even when git itself considered the directory a valid work tree. Trust the exit code as the primary signal: if `git rev-parse` exits non-zero, we're not in a git context. Then use a loose `endswith("true")` match to distinguish a work tree from a bare-repo interior. Add `logger.debug` lines so users running with `--debug` can see exactly what git returned when the check fails. Closescommitizen-tools#1497 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ac261a7 to
ee91310Comparebearomorphism
commented
May 9, 2026
Updated per @bearomorphism's review (thanks for catching the loose match!): Replaced cleaned=_ANSI_ESCAPE.sub("", c.out).strip().lower()
inside_work_tree=cleaned=="true"Now correctly rejects |
There was a problem hiding this comment.
Pull request overview
This PR fixes false NotAGitProjectError reports on Windows/Git Bash by making is_git_project() tolerant of ANSI escape sequences/noisy git rev-parse stdout while preserving strict "true" matching semantics.
Changes:
- Strip ANSI CSI escape sequences from
git rev-parse --is-inside-work-treeoutput and gate on exit code first. - Add
DEBUGlogging to help diagnose unexpectedgit rev-parseoutputs/exit codes undercz --debug. - Add regression tests covering inside/outside repo, ANSI-prefixed output, bare/non-worktree output, and substring-
truerejection.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| commitizen/git.py | Make is_git_project() robust to ANSI/noisy stdout and add debug diagnostics. |
| tests/test_git.py | Add targeted regression tests for is_git_project() behaviors and edge cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
* the 'not inside work tree' case covers both .git/ subdirs of normal repos AND the root of bare repos (which have no .git directory at all) * the substring-rejection test verifies strict full-string equality after ANSI/whitespace strip, not whole-word matching Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Closes#1497.
Why
Users running commitizen via
uv tool install commitizeninside Git Bash on Windows 11 encounterNotAGitProjectErroreven though runninggit rev-parse --is-inside-work-treemanually in the same shell returnstrue. Commands that require a git context —cz commit,cz bump,cz changelog— all raise the error, while read-only commands such ascz infoandcz lssucceed because they never callis_git_project().The root cause is the single-line check at
commitizen/git.py:316(master):Some shell wrappers — including Git Bash's colour-prompt infrastructure and certain terminal emulators on Windows — inject ANSI CSI escape sequences (e.g.,
\x1b[0m) into subprocess standard output. When that happens,c.out.strip()yields"\x1b[0mtrue"rather than the bare string"true", the strict equality check fails silently, andis_git_project()returnsFalse, triggering the error even inside a valid work tree.This was surfaced during the #1964 audit: "Depending on subprocess setup with
uv tool installon git-bash for Windows, [the output] may not match exactly." Because the original reporter's exact environment couldn't be reproduced locally, the new--debuglogging path provides the next-best diagnostic: any future reporter can runcz --debug commitand see the exact bytes returned bygit rev-parse.What changed
commitizen/git.py_ANSI_ESCAPEcompiled regex; rewriteis_git_project()to trust the exit code first, strip ANSI codes, then exact-match"true"; addlogger.debugfor both failure pathstests/test_git.pytruerejection (parametrised)How it works
git rev-parsereturns non-zero, we are definitively outside any git context —is_git_project()returnsFalseimmediately, independent of output parsing. This handles the genuine "not a repo" case cleanly and makes the diagnostic log message (rc=128) unambiguous._ANSI_ESCAPE = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")(compiled once at import time) covers all CSI sequences — SGR colour codes, cursor commands, and resets. It is applied via.sub("", c.out)before.strip().lower().== "true". An earlier draft used.lower().endswith("true"), which also accepts"untrue"or"nottrue"— stringsgit rev-parsewould never emit, but which a defensive matcher should still reject. The ANSI-strip step is the only looseness introduced; the final comparison remains a full-string equality check.logger.debugdiagnostics: both failure paths (non-zero exit and unexpected textual output) emit aDEBUG-level message that includesrc,out, anderr. Runningcz --debug commitwill now surface the exact bytes returned bygit rev-parse, eliminating guesswork for future environment-specific reports.Backward compatibility
is_git_project()is an internal helper with no public API surface; all callers are withincommitizen/commands/."true\n"), the new code is semantically identical to the original one-liner atcommitizen/git.py:316.is_staging_clean()and every other helper incommitizen/git.pyare untouched.is_git_project/is_staging_cleantest intests/test_git.pycontinue to pass.Checklist
Was generative AI tooling used to co-author this PR?
Generated-by: Claude following the guidelines
Code Changes
uv run poe alllocally to ensure this change passes linter check and testsDocumentation Changes
is_git_project()is an internal helper with no user-facing documentation page. A full docstring explaining the exit-code-first logic and ANSI-strip rationale has been added to the function itself.Expected Behavior
"true\n")is_git_project()returnsTrue— unchangedgit rev-parseexits 128)is_git_project()returnsFalse;--debugshowsrc=128and the fatal error text"\x1b[0mtrue\r\n")is_git_project()returnsTrue— regression fix for #1497.gitof a bare repo (git rev-parseexits 0, output"false\n")is_git_project()returnsFalse"true"as a substring ("untrue","nottrue")is_git_project()returnsFalseSteps to Test This Pull Request
Additional Context
This fix was surfaced during the #1964 issue audit. The triage comment on #1497 identified
commitizen/git.py:314–316(master) as the likely culprit — the strict== "true"equality check fails when a shell wrapper injects ANSI colour codes into subprocess output, a documented behaviour of Git Bash on Windows when colour is enabled globally. Because the exact failure environment couldn't be reproduced locally, the newlogger.debugpath at both non-zero-exit and unexpected-output branches is the primary diagnostic improvement: it closes the feedback loop for any future reporter without requiring a code change.