╭ changed ───╮ │src/fork.zig│ ╰────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │183 - const existing = if (moment_maybe) |moment| try evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, moment, &arena, &patch_id) else null;│ │370 + const existing = if (target_moment) |moment| │ │371 + try evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, moment, &arena, &patch_id) │ │372 + else │ │373 + null; │ │374 if (existing) |record| { │ │375 if (record.removed or !std.mem.eql(u8, record.author_email orelse "", input.author.email)) return error.InvalidPatch; │ │376 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │377 + if (local_patch.removed) { │ │378 + if (existing == null) return error.PatchDataUnavailable; │ │379 + return; │ │380 + } │ │381 │ │188 - var stage = try rp.Repo(.xit, repo_opts).open(io, allocator, .{ .path = draft_path }); │ │189 - defer stage.deinit(io, allocator); │ │190 - const source_oid = (try stage.readRef(io, ref)) orelse return error.PatchNotPushed; │ │191 - if (!std.mem.eql(u8, recorded_source_oid, &source_oid)) return error.InvalidPatchDraft; │ │382 + const revision_hex = local_patch.event.patchrev_id orelse return error.PatchNotPushed; │ │383 + const revision_id = try evt.parseEventId(&revision_hex); │ │384 + const revision = (try evt.PatchRev.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, fork_moment, &arena, &revision_id)) orelse return error.PatchNotPushed;│ │385 + if (revision.removed) return error.PatchNotPushed; │ │386 + const metadata = existing orelse local_patch; │ │387 │ │193 - const target_oid = (try target_repo.readRef(io, .{ .kind = .head, .name = target_name })) orelse return error.TargetNotFound; │ │194 - var stage_moment = try stage.core.latestMoment(); │ │195 - const stage_state = rp.Repo(.xit, repo_opts).State(.read_only){ .core = &stage.core, .extra = .{ .moment = &stage_moment } }; │ │196 - var objects = try obj.ObjectIterator(.xit, repo_opts).init(stage_state, io, allocator, .{ .kind = .all }); │ │197 - defer objects.deinit(); │ │198 - try objects.include(&source_oid); │ │199 - try target_repo.copyObjects(.xit, repo_opts, &objects, io, null); │ │200 - │ │201 - var target_moment = try target_repo.core.latestMoment(); │ │202 - const target_state = rp.Repo(.xit, repo_opts).State(.read_only){ .core = &target_repo.core, .extra = .{ .moment = &target_moment } }; │ │203 - const base_oid = try mrg.commonAncestor(.xit, repo_opts, target_state, io, allocator, &target_oid, &source_oid); │ │204 - var base_object = try obj.Object(.xit, repo_opts).initCommit(target_state, io, allocator, &base_oid); │ │205 - defer base_object.deinit(); │ │206 - var source_object = try obj.Object(.xit, repo_opts).initCommit(target_state, io, allocator, &source_oid); │ │207 - defer source_object.deinit(); │ │208 - const base_tree_oid = base_object.content.commit.tree; │ │209 - const head_tree_oid = source_object.content.commit.tree; │ │210 - │ │211 - var message: std.ArrayList(u8) = .empty; │ │212 - defer message.deinit(allocator); │ │213 - try source_object.readMessage(allocator, &message, .limited(10_000)); │ │214 - │ │215 - const target_ref = try std.fmt.allocPrint(arena.allocator(), "refs/heads/{s}", .{target_name}); │ │216 - var revision_changed = true; │ │217 - var patchrev_id: [evt.event_id_size]u8 = undefined; │ │218 - if (existing) |record| { │ │219 - patchrev_id = try evt.parseEventId(&record.event.patchrev_id); │ │220 - if (std.mem.eql(u8, record.event.target_ref, target_ref)) { │ │221 - const moment = moment_maybe orelse return error.PatchNotFound; │ │222 - if (try evt.PatchRev.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, moment, &arena, &patchrev_id)) |revision| { │ │223 - revision_changed = !std.mem.eql(u8, revision.event.base_oid, &base_oid) or !std.mem.eql(u8, revision.event.source_oid, &source_oid); │ │224 - } │ │225 - } │ │226 - } │ │227 - if (revision_changed) io.random(&patchrev_id); │ │228 - const patchrev_hex = std.fmt.bytesToHex(patchrev_id, .lower); │ │229 - │ │230 - const tree_entries = [_]evt.EventTreeEntry{ │ │231 - .{ .tree = .{ .name = "base", .oid = &base_tree_oid } }, │ │232 - .{ .tree = .{ .name = "head", .oid = &head_tree_oid } }, │ │233 - }; │ │234 - const patch_event = evt.EventWithId{ │ │388 + try evt.consume(.xit, repo_opts, io, allocator, target_repo, evt.events_ref, &.{.{ │ │389 .id = input.id, │ │390 .timestamp = input.timestamp, │ │391 .author = input.author, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │392 .event = .{ .patch = .{ │ │239 - .title = input.title, │ │240 - .description = input.description, │ │241 - .tags = input.tags, │ │242 - .target_ref = target_ref, │ │243 - .target_patch_id = if (existing) |record| record.event.target_patch_id else null, │ │244 - .patchrev_id = patchrev_hex, │ │393 + .title = metadata.event.title, │ │394 + .description = metadata.event.description, │ │395 + .tags = metadata.event.tags, │ │396 + .target_patch_id = metadata.event.target_patch_id, │ │397 + .patchrev_id = revision_hex, │ │398 } }, │ │246 - }; │ │247 - if (revision_changed) { │ │248 - try evt.consume(.xit, repo_opts, io, allocator, target_repo, evt.events_ref, &.{ │ │249 - .{ │ │250 - .id = patchrev_hex, │ │251 - .timestamp = input.timestamp, │ │252 - .author = input.author, │ │253 - .tree_entries = &tree_entries, │ │254 - .event = .{ .patchrev = .{ .base_oid = &base_oid, .source_oid = &source_oid, .message = message.items } },│ │255 - }, │ │256 - patch_event, │ │257 - }); │ │258 - } else { │ │259 - try evt.consume(.xit, repo_opts, io, allocator, target_repo, evt.events_ref, &.{patch_event}); │ │260 - } │ │399 + }}); │ │400 + │ │401 + try evt.consume(.xit, repo_opts, io, allocator, &fork_repo, evt.events_ref, &.{.{ │ │402 + .id = input.id, │ │403 + .timestamp = input.timestamp, │ │404 + .author = input.author, │ │405 + .event = .{ .patch = null }, │ │406 + }}); │ │407 } │ │408 │ │409 // delete first so a failed tombstone can be retried safely │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │423 const record = (try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, &arena, &fork_id)) orelse return error.InvalidPatchDraft;│ │424 if (!std.mem.eql(u8, record.event.user_id, user_id)) return error.InvalidPatchDraft; │ │425 │ │280 - const draft_path = try forkPath(allocator, repo_root_path, id); │ │281 - defer allocator.free(draft_path); │ │282 - try std.Io.Dir.cwd().deleteTree(io, draft_path); │ │426 + const path = try forkPath(allocator, repo_root_path, id); │ │427 + defer allocator.free(path); │ │428 + try std.Io.Dir.cwd().deleteTree(io, path); │ │429 if (!record.removed) try evt.remove(.xit, evt.admin_repo_opts, io, allocator, admin_repo, &fork_id, .fork, author); │ │430 } │ │431 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯