Skip to content

Show worktrees in the repository sidebar#1

Merged
ignatremizov merged 45 commits into
mainfrom
feat/sidebar-worktrees
Apr 4, 2026
Merged

Show worktrees in the repository sidebar#1
ignatremizov merged 45 commits into
mainfrom
feat/sidebar-worktrees

Conversation

@ignatremizov

@ignatremizov ignatremizov commented Mar 28, 2026

Copy link
Copy Markdown
Owner

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

  • 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
  • let explicit parent-row clicks in the sidebar open the main worktree instead of restoring the last selected linked worktree from the preferred-worktree flow added in store last selected worktree, and open that when reselecting repo pol-rivero/github-desktop-plus#106
  • 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 linked worktree rows as leaf nodes in the sidebar, regardless of whether they come from saved repositories or synthetic worktree discovery
  • replace Remove… with Delete… for linked worktree rows and make deleting a saved linked worktree remove both the Desktop repository entry and the on-disk worktree/Git worktree metadata
  • remove group-name actions from saved linked worktree rows while keeping alias support for explicitly-added saved worktrees
  • inherit the main repository's GitHub association when a known linked worktree is added through Add local repository, so saved worktrees stay grouped under the main repo instead of moving to Other
  • ignore stash-metric persistence for transient synthetic rows so sidebar-only worktree selection does not try to look up non-persisted repositories in repositories-store
  • keep transient synthetic worktree selections stable across repository-store refreshes so switching to a worktree from another repository family does not snap back to the previously selected saved repository
  • add stale worktree affordances in the sidebar with a warning icon, clearer tooltip text, click suppression, and a context-menu action that prunes stale worktree metadata for the repository
  • avoid an empty leading context-menu section for virtual rows by only rendering the alias/group separator when those actions are present
  • make the Add local repository dialog 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 runs
  • persist sidebar worktree metadata in repository state and extract the sidebar-specific state shaping into a dedicated helper to keep app-store.ts smaller
  • move the worktree-specific sidebar row construction into a dedicated helper so group-repositories.ts stays closer to upstream
  • 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, stale worktree rows, repository-list context menu behavior, and transient synthetic repository store behavior

Notes

This builds on the existing worktree support in pol-rivero#62, the toolbar hierarchy follow-up in pol-rivero#81, and the preferred-worktree persistence added in pol-rivero#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:

  • repositories that were already added to Desktop as standalone entries
  • worktrees that only exist in Git metadata and were never added through Desktop

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 pol-rivero#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.ts
  • yarn test:unit app/test/unit/repository-list-item-context-menu-test.ts
  • yarn test:unit app/test/unit/repositories-store-test.ts
  • yarn lint
  • yarn compile:dev
  • yarn compile:prod

Example

image

New setting if "show worktrees dropdown in toolbar" is ticked

image

Stale worktrees

image image image

dependabot Bot and others added 14 commits March 20, 2026 07:55
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.2.9 to 3.4.2.
- [Commits](WebReflection/flatted@v3.2.9...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Hooks has GA'ed, so we no longer need the `Beta` indicator nor the
disclaimer. Users can still file issues if they encounter anything
though of course.
Report whether the git hooks environment is enabled as part of the
daily telemetry payload, computed at submission time via
getHooksEnvEnabled().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This adds a CODEOWNERS file that automatically requests a review from
desktop/code-reviewers on all pull requests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Delete docs/process/first-responder.md (superseded by unified FR role doc)
- Update docs/process/issue-triage.md to align with unified triage process
- Update docs/process/pull-requests.md to align with unified PR review process
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…s-docs

Cleanup outdated process docs to align with unified team docs
Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.2.2 to 2.3.2.
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](micromatch/picomatch@2.2.2...2.3.2)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch from ffc6762 to b3dbfe1 Compare March 28, 2026 19:48

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for displaying linked worktrees in the repository sidebar, nested under their main repository. It includes logic for preloading worktree metadata, handling "virtual" worktrees, and UI updates for the repository list and preferences. Feedback focuses on optimizing synchronous I/O operations when resolving Git directory paths and ensuring all relevant props are checked in component update logic to prevent rendering issues.

Comment thread app/src/ui/repositories-list/repository-list-item.tsx
Comment thread app/src/lib/git/worktree.ts Outdated
@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch from b3dbfe1 to 74c1a1b Compare March 28, 2026 20:23
@ignatremizov ignatremizov requested a review from Copilot March 28, 2026 20:24
@ignatremizov

Copy link
Copy Markdown
Owner Author

@codex review

@ignatremizov

Copy link
Copy Markdown
Owner Author

@gemini-code-assist review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the ability to display linked worktrees nested under their main repository in the sidebar. It includes changes to the Git worktree utility to extract path information, updates to the app store for preloading and refreshing worktree metadata, and UI enhancements to the repository list and preferences. Feedback suggests parallelizing the worktree preloading process for better performance, refactoring duplicated logic in the Repository model into a helper method, and simplifying the repository filtering logic in the UI.

Comment thread app/src/lib/stores/app-store.ts Outdated
Comment thread app/src/models/repository.ts
Comment thread app/src/ui/repositories-list/repositories-list.tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an optional appearance mode to show Git linked worktrees as nested rows under their main repository in the sidebar, including “virtual” rows for discovered-but-not-yet-added worktrees, with supporting state preloading and refresh throttling to keep the repository switcher usable for worktree-heavy workflows.

Changes:

  • Introduces a new appearance setting (showWorktreesInSidebar) and threads it through UI → dispatcher → AppStore state.
  • Updates repository grouping/list rendering to nest linked worktrees and synthesize sidebar rows from git worktree list results (with loading hints and initial branch pill support).
  • Adds worktree metadata caching/preloading in the AppStore plus unit coverage for grouping and loading behavior.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
app/test/unit/repositories-list-grouping-test.ts Adds unit tests for nested worktree grouping, virtual rows, loading state, and branch label preloading.
app/styles/ui/_repository-list.scss Adds indentation for nested worktree rows and styling for a “loading worktrees” hint.
app/src/ui/repositories-list/repository-list-item.tsx Renders nested worktree rows (icon/indent/title) and shows a loading hint; expands props and update conditions.
app/src/ui/repositories-list/repositories-list.tsx Passes new item metadata to row renderer, supports selecting virtual worktrees (add+select), and avoids Pull All duplication for linked worktrees.
app/src/ui/repositories-list/group-repositories.ts Implements nesting logic and creation of synthetic “virtual” linked-worktree rows based on cached allWorktrees.
app/src/ui/preferences/preferences.tsx Wires the new preference through Preferences state and persistence via dispatcher.
app/src/ui/preferences/appearance.tsx Adds the nested checkbox under the existing “Show worktrees” setting.
app/src/ui/dispatcher/dispatcher.ts Adds dispatcher method to set the new appearance flag.
app/src/ui/app.tsx Threads showWorktreesInSidebar into RepositoriesList and filters linked worktrees only when not showing nested rows.
app/src/models/repository.ts Adds mainWorktreePath and expands ILocalRepositoryState with allWorktrees + isLoadingWorktrees.
app/src/lib/stores/helpers/sidebar-worktrees.ts New helper module for sidebar worktree state creation, loading transitions, and refresh throttling logic.
app/src/lib/stores/app-store.ts Persists the new setting, preloads worktree state for sidebar rendering, and throttles periodic refresh churn.
app/src/lib/git/worktree.ts Adds synchronous worktree path info parsing (getWorktreePathInfoSync) used by Repository.
app/src/lib/app-state.ts Adds showWorktreesInSidebar to the application state model.
.github/actions/setup-ci-environment/action.yml Retries yarn install up to 3 times to reduce CI flakiness.

Comment thread app/src/ui/preferences/appearance.tsx
Comment thread app/src/ui/repositories-list/group-repositories.ts Outdated
Comment thread app/src/ui/repositories-list/repositories-list.tsx
Comment thread app/src/ui/repositories-list/repository-list-item.tsx
Comment thread app/src/ui/repositories-list/repository-list-item.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 74c1a1bc00

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/src/lib/stores/app-store.ts
Comment thread app/src/lib/stores/app-store.ts Outdated
Comment thread app/src/ui/preferences/appearance.tsx
@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch from 74c1a1b to b451b05 Compare March 28, 2026 20:45
@ignatremizov ignatremizov requested a review from Copilot March 28, 2026 21:37
@ignatremizov

Copy link
Copy Markdown
Owner Author

@codex review

@ignatremizov

Copy link
Copy Markdown
Owner Author

@gemini-code-assist review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a feature to display linked worktrees nested under their main repository in the sidebar. Key changes include the implementation of worktree discovery logic, state management within the AppStore for preloading and refreshing worktree metadata, and UI updates to the repository list and preferences to support this new view. Additionally, a retry mechanism was added to the CI environment setup for dependency installation. I have no feedback to provide.

@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch 9 times, most recently from 8a87499 to 470f135 Compare April 1, 2026 13:15
Cooper Jones and others added 3 commits April 1, 2026 19:27
…lectedWorktree

store last selected worktree, and open that when reselecting repo
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
@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch from 44f87b6 to a229645 Compare April 2, 2026 19:07
…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
@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch 2 times, most recently from d9c0beb to fa07a22 Compare April 3, 2026 12:39
…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
@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch from 9180639 to 7aae8cf Compare April 3, 2026 13:17
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
@ignatremizov ignatremizov force-pushed the feat/sidebar-worktrees branch from 7665f78 to cf8bcb6 Compare April 3, 2026 13:30
@ignatremizov ignatremizov merged commit c56967c into main Apr 4, 2026
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.

9 participants