Skip to content

title_bar: Prefer most specific repository for nested git repos#51898

Merged
smitbarmase merged 3 commits intozed-industries:mainfrom
Caio-Ze:fix/nested-repo-branch-display
Mar 19, 2026
Merged

title_bar: Prefer most specific repository for nested git repos#51898
smitbarmase merged 3 commits intozed-industries:mainfrom
Caio-Ze:fix/nested-repo-branch-display

Conversation

@Caio-Ze
Copy link
Copy Markdown
Contributor

@Caio-Ze Caio-Ze commented Mar 19, 2026

Context

I have a project with a nested git repo inside a parent repo (separate .git directory, not a submodule). The title bar shows the parent's branch instead of the nested repo's own branch.

The issue is in get_repository_for_worktree() — it iterates git_store.repositories() (an FxHashMap) and returns the first starts_with match. Both the parent and nested repo match, and whichever one FxHashMap iterates first wins. There's no reason it should be one or the other.

The fix already exists elsewhere in the codebase — GitStore::repository_and_path_for_project_path() at git_store.rs:1826 uses .max_by_key() to pick the most specific (longest path) match. This PR applies the same approach to three functions that have the same problem:

  • TitleBar::get_repository_for_worktree() — branch display in title bar
  • resolve_active_repository() in git_ui — repository selection for the git panel
  • get_branch_for_worktree() in recent_projects — branch display in the project switcher

Two other locations use a similar starts_with pattern (effective_active_worktree() in title_bar.rs and worktree selection in recent_projects.rs) but those iterate worktrees against a single known repo, not repos against a worktree — so first-match and longest-match give the same result. Left those unchanged.

Closes #7566

How to Review

All three changes are the same transformation: first-match loop (or .find()) → .filter().max_by_key() on path length. The reference is at crates/project/src/git_store.rs:1826.

The primary fix is get_repository_for_worktree() in title_bar.rs. The other two are the same pattern.

One difference from the reference: I used .as_os_str().len() instead of .clone() for the max_by_key key — avoids cloning an Arc<Path> per comparison. Among prefix-related paths (which is all that passes the filter), the longer path is always the more specific match, so length comparison is equivalent.

title_bar has no existing test infrastructure. Happy to add a test if you'd like — the setup would follow the pattern in test_git_traversal_with_nested_repos (crates/project/tests/integration/git_store.rs).

cargo test -p title_bar -p git_ui -p recent_projects
./script/clippy

Self-Review Checklist

  • I've reviewed my own diff for quality, security, and reliability
  • Unsafe blocks (if any) have justifying comments
  • The content is consistent with the UI/UX checklist
  • Tests cover the new/changed behavior
  • Performance impact has been considered and is acceptable

Release Notes:

  • Fixed branch picker showing parent repository's branch instead of the nested repository's branch when working in submodules or nested git repos.

When a project contains nested git repositories, the title bar branch
picker can show the parent repo's branch instead of the nested repo's.

Repository lookup iterates an FxHashMap and returns the first
`starts_with` match. For nested repos, both parent and child match.
The function returns whichever one FxHashMap iterates first, which
has no guaranteed relationship to path specificity.

Apply the same `max_by_key` pattern already used by
`GitStore::repository_and_path_for_project_path()` to select the
repository with the longest (most specific) work directory path.

Apply the same change to `git_ui::resolve_active_repository()` and
`recent_projects::get_branch_for_worktree()`, which use the same
first-match pattern.

Closes zed-industries#7566
@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Mar 19, 2026
@zed-codeowner-coordinator zed-codeowner-coordinator bot requested review from a team, Anthony-Eid and osiewicz and removed request for a team March 19, 2026 05:47
@zed-community-bot zed-community-bot bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Mar 19, 2026
@zed-codeowner-coordinator zed-codeowner-coordinator bot requested review from cameron1024 and smitbarmase and removed request for a team March 19, 2026 05:47
@github-actions github-actions bot added size/S and removed size/S labels Mar 19, 2026
Copy link
Copy Markdown
Member

@smitbarmase smitbarmase left a comment

Choose a reason for hiding this comment

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

Thanks!

@smitbarmase smitbarmase enabled auto-merge (squash) March 19, 2026 15:51
@smitbarmase smitbarmase merged commit 806a72c into zed-industries:main Mar 19, 2026
30 checks passed
Caio-Ze added a commit to Caio-Ze/postprod-ide that referenced this pull request Mar 19, 2026
…industries#51898)

## Context

I have a project with a nested git repo inside a parent repo (separate
`.git` directory, not a submodule). The title bar shows the parent's
branch instead of the nested repo's own branch.

The issue is in `get_repository_for_worktree()` — it iterates
`git_store.repositories()` (an `FxHashMap`) and returns the first
`starts_with` match. Both the parent and nested repo match, and
whichever one FxHashMap iterates first wins. There's no reason it should
be one or the other.

The fix already exists elsewhere in the codebase —
`GitStore::repository_and_path_for_project_path()` at
`git_store.rs:1826` uses `.max_by_key()` to pick the most specific
(longest path) match. This PR applies the same approach to three
functions that have the same problem:

- `TitleBar::get_repository_for_worktree()` — branch display in title
bar
- `resolve_active_repository()` in `git_ui` — repository selection for
the git panel
- `get_branch_for_worktree()` in `recent_projects` — branch display in
the project switcher

Two other locations use a similar `starts_with` pattern
(`effective_active_worktree()` in `title_bar.rs` and worktree selection
in `recent_projects.rs`) but those iterate worktrees against a single
known repo, not repos against a worktree — so first-match and
longest-match give the same result. Left those unchanged.

Closes zed-industries#7566

## How to Review

All three changes are the same transformation: first-match loop (or
`.find()`) → `.filter().max_by_key()` on path length. The reference is
at `crates/project/src/git_store.rs:1826`.

The primary fix is `get_repository_for_worktree()` in `title_bar.rs`.
The other two are the same pattern.

One difference from the reference: I used `.as_os_str().len()` instead
of `.clone()` for the `max_by_key` key — avoids cloning an `Arc<Path>`
per comparison. Among prefix-related paths (which is all that passes the
filter), the longer path is always the more specific match, so length
comparison is equivalent.

`title_bar` has no existing test infrastructure. Happy to add a test if
you'd like — the setup would follow the pattern in
`test_git_traversal_with_nested_repos`
(`crates/project/tests/integration/git_store.rs`).

```
cargo test -p title_bar -p git_ui -p recent_projects
./script/clippy
```

## Self-Review Checklist

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed branch picker showing parent repository's branch instead of the
nested repository's branch when working in submodules or nested git
repos.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
AmaanBilwar pushed a commit to AmaanBilwar/zed that referenced this pull request Mar 20, 2026
…industries#51898)

## Context

I have a project with a nested git repo inside a parent repo (separate
`.git` directory, not a submodule). The title bar shows the parent's
branch instead of the nested repo's own branch.

The issue is in `get_repository_for_worktree()` — it iterates
`git_store.repositories()` (an `FxHashMap`) and returns the first
`starts_with` match. Both the parent and nested repo match, and
whichever one FxHashMap iterates first wins. There's no reason it should
be one or the other.

The fix already exists elsewhere in the codebase —
`GitStore::repository_and_path_for_project_path()` at
`git_store.rs:1826` uses `.max_by_key()` to pick the most specific
(longest path) match. This PR applies the same approach to three
functions that have the same problem:

- `TitleBar::get_repository_for_worktree()` — branch display in title
bar
- `resolve_active_repository()` in `git_ui` — repository selection for
the git panel
- `get_branch_for_worktree()` in `recent_projects` — branch display in
the project switcher

Two other locations use a similar `starts_with` pattern
(`effective_active_worktree()` in `title_bar.rs` and worktree selection
in `recent_projects.rs`) but those iterate worktrees against a single
known repo, not repos against a worktree — so first-match and
longest-match give the same result. Left those unchanged.

Closes zed-industries#7566

## How to Review

All three changes are the same transformation: first-match loop (or
`.find()`) → `.filter().max_by_key()` on path length. The reference is
at `crates/project/src/git_store.rs:1826`.

The primary fix is `get_repository_for_worktree()` in `title_bar.rs`.
The other two are the same pattern.

One difference from the reference: I used `.as_os_str().len()` instead
of `.clone()` for the `max_by_key` key — avoids cloning an `Arc<Path>`
per comparison. Among prefix-related paths (which is all that passes the
filter), the longer path is always the more specific match, so length
comparison is equivalent.

`title_bar` has no existing test infrastructure. Happy to add a test if
you'd like — the setup would follow the pattern in
`test_git_traversal_with_nested_repos`
(`crates/project/tests/integration/git_store.rs`).

```
cargo test -p title_bar -p git_ui -p recent_projects
./script/clippy
```

## Self-Review Checklist

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed branch picker showing parent repository's branch instead of the
nested repository's branch when working in submodules or nested git
repos.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
toshmukhamedov pushed a commit to toshmukhamedov/zed that referenced this pull request Mar 20, 2026
…industries#51898)

## Context

I have a project with a nested git repo inside a parent repo (separate
`.git` directory, not a submodule). The title bar shows the parent's
branch instead of the nested repo's own branch.

The issue is in `get_repository_for_worktree()` — it iterates
`git_store.repositories()` (an `FxHashMap`) and returns the first
`starts_with` match. Both the parent and nested repo match, and
whichever one FxHashMap iterates first wins. There's no reason it should
be one or the other.

The fix already exists elsewhere in the codebase —
`GitStore::repository_and_path_for_project_path()` at
`git_store.rs:1826` uses `.max_by_key()` to pick the most specific
(longest path) match. This PR applies the same approach to three
functions that have the same problem:

- `TitleBar::get_repository_for_worktree()` — branch display in title
bar
- `resolve_active_repository()` in `git_ui` — repository selection for
the git panel
- `get_branch_for_worktree()` in `recent_projects` — branch display in
the project switcher

Two other locations use a similar `starts_with` pattern
(`effective_active_worktree()` in `title_bar.rs` and worktree selection
in `recent_projects.rs`) but those iterate worktrees against a single
known repo, not repos against a worktree — so first-match and
longest-match give the same result. Left those unchanged.

Closes zed-industries#7566

## How to Review

All three changes are the same transformation: first-match loop (or
`.find()`) → `.filter().max_by_key()` on path length. The reference is
at `crates/project/src/git_store.rs:1826`.

The primary fix is `get_repository_for_worktree()` in `title_bar.rs`.
The other two are the same pattern.

One difference from the reference: I used `.as_os_str().len()` instead
of `.clone()` for the `max_by_key` key — avoids cloning an `Arc<Path>`
per comparison. Among prefix-related paths (which is all that passes the
filter), the longer path is always the more specific match, so length
comparison is equivalent.

`title_bar` has no existing test infrastructure. Happy to add a test if
you'd like — the setup would follow the pattern in
`test_git_traversal_with_nested_repos`
(`crates/project/tests/integration/git_store.rs`).

```
cargo test -p title_bar -p git_ui -p recent_projects
./script/clippy
```

## Self-Review Checklist

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed branch picker showing parent repository's branch instead of the
nested repository's branch when working in submodules or nested git
repos.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
AmaanBilwar pushed a commit to AmaanBilwar/zed that referenced this pull request Mar 23, 2026
…industries#51898)

## Context

I have a project with a nested git repo inside a parent repo (separate
`.git` directory, not a submodule). The title bar shows the parent's
branch instead of the nested repo's own branch.

The issue is in `get_repository_for_worktree()` — it iterates
`git_store.repositories()` (an `FxHashMap`) and returns the first
`starts_with` match. Both the parent and nested repo match, and
whichever one FxHashMap iterates first wins. There's no reason it should
be one or the other.

The fix already exists elsewhere in the codebase —
`GitStore::repository_and_path_for_project_path()` at
`git_store.rs:1826` uses `.max_by_key()` to pick the most specific
(longest path) match. This PR applies the same approach to three
functions that have the same problem:

- `TitleBar::get_repository_for_worktree()` — branch display in title
bar
- `resolve_active_repository()` in `git_ui` — repository selection for
the git panel
- `get_branch_for_worktree()` in `recent_projects` — branch display in
the project switcher

Two other locations use a similar `starts_with` pattern
(`effective_active_worktree()` in `title_bar.rs` and worktree selection
in `recent_projects.rs`) but those iterate worktrees against a single
known repo, not repos against a worktree — so first-match and
longest-match give the same result. Left those unchanged.

Closes zed-industries#7566

## How to Review

All three changes are the same transformation: first-match loop (or
`.find()`) → `.filter().max_by_key()` on path length. The reference is
at `crates/project/src/git_store.rs:1826`.

The primary fix is `get_repository_for_worktree()` in `title_bar.rs`.
The other two are the same pattern.

One difference from the reference: I used `.as_os_str().len()` instead
of `.clone()` for the `max_by_key` key — avoids cloning an `Arc<Path>`
per comparison. Among prefix-related paths (which is all that passes the
filter), the longer path is always the more specific match, so length
comparison is equivalent.

`title_bar` has no existing test infrastructure. Happy to add a test if
you'd like — the setup would follow the pattern in
`test_git_traversal_with_nested_repos`
(`crates/project/tests/integration/git_store.rs`).

```
cargo test -p title_bar -p git_ui -p recent_projects
./script/clippy
```

## Self-Review Checklist

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed branch picker showing parent repository's branch instead of the
nested repository's branch when working in submodules or nested git
repos.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Caio-Ze added a commit to Caio-Ze/postprod-ide that referenced this pull request Apr 10, 2026
Fixup for dd0d5be. The original commit hooked `active_item_path_changed`,
which fires on every file click and tab switch — making the profile react
to individual file focus rather than to project activation. That was wrong:
the profile should track which worktree is effectively active in the
workspace (the one whose name shows next to the branch in the title bar),
not the worktree of whichever file the user happened to click last.

This commit rewrites the identification logic to mirror
`TitleBar::effective_active_worktree` (crates/title_bar/src/title_bar.rs:463):

  1. `active_worktree_override` if set — the user's explicit pick via the
     project dropdown (introduced by Danilo Leal in c9003e1 for git
     operations).
  2. The worktree containing `Project::active_repository` — the repo of
     the active file, which thanks to zed-industries#51898 resolves to the most specific
     repository for nested git setups.
  3. First visible worktree as a final fallback.

With this logic, `apply_local_active_profile` and
`TitleBar::effective_active_worktree` always agree on "which worktree is
the current one." The profile name and the title bar project name stay
synchronized by definition — they read the same state through the same
ordered resolution.

Changes:

- Rewrote `apply_local_active_profile` to call a new private helper
  `effective_active_worktree_id`, which implements the three-tier logic.
  The helper is deliberately inlined (duplicated from
  `TitleBar::effective_active_worktree`) rather than extracted onto
  `Workspace` as a shared method — that extraction is a natural follow-up
  but kept out of scope here.
- Removed the `self.apply_local_active_profile(cx)` call at the end of
  `active_item_path_changed`. File clicks no longer trigger profile
  re-evaluation.
- Added `self.apply_local_active_profile(cx)` calls to
  `set_active_worktree_override` and `clear_active_worktree_override`, so
  picking a worktree from the project dropdown drives profile activation.

`Workspace::new`'s deferred setup hook and the `MultiWorkspace::activate`
hook are unchanged from dd0d5be.

Spec: private/specs/persistent-active-profile.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Caio-Ze added a commit to Caio-Ze/postprod-ide that referenced this pull request Apr 11, 2026
Fixup for dd0d5be. The original commit hooked `active_item_path_changed`,
which fires on every file click and tab switch — making the profile react
to individual file focus rather than to project activation. That was wrong:
the profile should track which worktree is effectively active in the
workspace (the one whose name shows next to the branch in the title bar),
not the worktree of whichever file the user happened to click last.

This commit rewrites the identification logic to mirror
`TitleBar::effective_active_worktree` (crates/title_bar/src/title_bar.rs:463):

  1. `active_worktree_override` if set — the user's explicit pick via the
     project dropdown (introduced by Danilo Leal in c9003e1 for git
     operations).
  2. The worktree containing `Project::active_repository` — the repo of
     the active file, which thanks to zed-industries#51898 resolves to the most specific
     repository for nested git setups.
  3. First visible worktree as a final fallback.

With this logic, `apply_local_active_profile` and
`TitleBar::effective_active_worktree` always agree on "which worktree is
the current one." The profile name and the title bar project name stay
synchronized by definition — they read the same state through the same
ordered resolution.

Changes:

- Rewrote `apply_local_active_profile` to call a new private helper
  `effective_active_worktree_id`, which implements the three-tier logic.
  The helper is deliberately inlined (duplicated from
  `TitleBar::effective_active_worktree`) rather than extracted onto
  `Workspace` as a shared method — that extraction is a natural follow-up
  but kept out of scope here.
- Removed the `self.apply_local_active_profile(cx)` call at the end of
  `active_item_path_changed`. File clicks no longer trigger profile
  re-evaluation.
- Added `self.apply_local_active_profile(cx)` calls to
  `set_active_worktree_override` and `clear_active_worktree_override`, so
  picking a worktree from the project dropdown drives profile activation.

`Workspace::new`'s deferred setup hook and the `MultiWorkspace::activate`
hook are unchanged from dd0d5be.

Spec: private/specs/persistent-active-profile.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show submodule branch on the GUI

3 participants