Skip to content

fix: copy-ignored handles non-ASCII filenames (git quotePath)#3487

Open
worktrunk-bot wants to merge 3 commits into
mainfrom
fix/issue-3486
Open

fix: copy-ignored handles non-ASCII filenames (git quotePath)#3487
worktrunk-bot wants to merge 3 commits into
mainfrom
fix/issue-3486

Conversation

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Problem

wt step copy-ignored failed to copy any ignored file whose path contains non-ASCII characters, and exited 1 — which, because the docs recommend wiring it into a blocking pre-start hook, made wt switch --create fail in any repo containing such a file.

Root cause: git ls-files quotes non-ASCII paths by default (core.quotePath = true), so list_ignored_entries in src/commands/step/shared.rs received e.g. "data/Report \342\200\224 Q1.txt" — surrounding double quotes and octal escapes included. That literal string was joined onto the worktree path and treated as a real filesystem path, so the entry was never matched and the subsequent metadata read failed with No such file or directory (os error 2).

Solution

Pass -z to git ls-files and split the output on NUL. -z NUL-terminates entries and disables path quoting, so paths come through verbatim regardless of core.quotePath. It also sidesteps the newline-in-filename edge case that line splitting can't represent. This matches the reporter's suggested fix.

Testing

Added list_ignored_entries_handles_non_ascii_names to the existing test module in src/commands/step/shared.rs. It creates an ignored file with an em dash (U+2014) in its name and asserts discovery returns a path that exists on disk. The test fails on the old code (the discovered path is the quoted/escaped string, which doesn't exist) and passes with the fix. The full step_copy_ignored integration suite passes (one unrelated pre-existing failure, test_copy_ignored_preserves_file_executable_permissions, is a sandbox-umask artifact — 0664 vs 0644 — and fails identically without this change).


Closes #3486 — automated triage

git ls-files quotes non-ASCII paths by default (core.quotePath=true),
returning e.g. "data/Report \342\200\224 Q1.txt" — surrounding quotes
and octal escapes included — which was then treated as a literal
filesystem path and never found, so the entry was silently dropped and
the metadata read failed with 'No such file or directory', exiting 1.

Pass -z to git ls-files and split on NUL, which disables quoting and also
sidesteps the newline-in-filename edge case.

Closes #3486
@worktrunk-bot worktrunk-bot added the automated-fix Automated CI fix label Jul 16, 2026

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The -z switch and NUL-split are the right fix for the reported case, and the new test pins it well — git ls-files --directory -z still trails directory entries with /, and the !entry.is_empty() filter correctly drops the trailing NUL terminator, so parsing is equivalent to the old .lines() path minus the quoting bug. Verified locally: list_ignored_entries_handles_non_ascii_names fails on the old code and passes here.

One residual worth a look (not a regression — it was equally broken before, and this diff strictly improves the common case): -z disables git's quoting, but list_ignored_entries then does String::from_utf8_lossy(&output.stdout) and builds the real PathBuf used for the copy from that lossy string. For a filename with genuinely invalid UTF-8 bytes (arbitrary bytes are legal on Linux), from_utf8_lossy substitutes U+FFFD, so the constructed path no longer exists on disk — copy-ignored silently skips the file, and wt step promote's stage_ignored/distribute_staged (guarded by symlink_metadata(...).is_ok()) silently leave it un-exchanged. So the fix resolves valid-UTF-8 non-ASCII names (the reporter's em dash), not the whole "verbatim regardless of core.quotePath" set the PR body describes.

The sibling -z reader in untracked_diff_stats (src/git/repository/working_tree.rs) sidesteps this by splitting the raw bytes on the NUL byte (output.stdout.split(|&b| b == 0)) and feeding those raw bytes to the git operation, keeping the lossy String display-only. If closing the invalid-UTF-8 gap is in scope here, the same shape — split output.stdout on the 0 byte and, on Unix, build each PathBuf via std::os::unix::ffi::OsStrExt::from_bytes rather than through from_utf8_lossy — would make discovery byte-exact. It adds #[cfg(unix)] handling, so it's a judgment call whether it belongs in this PR or a follow-up; flagging since correct non-ASCII handling is precisely this change's subject.

Use .expect() instead of unwrap_or_else(|| panic!(...)) so the assertion
has no failure-only line that codecov/patch flags as uncovered.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated-fix Automated CI fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

step copy-ignored: files with non-ASCII names are never copied (git quotePath)

1 participant