summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 17:12:05 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit3723046ff06dc3d954a611a2bb195790547669c0 (patch)
tree58af0b6697c916af484839a28ca5bbadad6337cc
parent4de0be4942b5aad143949826523ce68a0b1b03cc (diff)
downloadpardes-3723046ff06dc3d954a611a2bb195790547669c0.tar.gz
pardes-3723046ff06dc3d954a611a2bb195790547669c0.zip
Every line of a diff section that deletes its file looks for a file "deleted by this diff", the diff --git and --- lines too
In a section that deletes its file, a look at a removed or hunk line said "<file>: deleted by this diff". The `diff --git` and `---` lines said "no such file here", because they come before the `+++ /dev/null` (or `deleted file mode`) that says so, and the walk had not reached it yet. They now look ahead within the section, so every line gives the one message. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--src/diff.zig32
1 files changed, 30 insertions, 2 deletions
diff --git a/src/diff.zig b/src/diff.zig
index 9ab1bb23..4962cddc 100644
--- a/src/diff.zig
+++ b/src/diff.zig
@@ -299,6 +299,19 @@ pub const Target = struct {
/// from the end of the file. In a section that deletes its file there is
/// no new file, so a removed or context line opens the old one at its old
/// line (it is there while the diff is not applied).
+/// Whether the section `row` is in deletes its file by a line still ahead
+/// of it (`deleted file mode`, `+++ /dev/null`): its `diff --git` and `---`
+/// lines come first, and are about a file this diff deletes all the same.
+fn deletesAhead(lines: []const []const u8, row: usize) bool {
+ for (lines[row + 1 ..]) |raw| {
+ const l = std.mem.trimEnd(u8, raw, "\r");
+ if (std.mem.startsWith(u8, l, "diff ") or std.mem.startsWith(u8, l, "@@")) return false;
+ if (std.mem.startsWith(u8, l, "deleted file mode") or std.mem.eql(u8, l, "+++ /dev/null") or
+ (std.mem.startsWith(u8, l, "+++ /dev/null") and l.len > 13 and (l[13] == '\t' or l[13] == ' '))) return true;
+ }
+ return false;
+}
+
pub fn targetAt(lines: []const []const u8, row: usize, continued_rows: bool) ?Target {
if (row >= lines.len) return null;
var w: Walk = .{ .continued_rows = continued_rows };
@@ -310,7 +323,7 @@ pub fn targetAt(lines: []const []const u8, row: usize, continued_rows: bool) ?Ta
.other, .meta, .no_newline => {
// `diff --git a/x b/y`: the file, as its `+++` would.
if (got.kind == .meta and std.mem.startsWith(u8, line, "diff ") and w.git_path.len > 0)
- return .{ .kind = got.kind, .path = w.git_path, .line = 0 };
+ return .{ .kind = got.kind, .path = w.git_path, .line = 0, .old_side = deletesAhead(lines, row) };
return null;
},
.old_path, .new_path => {
@@ -324,7 +337,7 @@ pub fn targetAt(lines: []const []const u8, row: usize, continued_rows: bool) ?Ta
if (!isDevNull(new) and (isDevNull(own) or !std.mem.eql(u8, stripSide(new), stripSide(own))))
return .{ .kind = got.kind, .path = new, .line = 0 };
}
- if (!isDevNull(own)) return .{ .kind = got.kind, .path = own, .line = 0, .old_side = got.kind == .old_path and w.deleted() };
+ if (!isDevNull(own)) return .{ .kind = got.kind, .path = own, .line = 0, .old_side = got.kind == .old_path and (w.deleted() or deletesAhead(lines, row)) };
if (w.path().len == 0) return null;
return .{ .kind = got.kind, .path = w.path(), .line = 0, .old_side = w.deleted() };
},
@@ -389,6 +402,21 @@ const git_diff =
\\
;
+test "diff every line of a section that deletes its file is about a file this diff deletes" {
+ const lines = splitLines(git_diff);
+ var rows: usize = 0;
+ for (lines, 0..) |line, row| {
+ if (row < 15) continue; // the deleting section: `diff --git a/old.py` on
+ const t = targetAt(&lines, row, false) orelse continue;
+ try testing.expectEqualStrings("old.py", stripSide(t.path));
+ try testing.expect(t.old_side);
+ _ = line;
+ rows += 1;
+ }
+ // diff --git, ---, +++, @@ and the two removed lines.
+ try testing.expectEqual(@as(usize, 6), rows);
+}
+
test "diff a walk follows the hunk counts, so a removed `--` line is no header" {
const lines = splitLines(git_diff);
var w: Walk = .{};