SCM Changes view does not restore repository/group collapsed state after reload
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
Does this issue occur when all extensions are disabled?: Yes — reproduced in an isolated profile with no third-party SCM automation. Better Git's automatic SCM tree management was disabled, and its command trace remained empty during startup and restart.
- VS Code Version: 1.129.0 (commit `125df4672b8a6a34975303c6b0baa124e560a4f7`, arm64)
- OS Version: macOS 26.5.2 (25F84), arm64
## Steps to reproduce
1. Open a workspace where the main **Source Control: Changes** view contains at least two Git repositories or worktrees.
2. Wait for all repositories and their resource groups to finish loading.
3. Leave one repository expanded and manually collapse another repository header. A mixed resource-group state can also be used, for example leaving **Changes** expanded in one repository and collapsed in another.
4. Run **Developer: Reload Window**, or fully quit and reopen the same workspace.
5. Wait for Git repository discovery to settle.
## Expected
The main Changes tree restores the previous mixed expanded/collapsed state for repository headers and resource groups.
## Actual
Previously collapsed repositories and groups reopen. After the asynchronous SCM reconstruction settles, `scm.viewState2` is overwritten with the now-expanded tree state, so the user's collapsed choices are lost.
I reproduced this with eight repositories in an isolated profile. The pre-restart mixed state was present in `scm.viewState2`; after a full process restart every discovered repository/group started expanded.
## Regression history and likely mechanism
This exact behavior was previously reported in:
- #102831 — collapse a Git repository, reload, and it is expanded again
- #106196 — retain collapsed state of SCM providers across reloads
Those issues were closed as fixed after #89145 / #89313 implemented persisted tree collapse state.
The main Changes view was later migrated to `AsyncDataTree` in #197349. Current code:
- stores `this.tree.getViewState()` in `scm.viewState2`;
- persists only an `expanded` ID list for expansion state;
- configures repositories, resource groups, and resource folders as expanded by default.
Relevant code:
- https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/scm/browser/scmViewPane.ts#L1640-L1643
- https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/scm/browser/scmViewPane.ts#L1960-L1977
- https://github.com/microsoft/vscode/blob/main/src/vs/base/browser/ui/tree/asyncDataTree.ts#L1287-L1290
- https://github.com/microsoft/vscode/blob/main/src/vs/base/browser/ui/tree/asyncDataTree.ts#L1349-L1355
An ID omitted from `expanded` because the user collapsed it therefore falls through to the expanded default, just like a genuinely new node. This appears to regress the view-state-aware collapse behavior added by #89313.
## Related but distinct issues
- #270454 / #305267 concerns auto-reveal reopening **Staged Changes** when an active staged file is open; it does not cover cold-start restoration.
- #322318 / #322319 concerns the separate **Source Control Repositories** view, not the main Changes tree.
- #271554 / #307887 / #320526 concerns repository visibility selection and late-discovered worktrees. Hiding repositories can persist in the fast path, but hidden headers disappear entirely and it is not mixed expansion-state restoration.
## Workaround
**Source Control → View & Sort → Repositories** can hide unwanted repositories and persisted a two-of-eight selection in isolated restart tests. It is only a partial workaround: the headers are removed instead of remaining visible-but-collapsed, and late worktree discovery is separately tracked by #271554.
`scm.autoReveal: false` does not restore the cold-start collapse state. Extensions also have no public per-repository or per-group collapse API; only global collapse-all/expand-all behavior is safely available.
I do not currently have VS Code Insiders installed on this Mac, so the direct runtime reproduction above is from current Stable 1.129.0. The linked current-main code still contains the state/default interaction described above.
Contributor guide
Assessment
This issue has not been assessed yet.