Skip to content

Nested repository is detected only if its file is open at startup, not when opened later #327119

Description

@alike03

Does this issue occur when all extensions are disabled?: Yes (the cause is in the built-in Git extension, see analysis below)

  • VS Code Version: (behavior verified in the Git extension source on main, 1.131.0, commit 396ff629)
  • OS Version: macOS (Darwin 25.5.0)

Steps to Reproduce:

  1. Open a workspace whose root folder is a git repository (the "parent").
  2. Have a second git repository nested in a subfolder beneath it (any depth below the parent root).
  3. Case A: open a file from the nested repo, then reload the window. The nested repo shows up as its own source-control provider.
  4. Case B: from a clean load with no nested-repo file open, open that same file afterwards. The nested repo does not show up.

Same file, same repository, different result depending only on whether it was open at startup.

Summary

Whether a nested git repository (one whose root sits below an already-open workspace/parent repository) gets registered depends on timing:

  • If a file belonging to the nested repo is already open when the window loads, the nested repo appears in Source Control.
  • If the same file is opened later, the nested repo never appears.

The behavior is inconsistent.

Cause (from the Git extension source + trace)

Model.doInitialScan() in extensions/git/src/model.ts runs three tasks concurrently:

const initialScanFn = () => Promise.all([
  this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),
  this.onDidChangeVisibleTextEditors(window.visibleTextEditors),
  this.scanWorkspaceFolders()
]);
  • At load: onDidChangeVisibleTextEditors processes files already open. For a visible file, the parent repo may not be registered yet (opening it requires spawning git), so getRepository(uri) returns undefined and openRepository(path.dirname(uri)) is called. That resolves via git rev-parse --show-toplevel (Git.getRepositoryRoot() in extensions/git/src/git.ts) to the nested repo root and opens it.

  • Opened later: the parent repo is already registered, so getRepository(uri) returns it and the handler short-circuits:

    [Model][onDidChangeVisibleTextEditors] Repository for editor resource
      …/<nested-repo>/<file>
      already exists: /…/<parent-repo>
    

    openRepository is never called for the nested repo.

getRepository() matches purely by path ancestry (isDescendant over this.openRepositories, deepest root first) and only considers already-open repositories. Once the parent is open it always shadows a not-yet-open nested repo; the only carve-out is submodules of an open repository, which does not apply to plain nested repos.

Impact

Nested-repo detection is timing-dependent and inconsistent. In practice a nested repo can only be surfaced reliably by keeping one of its files open across reloads, or by declaring it explicitly in git.scanRepositories. Opening a file on demand, which works at startup, silently does nothing afterwards.

Note that Case A working at all is itself a race: the visible-editors task usually wins because opening the parent repo requires spawning a git process, but nothing guarantees the ordering.

Request

Make open-editors detection consistent: when a file is opened, open the deepest repository that contains it, even if an ancestor repository is already open (or provide an opt-in setting for this). The outcome should not depend on whether the file happened to be open at startup.

Workaround

  • git.scanRepositories with the nested repo's path. Only paths relative to a workspace folder are accepted; absolute paths are explicitly rejected (Model.scanWorkspaceFolders()).
  • Keeping a nested-repo file open across window reloads.

Related issues

None of these fixed the open-editors code path described above; the short-circuit still exists on current main (1.131.0).

  • #133577: umbrella feature request for nested repository detection. Closed when workspace folder scanning shipped; the openEditors gap remained.
  • #322859: same symptom (nested repo appears only after opening a file and reloading the window). Closed by the reporter; the root cause there was the git.repositoryScanMaxDepth default. The reload behavior it describes is exactly the startup race explained here.
  • #198744: same symptom (editing a nested-repo file no longer opens the repo). Turned out to be a GitLens regression and was closed by the reporter. It shows the open-on-edit behavior users expect comes from GitLens, not the built-in Git extension.
  • #189287 (bug, git, confirmed, still open): a different symptom of the same root cause, purely path-based attribution in getRepository(), in the opposite direction. A diff for a file tracked by the ancestor repo resolves its original content against the deepest open repo, where the file is ignored, so the original side shows empty. This report is the detection/registration counterpart: there the deepest open repo wrongly wins, here the already-open ancestor wrongly wins.

Metadata

Metadata

Labels

bugIssue identified by VS Code Team member as probable bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions