fix(git): show non-ASCII paths unescaped in git_status and git_diff* - #4879
tsurutanmen wants to merge 2 commits into
Conversation
git's default core.quotepath=true makes `git status` and `git diff` print non-ASCII paths as quoted octal escapes, e.g. "\346\227\245\346\234\254\350\252\236.txt" for 日本語.txt. Clients cannot map these back to the real file, so pass `-c core.quotepath=false` for git_status, git_diff_unstaged, git_diff_staged and git_diff. git_show already reports decoded paths through GitPython's Diff objects and is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chrikrah
left a comment
There was a problem hiding this comment.
@tsurutanmen the four new cases fail on the base commit and pass on e067aed.
non-blocking: ESCAPED_NON_ASCII_FILENAME at src/git/tests/test_server.py:190 uses Python octal escapes. The constant holds thirteen characters of mojibake that git never emits, so the four not in assertions also pass on f46d957. Git writes a forty-character token. r"\346\227\245\346\234\254\350\252\236.txt" makes those assertions bite.
#4848 replaces the same three return repo.git.diff(...) lines and adds its tests at the same insertion point. git merge-tree --write-tree across the two heads exits 1 with a conflict in both files.
Verification
Exported e067aed with git archive, then a fresh uv sync --frozen --dev. Inside pytest I asserted mcp_server_git.__file__ sits in that export.
$ cd src/git && uv run pytest tests -q
51 passed in 6.43s
# src/mcp_server_git/server.py restored to f46d9578, the four new tests kept
$ uv run pytest tests -q
4 failed, 47 passed in 13.05s
FAILED tests/test_server.py::test_git_status_non_ascii_filename
FAILED tests/test_server.py::test_git_diff_unstaged_non_ascii_filename
FAILED tests/test_server.py::test_git_diff_staged_non_ascii_filename
FAILED tests/test_server.py::test_git_diff_non_ascii_filename
# the same reverted tree, both candidate constants against git_status output
$ uv run python -c 'print(RAW in out, OCT in out)'
True False
$ git merge-tree --write-tree refs/remotes/pr/4879 refs/remotes/pr/4848; echo $?
CONFLICT (content): Merge conflict in src/git/src/mcp_server_git/server.py
CONFLICT (content): Merge conflict in src/git/tests/test_server.py
1
# not run: everything outside this file, and any run on Windows or macOS
src/git carries no CODEOWNERS entry, and @olaservo merged six of the changes touching these two files. @olaservo this one is ready to merge as it stands.
@tsurutanmen do you want the raw-string constant folded in here, or in a pull request of its own?
The constant used Python octal escapes, so it held 13 characters git never emits and the four not-in assertions passed even before the fix. As a raw string it is the 40-character token git writes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Thanks for checking this so carefully. Folded in here as 88150d6: the constant is now a raw string, so it is the 40-character token git prints. Against the pre-fix git status output it is found, where the old one was not, so the four On #4848: happy to rebase onto whichever lands first. |
Description
git_status,git_diff_unstaged,git_diff_stagedandgit_diffreturn non-ASCII file names as quoted octal escapes, because git's defaultcore.quotepath=trueapplies to their output. A file named日本語.txtshows up as:and in diffs as
diff --git "a/\346\227\245\346\234\254\350\252\236.txt" .... An LLM client cannot map that back to the real file, so any follow-up call such asgit_addwith the escaped name fails. This PR runs these four commands with-c core.quotepath=false, so the same file shows up as日本語.txt.git_showis unchanged. It builds its output from GitPython'sDiffobjects, which already decode the paths.Server Details
git_status,git_diff_unstaged,git_diff_staged,git_diff)Motivation and Context
This affects any repository with non-ASCII file names: Japanese, Chinese, Korean, accented Latin, emoji, and so on. The setting only changes how git prints paths. It does not touch repository config, and it does not change output for ASCII paths.
How Has This Been Tested?
core.quotepath=truein the test repository, so the result does not depend on the user's global config. All 4 fail without the fix and pass with it.pyrightandruff checkare clean.claude -p(Claude Code) against a repository containing an untracked日本語.txtand had the model callgit_status. Before the fix, the output contained"\346\227\245\346\234\254\350\252\236.txt". After the fix, it contained日本語.txt.Tested on Windows 11 with git for Windows and Python 3.11. On Windows, the existing
test_repositoryfixture teardown raisesPermissionErrorfromshutil.rmtreewhile GitPython still holds handles. This happens before and after this change and is unrelated to it. CI on Linux is not affected.Breaking Changes
None. ASCII output is identical. Clients that were parsing the escaped form will now receive the real UTF-8 path.
Types of changes
Checklist
Additional context
-c core.quotepath=falseis passed per command through GitPython (repo.git(c=...)), so it applies only to that single invocation.🤖 Generated with Claude Code