Skip to content

List worktrees with -z and decode quoted lock/prunable reasons [patch] - #207

Merged
matt-edmondson merged 3 commits into
mainfrom
fix/141-worktree-list-z
Oct 12, 2026
Merged

matt-edmondson merged 3 commits into
mainfrom
fix/141-worktree-list-z

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #141

Before:

  • Worktrees() parsed git worktree list --porcelain line by line. Git prints the worktree path raw, so a worktree at …/odd<LF>dir/wt was reported at …/odd with no error.
  • A lock reason such as needs "care"<LF>second line came back as "needs \"care\"\nsecond line", still quoted. Prunable reasons had the same problem.

After:

  • On git 2.36 and later, the builder runs worktree list --porcelain -z. Every field is NUL-terminated and printed raw, so paths and reasons come back as git stores them.
  • Below 2.36, -z doesn't exist and the newline form stays. Its C-quoted locked/prunable reasons are now decoded with the existing GitPatchParser.UnquotePath. A newline in a path can't be represented in that form, and the builder remarks say so.

How:

  • GitWorktreeListBuilder probes the installed git version through TryExecuteAsync, the same way GitFetchBuilder does. A failed probe falls back to the newline form. BuildArguments() on its own shows the -z vector.
  • GitWorktreeParser.Parse(output, nulTerminated) splits on NUL under -z and doesn't trim \r there. An empty field closes a record.

Tests (git 2.43 on Linux):

  • Parser: quoted lock reason, quoted prunable reason, a NUL-terminated listing, a newline in a path under -z, and a raw reason under -z.
  • Builder: -z is emitted on 2.36 and its output is parsed; it is not emitted on 2.35, where the output is decoded.
  • Integration: a real worktree under a path containing a newline, locked with a quoted multi-line reason, round-trips through Worktrees(). On Windows the path is plain and only the reason is awkward.
  • With the builder and parser changes reverted, all 9 new or updated tests fail. Full suite: 798/798 pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_015EHSar1iXDsM19Xk1rHpKp


Generated by Claude Code

worktree list --porcelain prints paths raw, so a path containing a newline
was split across two lines and reported as a different directory. On git
2.36 and later the builder now asks for -z, probing the version the way
Fetch does, and the parser reads NUL-terminated fields with raw values.
Below 2.36 the newline form remains, and its C-quoted locked/prunable
reasons are decoded with GitPatchParser.UnquotePath instead of being
returned still quoted.

Fixes #141

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015EHSar1iXDsM19Xk1rHpKp
On macOS the temp root is /var/folders/..., which git reports resolved as
/private/var/folders/..., so a full-path comparison failed on macOS only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015EHSar1iXDsM19Xk1rHpKp
Windows forbids a newline in a path, so AbsoluteDirectoryPath rejects the
fixture there; the same guard the colon-name integration test uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015EHSar1iXDsM19Xk1rHpKp
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit a941dab into main Oct 12, 2026
16 checks passed
@matt-edmondson
matt-edmondson deleted the fix/141-worktree-list-z branch October 12, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Worktrees() reports the wrong path for a worktree whose path contains a newline, and returns lock/prunable reasons still C-quoted

2 participants