╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ diff3.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
59 60 var list: std.ArrayList(Chunk) = .empty; 61 while (try diff3_iter.next()) |chunk| { 62 switch (chunk) { 63 .clean => |range| try list.append(aa, .{ .same = (try joinRange(aa, &base_iter, range)) orelse unreachable }), 64 .conflict => |ranges| { 65 // the iterator marks every non-identical chunk a conflict, so 66 // one-sided changes are classified here, like xit's merge does 67 const base_text = try joinRange(aa, &base_iter, ranges.o_range); 68 const ours_text = try joinRange(aa, &ours_iter, ranges.a_range); 69 const theirs_text = try joinRange(aa, &theirs_iter, ranges.b_range); 70 if (textEqual(ours_text, theirs_text)) { 71 // the same change on both sides; nothing left = no chunk 72 if (ours_text) |text| try list.append(aa, .{ .same = text }); 73 } else if (textEqual(base_text, ours_text)) { 74 try list.append(aa, .{ .auto = .{ .text = theirs_text, .theirs = true } }); 75 } else if (textEqual(base_text, theirs_text)) { 76 try list.append(aa, .{ .auto = .{ .text = ours_text, .theirs = false } }); 77 } else { 78 try list.append(aa, .{ .conflict = .{ .ours = ours_text, .theirs = theirs_text } }); 79 } 80 }, 81 } 82 } 83 return try list.toOwnedSlice(aa); 84 } 85 86 // join the chunks back into one document. `resolutions` holds the final text 87 // of each conflict chunk in order; an empty resolution drops the hunk's lines. 88 pub fn assemble( 89 allocator: std.mem.Allocator, 90 chunk_list: []const Chunk, 91 resolutions: []const []const u8, 92 ) ![]u8 { 93 var out: std.ArrayList(u8) = .empty; 94 errdefer out.deinit(allocator); 95 var resolution_index: usize = 0; 96 for (chunk_list) |chunk| { 97 const piece: ?[]const u8 = switch (chunk) { 98 .same => |text| text, 99 .auto => |auto| auto.text, 100 .conflict => blk: { 101 const resolution = resolutions[resolution_index]; 102 resolution_index += 1; 103 break :blk if (resolution.len > 0) resolution else null; 104 }, 105 }; 106 if (piece) |text| { 107 if (out.items.len > 0) try out.append(allocator, '\n'); 108 try out.appendSlice(allocator, text); 109 } 110 } 111 return try out.toOwnedSlice(allocator); 112 } 113 114 // a range's lines joined with newlines, or null when it spans no lines 115 fn joinRange( 116 aa: std.mem.Allocator, 117 iter: *LineIterator, 118 range_maybe: ?df.Diff3Iterator(repo_kind, repo_opts).Range, 119 ) !?[]const u8 { 120 const range = range_maybe orelse return null; 121 if (range.end <= range.begin) return null; 122 var out: std.ArrayList(u8) = .empty; 123 for (range.begin..range.end) |line_num| { 124 // buffer-backed lines are borrowed, so no free is needed 125 const line = try iter.get(line_num); 126 if (line_num > range.begin) try out.append(aa, '\n'); 127 try out.appendSlice(aa, line); 128 } 129 return try out.toOwnedSlice(aa); 130 } 131 132 fn textEqual(a: ?[]const u8, b: ?[]const u8) bool { 133 const a_text = a orelse return b == null; 134 const b_text = b orelse return false; 135 return std.mem.eql(u8, a_text, b_text); 136 } 137