mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
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) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - git: Fix remote branch picker
This commit is contained in:
parent
6e1c630290
commit
8589211c02
6 changed files with 156 additions and 0 deletions
|
|
@ -886,6 +886,7 @@ impl Database {
|
|||
current_merge_conflicts,
|
||||
branch_summary,
|
||||
head_commit_details,
|
||||
branch_list: Vec::new(),
|
||||
scan_id: db_repository_entry.scan_id as u64,
|
||||
is_last_update: true,
|
||||
merge_message: db_repository_entry.merge_message,
|
||||
|
|
|
|||
|
|
@ -790,6 +790,7 @@ impl Database {
|
|||
current_merge_conflicts,
|
||||
branch_summary,
|
||||
head_commit_details,
|
||||
branch_list: Vec::new(),
|
||||
project_id: project_id.to_proto(),
|
||||
id: db_repository.id as u64,
|
||||
abs_path: db_repository.abs_path.clone(),
|
||||
|
|
|
|||
|
|
@ -91,6 +91,29 @@ fn collect_diff_stats<C: gpui::AppContext>(
|
|||
})
|
||||
}
|
||||
|
||||
fn branch_list_snapshot(
|
||||
project: &gpui::Entity<project::Project>,
|
||||
cx: &mut TestAppContext,
|
||||
) -> (Option<String>, Vec<String>) {
|
||||
project.read_with(cx, |project, cx| {
|
||||
let repos = project.repositories(cx);
|
||||
assert_eq!(repos.len(), 1, "project should have exactly 1 repository");
|
||||
let repo = repos.values().next().unwrap();
|
||||
let snapshot = repo.read(cx).snapshot();
|
||||
(
|
||||
snapshot
|
||||
.branch
|
||||
.as_ref()
|
||||
.map(|branch| branch.name().to_string()),
|
||||
snapshot
|
||||
.branch_list
|
||||
.iter()
|
||||
.map(|branch| branch.ref_name.to_string())
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_project_diff(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
let mut server = TestServer::start(cx_a.background_executor.clone()).await;
|
||||
|
|
@ -480,6 +503,95 @@ async fn test_remote_git_head_sha(
|
|||
assert_eq!(remote_head_sha.unwrap(), local_head_sha);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_branch_list_sync(
|
||||
executor: BackgroundExecutor,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
let mut server = TestServer::start(executor.clone()).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
server
|
||||
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
.await;
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
client_a
|
||||
.fs()
|
||||
.insert_tree(
|
||||
path!("/project"),
|
||||
json!({ ".git": {}, "file.txt": "content" }),
|
||||
)
|
||||
.await;
|
||||
client_a.fs().insert_branches(
|
||||
Path::new(path!("/project/.git")),
|
||||
&["main", "feature-1", "feature-2"],
|
||||
);
|
||||
|
||||
let (project_a, _) = client_a.build_local_project(path!("/project"), cx_a).await;
|
||||
executor.run_until_parked();
|
||||
|
||||
let host_snapshot = branch_list_snapshot(&project_a, cx_a);
|
||||
assert_eq!(host_snapshot.0.as_deref(), Some("main"));
|
||||
assert_eq!(
|
||||
host_snapshot.1,
|
||||
vec![
|
||||
"refs/heads/feature-1".to_string(),
|
||||
"refs/heads/feature-2".to_string(),
|
||||
"refs/heads/main".to_string(),
|
||||
]
|
||||
);
|
||||
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let project_b = client_b.join_remote_project(project_id, cx_b).await;
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
|
||||
|
||||
cx_b.update(|cx| {
|
||||
repo_b.update(cx, |repository, _cx| {
|
||||
repository.create_branch("totally-new-branch".to_string(), None)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
cx_b.update(|cx| {
|
||||
repo_b.update(cx, |repository, _cx| {
|
||||
repository.change_branch("totally-new-branch".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
let host_snapshot_after_update = branch_list_snapshot(&project_a, cx_a);
|
||||
assert_eq!(
|
||||
host_snapshot_after_update.0.as_deref(),
|
||||
Some("totally-new-branch")
|
||||
);
|
||||
assert_eq!(
|
||||
host_snapshot_after_update.1,
|
||||
vec![
|
||||
"refs/heads/feature-1".to_string(),
|
||||
"refs/heads/feature-2".to_string(),
|
||||
"refs/heads/main".to_string(),
|
||||
"refs/heads/totally-new-branch".to_string(),
|
||||
]
|
||||
);
|
||||
|
||||
let guest_snapshot_after_update = branch_list_snapshot(&project_b, cx_b);
|
||||
assert_eq!(guest_snapshot_after_update, host_snapshot_after_update);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_linked_worktrees_sync(
|
||||
executor: BackgroundExecutor,
|
||||
|
|
|
|||
|
|
@ -3854,6 +3854,7 @@ impl RepositorySnapshot {
|
|||
fn initial_update(&self, project_id: u64) -> proto::UpdateRepository {
|
||||
proto::UpdateRepository {
|
||||
branch_summary: self.branch.as_ref().map(branch_to_proto),
|
||||
branch_list: self.branch_list.iter().map(branch_to_proto).collect(),
|
||||
head_commit_details: self.head_commit.as_ref().map(commit_details_to_proto),
|
||||
updated_statuses: self
|
||||
.statuses_by_path
|
||||
|
|
@ -3939,6 +3940,7 @@ impl RepositorySnapshot {
|
|||
|
||||
proto::UpdateRepository {
|
||||
branch_summary: self.branch.as_ref().map(branch_to_proto),
|
||||
branch_list: self.branch_list.iter().map(branch_to_proto).collect(),
|
||||
head_commit_details: self.head_commit.as_ref().map(commit_details_to_proto),
|
||||
updated_statuses,
|
||||
removed_statuses,
|
||||
|
|
@ -6758,6 +6760,15 @@ impl Repository {
|
|||
self.snapshot.branch = new_branch;
|
||||
self.snapshot.head_commit = new_head_commit;
|
||||
|
||||
if update.is_last_update {
|
||||
let new_branch_list: Arc<[Branch]> =
|
||||
update.branch_list.iter().map(proto_to_branch).collect();
|
||||
if *self.snapshot.branch_list != *new_branch_list {
|
||||
cx.emit(RepositoryEvent::BranchListChanged);
|
||||
}
|
||||
self.snapshot.branch_list = new_branch_list;
|
||||
}
|
||||
|
||||
// We don't store any merge head state for downstream projects; the upstream
|
||||
// will track it and we will just get the updated conflicts
|
||||
let new_merge_heads = TreeMap::from_ordered_entries(
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ message UpdateRepository {
|
|||
optional string remote_origin_url = 15;
|
||||
optional string original_repo_abs_path = 16;
|
||||
repeated Worktree linked_worktrees = 17;
|
||||
repeated Branch branch_list = 18;
|
||||
}
|
||||
|
||||
message RemoveRepository {
|
||||
|
|
|
|||
|
|
@ -917,6 +917,7 @@ pub fn split_repository_update(
|
|||
) -> impl Iterator<Item = UpdateRepository> {
|
||||
let mut updated_statuses_iter = mem::take(&mut update.updated_statuses).into_iter().fuse();
|
||||
let mut removed_statuses_iter = mem::take(&mut update.removed_statuses).into_iter().fuse();
|
||||
let branch_list = mem::take(&mut update.branch_list);
|
||||
std::iter::from_fn({
|
||||
let update = update.clone();
|
||||
move || {
|
||||
|
|
@ -934,6 +935,7 @@ pub fn split_repository_update(
|
|||
Some(UpdateRepository {
|
||||
updated_statuses,
|
||||
removed_statuses,
|
||||
branch_list: Vec::new(),
|
||||
is_last_update: false,
|
||||
..update.clone()
|
||||
})
|
||||
|
|
@ -942,6 +944,7 @@ pub fn split_repository_update(
|
|||
.chain([UpdateRepository {
|
||||
updated_statuses: Vec::new(),
|
||||
removed_statuses: Vec::new(),
|
||||
branch_list,
|
||||
is_last_update: true,
|
||||
..update
|
||||
}])
|
||||
|
|
@ -999,4 +1002,31 @@ mod tests {
|
|||
};
|
||||
assert_eq!(PeerId::from_u64(peer_id.as_u64()), peer_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_repository_update_keeps_branch_list_on_final_chunk() {
|
||||
let update = UpdateRepository {
|
||||
updated_statuses: vec![
|
||||
StatusEntry::default(),
|
||||
StatusEntry::default(),
|
||||
StatusEntry::default(),
|
||||
],
|
||||
branch_list: vec![Branch {
|
||||
ref_name: "refs/heads/main".into(),
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let chunks = split_repository_update(update).collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(chunks.len(), 3);
|
||||
assert!(chunks[0].branch_list.is_empty());
|
||||
assert!(chunks[1].branch_list.is_empty());
|
||||
assert_eq!(chunks[2].branch_list.len(), 1);
|
||||
assert_eq!(chunks[2].branch_list[0].ref_name, "refs/heads/main");
|
||||
assert!(!chunks[0].is_last_update);
|
||||
assert!(!chunks[1].is_last_update);
|
||||
assert!(chunks[2].is_last_update);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue