Move repository state RPC handlers to the GitStore (#27391)

This is another in the series of PRs to make the GitStore own all
repository state and enable better concurrency control for git
repository scans.

After this PR, the `RepositoryEntry`s stored in worktree snapshots are
used only as a staging ground for local GitStores to pull from after
git-related events; non-local worktrees don't store them at all,
although this is not reflected in the types. GitTraversal and other
places that need information about repositories get it from the
GitStore. The GitStore also takes over handling of the new
UpdateRepository and RemoveRepository messages. However, repositories
are still discovered and scanned on a per-worktree basis, and we're
still identifying them by the (worktree-specific) project entry ID of
their working directory.

- [x] Remove WorkDirectory from RepositoryEntry
- [x] Remove worktree IDs from repository-related RPC messages
- [x] Handle UpdateRepository and RemoveRepository RPCs from the
GitStore

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Cole Miller 2025-03-26 18:23:44 -04:00 committed by GitHub
parent 1e8b50f471
commit 6924720b35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 1387 additions and 1399 deletions

View file

@ -2892,15 +2892,17 @@ async fn test_git_branch_name(
#[track_caller]
fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &App) {
let branch_name = branch_name.map(Into::into);
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 1);
let worktree = worktrees[0].clone();
let snapshot = worktree.read(cx).snapshot();
let repo = snapshot.repositories().first().unwrap();
let repositories = project.repositories(cx).values().collect::<Vec<_>>();
assert_eq!(repositories.len(), 1);
let repository = repositories[0].clone();
assert_eq!(
repo.branch().map(|branch| branch.name.to_string()),
repository
.read(cx)
.repository_entry
.branch()
.map(|branch| branch.name.to_string()),
branch_name
);
)
}
// Smoke test branch reading
@ -3022,11 +3024,20 @@ async fn test_git_status_sync(
cx: &App,
) {
let file = file.as_ref();
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 1);
let worktree = worktrees[0].clone();
let snapshot = worktree.read(cx).snapshot();
assert_eq!(snapshot.status_for_file(file), status);
let repos = project
.repositories(cx)
.values()
.cloned()
.collect::<Vec<_>>();
assert_eq!(repos.len(), 1);
let repo = repos.into_iter().next().unwrap();
assert_eq!(
repo.read(cx)
.repository_entry
.status_for_path(&file.into())
.map(|entry| entry.status),
status
);
}
project_local.read_with(cx_a, |project, cx| {
@ -3094,6 +3105,27 @@ async fn test_git_status_sync(
assert_status("b.txt", Some(B_STATUS_END), project, cx);
assert_status("c.txt", Some(C_STATUS_END), project, cx);
});
// Now remove the original git repository and check that collaborators are notified.
client_a
.fs()
.remove_dir("/dir/.git".as_ref(), RemoveOptions::default())
.await
.unwrap();
executor.run_until_parked();
project_remote.update(cx_b, |project, cx| {
pretty_assertions::assert_eq!(
project.git_store().read(cx).repo_snapshots(cx),
HashMap::default()
);
});
project_remote_c.update(cx_c, |project, cx| {
pretty_assertions::assert_eq!(
project.git_store().read(cx).repo_snapshots(cx),
HashMap::default()
);
});
}
#[gpui::test(iterations = 10)]