╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ fork.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
352 } 353 } 354 355 // posted patches keep only revisions in the fork 356 if (ctx.posted and !ctx.patch.removed) { 357 events[event_count] = .{ 358 .id = ctx.patch_id, 359 .timestamp = ctx.timestamp, 360 .author = ctx.author, 361 .event = .{ .patch = null }, 362 }; 363 event_count += 1; 364 } 365 366 // commit and index any new events 367 if (event_count > 0) { 368 try evt.commitEvents(.xit, repo_opts, state, ctx.io, ctx.allocator, evt.events_ref, events[0..event_count], null); 369 if (!try evt.consumeInTransaction(.fork, .xit, repo_opts, state, &ctx.core.db, &moment, ctx.io, ctx.allocator, evt.events_ref)) return error.CancelTransaction; 370 } 371 try xit.undo.writeMessage(repo_opts, state, .push); 372 } 373 }; 374 375 try fork_repo.core.db_file.lock(io, .exclusive); 376 defer fork_repo.core.db_file.unlock(io); 377 378 const history = try DB.ArrayList(.read_write).init(fork_repo.core.db.rootCursor()); 379 history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ 380 .core = &fork_repo.core, 381 .target_core = &target_repo.core, 382 .io = io, 383 .allocator = allocator, 384 .reader = reader, 385 .writer = writer, 386 .patch_id = id.*, 387 .patch = fork_patch, 388 .posted = posted, 389 .target_oid = target_oid, 390 .target_ref = target_ref, 391 .title = title, 392 .author = author, 393 .timestamp = timestamp, 394 .newest = newest, 395 .revision_id_maybe = &revision_id_maybe, 396 }) catch |err| switch (err) { 397 error.CancelTransaction => {}, 398 else => |other| return other, 399 }; 400 try writer.flush(); 401 } 402 403 // best-effort update the posted patch so it has the new revision 404 { 405 const revision_id = revision_id_maybe orelse return; 406 if (!posted) return; 407 var update_arena = std.heap.ArenaAllocator.init(allocator); 408 defer update_arena.deinit(); 409 const target_update_moment = evt.currentMoment(repo_opts, target_repo) catch return; 410 const posted_patch = (evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, target_update_moment, &update_arena, &patch_id) catch return) orelse return; 411 if (posted_patch.removed) return; 412 const fork_update_moment = evt.currentMoment(repo_opts, fork_repo) catch return; 413 const revision = (evt.PatchRev.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, fork_update_moment, &update_arena, &revision_id) catch return) orelse return; 414 if (revision.removed) return; 415 const selected = evt.Patch.Revision.fromRecord(revision_id, revision); 416 if (posted_patch.event.revision) |current| { 417 if (evt.fieldEqual(evt.Patch.Revision, current, selected)) return; 418 } 419 var patch = posted_patch.event; 420 patch.revision = selected; 421 evt.consume(.repo, .xit, repo_opts, io, allocator, target_repo, evt.events_ref, &.{.{ 422 .id = id.*, 423 .timestamp = timestamp, 424 .author = author, 425 .event = .{ .patch = patch }, 426 }}) catch |update_err| { 427 serve_common.logError(io, error_writer, "failed to update posted patch {s}: {s}\n", .{ id, @errorName(update_err) }); 428 }; 429 } 430 } 431 432 // delete first so a failed tombstone can be retried safely 433 pub fn remove( 434 io: std.Io, 435 allocator: std.mem.Allocator, 436 repo_root_path: []const u8, 437 admin_repo: *rp.Repo(.xit, evt.admin_repo_opts), 438 id: *const [evt.event_id_size * 2]u8, 439 user_id: *const [evt.event_id_size]u8, 440 author: evt.CommitAuthor, 441 ) !void { 442 const fork_id = try evt.parseEventId(id); 443 var arena = std.heap.ArenaAllocator.init(allocator); 444 defer arena.deinit(); 445 const moment = try evt.currentMoment(evt.admin_repo_opts, admin_repo); 446 const record = (try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, &arena, &fork_id)) orelse return error.InvalidPatchDraft; 447 if (!std.mem.eql(u8, record.event.user_id, user_id)) return error.InvalidPatchDraft; 448 449 const path = try forkPath(allocator, repo_root_path, id); 450 defer allocator.free(path); 451 try std.Io.Dir.cwd().deleteTree(io, path); 452 if (!record.removed) try evt.remove(.admin, .xit, evt.admin_repo_opts, io, allocator, admin_repo, &fork_id, .fork, author); 453 } 454