GitComet/crates/gitcomet-git-gix/tests/patch_integration.rs
2026-03-09 08:51:17 +02:00

174 lines
5.3 KiB
Rust

use gitcomet_core::domain::CommitId;
use gitcomet_core::services::GitBackend;
use gitcomet_git_gix::GixBackend;
use std::fs;
use std::path::Path;
use std::process::Command;
#[cfg(windows)]
const NULL_DEVICE: &str = "NUL";
#[cfg(not(windows))]
const NULL_DEVICE: &str = "/dev/null";
fn git_command() -> Command {
let mut cmd = Command::new("git");
// Keep tests deterministic by isolating from host git config.
cmd.env("GIT_CONFIG_NOSYSTEM", "1");
cmd.env("GIT_CONFIG_GLOBAL", NULL_DEVICE);
cmd
}
fn run_git(repo: &Path, args: &[&str]) {
let status = git_command()
.arg("-C")
.arg(repo)
.args(args)
.status()
.expect("git command to run");
assert!(status.success(), "git {:?} failed", args);
}
fn run_git_capture(repo: &Path, args: &[&str]) -> String {
let output = git_command()
.arg("-C")
.arg(repo)
.args(args)
.output()
.expect("git command to run");
assert!(
output.status.success(),
"git {:?} failed: {}",
args,
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8_lossy(&output.stdout).to_string()
}
#[test]
fn export_patch_and_apply_patch_round_trip() {
let dir = tempfile::tempdir().expect("create tempdir");
let repo = dir.path();
run_git(repo, &["init"]);
run_git(repo, &["config", "user.email", "you@example.com"]);
run_git(repo, &["config", "user.name", "You"]);
run_git(repo, &["config", "commit.gpgsign", "false"]);
run_git(repo, &["config", "core.autocrlf", "false"]);
run_git(repo, &["config", "core.eol", "lf"]);
let note = repo.join("note.txt");
fs::write(&note, "one\n").expect("write initial file");
run_git(repo, &["add", "note.txt"]);
run_git(
repo,
&["-c", "commit.gpgsign=false", "commit", "-m", "base"],
);
fs::write(&note, "one\ntwo\n").expect("write modified file");
run_git(repo, &["add", "note.txt"]);
run_git(
repo,
&["-c", "commit.gpgsign=false", "commit", "-m", "add line"],
);
let head = run_git_capture(repo, &["rev-parse", "HEAD"])
.trim()
.to_string();
let patch_path = dir.path().join("change.patch");
let backend = GixBackend;
let opened = backend.open(repo).expect("open repository");
let export_output = opened
.export_patch_with_output(&CommitId(head), &patch_path)
.expect("export patch");
assert_eq!(export_output.exit_code, Some(0));
assert!(patch_path.exists(), "expected patch file to exist");
let patch_text = fs::read_to_string(&patch_path).expect("read patch file");
assert!(patch_text.contains("Subject: [PATCH] add line"));
assert!(patch_text.contains("+two"));
run_git(repo, &["reset", "--hard", "HEAD~1"]);
assert_eq!(
fs::read_to_string(&note).expect("read reset file"),
"one\n",
"reset should remove second line"
);
let apply_output = opened
.apply_patch_with_output(&patch_path)
.expect("apply exported patch");
assert_eq!(apply_output.exit_code, Some(0));
assert_eq!(
fs::read_to_string(&note).expect("read applied file"),
"one\ntwo\n"
);
let subject = run_git_capture(repo, &["log", "-1", "--pretty=%s"])
.trim()
.to_string();
assert_eq!(subject, "add line");
}
#[test]
fn apply_unified_patch_to_worktree_applies_and_reverses() {
let dir = tempfile::tempdir().expect("create tempdir");
let repo = dir.path();
run_git(repo, &["init"]);
run_git(repo, &["config", "user.email", "you@example.com"]);
run_git(repo, &["config", "user.name", "You"]);
run_git(repo, &["config", "commit.gpgsign", "false"]);
run_git(repo, &["config", "core.autocrlf", "false"]);
run_git(repo, &["config", "core.eol", "lf"]);
let file = repo.join("a.txt");
fs::write(&file, "base\n").expect("write base file");
run_git(repo, &["add", "a.txt"]);
run_git(
repo,
&["-c", "commit.gpgsign=false", "commit", "-m", "base"],
);
fs::write(&file, "base\nchanged\n").expect("write modified file");
let patch = run_git_capture(repo, &["diff", "--", "a.txt"]);
assert!(patch.contains("+changed"));
run_git(repo, &["checkout", "--", "a.txt"]);
assert_eq!(
fs::read_to_string(&file).expect("read restored file"),
"base\n"
);
let backend = GixBackend;
let opened = backend.open(repo).expect("open repository");
let apply_output = opened
.apply_unified_patch_to_worktree_with_output(&patch, false)
.expect("apply worktree patch");
assert_eq!(apply_output.exit_code, Some(0));
assert!(
apply_output.command.starts_with("git apply "),
"unexpected command label: {}",
apply_output.command
);
assert_eq!(
fs::read_to_string(&file).expect("read patched file"),
"base\nchanged\n"
);
let reverse_output = opened
.apply_unified_patch_to_worktree_with_output(&patch, true)
.expect("reverse worktree patch");
assert_eq!(reverse_output.exit_code, Some(0));
assert!(
reverse_output.command.starts_with("git apply --reverse "),
"unexpected command label: {}",
reverse_output.command
);
assert_eq!(
fs::read_to_string(&file).expect("read reversed file"),
"base\n"
);
}