╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │977 .timestamp = input.timestamp, │ │978 }); │ │979 } │ │980 + │ │981 + // refresh other patches affected by the updated target branch. │ │982 refreshMergeability(repo_opts, io, allocator, target_repo, null); │ │983 + │ │984 // the merge moved the target branch, so its indexes follow │ │985 find.refresh(repo_opts, io, allocator, target_repo) catch |err| { │ │986 std.log.warn("failed to refresh file index: {s}", .{@errorName(err)});│ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1003 try merge(repo_opts, io, allocator, repo_root_path, target_repo, input); │ │1004 var arena = std.heap.ArenaAllocator.init(allocator); │ │1005 defer arena.deinit(); │ │1006 + │ │1007 + // remove fork data only for patches backed by a fork. │ │1008 const record = (try evt.readFromRepo(evt.Patch, .xit, repo_opts, io, allocator, &arena, target_repo, &try evt.parseEventId(&input.id))) orelse return error.InvalidPatch;│ │1009 if (record.event.source_branch == null) try fork.remove(io, allocator, repo_root_path, admin_repo, &input.id, null, input.author); │ │1010 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │1034 var arena = std.heap.ArenaAllocator.init(allocator); │ │1035 defer arena.deinit(); │ │1036 │ │1037 + // identify which selected revision was merged by the client. │ │1038 const patch_hex = std.fmt.bytesToHex(patch_id.*, .lower); │ │1039 const fork_path = try fork.forkPath(arena.allocator(), repo_root_path, &patch_hex); │ │1040 const events = try evt.currentMomentFromRepoMoment(repo_opts.hash, moment.readOnly());│ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1041 const record = (try evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, events, &arena, patch_id)) orelse return; │ │1042 const selected = record.event.revision orelse return; │ │1043 const revision: evt.Patch.MergeRevision = if (std.mem.eql(u8, matched_oid, selected.source_oid)) .source else if (std.mem.eql(u8, matched_oid, selected.squash_oid)) .squash else return; │ │1044 + │ │1045 + // open the fork when the revision is not stored in the target repository. │ │1046 var fork_repo_maybe: ?rp.Repo(.xit, repo_opts) = if (record.event.source_branch != null) null else try rp.Repo(.xit, repo_opts).open(io, allocator, .{ .path = fork_path, .require_repo_root = true });│ │1047 defer if (fork_repo_maybe) |*repo| repo.deinit(io, allocator); │ │1048 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1049 + // import the revision and record the client-side merge. │ │1050 _ = try importMergedRevision(repo_opts, state, db, moment, io, allocator, if (fork_repo_maybe) |*repo| repo else null, patch_id, .{ .revision = revision, .before_oid = before_oid, .after_oid = after_oid }, null, null, 0);│ │1051 } │ │1052 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1073 var arena = std.heap.ArenaAllocator.init(allocator); │ │1074 defer arena.deinit(); │ │1075 │ │1076 + // read the patch and reject outdated or conflicting merge requests. │ │1077 const haxy_moment = try evt.currentMomentFromRepoMoment(repo_opts.hash, moment.readOnly()); │ │1078 const patch_record = (try evt.Patch.readById(DB, repo_opts.hash, haxy_moment, &arena, patch_id)) orelse return false;│ │1079 const selected = patch_record.event.revision orelse return false; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1087 } │ │1088 const revision_id = try evt.parseEventId(&selected.id); │ │1089 │ │1090 + // resolve the selected revision from the target or its fork. │ │1091 const current_revision = try evt.PatchRev.readById(DB, repo_opts.hash, haxy_moment, &arena, &revision_id); │ │1092 const revision = (if (fork_repo) |repo| │ │1093 try evt.PatchRev.readById(DB, repo_opts.hash, try evt.currentMoment(repo_opts, repo), &arena, &revision_id)│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1095 current_revision) orelse return false; │ │1096 if (!selected.matches(revision)) return false; │ │1097 │ │1098 + // read the original revision commit and author. │ │1099 var fork_repo_moment = if (fork_repo) |repo| try repo.core.latestMoment() else moment.readOnly(); │ │1100 const fork_state = rp.Repo(.xit, repo_opts).State(.read_only){ .core = if (fork_repo) |repo| &repo.core else state.core, .extra = .{ .moment = &fork_repo_moment } };│ │1101 var event_oid: [hash.hexLen(repo_opts.hash)]u8 = undefined; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1105 defer event_object.deinit(); │ │1106 const original_author = try commitAuthor(event_object.content.commit.metadata.author orelse return false);│ │1107 │ │1108 + // retain the submitted revision if the target has not imported it yet. │ │1109 var events_buffer: [3]evt.EventWithId = undefined; │ │1110 var events: std.ArrayList(evt.EventWithId) = .initBuffer(&events_buffer); │ │1111 var tree_entries: [2]evt.EventTreeEntry = undefined; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1127 if (!selected.matches(existing)) return error.InvalidPatch; │ │1128 } │ │1129 │ │ 892 - const author = merged_author orelse original_author; │ │ 893 - const timestamp = if (merged_author != null) merged_timestamp else @as(u64, @intCast(std.Io.Timestamp.now(io, .real).toSeconds())); │ │ 894 - var patch = patch_record.event; │ │ 895 - const result = try evt.PatchRev.prepare(.xit, repo_opts, state, io, &arena, merged.before_oid, merged.after_oid, patch.title, author, timestamp); │ │ 896 - events.appendAssumeCapacity(result.event); │ │ 897 - patch.status = .{ .merged = .{ .revision = merged.revision, .patchrev_id = result.event.id } }; │ │ 898 - events.appendAssumeCapacity(.{ │ │ 899 - .id = std.fmt.bytesToHex(patch_id.*, .lower), │ │ 900 - .timestamp = timestamp, │ │ 901 - .author = author, │ │ 902 - .event = .{ .patch = patch }, │ │ 903 - }); │ │1130 + // snapshot the merge result and mark the patch as merged. │ │1131 + { │ │1132 + const author = merged_author orelse original_author; │ │1133 + const timestamp = if (merged_author != null) merged_timestamp else @as(u64, @intCast(std.Io.Timestamp.now(io, .real).toSeconds())); │ │1134 + var patch = patch_record.event; │ │1135 + const result = try evt.PatchRev.prepare(.xit, repo_opts, state, io, &arena, merged.before_oid, merged.after_oid, patch.title, author, timestamp);│ │1136 + events.appendAssumeCapacity(result.event); │ │1137 + patch.status = .{ .merged = .{ .revision = merged.revision, .patchrev_id = result.event.id } }; │ │1138 + events.appendAssumeCapacity(.{ │ │1139 + .id = std.fmt.bytesToHex(patch_id.*, .lower), │ │1140 + .timestamp = timestamp, │ │1141 + .author = author, │ │1142 + .event = .{ .patch = patch }, │ │1143 + }); │ │1144 + } │ │1145 │ │1146 + // commit and consume the revision and patch events together. │ │1147 try evt.commitEvents(.xit, repo_opts, state, io, allocator, evt.events_ref, events.items, null); │ │1148 if (!try evt.consumeInTransaction(.repo, .xit, repo_opts, state, db, moment, io, allocator, evt.events_ref)) return error.CancelTransaction; │ │1149 return true; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯