Give linked worktrees their own row - #2
Open
HugoHeneault wants to merge 4 commits into
Open
HugoHeneault wants to merge 4 commits into
HugoHeneault wants to merge 4 commits into
Conversation
A worktree checked out under .claude/worktrees was never scanned: the walk skips dotted directories and stops at the first .git. Its parent listed it as an untracked +0 -0 file instead, and a commit with nothing selected ran git add --all, which records it as an embedded gitlink. - Worktrees are read from the repo's .git/worktrees, wherever they are checked out, and deduplicated against the walk. - A worktree row carries its uncommitted changes only: branches are shared and stay on the main repo's row. - A nested repo is left out of its parent's untracked files, and add --all excludes it.
A worktree row was labelled by its directory, so claude-OW-701 or .claude/worktrees/tech-writer did not say which repo it belonged to, and its branch sat on the main repo's row, away from the changes made on it. - The row is named after the main repo and the checked out branch: ownweb/claude [OW-701-skill-owasp-mobile]. A detached worktree falls back to its directory name, a bare repo to its own directory. - A worktree row lists the unpushed commits of its own branch. The main repo keeps every branch no live worktree has checked out. - Both are read from the .git files and .git/worktrees, still three git calls per row.
One row per worktree still read as several repos: ownweb/claude showed up three times. A repo now keeps a single row, and its worktrees are listed with its branches, which is what they are. - A linked worktree the walk reaches is mapped to its main repo, which scans every worktree listed in .git/worktrees. A worktree outside DIR is found through its repo, so the worktree without main repo case is gone. - UNPUSHED lists worktrees holding changes first, as branch [n files], merged with that branch's unpushed commits when it has some, then the other branches. CHANGES stays the main checkout's. - Inside a repo a worktree is a row: space selects it, d shows its diff, and commit goes through every checkout showing changes, or the selected files and worktrees only, each asking for its own message.
Selecting a worktree narrowed the commit to it but not the push: with no branch selected, p went on to push every unpushed branch of the repo. A selected worktree now narrows the push to the branch it has checked out, next to any selected branch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Généré par Claude Opus 5.5
A repo with worktrees under
.claude/worktreesor next to it showed them wrong. It now keeps a single row, and its worktrees are listed with its branches.Worktrees were never scanned. The walk skips dotted directories and stops at the first
.git. A repo's worktrees are now read from its.git/worktrees/*/gitdir, a filesystem read rather than an extra git call, so they are found wherever they are checked out: inside the repo, next to it, in an ignored directory or outside DIR. A worktree the walk reaches on its own is mapped to its main repo through its.gitfile andcommondir, and the root is canonicalized so both paths compare. Stale entries (worktree deleted withoutgit worktree remove),worktree.useRelativePaths, detached worktrees and bare main repos are handled.One row per repo.
CHANGEScounts the main checkout. A worktree holding changes joinsUNPUSHEDasbranch [n files], merged with that branch's unpushed commits when it has some, before the other branches. Inside a repo each worktree is a row:spaceselects it,dshows its diff, a commit goes through every checkout showing changes, or only the selected files and worktrees, each asking for its own message, and a push covers every unpushed branch, or only the selected branches and the ones the selected worktrees have checked out.The parent listed them as files.
ls-files --othersreports a nested repo asdir/, which showed up as an untracked+0 -0file. A trailing slash now marks a nested repo, left out of the parent's files.add --allstaged them as embedded gitlinks. Committing with nothing selected would have recorded the worktree as a gitlink in its own parent (reproduced in a throwaway repo:A .claude/worktrees/feat).add --allnow excludes each nested repo with:(exclude,literal)<path>.Limits
git add --all): files inside it cannot be selected one by one.UNPUSHEDkeeps its 44 column cap, so a repo with several worktrees shows the first ones and+n more; the repo view lists them all.Test
Four tests added (
add --allargument vector,UNPUSHEDlabels, commit scope, push scope), 20 pass. Checked against~/dev: one row per repo, 87 scanned instead of 96. Checked in throwaway repos with a nested, a sibling, a relative path, a detached, a bare repo and an outside DIR worktree, and drove the repo view in a pty: worktree row, selection, and a preview that lists only the selected worktree's branch. README, CHANGELOG and CLAUDE.md updated.