use crate::core::test_helpers::TestRepo;
use crate::git;
fn stage_deletion(test_repo: &TestRepo, path: &str) {
let workdir = test_repo.workdir();
std::fs::remove_file(workdir.join(path)).unwrap();
git::run_git(workdir.as_path(), &["add", "-A", "--", path]).unwrap();
}
#[test]
fn stage_files_accepts_already_staged_deletion() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
stage_deletion(&test_repo, "file1.txt");
let result = git::stage_files(test_repo.workdir().as_path(), &["file1.txt"]);
assert!(result.is_ok(), "staging failed: {:?}", result);
assert_eq!(test_repo.status_porcelain().trim(), "D file1.txt");
}
#[test]
fn stage_files_stages_unstaged_deletion() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
std::fs::remove_file(test_repo.workdir().join("file1.txt")).unwrap();
git::stage_files(test_repo.workdir().as_path(), &["file1.txt"]).unwrap();
assert_eq!(test_repo.status_porcelain().trim(), "D file1.txt");
}
#[test]
fn stage_files_stages_the_rest_of_the_batch() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
test_repo.commit("Second commit", "file2.txt");
stage_deletion(&test_repo, "file1.txt");
test_repo.write_file("file2.txt", "changed");
git::stage_files(test_repo.workdir().as_path(), &["file1.txt", "file2.txt"]).unwrap();
let status = test_repo.status_porcelain();
assert!(status.contains("D file1.txt"), "status: {status}");
assert!(status.contains("M file2.txt"), "status: {status}");
}
#[test]
fn stage_files_rejects_an_unknown_path() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
let result = git::stage_files(test_repo.workdir().as_path(), &["nope.txt"]);
assert!(result.is_err(), "unknown path should not be accepted");
}
#[cfg(unix)]
#[test]
fn stage_files_stages_a_symlink_over_a_staged_deletion() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
stage_deletion(&test_repo, "file1.txt");
std::os::unix::fs::symlink("nowhere", test_repo.workdir().join("file1.txt")).unwrap();
git::stage_files(test_repo.workdir().as_path(), &["file1.txt"]).unwrap();
assert_eq!(test_repo.status_porcelain().trim(), "T file1.txt");
}
#[test]
fn stage_path_accepts_already_staged_deletion() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
stage_deletion(&test_repo, "file1.txt");
let result = git::stage_path(test_repo.workdir().as_path(), "file1.txt");
assert!(result.is_ok(), "staging failed: {:?}", result);
assert_eq!(test_repo.status_porcelain().trim(), "D file1.txt");
}
#[test]
fn commit_amend_no_edit_refuses_an_amend_that_did_not_commit() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
test_repo.write_file("file1.txt", "amended");
let workdir = test_repo.workdir();
git::stage_files(workdir.as_path(), &["file1.txt"]).unwrap();
let head = test_repo.head_oid();
let result = git::commit_amend_no_edit(workdir.as_path(), &["--porcelain"]);
assert!(result.is_err(), "the dry run should not pass for an amend");
assert_eq!(test_repo.head_oid(), head);
assert_eq!(test_repo.status_porcelain().trim(), "M file1.txt");
}
#[test]
fn commit_amend_no_edit_outranks_a_forwarded_no_amend() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
test_repo.write_file("file1.txt", "amended");
let workdir = test_repo.workdir();
git::stage_files(workdir.as_path(), &["file1.txt"]).unwrap();
let head = test_repo.head_oid();
let parent = git::rev_parse(workdir.as_path(), "HEAD^").unwrap();
git::commit_amend_no_edit(workdir.as_path(), &["--no-amend"]).unwrap();
assert_ne!(test_repo.head_oid(), head, "the amend replaced HEAD");
assert_eq!(
git::rev_parse(workdir.as_path(), "HEAD^").unwrap(),
parent,
"nothing was committed on top"
);
assert_eq!(test_repo.get_message(0), "First commit");
}
#[cfg(unix)]
#[test]
fn commit_amend_no_edit_survives_a_hook_that_stages() {
for (when, arg) in [("pre-commit", "-q"), ("post-commit", "--no-verify")] {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
let workdir = test_repo.workdir();
test_repo.install_hook(when, "echo hooked > hooked.txt\ngit add hooked.txt\n");
test_repo.write_file("file1.txt", "amended");
git::stage_files(workdir.as_path(), &["file1.txt"]).unwrap();
git::commit_amend_no_edit(workdir.as_path(), &[arg]).unwrap();
assert_eq!(test_repo.get_message(0), "First commit", "{when}");
assert_eq!(test_repo.read_file("file1.txt"), "amended", "{when}");
let hooked_committed = test_repo.commit_has_file(test_repo.head_oid(), "hooked.txt");
assert_eq!(hooked_committed, when == "pre-commit", "{when}");
}
}
#[test]
fn commit_amend_no_edit_takes_an_argument_that_does_commit() {
let test_repo = TestRepo::new();
test_repo.commit("First commit", "file1.txt");
test_repo.write_file("file1.txt", "amended");
let workdir = test_repo.workdir();
git::stage_files(workdir.as_path(), &["file1.txt"]).unwrap();
git::commit_amend_no_edit(workdir.as_path(), &["--no-verify"]).unwrap();
assert_eq!(test_repo.get_message(0), "First commit");
assert_eq!(test_repo.status_porcelain().trim(), "");
}