Represent git statuses more faithfully (#23082)
First, parse the output of `git status --porcelain=v1` into a representation that can handle the full "grammar" and doesn't lose information. Second, as part of pushing this throughout the codebase, expand the use of the existing `GitSummary` type to all the places where status propagation is in play (i.e., anywhere we're dealing with a mix of files and directories), and get rid of the previous `GitSummary -> GitFileStatus` conversion. - [x] Synchronize new representation over collab - [x] Update zed.proto - [x] Update DB models - [x] Update `GitSummary` and summarization for the new `FileStatus` - [x] Fix all tests - [x] worktree - [x] collab - [x] Clean up `FILE_*` constants - [x] New collab tests to exercise syncing of complex statuses - [x] Run it locally and make sure it looks good Release Notes: - N/A --------- Co-authored-by: Mikayla <mikayla@zed.dev> Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
parent
224f3d4746
commit
a41d72ee81
24 changed files with 1015 additions and 552 deletions
|
@ -13,7 +13,8 @@ use client::{User, RECEIVE_TIMEOUT};
|
|||
use collections::{HashMap, HashSet};
|
||||
use fs::{FakeFs, Fs as _, RemoveOptions};
|
||||
use futures::{channel::mpsc, StreamExt as _};
|
||||
use git::repository::GitFileStatus;
|
||||
|
||||
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
|
||||
use gpui::{
|
||||
px, size, AppContext, BackgroundExecutor, Model, Modifiers, MouseButton, MouseDownEvent,
|
||||
TestAppContext, UpdateGlobal,
|
||||
|
@ -2889,11 +2890,20 @@ async fn test_git_status_sync(
|
|||
const A_TXT: &str = "a.txt";
|
||||
const B_TXT: &str = "b.txt";
|
||||
|
||||
const A_STATUS_START: FileStatus = FileStatus::Tracked(TrackedStatus {
|
||||
index_status: StatusCode::Added,
|
||||
worktree_status: StatusCode::Modified,
|
||||
});
|
||||
const B_STATUS_START: FileStatus = FileStatus::Unmerged(UnmergedStatus {
|
||||
first_head: UnmergedStatusCode::Updated,
|
||||
second_head: UnmergedStatusCode::Deleted,
|
||||
});
|
||||
|
||||
client_a.fs().set_status_for_repo_via_git_operation(
|
||||
Path::new("/dir/.git"),
|
||||
&[
|
||||
(Path::new(A_TXT), GitFileStatus::Added),
|
||||
(Path::new(B_TXT), GitFileStatus::Added),
|
||||
(Path::new(A_TXT), A_STATUS_START),
|
||||
(Path::new(B_TXT), B_STATUS_START),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -2913,7 +2923,7 @@ async fn test_git_status_sync(
|
|||
#[track_caller]
|
||||
fn assert_status(
|
||||
file: &impl AsRef<Path>,
|
||||
status: Option<GitFileStatus>,
|
||||
status: Option<FileStatus>,
|
||||
project: &Project,
|
||||
cx: &AppContext,
|
||||
) {
|
||||
|
@ -2926,20 +2936,29 @@ async fn test_git_status_sync(
|
|||
}
|
||||
|
||||
project_local.read_with(cx_a, |project, cx| {
|
||||
assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
|
||||
assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
|
||||
assert_status(&Path::new(A_TXT), Some(A_STATUS_START), project, cx);
|
||||
assert_status(&Path::new(B_TXT), Some(B_STATUS_START), project, cx);
|
||||
});
|
||||
|
||||
project_remote.read_with(cx_b, |project, cx| {
|
||||
assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
|
||||
assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
|
||||
assert_status(&Path::new(A_TXT), Some(A_STATUS_START), project, cx);
|
||||
assert_status(&Path::new(B_TXT), Some(B_STATUS_START), project, cx);
|
||||
});
|
||||
|
||||
const A_STATUS_END: FileStatus = FileStatus::Tracked(TrackedStatus {
|
||||
index_status: StatusCode::Added,
|
||||
worktree_status: StatusCode::Unmodified,
|
||||
});
|
||||
const B_STATUS_END: FileStatus = FileStatus::Tracked(TrackedStatus {
|
||||
index_status: StatusCode::Deleted,
|
||||
worktree_status: StatusCode::Unmodified,
|
||||
});
|
||||
|
||||
client_a.fs().set_status_for_repo_via_working_copy_change(
|
||||
Path::new("/dir/.git"),
|
||||
&[
|
||||
(Path::new(A_TXT), GitFileStatus::Modified),
|
||||
(Path::new(B_TXT), GitFileStatus::Modified),
|
||||
(Path::new(A_TXT), A_STATUS_END),
|
||||
(Path::new(B_TXT), B_STATUS_END),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -2949,33 +2968,13 @@ async fn test_git_status_sync(
|
|||
// Smoke test status reading
|
||||
|
||||
project_local.read_with(cx_a, |project, cx| {
|
||||
assert_status(
|
||||
&Path::new(A_TXT),
|
||||
Some(GitFileStatus::Modified),
|
||||
project,
|
||||
cx,
|
||||
);
|
||||
assert_status(
|
||||
&Path::new(B_TXT),
|
||||
Some(GitFileStatus::Modified),
|
||||
project,
|
||||
cx,
|
||||
);
|
||||
assert_status(&Path::new(A_TXT), Some(A_STATUS_END), project, cx);
|
||||
assert_status(&Path::new(B_TXT), Some(B_STATUS_END), project, cx);
|
||||
});
|
||||
|
||||
project_remote.read_with(cx_b, |project, cx| {
|
||||
assert_status(
|
||||
&Path::new(A_TXT),
|
||||
Some(GitFileStatus::Modified),
|
||||
project,
|
||||
cx,
|
||||
);
|
||||
assert_status(
|
||||
&Path::new(B_TXT),
|
||||
Some(GitFileStatus::Modified),
|
||||
project,
|
||||
cx,
|
||||
);
|
||||
assert_status(&Path::new(A_TXT), Some(A_STATUS_END), project, cx);
|
||||
assert_status(&Path::new(B_TXT), Some(B_STATUS_END), project, cx);
|
||||
});
|
||||
|
||||
// And synchronization while joining
|
||||
|
@ -2983,18 +2982,8 @@ async fn test_git_status_sync(
|
|||
executor.run_until_parked();
|
||||
|
||||
project_remote_c.read_with(cx_c, |project, cx| {
|
||||
assert_status(
|
||||
&Path::new(A_TXT),
|
||||
Some(GitFileStatus::Modified),
|
||||
project,
|
||||
cx,
|
||||
);
|
||||
assert_status(
|
||||
&Path::new(B_TXT),
|
||||
Some(GitFileStatus::Modified),
|
||||
project,
|
||||
cx,
|
||||
);
|
||||
assert_status(&Path::new(A_TXT), Some(A_STATUS_END), project, cx);
|
||||
assert_status(&Path::new(B_TXT), Some(B_STATUS_END), project, cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use call::ActiveCall;
|
|||
use collections::{BTreeMap, HashMap};
|
||||
use editor::Bias;
|
||||
use fs::{FakeFs, Fs as _};
|
||||
use git::repository::GitFileStatus;
|
||||
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
|
||||
use gpui::{BackgroundExecutor, Model, TestAppContext};
|
||||
use language::{
|
||||
range_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, PointUtf16,
|
||||
|
@ -127,7 +127,7 @@ enum GitOperation {
|
|||
},
|
||||
WriteGitStatuses {
|
||||
repo_path: PathBuf,
|
||||
statuses: Vec<(PathBuf, GitFileStatus)>,
|
||||
statuses: Vec<(PathBuf, FileStatus)>,
|
||||
git_operation: bool,
|
||||
},
|
||||
}
|
||||
|
@ -1458,17 +1458,7 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
|
|||
|
||||
let statuses = file_paths
|
||||
.into_iter()
|
||||
.map(|paths| {
|
||||
(
|
||||
paths,
|
||||
match rng.gen_range(0..3_u32) {
|
||||
0 => GitFileStatus::Added,
|
||||
1 => GitFileStatus::Modified,
|
||||
2 => GitFileStatus::Conflict,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.map(|path| (path, gen_status(rng)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let git_operation = rng.gen::<bool>();
|
||||
|
@ -1613,3 +1603,41 @@ fn gen_file_name(rng: &mut StdRng) -> String {
|
|||
}
|
||||
name
|
||||
}
|
||||
|
||||
fn gen_status(rng: &mut StdRng) -> FileStatus {
|
||||
fn gen_status_code(rng: &mut StdRng) -> StatusCode {
|
||||
match rng.gen_range(0..7) {
|
||||
0 => StatusCode::Modified,
|
||||
1 => StatusCode::TypeChanged,
|
||||
2 => StatusCode::Added,
|
||||
3 => StatusCode::Deleted,
|
||||
4 => StatusCode::Renamed,
|
||||
5 => StatusCode::Copied,
|
||||
6 => StatusCode::Unmodified,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_unmerged_status_code(rng: &mut StdRng) -> UnmergedStatusCode {
|
||||
match rng.gen_range(0..3) {
|
||||
0 => UnmergedStatusCode::Updated,
|
||||
1 => UnmergedStatusCode::Added,
|
||||
2 => UnmergedStatusCode::Deleted,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
match rng.gen_range(0..4) {
|
||||
0 => FileStatus::Untracked,
|
||||
1 => FileStatus::Ignored,
|
||||
2 => FileStatus::Unmerged(UnmergedStatus {
|
||||
first_head: gen_unmerged_status_code(rng),
|
||||
second_head: gen_unmerged_status_code(rng),
|
||||
}),
|
||||
3 => FileStatus::Tracked(TrackedStatus {
|
||||
index_status: gen_status_code(rng),
|
||||
worktree_status: gen_status_code(rng),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue