╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───╮ │src/fork.zig│ ╰────────────╯ ╭─────────────────────────────────────────────────────────╮ │251 writer: *std.Io.Writer, │ │252 patch_id: [evt.event_id_size * 2]u8, │ │253 patch: evt.Patch.Record, │ │254 - published: bool, │ │254 + posted: bool, │ │255 target_oid: [hash.hexLen(repo_opts.hash)]u8,│ │256 target_ref: []const u8, │ │257 title: []const u8, │ ╰─────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │287 var head_tree_oid: [hash.hexLen(repo_opts.hash)]u8 = undefined; │ │288 var tree_entries: [2]evt.EventTreeEntry = undefined; │ │289 if (existing_revision) |latest| { │ │290 - if (ctx.published) ctx.revision_id_maybe.* = latest.id; │ │290 + if (ctx.posted) ctx.revision_id_maybe.* = latest.id; │ │291 } else { │ │292 var base_object = try obj.Object(.xit, repo_opts).initCommit(state.readOnly(), ctx.io, ctx.allocator, &base_oid);│ │293 defer base_object.deinit(); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │299 var revision_id: [evt.event_id_size]u8 = undefined; │ │300 ctx.io.random(&revision_id); │ │301 const revision_hex = std.fmt.bytesToHex(revision_id, .lower); │ │302 + const revision_event: evt.PatchRev = .{ │ │303 + .base_oid = &base_oid, │ │304 + .source_oid = &source_oid, │ │305 + .target_ref = ctx.target_ref, │ │306 + .message = ctx.title, │ │307 + }; │ │308 + const identity = try std.fmt.allocPrint(ctx.allocator, "{s} <{s}>", .{ ctx.author.name, ctx.author.email });│ │309 + defer ctx.allocator.free(identity); │ │310 + const patch_oid = try evt.PatchRev.writeSquashCommit( │ │311 + .xit, │ │312 + repo_opts, │ │313 + state, │ │314 + ctx.io, │ │315 + ctx.allocator, │ │316 + revision_event, │ │317 + &head_tree_oid, │ │318 + identity, │ │319 + identity, │ │320 + ctx.timestamp, │ │321 + ); │ │322 tree_entries = .{ │ │323 .{ .tree = .{ .name = "base", .oid = &base_tree_oid } }, │ │324 .{ .tree = .{ .name = "head", .oid = &head_tree_oid } }, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │328 .timestamp = ctx.timestamp, │ │329 .author = ctx.author, │ │330 .tree_entries = &tree_entries, │ │311 - .event = .{ .patchrev = .{ │ │312 - .base_oid = &base_oid, │ │313 - .source_oid = &source_oid, │ │314 - .target_ref = ctx.target_ref, │ │315 - .message = ctx.title, │ │316 - } }, │ │331 + .event = .{ .patchrev = revision_event },│ │332 }; │ │333 event_count += 1; │ │334 ctx.revision_id_maybe.* = revision_id; │ ╰──────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────╮ │335 │ │321 - if (!ctx.published) { │ │336 + if (!ctx.posted) { │ │337 var patch = ctx.patch.event; │ │323 - patch.patchrev_id = revision_hex;│ │338 + patch.revision = .{ │ │339 + .id = revision_hex, │ │340 + .squash_oid = &patch_oid, │ │341 + .source_oid = &source_oid, │ │342 + .target_ref = ctx.target_ref,│ │343 + }; │ │344 events[event_count] = .{ │ │345 .id = ctx.patch_id, │ │346 .timestamp = ctx.timestamp, │ ╰──────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────╮ │351 } │ │352 } │ │353 │ │334 - if (ctx.published and !ctx.patch.removed) {│ │354 + if (ctx.posted and !ctx.patch.removed) { │ │355 events[event_count] = .{ │ │356 .id = ctx.patch_id, │ │357 .timestamp = ctx.timestamp, │ ╰────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────╮ │381 .writer = writer, │ │382 .patch_id = id.*, │ │383 .patch = fork_patch, │ │364 - .published = published, │ │384 + .posted = posted, │ │385 .target_oid = target_oid,│ │386 .target_ref = target_ref,│ │387 .title = title, │ ╰──────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │396 try writer.flush(); │ │397 │ │398 const revision_id = revision_id_maybe orelse return; │ │379 - if (!published) return; │ │399 + if (!posted) return; │ │400 var update_arena = std.heap.ArenaAllocator.init(allocator); │ │401 defer update_arena.deinit(); │ │402 const update_moment = evt.currentMoment(repo_opts, target_repo) catch return;│ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │383 - const published_patch = (evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, update_moment, &update_arena, &patch_id) catch return) orelse return; │ │384 - if (published_patch.removed) return; │ │385 - const revision_hex = std.fmt.bytesToHex(revision_id, .lower); │ │386 - if (published_patch.event.patchrev_id) |*published_revision| { │ │387 - if (std.mem.eql(u8, published_revision, &revision_hex)) return; │ │403 + const posted_patch = (evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, update_moment, &update_arena, &patch_id) catch return) orelse return; │ │404 + if (posted_patch.removed) return; │ │405 + const fork_update_moment = evt.currentMoment(repo_opts, fork_repo) catch return; │ │406 + const revision = (evt.PatchRev.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, fork_update_moment, &update_arena, &revision_id) catch return) orelse return;│ │407 + if (revision.removed) return; │ │408 + const selected = evt.Patch.Revision.fromRecord(revision_id, revision); │ │409 + if (posted_patch.event.revision) |current| { │ │410 + if (evt.fieldEqual(evt.Patch.Revision, current, selected)) return; │ │411 } │ │389 - var patch = published_patch.event; │ │390 - patch.patchrev_id = revision_hex; │ │412 + var patch = posted_patch.event; │ │413 + patch.revision = selected; │ │414 evt.consume(.repo, .xit, repo_opts, io, allocator, target_repo, evt.events_ref, &.{.{ │ │415 .id = id.*, │ │416 .timestamp = timestamp, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │417 .author = author, │ │418 .event = .{ .patch = patch }, │ │419 }}) catch {}; │ │397 -} │ │398 - │ │399 -pub const PublishInput = struct { │ │400 - id: [evt.event_id_size * 2]u8, │ │401 - user_id: [evt.event_id_size]u8, │ │402 - repo_id: [evt.event_id_size]u8, │ │403 - author: evt.CommitAuthor, │ │404 - timestamp: u64, │ │405 -}; │ │406 - │ │407 -pub fn publish( │ │408 - comptime repo_opts: rp.RepoOpts(.xit), │ │409 - io: std.Io, │ │410 - allocator: std.mem.Allocator, │ │411 - admin_repo: *rp.Repo(.xit, evt.admin_repo_opts), │ │412 - target_repo: *rp.Repo(.xit, repo_opts), │ │413 - path: []const u8, │ │414 - input: PublishInput, │ │415 -) !void { │ │416 - const patch_id = try evt.parseEventId(&input.id); │ │417 - var arena = std.heap.ArenaAllocator.init(allocator); │ │418 - defer arena.deinit(); │ │419 - │ │420 - const admin_moment = try evt.currentMoment(evt.admin_repo_opts, admin_repo); │ │421 - const fork_record = (try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, admin_moment, &arena, &patch_id)) orelse return error.InvalidPatchDraft; │ │422 - if (fork_record.removed or │ │423 - !std.mem.eql(u8, fork_record.event.user_id, &input.user_id) or │ │424 - !std.mem.eql(u8, fork_record.event.repo_id, &input.repo_id)) return error.InvalidPatchDraft; │ │425 - const user = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, admin_moment, &arena, &input.user_id)) orelse return error.InvalidPatchDraft; │ │426 - if (user.removed or │ │427 - !std.mem.eql(u8, user.event.name, input.author.name) or │ │428 - !std.mem.eql(u8, user.event.email, input.author.email)) return error.InvalidPatchDraft; │ │429 - │ │430 - var fork_repo = rp.Repo(.xit, repo_opts).open(io, allocator, .{ .path = path }) catch return error.PatchDataUnavailable; │ │431 - defer fork_repo.deinit(io, allocator); │ │432 - const fork_moment = evt.currentMoment(repo_opts, &fork_repo) catch return error.PatchDataUnavailable; │ │433 - const local_patch = (try evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, fork_moment, &arena, &patch_id)) orelse return error.PatchDataUnavailable; │ │434 - │ │435 - const target_moment = evt.currentMoment(repo_opts, target_repo) catch |err| switch (err) { │ │436 - error.NotFound => null, │ │437 - else => |other| return other, │ │438 - }; │ │439 - const existing = if (target_moment) |moment| │ │440 - try evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, moment, &arena, &patch_id) │ │441 - else │ │442 - null; │ │443 - if (existing) |record| { │ │444 - if (record.removed or !std.mem.eql(u8, record.author_email orelse "", input.author.email)) return error.InvalidPatch; │ │445 - } │ │446 - if (local_patch.removed) { │ │447 - if (existing == null) return error.PatchDataUnavailable; │ │448 - } else { │ │449 - const revision_hex = local_patch.event.patchrev_id orelse return error.PatchNotPushed; │ │450 - const revision_id = try evt.parseEventId(&revision_hex); │ │451 - const revision = (try evt.PatchRev.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, fork_moment, &arena, &revision_id)) orelse return error.PatchNotPushed;│ │452 - if (revision.removed) return error.PatchNotPushed; │ │453 - var patch = (existing orelse local_patch).event; │ │454 - patch.patchrev_id = revision_hex; │ │455 - if (existing == null) patch.status = .open; │ │456 - │ │457 - try evt.consume(.repo, .xit, repo_opts, io, allocator, target_repo, evt.events_ref, &.{.{ │ │458 - .id = input.id, │ │459 - .timestamp = input.timestamp, │ │460 - .author = input.author, │ │461 - .event = .{ .patch = patch }, │ │462 - }}); │ │463 - │ │464 - try evt.consume(.fork, .xit, repo_opts, io, allocator, &fork_repo, evt.events_ref, &.{.{ │ │465 - .id = input.id, │ │466 - .timestamp = input.timestamp, │ │467 - .author = input.author, │ │468 - .event = .{ .patch = null }, │ │469 - }}); │ │470 - } │ │471 - │ │472 - if (fork_record.event.stage == .draft) { │ │473 - var posted = fork_record.event; │ │474 - posted.stage = .posted; │ │475 - try evt.consume(.admin, .xit, evt.admin_repo_opts, io, allocator, admin_repo, evt.events_ref, &.{.{ │ │476 - .id = input.id, │ │477 - .timestamp = input.timestamp, │ │478 - .author = input.author, │ │479 - .event = .{ .fork = posted }, │ │480 - }}); │ │481 - } │ │420 } │ │421 │ │422 // delete first so a failed tombstone can be retried safely │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯