feat: track checkout identity for pinned diff editability
This commit is contained in:
+2
-2
@@ -548,7 +548,8 @@ fn branch_changes_default_and_mode_switches_compare_the_selected_divergent_tips(
|
||||
let comparison = diff.comparison.as_ref().unwrap();
|
||||
assert_eq!(comparison.files.len(), 1);
|
||||
assert_eq!(comparison.files[0].path, Path::new("feature-only"));
|
||||
assert_eq!(comparison.files[0].after, ["feature branch"]);
|
||||
assert_eq!(session.editor().context().text, "feature branch\n");
|
||||
assert!(diff.after_editable());
|
||||
assert_eq!(
|
||||
comparison.before,
|
||||
format!("merge base of {before} and {after} · {base}")
|
||||
@@ -590,7 +591,6 @@ fn branch_changes_default_and_mode_switches_compare_the_selected_divergent_tips(
|
||||
assert_eq!(diff.before.as_ref().unwrap().label, before);
|
||||
assert_eq!(diff.after.as_ref().unwrap().label, after);
|
||||
session.execute("diff.close");
|
||||
assert_eq!(session.editor().path(), Some(root.join("common").as_path()));
|
||||
|
||||
session.execute("diff");
|
||||
wait_until(&mut session, |session| {
|
||||
|
||||
@@ -558,6 +558,7 @@ fn diff_repository(fixture: &Fixture, historical: bool) -> (String, Option<Strin
|
||||
repository(fixture, &["add", "."]);
|
||||
repository(fixture, &["commit", "-qm", "after"]);
|
||||
let after = repository(fixture, &["rev-parse", "HEAD"]);
|
||||
repository(fixture, &["checkout", "--detach", &before]);
|
||||
fs::write(&fixture.source, "disk is not the historical source\n").unwrap();
|
||||
fs::write(&fixture.target, "disk is not the historical target\n").unwrap();
|
||||
after
|
||||
|
||||
+2
-1
@@ -275,6 +275,7 @@ fn readonly_before_and_historical_after_seeks_preserve_snapshots_and_folded_exce
|
||||
fs::write(&source, &after_text).unwrap();
|
||||
fixture.git(&["commit", "-qam", "after"]);
|
||||
let after = fixture.git(&["rev-parse", "HEAD"]);
|
||||
fixture.git(&["checkout", "--detach", &before]);
|
||||
lines[19] = "working x x tail".into();
|
||||
let working_text = lines.join("\n") + "\n";
|
||||
fs::write(&source, &working_text).unwrap();
|
||||
@@ -386,5 +387,5 @@ fn readonly_before_and_historical_after_seeks_preserve_snapshots_and_folded_exce
|
||||
assert_eq!(session.editor().cursor(), original_cursor);
|
||||
assert!(session.editor().dirty());
|
||||
assert_eq!(fs::read_to_string(&source).unwrap(), working_text);
|
||||
assert_eq!(fixture.git(&["rev-parse", "HEAD"]), after);
|
||||
assert_eq!(fixture.git(&["rev-parse", "HEAD"]), before);
|
||||
}
|
||||
|
||||
+199
-24
@@ -375,7 +375,10 @@ fn git_working_reads_disk_and_handles_ranges_ignored_files_binary_links_and_odd_
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(reverse.editable_paths.is_empty());
|
||||
assert!(reverse.editable_paths.contains(Path::new("text")));
|
||||
assert!(!reverse.editable_paths.contains(Path::new("added")));
|
||||
assert!(!reverse.editable_paths.contains(Path::new("link")));
|
||||
assert_eq!(changed(&reverse, Path::new("text")).after, ["one", "three"]);
|
||||
assert_eq!(
|
||||
changed(&reverse, Path::new("added")).status,
|
||||
FileStatus::Removed
|
||||
@@ -516,6 +519,46 @@ fn git_remote_refs_and_annotated_tags_are_discovered_before_fetch_and_history_st
|
||||
.success()
|
||||
);
|
||||
assert_eq!(disk_state(&active), state);
|
||||
// An advertised remote alias already available at HEAD is eligible without
|
||||
// importing refs or substituting the local unsaved overlay for pinned bytes.
|
||||
git(
|
||||
&source,
|
||||
&["remote", "add", "origin", bare.to_str().unwrap()],
|
||||
);
|
||||
let source_state = disk_state(&source);
|
||||
let mut checked_out = open(&source);
|
||||
let advertised = checked_out.list_remote("origin").unwrap();
|
||||
let alias = advertised
|
||||
.entries
|
||||
.iter()
|
||||
.find(|entry| entry.target == remote.target)
|
||||
.unwrap();
|
||||
let pinned = checked_out
|
||||
.compare(
|
||||
20,
|
||||
&revision(&ancestor, RepoKind::Git),
|
||||
alias,
|
||||
ComparisonMode::Snapshots,
|
||||
&[(source.join("file"), "dirty live\n".into())],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(pinned.editable_paths.contains(Path::new("file")));
|
||||
assert_eq!(checked_out.load_file(20, 0).unwrap().after, ["remote tip"]);
|
||||
assert_eq!(disk_state(&source), source_state);
|
||||
git(&source, &["checkout", "--detach", &ancestor]);
|
||||
let readonly = checked_out
|
||||
.refresh_checkout(20, 21, false, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(readonly.editable_paths.is_empty());
|
||||
assert_eq!(checked_out.load_file(21, 0).unwrap().after, ["remote tip"]);
|
||||
git(&source, &["checkout", "--detach", &tip]);
|
||||
let promoted = checked_out
|
||||
.refresh_checkout(21, 22, false, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(promoted.editable_paths, pinned.editable_paths);
|
||||
assert_eq!(checked_out.load_file(22, 0).unwrap().after, ["remote tip"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -707,6 +750,7 @@ fn jj_private_fetch_discovers_never_fetched_bookmark_and_searchable_remote_chang
|
||||
assert!(branch.before.ends_with(&"0".repeat(40)));
|
||||
assert_eq!(branch.files.len(), 1);
|
||||
assert_eq!(repository.load_file(60, 0).unwrap().after, ["remote tip"]);
|
||||
assert!(branch.editable_paths.is_empty());
|
||||
let comparison =
|
||||
compare_loaded(&mut repository, &remote, &working(RepoKind::Jujutsu), &[]).unwrap();
|
||||
assert_eq!(
|
||||
@@ -760,6 +804,31 @@ fn jj_private_fetch_discovers_never_fetched_bookmark_and_searchable_remote_chang
|
||||
.success()
|
||||
);
|
||||
assert_eq!(disk_state(&active), state);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_checkout(61, 62, false, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
// Simulate importing and checking out the privately selected endpoint behind
|
||||
// the viewer. Its initially unavailable active-jj identity must be retried.
|
||||
jj(&active, &["git", "fetch", "--remote", "origin"]);
|
||||
jj(&active, &["edit", "--ignore-immutable", &tip]);
|
||||
let checked_out_state = disk_state(&active);
|
||||
let promoted = repository
|
||||
.refresh_checkout(
|
||||
61,
|
||||
62,
|
||||
false,
|
||||
&[(active.join("file"), "dirty live\n".into())],
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(promoted.before, branch.before);
|
||||
assert_eq!(promoted.after, branch.after);
|
||||
assert!(promoted.editable_paths.contains(Path::new("file")));
|
||||
assert_eq!(repository.load_file(62, 0).unwrap().after, ["remote tip"]);
|
||||
assert_eq!(disk_state(&active), checked_out_state);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1327,7 +1396,7 @@ fn git_working_after_advances_only_to_descendants_with_new_paths_and_unsaved_tex
|
||||
fs::write(root.join("file"), "later disk\n").unwrap();
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.refresh_checkout(1, 2, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1340,12 +1409,19 @@ fn git_working_after_advances_only_to_descendants_with_new_paths_and_unsaved_tex
|
||||
let state = disk_state(&root);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(99, 2, &[])
|
||||
.refresh_checkout(99, 2, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_checkout(1, 2, false, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(repository.load_file(1, 0).unwrap().after, ["original disk"]);
|
||||
let mut refreshed = repository
|
||||
.refresh_working_after(1, 2, &[(root.join("file"), "unsaved\n".into())])
|
||||
.refresh_checkout(1, 2, true, &[(root.join("file"), "unsaved\n".into())])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(refreshed.before, original.before);
|
||||
@@ -1369,7 +1445,7 @@ fn git_working_after_advances_only_to_descendants_with_new_paths_and_unsaved_tex
|
||||
assert!(repository.load_file(1, 0).is_err());
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.refresh_checkout(2, 3, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1377,7 +1453,7 @@ fn git_working_after_advances_only_to_descendants_with_new_paths_and_unsaved_tex
|
||||
git(&root, &["checkout", "--detach", &base]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.refresh_checkout(2, 3, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1387,7 +1463,7 @@ fn git_working_after_advances_only_to_descendants_with_new_paths_and_unsaved_tex
|
||||
git(&root, &["commit", "-m", "unrelated to tip"]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.refresh_checkout(2, 3, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1400,7 +1476,7 @@ fn git_working_after_advances_only_to_descendants_with_new_paths_and_unsaved_tex
|
||||
git(&root, &["checkout", "--detach", &tip]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.refresh_checkout(2, 3, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1431,18 +1507,18 @@ fn working_after_refresh_preserves_frozen_working_before_and_sources_on_failure(
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "next"]);
|
||||
cancelled.store(true, Ordering::Release);
|
||||
assert!(repository.refresh_working_after(10, 11, &[]).is_err());
|
||||
assert!(repository.refresh_checkout(10, 11, true, &[]).is_err());
|
||||
cancelled.store(false, Ordering::Release);
|
||||
assert_eq!(repository.load_file(10, 0).unwrap().after, ["frozen after"]);
|
||||
fs::write(root.join("oversized"), vec![b'x'; 17 * 1024 * 1024]).unwrap();
|
||||
assert!(repository.refresh_working_after(10, 11, &[]).is_err());
|
||||
assert!(repository.refresh_checkout(10, 11, true, &[]).is_err());
|
||||
let original = repository.load_file(10, 0).unwrap();
|
||||
assert_eq!(original.before, ["frozen before"]);
|
||||
assert_eq!(original.after, ["frozen after"]);
|
||||
assert!(repository.load_file(11, 0).is_err());
|
||||
fs::remove_file(root.join("oversized")).unwrap();
|
||||
let refreshed = repository
|
||||
.refresh_working_after(10, 11, &[])
|
||||
.refresh_checkout(10, 11, true, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(refreshed.files.len(), 1);
|
||||
@@ -1450,20 +1526,63 @@ fn working_after_refresh_preserves_frozen_working_before_and_sources_on_failure(
|
||||
assert_eq!(file.before, ["frozen before"]);
|
||||
assert_eq!(file.after, ["next checkout"]);
|
||||
let tip = revision(&git(&root, &["rev-parse", "HEAD"]), RepoKind::Git);
|
||||
repository
|
||||
let pinned = repository
|
||||
.compare(12, &base, &tip, ComparisonMode::Snapshots, &[])
|
||||
.unwrap();
|
||||
assert!(pinned.editable_paths.contains(Path::new("file")));
|
||||
fs::write(root.join("file"), "newer checkout\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "newer"]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(12, 13, &[])
|
||||
.refresh_checkout(99, 13, false, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let readonly = repository
|
||||
.refresh_checkout(12, 13, false, &[(root.join("file"), "dirty live\n".into())])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(readonly.before, pinned.before);
|
||||
assert_eq!(readonly.after, pinned.after);
|
||||
assert!(readonly.editable_paths.is_empty());
|
||||
assert_eq!(readonly.files.len(), pinned.files.len());
|
||||
assert_eq!(readonly.files[0].path, pinned.files[0].path);
|
||||
assert!(repository.load_file(12, 0).is_err());
|
||||
assert_eq!(
|
||||
repository.load_file(12, 0).unwrap().after,
|
||||
repository.load_file(13, 0).unwrap().after,
|
||||
["next checkout"]
|
||||
);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_checkout(13, 14, false, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let RevisionTarget::Commit(tip_id) = &tip.target else {
|
||||
unreachable!()
|
||||
};
|
||||
git(&root, &["checkout", "--detach", tip_id]);
|
||||
let promoted = repository
|
||||
.refresh_checkout(13, 14, false, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(promoted.editable_paths, pinned.editable_paths);
|
||||
fs::write(root.join("file"), "dirty disk\n").unwrap();
|
||||
assert_eq!(
|
||||
repository.load_file(14, 0).unwrap().after,
|
||||
["next checkout"]
|
||||
);
|
||||
cancelled.store(true, Ordering::Release);
|
||||
assert!(repository.refresh_checkout(14, 15, false, &[]).is_err());
|
||||
cancelled.store(false, Ordering::Release);
|
||||
let recovered = repository
|
||||
.refresh_checkout(14, 15, false, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(recovered.editable_paths, pinned.editable_paths);
|
||||
assert_eq!(
|
||||
repository.load_file(15, 0).unwrap().after,
|
||||
["next checkout"]
|
||||
);
|
||||
}
|
||||
@@ -1498,7 +1617,7 @@ fn refresh_materialization_failure_restores_prepared_file_sources() {
|
||||
fs::write(root.join("a-file"), "new checkout\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "descendant"]);
|
||||
assert!(repository.refresh_working_after(1, 2, &[]).is_err());
|
||||
assert!(repository.refresh_checkout(1, 2, true, &[]).is_err());
|
||||
let file = repository.load_file(1, index).unwrap();
|
||||
assert_eq!(file.before, ["frozen source"]);
|
||||
assert!(file.after.is_empty());
|
||||
@@ -1523,7 +1642,7 @@ fn unborn_git_working_after_safely_advances_to_its_first_commit() {
|
||||
.unwrap();
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.refresh_checkout(1, 2, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1531,7 +1650,7 @@ fn unborn_git_working_after_safely_advances_to_its_first_commit() {
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "initial"]);
|
||||
let comparison = repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.refresh_checkout(1, 2, true, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(comparison.files.len(), 1);
|
||||
@@ -1563,7 +1682,7 @@ fn jj_working_after_follows_changes_not_rewrites_and_rejects_other_history() {
|
||||
let state = disk_state(&root);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.refresh_checkout(1, 2, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1574,7 +1693,7 @@ fn jj_working_after_follows_changes_not_rewrites_and_rejects_other_history() {
|
||||
fs::write(root.join("added"), "new file\n").unwrap();
|
||||
let state = disk_state(&root);
|
||||
let mut comparison = repository
|
||||
.refresh_working_after(1, 2, &[(root.join("file"), "unsaved\n".into())])
|
||||
.refresh_checkout(1, 2, true, &[(root.join("file"), "unsaved\n".into())])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
for (index, file) in comparison.files.iter_mut().enumerate() {
|
||||
@@ -1594,21 +1713,21 @@ fn jj_working_after_follows_changes_not_rewrites_and_rejects_other_history() {
|
||||
assert_eq!(disk_state(&root), state);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.refresh_checkout(2, 3, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
jj(&root, &["edit", &base]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.refresh_checkout(2, 3, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
jj(&root, &["new", &base]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.refresh_checkout(2, 3, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
@@ -1630,7 +1749,7 @@ fn jj_working_after_follows_changes_not_rewrites_and_rejects_other_history() {
|
||||
fs::write(root.join("file"), "new checkout\n").unwrap();
|
||||
jj(&root, &["new"]);
|
||||
let refreshed = repository
|
||||
.refresh_working_after(10, 11, &[])
|
||||
.refresh_checkout(10, 11, true, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(refreshed.files.len(), 1);
|
||||
@@ -1644,10 +1763,66 @@ fn jj_working_after_follows_changes_not_rewrites_and_rejects_other_history() {
|
||||
jj(&root, &["new"]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(12, 13, &[])
|
||||
.refresh_checkout(12, 13, true, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let tip_id = jj(&root, &["log", "--no-graph", "-r", "@", "-T", "commit_id"]);
|
||||
let pinned_tip = revision(&tip_id, RepoKind::Jujutsu);
|
||||
let pinned = repository
|
||||
.compare(20, &historical, &pinned_tip, ComparisonMode::Snapshots, &[])
|
||||
.unwrap();
|
||||
assert!(pinned.editable_paths.contains(Path::new("file")));
|
||||
let index = pinned
|
||||
.files
|
||||
.iter()
|
||||
.position(|file| file.path == Path::new("file"))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
repository.load_file(20, index).unwrap().after,
|
||||
["new checkout"]
|
||||
);
|
||||
fs::write(root.join("file"), "rewritten checkout\n").unwrap();
|
||||
jj(&root, &["describe", "-m", "same pinned change"]);
|
||||
let rewritten = jj(&root, &["log", "--no-graph", "-r", "@", "-T", "commit_id"]);
|
||||
assert_ne!(rewritten, tip_id);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_checkout(20, 21, false, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(
|
||||
repository.load_file(20, index).unwrap().after,
|
||||
["new checkout"]
|
||||
);
|
||||
let same_change = repository
|
||||
.compare(21, &historical, &pinned_tip, ComparisonMode::Snapshots, &[])
|
||||
.unwrap();
|
||||
assert_eq!(same_change.editable_paths, pinned.editable_paths);
|
||||
jj(&root, &["new"]);
|
||||
let readonly = repository
|
||||
.refresh_checkout(21, 22, false, &[(root.join("file"), "dirty live\n".into())])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(readonly.editable_paths.is_empty());
|
||||
assert_eq!(readonly.before, pinned.before);
|
||||
assert_eq!(readonly.after, pinned.after);
|
||||
assert_eq!(readonly.files.len(), pinned.files.len());
|
||||
assert_eq!(
|
||||
repository.load_file(22, index).unwrap().after,
|
||||
["new checkout"]
|
||||
);
|
||||
jj(&root, &["edit", &rewritten]);
|
||||
let promoted = repository
|
||||
.refresh_checkout(22, 23, false, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(promoted.editable_paths, pinned.editable_paths);
|
||||
assert_eq!(
|
||||
repository.load_file(23, index).unwrap().after,
|
||||
["new checkout"]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user