feat: follow forward working-checkout changes in diffs
This commit is contained in:
@@ -1305,6 +1305,352 @@ fn deferred_file_details_keep_frozen_checkout_and_unsaved_after() {
|
||||
assert_eq!(again.after, details.after);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_working_after_advances_only_to_descendants_with_new_paths_and_unsaved_text() {
|
||||
let fixture = Fixture::new();
|
||||
let root = fixture.dir("follow").canonicalize().unwrap();
|
||||
init_git(&root);
|
||||
fs::write(root.join("file"), "base\n").unwrap();
|
||||
fs::write(root.join("removed"), "remove me\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "base"]);
|
||||
let base = git(&root, &["rev-parse", "HEAD"]);
|
||||
fs::write(root.join("file"), "original disk\n").unwrap();
|
||||
let mut repository = open(&root);
|
||||
let original = compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&base, RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(root.join("file"), "later disk\n").unwrap();
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(repository.load_file(1, 0).unwrap().after, ["original disk"]);
|
||||
fs::remove_file(root.join("removed")).unwrap();
|
||||
fs::write(root.join("added"), "new file\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "descendant"]);
|
||||
let tip = git(&root, &["rev-parse", "HEAD"]);
|
||||
let state = disk_state(&root);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(99, 2, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let mut refreshed = repository
|
||||
.refresh_working_after(1, 2, &[(root.join("file"), "unsaved\n".into())])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(refreshed.before, original.before);
|
||||
assert_eq!(refreshed.after, original.after);
|
||||
for (index, file) in refreshed.files.iter_mut().enumerate() {
|
||||
*file = repository.load_file(2, index).unwrap();
|
||||
}
|
||||
assert_eq!(changed(&refreshed, Path::new("file")).before, ["base"]);
|
||||
assert_eq!(changed(&refreshed, Path::new("file")).after, ["unsaved"]);
|
||||
assert_eq!(
|
||||
changed(&refreshed, Path::new("added")).status,
|
||||
FileStatus::Added
|
||||
);
|
||||
assert_eq!(
|
||||
changed(&refreshed, Path::new("removed")).status,
|
||||
FileStatus::Removed
|
||||
);
|
||||
assert!(refreshed.editable_paths.contains(Path::new("file")));
|
||||
assert!(refreshed.editable_paths.contains(Path::new("added")));
|
||||
assert!(!refreshed.editable_paths.contains(Path::new("removed")));
|
||||
assert!(repository.load_file(1, 0).is_err());
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(disk_state(&root), state);
|
||||
git(&root, &["checkout", "--detach", &base]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
git(&root, &["checkout", "-b", "other"]);
|
||||
fs::write(root.join("file"), "other branch\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "unrelated to tip"]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let index = refreshed
|
||||
.files
|
||||
.iter()
|
||||
.position(|file| file.path == Path::new("file"))
|
||||
.unwrap();
|
||||
assert_eq!(repository.load_file(2, index).unwrap().after, ["unsaved"]);
|
||||
git(&root, &["checkout", "--detach", &tip]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn working_after_refresh_preserves_frozen_working_before_and_sources_on_failure() {
|
||||
let fixture = Fixture::new();
|
||||
let root = fixture.dir("frozen-follow").canonicalize().unwrap();
|
||||
init_git(&root);
|
||||
fs::write(root.join("file"), "base\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "base"]);
|
||||
let base = revision(&git(&root, &["rev-parse", "HEAD"]), RepoKind::Git);
|
||||
fs::write(root.join("file"), "frozen before\n").unwrap();
|
||||
let cancelled = Arc::new(AtomicBool::new(false));
|
||||
let mut repository = Repository::open(&root, cancelled.clone()).unwrap();
|
||||
repository
|
||||
.compare(
|
||||
10,
|
||||
&working(RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
ComparisonMode::Snapshots,
|
||||
&[(root.join("file"), "frozen after\n".into())],
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(root.join("file"), "next checkout\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "next"]);
|
||||
cancelled.store(true, Ordering::Release);
|
||||
assert!(repository.refresh_working_after(10, 11, &[]).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());
|
||||
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, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(refreshed.files.len(), 1);
|
||||
let file = repository.load_file(11, 0).unwrap();
|
||||
assert_eq!(file.before, ["frozen before"]);
|
||||
assert_eq!(file.after, ["next checkout"]);
|
||||
let tip = revision(&git(&root, &["rev-parse", "HEAD"]), RepoKind::Git);
|
||||
repository
|
||||
.compare(12, &base, &tip, ComparisonMode::Snapshots, &[])
|
||||
.unwrap();
|
||||
fs::write(root.join("file"), "newer checkout\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "newer"]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(12, 13, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(
|
||||
repository.load_file(12, 0).unwrap().after,
|
||||
["next checkout"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refresh_materialization_failure_restores_prepared_file_sources() {
|
||||
let fixture = Fixture::new();
|
||||
let root = fixture.dir("failed-materialization");
|
||||
init_git(&root);
|
||||
fs::write(root.join("a-file"), "frozen source\n").unwrap();
|
||||
fs::write(root.join("z-large"), vec![b'x'; 17 * 1024 * 1024]).unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "base"]);
|
||||
let base = revision(&git(&root, &["rev-parse", "HEAD"]), RepoKind::Git);
|
||||
fs::remove_file(root.join("a-file")).unwrap();
|
||||
fs::remove_file(root.join("z-large")).unwrap();
|
||||
let mut repository = open(&root);
|
||||
let original = repository
|
||||
.compare(
|
||||
1,
|
||||
&base,
|
||||
&working(RepoKind::Git),
|
||||
ComparisonMode::Snapshots,
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let index = original
|
||||
.files
|
||||
.iter()
|
||||
.position(|file| file.path == Path::new("a-file"))
|
||||
.unwrap();
|
||||
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());
|
||||
let file = repository.load_file(1, index).unwrap();
|
||||
assert_eq!(file.before, ["frozen source"]);
|
||||
assert!(file.after.is_empty());
|
||||
assert_eq!(file.status, FileStatus::Removed);
|
||||
assert!(repository.load_file(2, index).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unborn_git_working_after_safely_advances_to_its_first_commit() {
|
||||
let fixture = Fixture::new();
|
||||
let root = fixture.dir("unborn");
|
||||
init_git(&root);
|
||||
let mut repository = open(&root);
|
||||
repository
|
||||
.compare(
|
||||
1,
|
||||
&working(RepoKind::Git),
|
||||
&working(RepoKind::Git),
|
||||
ComparisonMode::Snapshots,
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
fs::write(root.join("first"), "initial\n").unwrap();
|
||||
git(&root, &["add", "."]);
|
||||
git(&root, &["commit", "-m", "initial"]);
|
||||
let comparison = repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(comparison.files.len(), 1);
|
||||
assert_eq!(repository.load_file(2, 0).unwrap().after, ["initial"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jj_working_after_follows_changes_not_rewrites_and_rejects_other_history() {
|
||||
for colocate in [false, true] {
|
||||
let fixture = Fixture::new();
|
||||
let root = fixture.dir("jj-follow").canonicalize().unwrap();
|
||||
init_jj(&root, colocate);
|
||||
fs::write(root.join("file"), "base\n").unwrap();
|
||||
fs::write(root.join("removed"), "remove me\n").unwrap();
|
||||
jj(&root, &["describe", "-m", "base"]);
|
||||
let base = jj(&root, &["log", "--no-graph", "-r", "@", "-T", "commit_id"]);
|
||||
jj(&root, &["new"]);
|
||||
fs::write(root.join("file"), "original disk\n").unwrap();
|
||||
let mut repository = open(&root);
|
||||
compare_loaded(
|
||||
&mut repository,
|
||||
&revision(&base, RepoKind::Jujutsu),
|
||||
&working(RepoKind::Jujutsu),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(root.join("file"), "same-change rewrite\n").unwrap();
|
||||
jj(&root, &["describe", "-m", "rewritten"]);
|
||||
let state = disk_state(&root);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(1, 2, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(repository.load_file(1, 0).unwrap().after, ["original disk"]);
|
||||
assert_eq!(disk_state(&root), state);
|
||||
jj(&root, &["new"]);
|
||||
fs::remove_file(root.join("removed")).unwrap();
|
||||
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())])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
for (index, file) in comparison.files.iter_mut().enumerate() {
|
||||
*file = repository.load_file(2, index).unwrap();
|
||||
}
|
||||
assert_eq!(changed(&comparison, Path::new("file")).before, ["base"]);
|
||||
assert_eq!(changed(&comparison, Path::new("file")).after, ["unsaved"]);
|
||||
assert_eq!(
|
||||
changed(&comparison, Path::new("added")).status,
|
||||
FileStatus::Added
|
||||
);
|
||||
assert_eq!(
|
||||
changed(&comparison, Path::new("removed")).status,
|
||||
FileStatus::Removed
|
||||
);
|
||||
assert!(repository.load_file(1, 0).is_err());
|
||||
assert_eq!(disk_state(&root), state);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
jj(&root, &["edit", &base]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
jj(&root, &["new", &base]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(2, 3, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let index = comparison
|
||||
.files
|
||||
.iter()
|
||||
.position(|file| file.path == Path::new("file"))
|
||||
.unwrap();
|
||||
assert_eq!(repository.load_file(2, index).unwrap().after, ["unsaved"]);
|
||||
repository
|
||||
.compare(
|
||||
10,
|
||||
&working(RepoKind::Jujutsu),
|
||||
&working(RepoKind::Jujutsu),
|
||||
ComparisonMode::Snapshots,
|
||||
&[(root.join("file"), "frozen after\n".into())],
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(root.join("file"), "new checkout\n").unwrap();
|
||||
jj(&root, &["new"]);
|
||||
let refreshed = repository
|
||||
.refresh_working_after(10, 11, &[])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(refreshed.files.len(), 1);
|
||||
let file = repository.load_file(11, 0).unwrap();
|
||||
assert_eq!(file.before, ["base"]);
|
||||
assert_eq!(file.after, ["new checkout"]);
|
||||
let historical = revision(&base, RepoKind::Jujutsu);
|
||||
repository
|
||||
.compare(12, &historical, &historical, ComparisonMode::Snapshots, &[])
|
||||
.unwrap();
|
||||
jj(&root, &["new"]);
|
||||
assert!(
|
||||
repository
|
||||
.refresh_working_after(12, 13, &[])
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_changes_resolves_local_and_private_remote_ancestry_without_active_mutation() {
|
||||
let fixture = Fixture::new();
|
||||
|
||||
Reference in New Issue
Block a user