Skip to content

Commit 5c46ca6

Browse files
author
Rodolfo Gatti
committed
Correct exit codes to match GNU diff3 behavior
1 parent cec5b9c commit 5c46ca6

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

src/diff3.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ fn compute_diff3(mine: &[u8], older: &[u8], yours: &[u8], params: &Diff3Params)
376376
let diff_older_yours: Vec<_> = diff::slice(older_lines, yours_lines);
377377
let diff_mine_yours: Vec<_> = diff::slice(mine_lines, yours_lines);
378378

379-
let has_conflicts = detect_conflicts(
379+
let _has_conflicts = detect_conflicts(
380380
&diff_mine_older,
381381
&diff_older_yours,
382382
&diff_mine_yours,
@@ -395,6 +395,61 @@ fn compute_diff3(mine: &[u8], older: &[u8], yours: &[u8], params: &Diff3Params)
395395
yours_lines,
396396
);
397397

398+
// Determine the appropriate exit code based on format and output mode
399+
let should_report_conflict = match params.format {
400+
Diff3Format::Ed => {
401+
// -e mode: conflicts are auto-resolved by choosing "yours", so return 0
402+
// unless we're in ShowOverlapEd mode which needs manual resolution
403+
match params.output_mode {
404+
Diff3OutputMode::ShowOverlapEd => {
405+
// -E mode: return 1 if there are overlapping conflicts
406+
regions
407+
.iter()
408+
.any(|r| r.conflict == ConflictType::OverlappingConflict)
409+
}
410+
_ => false, // -e mode always succeeds
411+
}
412+
}
413+
Diff3Format::ShowOverlap => {
414+
// -E mode: return 1 if there are overlapping conflicts
415+
regions
416+
.iter()
417+
.any(|r| r.conflict == ConflictType::OverlappingConflict)
418+
}
419+
Diff3Format::Normal => {
420+
// Normal format: return 1 only if both sides changed differently
421+
match params.output_mode {
422+
Diff3OutputMode::EasyOnly => {
423+
// -3: showing easy conflicts doesn't count as failure (exit 0)
424+
false
425+
}
426+
Diff3OutputMode::OverlapOnly | Diff3OutputMode::OverlapOnlyMarked => {
427+
// -x or -X: return 1 if there are overlapping conflicts
428+
regions
429+
.iter()
430+
.any(|r| r.conflict == ConflictType::OverlappingConflict)
431+
}
432+
_ => {
433+
// Default: return 1 only if there are overlapping conflicts
434+
// (both sides changed differently)
435+
regions
436+
.iter()
437+
.any(|r| r.conflict == ConflictType::OverlappingConflict)
438+
}
439+
}
440+
}
441+
Diff3Format::Merged => {
442+
// Merged format: return 1 if there are ANY conflicts needing resolution
443+
// This includes both easy conflicts (one side changed) and overlapping (both changed)
444+
regions.iter().any(|r| {
445+
matches!(
446+
r.conflict,
447+
ConflictType::EasyConflict | ConflictType::OverlappingConflict
448+
)
449+
})
450+
}
451+
};
452+
398453
match params.format {
399454
Diff3Format::Normal => (
400455
generate_normal_output(
@@ -407,7 +462,7 @@ fn compute_diff3(mine: &[u8], older: &[u8], yours: &[u8], params: &Diff3Params)
407462
&regions,
408463
params,
409464
),
410-
has_conflicts,
465+
should_report_conflict,
411466
),
412467
Diff3Format::Merged => (
413468
generate_merged_output(
@@ -420,7 +475,7 @@ fn compute_diff3(mine: &[u8], older: &[u8], yours: &[u8], params: &Diff3Params)
420475
&regions,
421476
params,
422477
),
423-
has_conflicts,
478+
should_report_conflict,
424479
),
425480
Diff3Format::Ed | Diff3Format::ShowOverlap => (
426481
generate_ed_script(
@@ -433,7 +488,7 @@ fn compute_diff3(mine: &[u8], older: &[u8], yours: &[u8], params: &Diff3Params)
433488
&regions,
434489
params,
435490
),
436-
has_conflicts,
491+
should_report_conflict,
437492
),
438493
}
439494
}

tests/integration.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,8 +1311,9 @@ mod diff3 {
13111311
cmd.arg("-e");
13121312
cmd.arg("-i");
13131313
cmd.arg(&mine_path).arg(&older_path).arg(&yours_path);
1314-
// Even with conflicts, -e -i should produce a valid ed script with w and q
1315-
cmd.assert().code(predicate::eq(1)).failure();
1314+
// -e mode auto-resolves conflicts by choosing "yours", so exit code is 0
1315+
// The -i flag adds w and q commands for ed compatibility
1316+
cmd.assert().code(predicate::eq(0)).success();
13161317

13171318
Ok(())
13181319
}

0 commit comments

Comments
 (0)