╭ changed ───────────────╮ │src/test/event/patch.zig│ ╰────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────╮ │ 8 const fork = @import("../../fork.zig"); │ │ 9 const pch = @import("../../patch.zig"); │ │10 const push = @import("../../push.zig"); │ │11 +const ui = @import("../../ui.zig"); │ │12 │ │13 const repo_opts: rp.RepoOpts(.xit) = .{ .is_test = true };│ │14 const Repo = rp.Repo(.xit, repo_opts); │ ╰──────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │90 │ │91 // distinct branches at the same commit can still create a patch │ │92 try pch.writeBranchPatch(.local, kind, opts, io, allocator, &repo, std.fmt.bytesToHex(id, .lower), patch, null, author); │ │92 - const first = (try evt.Patch.readFromRepo(kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │93 + const first = (try evt.readFromRepo(evt.Patch, kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound;│ │94 const first_revision = first.event.revision orelse return error.NotFound; │ │95 try std.testing.expectEqualStrings(&base, first_revision.source_oid); │ │96 try std.testing.expectEqual(@as(?u64, if (kind == .xit) 0 else null), first_revision.commit_count); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │113 // a source update replaces the revision, not the patch metadata │ │114 const next = try repo.commitAtRef(io, allocator, .{ .message = "next feature" }, null, .{ .kind = .head, .name = "feature" });│ │115 try pch.refreshBranches(.local, kind, opts, io, allocator, &repo, null, null); │ │115 - const updated = (try evt.Patch.readFromRepo(kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │116 + const updated = (try evt.readFromRepo(evt.Patch, kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound;│ │117 const revision = updated.event.revision orelse return error.NotFound; │ │118 try std.testing.expectEqualStrings(&next, revision.source_oid); │ │119 try std.testing.expectEqual(@as(?u64, if (kind == .xit) 1 else null), revision.commit_count); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │128 .description = patch.description, │ │129 .target_branch = "other-target", │ │130 } }, author); │ │130 - const retargeted = (try evt.Patch.readFromRepo(kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │131 + const retargeted = (try evt.readFromRepo(evt.Patch, kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound;│ │132 try std.testing.expectEqualStrings("other-target", retargeted.event.target_branch); │ │133 try std.testing.expectEqualStrings("feature", retargeted.event.source_branch orelse return error.NotFound); │ │134 try std.testing.expect(retargeted.event.revision != null); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │162 // removing the patch leaves its branch intact │ │163 try evt.remove(.local, .repo, kind, opts, io, allocator, &repo, &id, .patch, author); │ │164 try pch.refreshBranches(.local, kind, opts, io, allocator, &repo, null, null); │ │164 - const removed = (try evt.Patch.readFromRepo(kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │165 + const removed = (try evt.readFromRepo(evt.Patch, kind, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │166 try std.testing.expect(removed.removed); │ │167 try std.testing.expectEqualStrings(&next, &(try repo.readRef(io, .{ .kind = .head, .name = "feature" }) orelse return error.NotFound));│ │168 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │197 try repo.add(io, allocator, &.{"base.txt"}); │ │198 _ = try repo.commit(io, allocator, .{ .message = "base" }); │ │199 try repo.addBranch(io, .{ .name = "feature" }); │ │199 - _ = try repo.commitAtRef(io, allocator, .{ .message = "remove file" }, null, .{ .kind = .head, .name = "feature" }); │ │200 + { │ │201 + var selected = try repo.switchDir(io, allocator, .{ .target = .{ .ref = .{ .kind = .head, .name = "feature" } } });│ │202 + defer selected.deinit(); │ │203 + const file = try repo.core.work_dir.createFile(io, "base.txt", .{}); │ │204 + defer file.close(io); │ │205 + try file.writeStreamingAll(io, "source\n"); │ │206 + try repo.add(io, allocator, &.{"base.txt"}); │ │207 + _ = try repo.commit(io, allocator, .{ .message = "edit file" }); │ │208 + var restored = try repo.switchDir(io, allocator, .{ .target = .{ .ref = .{ .kind = .head, .name = "master" } } }); │ │209 + defer restored.deinit(); │ │210 + } │ │211 const id = [_]u8{9} ** evt.event_id_size; │ │212 const id_hex = std.fmt.bytesToHex(id, .lower); │ │213 try pch.writeBranchPatch(.server, .xit, opts, io, allocator, &repo, id_hex, .{ │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │242 try evt.Patch.update(.server, .xit, opts, io, allocator, &repo, &id, .{ .status = .open }, author); │ │243 var arena = std.heap.ArenaAllocator.init(allocator); │ │244 defer arena.deinit(); │ │234 - const before = (try evt.Patch.readFromRepo(.xit, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │245 + const before = (try evt.readFromRepo(evt.Patch, .xit, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound;│ │246 const revision = before.event.revision orelse return error.NotFound; │ │247 try std.testing.expectEqualStrings(&next, revision.source_oid); │ │248 { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │263 │ │264 // both merge styles use the stored revision, without fork data │ │265 try pch.merge(opts, io, allocator, path, &repo, .{ .id = id_hex, .revision = selection, .author = author, .timestamp = 100 });│ │255 - const merged = (try evt.Patch.readFromRepo(.xit, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │256 - const range = switch (merged.event.status) { │ │266 + const merged = (try evt.readFromRepo(evt.Patch, .xit, opts, io, allocator, &arena, &repo, &id)) orelse return error.NotFound; │ │267 + const merged_status = switch (merged.event.status) { │ │268 .merged => |value| value, │ │269 else => return error.NotMerged, │ │270 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │271 const result_oid = (try repo.readRef(io, .{ .kind = .head, .name = "master" })) orelse return error.NotFound; │ │261 - try std.testing.expectEqual(selection, range.revision); │ │262 - try std.testing.expectEqualStrings(&target_oid, range.before_oid); │ │263 - try std.testing.expectEqualStrings(&result_oid, range.after_oid); │ │272 + try std.testing.expectEqual(selection, merged_status.revision); │ │273 + const result_id = try evt.parseEventId(&merged_status.patchrev_id); │ │274 + const result = (try evt.readFromRepo(evt.PatchRev, .xit, opts, io, allocator, &arena, &repo, &result_id)) orelse return error.NotFound;│ │275 + try std.testing.expectEqualStrings(&target_oid, result.event.base_oid); │ │276 + try std.testing.expectEqualStrings(&result_oid, result.event.source_oid); │ │277 + try std.testing.expect(!std.mem.eql(u8, &revision.id, &merged_status.patchrev_id)); │ │278 + try std.testing.expectEqualDeep(before.event.revision, merged.event.revision); │ │279 { │ │280 var commits = try repo.log(io, allocator, .{ .start_oids = &.{result_oid} }); │ │281 defer commits.deinit(); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │294 try pch.refreshBranches(.server, .xit, opts, io, allocator, &repo, null, &progress); │ │295 try std.testing.expectEqual(progress_len, output.written().len); │ │296 try std.testing.expectEqualStrings(&tip, &(try repo.readRef(io, evt.events_ref) orelse return error.NotFound)); │ │297 + │ │298 + // retain the exact merge diff after its original commits are collected │ │299 + const diff_route = (try ui.Repo.Patches.diffRoute(allocator, "", .{ .id = &id_hex, .record = merged })) orelse return error.BadRoute; │ │300 + const diff_before = try ui.Repo.Diff.init(.xit, opts, &arena, &repo, io, allocator, diff_route.repo_diff); │ │301 + try std.testing.expectEqual(1, diff_before.window.hunks.len); │ │302 + try std.testing.expectEqualStrings("base.txt", diff_before.window.hunks[0].path orelse return error.MissingPath); │ │303 + _ = try repo.commitAtRef(io, allocator, .{ .message = "rewrite history", .parent_oids = &.{} }, null, .{ .kind = .head, .name = "master" }); │ │304 + try repo.removeBranch(io, .{ .name = "feature" }); │ │305 + _ = try repo.garbageCollect(io, allocator, .{}); │ │306 + { │ │307 + var moment = try repo.core.latestMoment(); │ │308 + const state = rp.Repo(.xit, opts).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } }; │ │309 + try std.testing.expectError(error.ObjectNotFound, obj.Object(.xit, opts).initCommit(state, io, allocator, &result_oid)); │ │310 + try std.testing.expectError(error.ObjectNotFound, obj.Object(.xit, opts).initCommit(state, io, allocator, &target_oid)); │ │311 + } │ │312 + const diff_after = try ui.Repo.Diff.init(.xit, opts, &arena, &repo, io, allocator, diff_route.repo_diff); │ │313 + try std.testing.expectEqualDeep(diff_before.window, diff_after.window); │ │314 + const files = try ui.Repo.Files.initPatchRev(.xit, opts, &arena, &repo, io, allocator, .{ .repo = "" }, &merged_status.patchrev_id, "base.txt", 0); │ │315 + try std.testing.expectEqual(2, files.entries.len); │ │316 + try std.testing.expectEqualStrings("base.txt", files.entries[0].name); │ │317 + try std.testing.expectEqualStrings("source", files.entries[0].lines[0]); │ │318 + const files_route = files.filesRoute("base.txt", 0) orelse return error.BadRoute; │ │319 + try std.testing.expectEqualStrings(&merged_status.patchrev_id, files_route.repo_files.patchrev_id.slice()); │ │320 + │ │321 + // opening either tab must not load a commit log from the pruned history │ │322 + var session = ui.Session{ .io = io, .arena = &arena, .page_arena = &arena, .local = .{ .path = path, .repo_kind = .xit }, .data = .{ .host_kind = .local } };│ │323 + for ([_]ui.RoutablePage{ diff_route, files_route }) |route| { │ │324 + session.data.current_page = route; │ │325 + const page = try ui.Repo.init(&arena, &session, route); │ │326 + const diff = switch (page.changes) { │ │327 + .diff => |diff| diff, │ │328 + .commits => return error.MissingDiff, │ │329 + }; │ │330 + try std.testing.expectEqualDeep(diff_before.window, diff.window); │ │331 + try std.testing.expectEqualStrings(&merged_status.patchrev_id, page.files.patchrev_id orelse return error.MissingRevision); │ │332 + } │ │333 } │ │334 │ │335 fn commitTree( │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯