add something
This commit is contained in:
+110
-12
@@ -117,6 +117,8 @@ enum CheckoutPlan {
|
||||
Raw(String),
|
||||
}
|
||||
|
||||
const FULL_FILE_DIFF_CONTEXT: &str = "--unified=1000000";
|
||||
|
||||
#[tauri::command]
|
||||
pub fn open_repository(path: String) -> Result<GitStatus, String> {
|
||||
let repo = resolve_repo(&path)?;
|
||||
@@ -247,6 +249,13 @@ pub fn commit(path: String, message: String) -> Result<GitStatus, String> {
|
||||
return Err("Commit-Message darf nicht leer sein.".to_string());
|
||||
}
|
||||
|
||||
let current_status = status_for_repo(&repo)?;
|
||||
if has_unresolved_conflicts(¤t_status) {
|
||||
return Err(
|
||||
"Merge-Konflikte muessen geloest werden, bevor du committen kannst.".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
run_git(&repo, ["commit", "-m", message.as_str()])?;
|
||||
status_for_repo(&repo)
|
||||
}
|
||||
@@ -310,10 +319,7 @@ pub fn merge_branch(path: String, branch: String) -> Result<GitStatus, String> {
|
||||
// Surface those through the status so the UI can offer conflict resolution
|
||||
// instead of treating the conflict as a hard error.
|
||||
let status = status_for_repo(&repo)?;
|
||||
if status.files.iter().any(|file| {
|
||||
matches!(file.staged, Some(FileStatusKind::Conflicted))
|
||||
|| matches!(file.unstaged, Some(FileStatusKind::Conflicted))
|
||||
}) {
|
||||
if has_unresolved_conflicts(&status) {
|
||||
return Ok(status);
|
||||
}
|
||||
|
||||
@@ -447,7 +453,16 @@ pub fn compare_commits(
|
||||
to_hash.as_str(),
|
||||
],
|
||||
)?;
|
||||
let patch_output = run_git(&repo, ["diff", "-M", from_hash.as_str(), to_hash.as_str()])?;
|
||||
let patch_output = run_git(
|
||||
&repo,
|
||||
[
|
||||
"diff",
|
||||
"-M",
|
||||
FULL_FILE_DIFF_CONTEXT,
|
||||
from_hash.as_str(),
|
||||
to_hash.as_str(),
|
||||
],
|
||||
)?;
|
||||
|
||||
let files = parse_diff_files(&name_status, &numstat)?;
|
||||
let patch = String::from_utf8_lossy(&patch_output).to_string();
|
||||
@@ -484,7 +499,7 @@ pub fn diff_file_against_working_tree(
|
||||
)?;
|
||||
let patch_output = run_git_with_paths(
|
||||
&repo,
|
||||
&["diff", "-M", commit_hash.as_str()],
|
||||
&["diff", "-M", FULL_FILE_DIFF_CONTEXT, commit_hash.as_str()],
|
||||
std::slice::from_ref(&file),
|
||||
)?;
|
||||
|
||||
@@ -748,6 +763,13 @@ fn status_for_file(statuses: &[GitFileStatus], path: &str) -> Option<FileStatusK
|
||||
status.unstaged.or(status.staged)
|
||||
}
|
||||
|
||||
fn has_unresolved_conflicts(status: &GitStatus) -> bool {
|
||||
status.files.iter().any(|file| {
|
||||
matches!(file.staged, Some(FileStatusKind::Conflicted))
|
||||
|| matches!(file.unstaged, Some(FileStatusKind::Conflicted))
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_commit_log(repo: &Path, output: &[u8]) -> Result<Vec<GitCommit>, String> {
|
||||
const FIELD_SEPARATOR: char = '\x1f';
|
||||
const RECORD_SEPARATOR: char = '\x1e';
|
||||
@@ -1713,7 +1735,8 @@ mod tests {
|
||||
|
||||
fs::write(repo.path.join("old.txt"), "original\nsecond line\n")
|
||||
.expect("tracked file should change");
|
||||
fs::write(repo.path.join("added.txt"), "brand new\n").expect("added file should be written");
|
||||
fs::write(repo.path.join("added.txt"), "brand new\n")
|
||||
.expect("added file should be written");
|
||||
run_git_test(&repo.path, ["add", "old.txt", "added.txt"]);
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "second"]);
|
||||
let second_commit = git_output_test(&repo.path, ["rev-parse", "HEAD"]);
|
||||
@@ -1736,6 +1759,40 @@ mod tests {
|
||||
assert!(comparison.patch.contains("second line"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare_commits_includes_full_file_context() {
|
||||
let repo = init_temp_repo("compare_full_context");
|
||||
let before = (1..=60)
|
||||
.map(|line| format!("line {line}\n"))
|
||||
.collect::<String>();
|
||||
|
||||
fs::write(repo.path.join("context.txt"), &before).expect("context file should be written");
|
||||
run_git_test(&repo.path, ["add", "context.txt"]);
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "base"]);
|
||||
let first_commit = git_output_test(&repo.path, ["rev-parse", "HEAD"]);
|
||||
|
||||
fs::write(
|
||||
repo.path.join("context.txt"),
|
||||
before.replace("line 30\n", "line 30 changed\n"),
|
||||
)
|
||||
.expect("context file should be changed");
|
||||
run_git_test(&repo.path, ["add", "context.txt"]);
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "change"]);
|
||||
let second_commit = git_output_test(&repo.path, ["rev-parse", "HEAD"]);
|
||||
|
||||
let comparison = compare_commits(
|
||||
repo.path.to_string_lossy().to_string(),
|
||||
first_commit,
|
||||
second_commit,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(comparison.patch.contains(" line 1\n"));
|
||||
assert!(comparison.patch.contains(" line 60\n"));
|
||||
assert!(comparison.patch.contains("-line 30\n"));
|
||||
assert!(comparison.patch.contains("+line 30 changed\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_file_against_working_tree_reports_uncommitted_changes() {
|
||||
let repo = init_temp_repo("diff_against_working_tree");
|
||||
@@ -1786,8 +1843,11 @@ mod tests {
|
||||
.output()
|
||||
.expect("git merge should start");
|
||||
|
||||
let conflict =
|
||||
read_conflict(repo.path.to_string_lossy().to_string(), "file.txt".to_string()).unwrap();
|
||||
let conflict = read_conflict(
|
||||
repo.path.to_string_lossy().to_string(),
|
||||
"file.txt".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
conflict.ours.unwrap().replace("\r\n", "\n"),
|
||||
"ours change\n"
|
||||
@@ -1813,6 +1873,40 @@ mod tests {
|
||||
assert_eq!(contents.replace("\r\n", "\n"), "resolved\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commit_rejects_unresolved_merge_conflicts() {
|
||||
let repo = init_temp_repo("commit_rejects_conflicts");
|
||||
fs::write(repo.path.join("file.txt"), "base\n").expect("base file should be written");
|
||||
run_git_test(&repo.path, ["add", "file.txt"]);
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "base"]);
|
||||
let main_branch = git_output_test(&repo.path, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
||||
|
||||
run_git_test(&repo.path, ["checkout", "-q", "-b", "feature"]);
|
||||
fs::write(repo.path.join("file.txt"), "theirs change\n").expect("feature change");
|
||||
run_git_test(&repo.path, ["commit", "-q", "-am", "feature change"]);
|
||||
|
||||
run_git_test(&repo.path, ["checkout", "-q", main_branch.as_str()]);
|
||||
fs::write(repo.path.join("file.txt"), "ours change\n").expect("main change");
|
||||
run_git_test(&repo.path, ["commit", "-q", "-am", "main change"]);
|
||||
|
||||
let _ = Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(&repo.path)
|
||||
.args(["merge", "--no-edit", "feature"])
|
||||
.output()
|
||||
.expect("git merge should start");
|
||||
|
||||
let err = commit(
|
||||
repo.path.to_string_lossy().to_string(),
|
||||
"should not commit".to_string(),
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(err.contains("Merge-Konflikte"));
|
||||
let status = status_for_repo(&repo.path).unwrap();
|
||||
assert!(has_unresolved_conflicts(&status));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_binary_content_by_nul_byte() {
|
||||
assert!(is_binary_bytes(&[0u8, 1, 2, 3]));
|
||||
@@ -1822,7 +1916,8 @@ mod tests {
|
||||
#[test]
|
||||
fn binary_conflict_can_be_resolved_by_side() {
|
||||
let repo = init_temp_repo("binary_conflict");
|
||||
fs::write(repo.path.join("img.bin"), [0u8, 1, 2, 3]).expect("base binary should be written");
|
||||
fs::write(repo.path.join("img.bin"), [0u8, 1, 2, 3])
|
||||
.expect("base binary should be written");
|
||||
run_git_test(&repo.path, ["add", "img.bin"]);
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "base"]);
|
||||
let main_branch = git_output_test(&repo.path, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
||||
@@ -1842,8 +1937,11 @@ mod tests {
|
||||
.output()
|
||||
.expect("git merge should start");
|
||||
|
||||
let conflict =
|
||||
read_conflict(repo.path.to_string_lossy().to_string(), "img.bin".to_string()).unwrap();
|
||||
let conflict = read_conflict(
|
||||
repo.path.to_string_lossy().to_string(),
|
||||
"img.bin".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(conflict.binary);
|
||||
assert!(conflict.content.is_empty());
|
||||
assert_eq!(conflict.ours_size, Some(3));
|
||||
|
||||
Reference in New Issue
Block a user