Show worktrees in the repository sidebar#105
Conversation
45161c8 to
8a562d1
Compare
|
I think this is a much needed feature, but I'm not a big fan of the large diff in This was added here: 546af41. Conditionally skipping this filter should be enough to show linked workspaces in the sidebar, I think. |
0cd4fad to
a7260a0
Compare
Ah I see, that makes sense. Edit: I've done an initial pass on your PR. While it's still bigger than I'd like, most of the complexity seems justified, I haven't found any good workaround to avoid creating those synthetic rows. |
8a87499 to
470f135
Compare
|
Added:
|
|
@pol-rivero check if anything else is missing or you want moved around |
|
Thanks for the update. I've done some more tests and found the following:
Also, I'd appreciate it if you could do separate commits instead of just updating a single commit, this way I can see the actual changes between revisions. |
|
@pol-rivero I pushed a follow-up fix for the synthetic-row state issue. Root cause was not the negative ids by themselves, but that synthetic sidebar-only worktree rows are transient I fixed that by treating transient synthetic rows as non-persisted in the stash-check tracking path, and added a regression test for it. On the context menu behavior: I intentionally kept stored linked-worktree rows on the normal repository menu for now. My reasoning is that if a user explicitly added a worktree as a regular Desktop repository, they should still be able to use the normal repo actions there (alias, grouping, etc), especially That keeps the behavior compatible with the base GitHub Desktop mental model instead of automatically "converting" stored repo rows to worktrees, or hiding them entirely. If you’d prefer a different UX here, I can revisit that part separately, but I wanted to avoid implicitly changing persisted repo rows into a different interaction model, even if they are semantically worktrees. |
Add an optional sidebar mode that shows linked worktrees nested under their repository in the main repository list so repository switching can stay in the main sidebar instead of requiring the worktree dropdown. Changes: - add a secondary Appearance setting to show worktrees in the repository sidebar when worktree support is enabled - group linked worktrees under their main repository in the sidebar - synthesize child rows for linked worktrees discovered from `git worktree list` even when those worktrees were never added as repositories - support linked-only setups by synthesizing sibling worktree rows even when the stored entry is itself a linked worktree instead of the main worktree - use worktree folder names for child row labels while preserving existing alias styling for saved repository entries - use the same displayed-title logic for sorting and disambiguation so nested rows sort and label consistently with what the user sees - preload main-repository worktree state for the sidebar so nested rows and stored linked-worktree branch pills are available on initial render instead of only after opening the worktree dropdown or forcing another sidebar refresh - refresh parent sidebar rows when linked worktrees are selected so nested rows stay in sync with the active repository view - surface nested worktree rows from both saved worktree repositories and synthetic virtual rows without duplicating entries already stored in Desktop - avoid duplicate `Pull all` work for linked worktrees while still including orphan linked worktrees when the main repo is absent from the stored repository list - route virtual worktree open failures through the normal app error path instead of silently failing - open synthetic worktree rows transiently instead of persisting them as top-level repositories under `Other` - keep stored linked-worktree rows on the repository-management context menu while giving synthetic rows a worktree-specific context menu that uses `PopupType.DeleteWorktree` - persist sidebar worktree metadata in repository state and extract the sidebar-specific state shaping into a dedicated helper to keep `app-store.ts` smaller - throttle sidebar worktree refreshes during repository indicator updates to reduce repeated `git worktree list` churn - prune sidebar worktree refresh timestamps when repository lists change so stale cache entries do not accumulate - tighten TypeScript null/undefined handling in repository and sidebar list code so production webpack builds pass across the full CI matrix - add and extend unit coverage for grouped rows, synthetic rows, linked-only setups, stored linked-worktree branch labels, and repository-list context menu behavior Behavioral effect: Users can opt into seeing and switching linked worktrees directly from the main repository sidebar, including unstored Git worktrees, with branch labels, parent-child grouping, and worktree-aware context menu behavior available without forcing those virtual rows into the saved repository list. Testing: - yarn test:unit app/test/unit/repositories-list-grouping-test.ts - yarn test:unit app/test/unit/repository-list-item-context-menu-test.ts - yarn lint - yarn compile:dev - yarn compile:prod
44f87b6 to
a229645
Compare
…ktree rows Synthetic sidebar worktree rows are transient `Repository` objects created only for sidebar navigation and they are not persisted in `repositories-store`. Stash metric collection still assumed every selected repository existed in the store, which caused fatal lookup failures when a synthetic row became active. Changes: - short-circuit stash-check read/write helpers for transient repositories with negative ids instead of querying the repositories database - keep persisted repositories on the existing stash metric path unchanged - add a repositories-store regression test covering synthetic sidebar rows so transient worktree selection cannot reintroduce the fatal lookup path Behavioral effect: Selecting a synthetic sidebar worktree row no longer throws `getLastStashCheckDate` errors or leaves the app in an inconsistent state just because the row is transient and not saved in Desktop's repository store. Testing: - yarn test:unit app/test/unit/repositories-store-test.ts - yarn eslint app/src/lib/stores/repositories-store.ts app/test/unit/repositories-store-test.ts - yarn compile:prod
Upstream now restores the last selected linked worktree when reselecting a repository, which is useful for normal repo switching but conflicts with the sidebar parent-row interaction. After visiting a synthetic child row, clicking the parent row should open the parent repository itself instead of bouncing back into the remembered worktree. Changes: - add an explicit `followPreferredWorktree` selection flag through dispatcher and app-store repository selection paths - keep the upstream preferred-worktree restore behavior enabled by default for existing selection flows - make repository sidebar row clicks opt out of preferred-worktree restoration so an explicit parent-row click opens the main worktree Behavioral effect: When a user clicks a repository parent row in the sidebar after visiting a linked worktree, the app now opens the parent repository directly instead of restoring the previously selected child worktree behind the user's back. Testing: - yarn eslint app/src/ui/dispatcher/dispatcher.ts app/src/lib/stores/app-store.ts app/src/ui/app.tsx - yarn compile:prod
bf73cff to
43a2ddc
Compare
|
@ignatremizov It seems this is still under active development. Let me know once you have a definitive and completely tested version that I can review. :) Due to the force-pushing, it's very exhausting to continue re-reviewing the same changes all the time. |
d9c0beb to
fa07a22
Compare
…behavior Tighten the follow-up linked-worktree behavior so saved linked worktrees stay attached to their main repository, remain leaf rows, and do not lose selection state when synthetic sidebar rows are active. Changes: - keep linked worktree rows as leaf nodes so only main worktrees own nested children in the sidebar - use `Delete…` for both saved and synthetic linked worktree rows, while removing group-name actions from saved linked worktree rows - ensure deleting a saved linked worktree also removes its Desktop repository entry so the stored list stays in sync with Git worktree deletion - inherit the main repository's GitHub association when a linked worktree is added through the local repository flow and its main worktree is already known to Desktop - keep saved linked worktrees in the same top-level group as their main repository after restart instead of dropping them into `Other` - preserve transient synthetic worktree selections across repository-store updates so selecting a worktree from another repository family does not snap back to the previously selected saved repository - keep alias support for saved linked worktrees while treating synthetic rows as transient sidebar-only entries - retain the regression coverage for orphan linked rows staying flat and for the linked-worktree context menu behavior Behavioral effect: Linked worktrees now behave consistently as leaf rows whether they are saved or synthetic. Saved linked worktrees stay grouped under their real main repository, deleting a saved linked worktree updates both Git and Desktop state, and synthetic worktree selection no longer jumps back to an unrelated repository when the saved repository list refreshes. Testing: - yarn test:unit app/test/unit/repositories-list-grouping-test.ts - yarn test:unit app/test/unit/repository-list-item-context-menu-test.ts - yarn eslint app/src/ui/repositories-list/worktree-list-items.ts app/src/ui/repositories-list/repository-list-item-context-menu.ts app/src/ui/repositories-list/repositories-list.tsx app/src/ui/worktrees/delete-worktree-dialog.tsx app/src/ui/app.tsx app/src/models/popup.ts app/src/lib/stores/app-store.ts app/test/unit/repositories-list-grouping-test.ts app/test/unit/repository-list-item-context-menu-test.ts - yarn compile:prod
9180639 to
7aae8cf
Compare
Tighten the Add Local Repository dialog so choosing a valid repository path and immediately pressing Add consistently runs the add flow instead of silently stalling in the dialog. Changes: - resolve the entered local path before submit-time validation so `validatePath(...)` uses the same normalized path handling as the live `onPathChanged(...)` validation path - wait for the folder-picker path to finish landing in component state before allowing the chosen path to be used for submission - keep the rest of the add-existing-repository flow unchanged so successful submissions still dismiss the dialog, add the repository, and select it Behavioral effect: The Add Local Repository dialog no longer ends up in a no-op state where a repository path appears valid in the picker flow but submit-time validation never reaches `_addRepositories(...)`. Choosing a worktree path from the folder picker and immediately pressing Add now reliably adds the repository. Testing: - yarn eslint app/src/ui/add-repository/add-existing-repository.tsx - yarn compile:prod
7665f78 to
cf8bcb6
Compare
|
@pol-rivero I pushed another follow-up based on your feedback. Changes in this round:
I agree for the behavior to be aligned more closely with “linked worktrees are an implementation detail” rather than a second repository model in the UI. I kept alias support for saved linked worktrees for now. I did not add alias support for synthetic rows in this PR, because that would require introducing a separate store keyed by worktree path instead of relying on the normal repository persistence model. My current thinking is that synthetic alias support is probably follow-up work if we still want it after merge. In practice, renaming the worktree folder itself already gives a reasonable alias-like result for synthetic rows, since those rows display the basename when no explicit alias exists. If it's still important, I’d prefer to handle that in a separate PR with a small dedicated metadata store rather than growing the repository persistence model further. About the force-push: I had to rebase after #106 landed because this branch needed to opt explicit sidebar parent-row clicks out of the new "reopen last opened worktree" behavior. |
|
Phenomenal job! Let's get this merged into the main branch so I can start daily-driving it in search of bugs. :)
I completely agree, renaming a worktree is enough. That's why I originally proposed not showing the alias option in linked worktrees (synthetic or otherwise). |
|
This is now available in v3.5.8 |
This will be reimplemented later on top of the new worktrees data model. Reverts c56967c




Summary
Add an optional sidebar mode that shows linked worktrees nested under their repository in the main repository list.
This keeps repository switching fast for worktree-heavy workflows without forcing users through the worktree dropdown for every switch.
Changes
git worktree listeven when those worktrees were never added as repositoriesPull allwork for linked worktrees while still including orphan linked worktrees when the main repo is absent from the stored repository listOtherRemove…withDelete…for linked worktree rows and make deleting a saved linked worktree remove both the Desktop repository entry and the on-disk worktree/Git worktree metadataAdd local repository, so saved worktrees stay grouped under the main repo instead of moving toOtherrepositories-storeAdd local repositorydialog submission reliable for linked worktree paths by resolving the submitted path consistently and waiting for picker-selected paths to land in component state before submit runsapp-store.tssmallergroup-repositories.tsstays closer to upstreamgit worktree listchurnNotes
This builds on the existing worktree support in #62, the toolbar hierarchy follow-up in #81, and the preferred-worktree persistence added in #106.
The feature stays behind a separate Appearance setting so users can keep the existing grouped-in-dropdown workflow if they prefer it.
The sidebar intentionally supports both kinds of linked worktrees:
That second case is why the implementation includes synthetic child rows and startup/sidebar refresh state, rather than only changing the existing filter logic.
Because #106 persists the last selected worktree for a repository, this PR also makes explicit parent-row clicks in the sidebar opt out of that restore path so clicking the repository row still opens the main worktree directly.
Linked worktrees are now always treated as leaf nodes in the sidebar, even when they were explicitly added to Desktop as saved repositories. Only the main worktree row owns nested worktree children.
Saved linked worktrees still keep alias support, but group-name actions are removed for them to avoid implying that linked worktrees can act as group parents. Synthetic worktree alias support would require a separate metadata store keyed by worktree path, so that is left as possible follow-up work instead of extending the normal repository persistence model in this PR.
For stale worktrees, the new prune action is repository-scoped because it maps to
git worktree prune, which removes any stale worktree metadata Git reports for that repository.Testing
yarn test:unit app/test/unit/repositories-list-grouping-test.tsyarn test:unit app/test/unit/repository-list-item-context-menu-test.tsyarn test:unit app/test/unit/repositories-store-test.tsyarn lintyarn compile:devyarn compile:prodExample
New setting if "show worktrees dropdown in toolbar" is ticked
Stale worktrees