perf: avoid eager diff loading and full-file typing work
This commit is contained in:
+185
-64
@@ -127,6 +127,26 @@ fn working(kind: RepoKind) -> Revision {
|
||||
r.target = RevisionTarget::Working;
|
||||
r
|
||||
}
|
||||
|
||||
fn compare_loaded(
|
||||
repository: &mut Repository,
|
||||
before: &Revision,
|
||||
after: &Revision,
|
||||
buffers: &[(PathBuf, String)],
|
||||
) -> Result<Comparison, String> {
|
||||
let mut comparison = repository.compare(1, before, after, buffers)?;
|
||||
for (index, file) in comparison.files.iter_mut().enumerate() {
|
||||
*file = repository.load_file(1, index)?;
|
||||
if file
|
||||
.notice
|
||||
.as_deref()
|
||||
.is_some_and(|notice| notice.contains("Binary or non-UTF-8"))
|
||||
{
|
||||
comparison.editable_paths.remove(&file.path);
|
||||
}
|
||||
}
|
||||
Ok(comparison)
|
||||
}
|
||||
fn changed<'a>(comparison: &'a Comparison, path: &Path) -> &'a vamp::diff::ChangedFile {
|
||||
comparison
|
||||
.files
|
||||
@@ -236,9 +256,13 @@ fn git_working_reads_disk_and_handles_ranges_ignored_files_binary_links_and_odd_
|
||||
assert!(catalog.entries.iter().any(|r| r.label == "nested"
|
||||
&& r.category == Category::Tags
|
||||
&& r.target == RevisionTarget::Commit(id.clone())));
|
||||
let comparison = repository
|
||||
.compare(&revision(&id, RepoKind::Git), &working(RepoKind::Git), &[])
|
||||
.unwrap();
|
||||
let comparison = compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&id, RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let text = changed(&comparison, Path::new("text"));
|
||||
assert_eq!(comparison.root, fs::canonicalize(&root).unwrap());
|
||||
assert!(comparison.editable_paths.contains(Path::new("text")));
|
||||
@@ -343,9 +367,13 @@ fn git_working_reads_disk_and_handles_ranges_ignored_files_binary_links_and_odd_
|
||||
assert_eq!(moved.old_path.as_deref(), Some(Path::new("rename-old")));
|
||||
assert_eq!(moved.before, moved.after);
|
||||
assert!(moved.hunks.is_empty());
|
||||
let reverse = repository
|
||||
.compare(&working(RepoKind::Git), &revision(&id, RepoKind::Git), &[])
|
||||
.unwrap();
|
||||
let reverse = compare_loaded(
|
||||
&mut repository,
|
||||
&working(RepoKind::Git),
|
||||
&revision(&id, RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(reverse.editable_paths.is_empty());
|
||||
assert_eq!(
|
||||
changed(&reverse, Path::new("added")).status,
|
||||
@@ -353,7 +381,7 @@ fn git_working_reads_disk_and_handles_ranges_ignored_files_binary_links_and_odd_
|
||||
);
|
||||
assert!(
|
||||
repository
|
||||
.compare(&working(RepoKind::Git), &working(RepoKind::Git), &[])
|
||||
.compare(1, &working(RepoKind::Git), &working(RepoKind::Git), &[])
|
||||
.unwrap()
|
||||
.files
|
||||
.is_empty()
|
||||
@@ -397,7 +425,7 @@ fn git_remote_refs_and_annotated_tags_are_discovered_before_fetch_and_history_st
|
||||
);
|
||||
let state = disk_state(&active);
|
||||
let mut repository = open(&active);
|
||||
let catalog = repository.catalog().unwrap();
|
||||
let catalog = repository.list_remote("origin").unwrap();
|
||||
let remote = catalog
|
||||
.entries
|
||||
.iter()
|
||||
@@ -439,24 +467,23 @@ fn git_remote_refs_and_annotated_tags_are_discovered_before_fetch_and_history_st
|
||||
.status
|
||||
.success()
|
||||
);
|
||||
let comparison = repository
|
||||
.compare(
|
||||
&revision(&local, RepoKind::Git),
|
||||
&remote,
|
||||
&[(active.join("file"), "unsaved local text\n".into())],
|
||||
)
|
||||
.unwrap();
|
||||
let comparison = compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&local, RepoKind::Git),
|
||||
&remote,
|
||||
&[(active.join("file"), "unsaved local text\n".into())],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(comparison.editable_paths.is_empty());
|
||||
assert_eq!(changed(&comparison, Path::new("file")).before, ["local"]);
|
||||
assert_eq!(
|
||||
changed(&comparison, Path::new("file")).after,
|
||||
["remote tip"]
|
||||
);
|
||||
let comparison = repository.compare(&tag, &remote, &[]).unwrap();
|
||||
let comparison = compare_loaded(&mut repository, &tag, &remote, &[]).unwrap();
|
||||
assert_eq!(changed(&comparison, Path::new("file")).before, ["ancestor"]);
|
||||
let comparison = repository
|
||||
.compare(&remote, &working(RepoKind::Git), &[])
|
||||
.unwrap();
|
||||
let comparison =
|
||||
compare_loaded(&mut repository, &remote, &working(RepoKind::Git), &[]).unwrap();
|
||||
assert_eq!(changed(&comparison, Path::new("file")).after, ["local"]);
|
||||
let loaded = repository.fetch_remote("origin").unwrap();
|
||||
let historic = loaded
|
||||
@@ -468,9 +495,13 @@ fn git_remote_refs_and_annotated_tags_are_discovered_before_fetch_and_history_st
|
||||
.unwrap();
|
||||
assert_eq!(historic.label, ancestor);
|
||||
assert_eq!(historic.description, "remote ancestor needle");
|
||||
let comparison = repository
|
||||
.compare(historic, &revision(&local, RepoKind::Git), &[])
|
||||
.unwrap();
|
||||
let comparison = compare_loaded(
|
||||
&mut repository,
|
||||
historic,
|
||||
&revision(&local, RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(changed(&comparison, Path::new("file")).before, ["ancestor"]);
|
||||
assert!(
|
||||
!command("git", &active, &["cat-file", "-e", &tip])
|
||||
@@ -541,9 +572,8 @@ fn jj_catalog_and_working_comparisons_prefer_jj_and_never_snapshot_or_merge_oper
|
||||
.iter()
|
||||
.any(|r| r.label == "baseline" && r.category == Category::Bookmarks)
|
||||
);
|
||||
let comparison = repository
|
||||
.compare(baseline, &working(RepoKind::Jujutsu), &[])
|
||||
.unwrap();
|
||||
let comparison =
|
||||
compare_loaded(&mut repository, baseline, &working(RepoKind::Jujutsu), &[]).unwrap();
|
||||
assert_eq!(changed(&comparison, Path::new("file")).after, ["disk"]);
|
||||
assert_eq!(
|
||||
changed(&comparison, Path::new("deleted")).status,
|
||||
@@ -644,7 +674,7 @@ fn jj_private_fetch_discovers_never_fetched_bookmark_and_searchable_remote_chang
|
||||
fs::write(active.join("unsnapshotted"), "keep disk\n").unwrap();
|
||||
let state = disk_state(&active);
|
||||
let mut repository = open(&active);
|
||||
let catalog = repository.catalog().unwrap();
|
||||
let catalog = repository.list_remote("origin").unwrap();
|
||||
let remote = catalog
|
||||
.entries
|
||||
.iter()
|
||||
@@ -663,9 +693,8 @@ fn jj_private_fetch_discovers_never_fetched_bookmark_and_searchable_remote_chang
|
||||
.iter()
|
||||
.any(|r| r.target == RevisionTarget::Commit(ancestor.clone()))
|
||||
);
|
||||
let comparison = repository
|
||||
.compare(&remote, &working(RepoKind::Jujutsu), &[])
|
||||
.unwrap();
|
||||
let comparison =
|
||||
compare_loaded(&mut repository, &remote, &working(RepoKind::Jujutsu), &[]).unwrap();
|
||||
assert_eq!(
|
||||
changed(&comparison, Path::new("file")).before,
|
||||
["remote tip"]
|
||||
@@ -682,7 +711,7 @@ fn jj_private_fetch_discovers_never_fetched_bookmark_and_searchable_remote_chang
|
||||
.unwrap();
|
||||
assert!(historic.label.contains(&ancestor) && historic.label.contains(&change));
|
||||
assert_eq!(historic.description, "remote historic needle");
|
||||
let comparison = repository.compare(historic, &remote, &[]).unwrap();
|
||||
let comparison = compare_loaded(&mut repository, historic, &remote, &[]).unwrap();
|
||||
assert_eq!(
|
||||
changed(&comparison, Path::new("file")).before,
|
||||
["remote ancestor"]
|
||||
@@ -735,7 +764,14 @@ fn errors_are_explicit_and_do_not_modify_active_repository() {
|
||||
let state = disk_state(&root);
|
||||
let mut repository = open(&root);
|
||||
let catalog = repository.catalog().unwrap();
|
||||
assert!(catalog.notice.unwrap().contains("broken"));
|
||||
assert!(catalog.notice.is_none());
|
||||
assert!(
|
||||
catalog
|
||||
.entries
|
||||
.iter()
|
||||
.any(|revision| revision.target == RevisionTarget::ListRemote("broken".into()))
|
||||
);
|
||||
assert!(repository.list_remote("broken").is_err());
|
||||
assert!(repository.fetch_remote("broken").is_err());
|
||||
assert!(
|
||||
repository
|
||||
@@ -746,6 +782,7 @@ fn errors_are_explicit_and_do_not_modify_active_repository() {
|
||||
assert!(
|
||||
repository
|
||||
.compare(
|
||||
1,
|
||||
&revision("--bad", RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[]
|
||||
@@ -756,6 +793,7 @@ fn errors_are_explicit_and_do_not_modify_active_repository() {
|
||||
assert!(
|
||||
repository
|
||||
.compare(
|
||||
1,
|
||||
&revision(&"f".repeat(40), RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[],
|
||||
@@ -769,7 +807,7 @@ fn errors_are_explicit_and_do_not_modify_active_repository() {
|
||||
};
|
||||
assert!(
|
||||
repository
|
||||
.compare(&loader, &revision(&id, RepoKind::Git), &[])
|
||||
.compare(1, &loader, &revision(&id, RepoKind::Git), &[])
|
||||
.unwrap_err()
|
||||
.contains("history loader")
|
||||
);
|
||||
@@ -810,6 +848,7 @@ fn unchanged_submodules_are_not_false_changes_and_changed_pointers_are_explicit(
|
||||
assert!(
|
||||
repository
|
||||
.compare(
|
||||
1,
|
||||
&revision(&baseline, RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[]
|
||||
@@ -824,13 +863,13 @@ fn unchanged_submodules_are_not_false_changes_and_changed_pointers_are_explicit(
|
||||
git(&root.join("nested"), &["fetch", "origin"]);
|
||||
git(&root.join("nested"), &["checkout", "--detach", &second]);
|
||||
let state = disk_state(&root);
|
||||
let comparison = repository
|
||||
.compare(
|
||||
&revision(&baseline, RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let comparison = compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&baseline, RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let submodule = changed(&comparison, Path::new("nested"));
|
||||
assert_eq!(submodule.status, FileStatus::Modified);
|
||||
let notice = submodule.notice.as_ref().unwrap();
|
||||
@@ -897,13 +936,13 @@ fn multiple_remotes_retain_tag_only_history_even_when_tag_names_collide() {
|
||||
&& r.description == format!("{name} tag-only needle"))
|
||||
);
|
||||
}
|
||||
let comparison = repository
|
||||
.compare(
|
||||
&revision(&ids[0], RepoKind::Git),
|
||||
&revision(&ids[1], RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let comparison = compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&ids[0], RepoKind::Git),
|
||||
&revision(&ids[1], RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(changed(&comparison, Path::new("file")).before, ["one"]);
|
||||
assert_eq!(changed(&comparison, Path::new("file")).after, ["two"]);
|
||||
assert_eq!(disk_state(&active), state);
|
||||
@@ -1011,13 +1050,13 @@ fn jj_conflicted_revisions_show_cli_projection_and_keep_ordinary_files_navigable
|
||||
.iter()
|
||||
.any(|r| r.target == RevisionTarget::Commit(merged.clone()))
|
||||
);
|
||||
let comparison = repository
|
||||
.compare(
|
||||
&revision(&base, RepoKind::Jujutsu),
|
||||
&revision(&merged, RepoKind::Jujutsu),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let comparison = compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&base, RepoKind::Jujutsu),
|
||||
&revision(&merged, RepoKind::Jujutsu),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let conflict = changed(&comparison, Path::new("conflict"));
|
||||
assert_eq!(conflict.after, expected);
|
||||
assert!(
|
||||
@@ -1032,13 +1071,13 @@ fn jj_conflicted_revisions_show_cli_projection_and_keep_ordinary_files_navigable
|
||||
assert_eq!(ordinary.before, ["old"]);
|
||||
assert_eq!(ordinary.after, ["left ordinary"]);
|
||||
assert!(ordinary.notice.is_none());
|
||||
let working_comparison = repository
|
||||
.compare(
|
||||
&revision(&merged, RepoKind::Jujutsu),
|
||||
&working(RepoKind::Jujutsu),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let working_comparison = compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&merged, RepoKind::Jujutsu),
|
||||
&working(RepoKind::Jujutsu),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
changed(&working_comparison, Path::new("conflict"))
|
||||
.notice
|
||||
@@ -1079,7 +1118,7 @@ fn unsaved_overlays_are_working_after_only_and_cannot_follow_nonregular_paths()
|
||||
let mut repository = open(&root.join("sub"));
|
||||
let base = revision(&id, RepoKind::Git);
|
||||
let checkout = working(RepoKind::Git);
|
||||
let comparison = repository.compare(&base, &checkout, &buffers).unwrap();
|
||||
let comparison = compare_loaded(&mut repository, &base, &checkout, &buffers).unwrap();
|
||||
assert_eq!(comparison.root, fs::canonicalize(&root).unwrap());
|
||||
assert_eq!(changed(&comparison, Path::new("file")).after, ["unsaved"]);
|
||||
assert!(comparison.editable_paths.contains(Path::new("file")));
|
||||
@@ -1101,7 +1140,7 @@ fn unsaved_overlays_are_working_after_only_and_cannot_follow_nonregular_paths()
|
||||
|| file.path == Path::new("missing")
|
||||
|| file.path == Path::new("linked/file"))
|
||||
);
|
||||
let immutable = repository.compare(&checkout, &base, &buffers).unwrap();
|
||||
let immutable = compare_loaded(&mut repository, &checkout, &base, &buffers).unwrap();
|
||||
assert!(immutable.editable_paths.is_empty());
|
||||
assert!(
|
||||
!immutable
|
||||
@@ -1111,12 +1150,12 @@ fn unsaved_overlays_are_working_after_only_and_cannot_follow_nonregular_paths()
|
||||
);
|
||||
assert!(
|
||||
repository
|
||||
.compare(&base, &base, &buffers)
|
||||
.compare(1, &base, &base, &buffers)
|
||||
.unwrap()
|
||||
.files
|
||||
.is_empty()
|
||||
);
|
||||
let same_checkout = repository.compare(&checkout, &checkout, &buffers).unwrap();
|
||||
let same_checkout = compare_loaded(&mut repository, &checkout, &checkout, &buffers).unwrap();
|
||||
assert_eq!(changed(&same_checkout, Path::new("file")).before, ["base"]);
|
||||
assert_eq!(
|
||||
changed(&same_checkout, Path::new("file")).after,
|
||||
@@ -1128,3 +1167,85 @@ fn unsaved_overlays_are_working_after_only_and_cannot_follow_nonregular_paths()
|
||||
"outside\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn immutable_catalog_skips_unchanged_blobs_and_prepares_only_requested_details() {
|
||||
let fixture = Fixture::new();
|
||||
let root = fixture.dir("lazy");
|
||||
init_git(&root);
|
||||
fs::write(root.join("unchanged-large"), vec![b'x'; 17 * 1024 * 1024]).unwrap();
|
||||
fs::write(root.join("selected"), "before\n").unwrap();
|
||||
fs::write(root.join("unselected"), "old\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "before"]);
|
||||
let before = git(&root, &["rev-parse", "HEAD"]);
|
||||
fs::write(root.join("selected"), "after\n").unwrap();
|
||||
fs::write(root.join("unselected"), vec![b'y'; 17 * 1024 * 1024]).unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "after"]);
|
||||
let after = git(&root, &["rev-parse", "HEAD"]);
|
||||
let mut repository = open(&root);
|
||||
let comparison = repository
|
||||
.compare(
|
||||
100,
|
||||
&revision(&before, RepoKind::Git),
|
||||
&revision(&after, RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(comparison.files.len(), 2);
|
||||
assert!(comparison.files.iter().all(|file| !file.loaded
|
||||
&& file.before.is_empty()
|
||||
&& file.after.is_empty()
|
||||
&& file.hunks.is_empty()));
|
||||
let index = comparison
|
||||
.files
|
||||
.iter()
|
||||
.position(|file| file.path == Path::new("selected"))
|
||||
.unwrap();
|
||||
let selected = repository.load_file(100, index).unwrap();
|
||||
assert!(selected.loaded);
|
||||
assert_eq!(selected.before, ["before"]);
|
||||
assert_eq!(selected.after, ["after"]);
|
||||
assert_eq!(selected.hunks.len(), 1);
|
||||
assert!(repository.load_file(99, index).is_err());
|
||||
let empty = repository
|
||||
.compare(
|
||||
101,
|
||||
&revision(&after, RepoKind::Git),
|
||||
&revision(&after, RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(empty.files.is_empty());
|
||||
assert!(repository.load_file(100, index).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deferred_file_details_keep_frozen_checkout_and_unsaved_after() {
|
||||
let fixture = Fixture::new();
|
||||
let root = fixture.dir("frozen").canonicalize().unwrap();
|
||||
init_git(&root);
|
||||
fs::write(root.join("file"), "committed\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "before"]);
|
||||
fs::write(root.join("file"), "disk before\n").unwrap();
|
||||
let mut repository = open(&root);
|
||||
let comparison = repository
|
||||
.compare(
|
||||
50,
|
||||
&working(RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[(root.join("file"), "unsaved after\n".into())],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(comparison.files.len(), 1);
|
||||
assert!(!comparison.files[0].loaded);
|
||||
fs::write(root.join("file"), "later disk\n").unwrap();
|
||||
let details = repository.load_file(50, 0).unwrap();
|
||||
assert_eq!(details.before, ["disk before"]);
|
||||
assert_eq!(details.after, ["unsaved after"]);
|
||||
let again = repository.load_file(50, 0).unwrap();
|
||||
assert_eq!(again.before, details.before);
|
||||
assert_eq!(again.after, details.after);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user