╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ push.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
307 const existing_revision = if (ctx.newest) |latest| 308 if (std.mem.eql(u8, latest.record.event.base_oid, &base_oid) and 309 std.mem.eql(u8, latest.record.event.source_oid, &source_oid)) latest else null 310 else 311 null; 312 313 // reuse an identical revision or record a new one 314 var revision_arena = std.heap.ArenaAllocator.init(ctx.allocator); 315 defer revision_arena.deinit(); 316 var events: [2]evt.EventWithId = undefined; 317 var event_count: usize = 0; 318 if (existing_revision) |latest| { 319 if (ctx.published) ctx.revision_id_maybe.* = latest.id; 320 } else { 321 const prepared = try evt.PatchRev.prepare(.xit, repo_opts, state, ctx.io, &revision_arena, &base_oid, &source_oid, ctx.title, ctx.author, ctx.timestamp); 322 events[event_count] = prepared.event; 323 event_count += 1; 324 ctx.revision_id_maybe.* = try evt.parseEventId(&prepared.event.id); 325 326 if (!ctx.published) { 327 var patch = ctx.patch.event; 328 patch.revision = prepared.revision; 329 events[event_count] = .{ 330 .id = ctx.patch_id, 331 .timestamp = ctx.timestamp, 332 .author = ctx.author, 333 .event = .{ .patch = patch }, 334 }; 335 event_count += 1; 336 } 337 } 338 339 // published patches keep only revisions in the fork 340 if (ctx.published and !ctx.patch.removed) { 341 events[event_count] = .{ 342 .id = ctx.patch_id, 343 .timestamp = ctx.timestamp, 344 .author = ctx.author, 345 .event = .{ .patch = null }, 346 }; 347 event_count += 1; 348 } 349 350 // commit and index any new events 351 if (event_count > 0) { 352 try evt.commitEvents(.xit, repo_opts, state, ctx.io, ctx.allocator, evt.events_ref, events[0..event_count], null); 353 if (!try evt.consumeInTransaction(.fork, .xit, repo_opts, state, &ctx.core.db, &moment, ctx.io, ctx.allocator, evt.events_ref)) return error.CancelTransaction; 354 } 355 // a fork push tracks no ref updates, so it records no ranges 356 try writeUndo(repo_opts, state, ctx.io, ctx.allocator, ctx.author, &.{}); 357 } 358 }; 359 360 try fork_repo.core.db_file.lock(io, .exclusive); 361 defer fork_repo.core.db_file.unlock(io); 362 363 const history = try DB.ArrayList(.read_write).init(fork_repo.core.db.rootCursor()); 364 break :blk history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ 365 .core = &fork_repo.core, 366 .target_core = &target_repo.core, 367 .io = io, 368 .allocator = allocator, 369 .reader = reader, 370 .writer = writer, 371 .response = &response, 372 .patch_id = id.*, 373 .patch = fork_patch, 374 .published = published, 375 .target_oid = target_oid, 376 .title = title, 377 .author = author, 378 .timestamp = timestamp, 379 .newest = newest, 380 .revision_id_maybe = &revision_id_maybe, 381 .sideband = &sideband, 382 }); 383 }; 384 result catch |err| { 385 response.finish(writer, @errorName(err)) catch {}; 386 if (err == error.CancelTransaction) return; 387 return err; 388 }; 389 390 find.refresh(repo_opts, io, allocator, fork_repo) catch |err| { 391 serve_common.logError(io, error_writer, "failed to refresh file index: {s}\n", .{@errorName(err)}); 392 }; 393 if (!published) return response.finish(writer, null); 394 395 sideband.run(io, .{ .text = "Updating patches" }) catch {}; 396 sideband.run(io, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = 1 } }) catch {}; 397 398 // best-effort update the published patch so it has the new revision 399 var refreshed = false; 400 update: { 401 const revision_id = revision_id_maybe orelse break :update; 402 var update_arena = std.heap.ArenaAllocator.init(allocator); 403 defer update_arena.deinit(); 404 const target_update_moment = evt.currentMoment(repo_opts, target_repo) catch break :update; 405 const published_patch = (evt.Patch.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, target_update_moment, &update_arena, &patch_id) catch break :update) orelse break :update; 406 if (published_patch.removed) break :update; 407 const fork_update_moment = evt.currentMoment(repo_opts, fork_repo) catch break :update; 408 const revision = (evt.PatchRev.readById(evt.EventDB(repo_opts.hash), repo_opts.hash, fork_update_moment, &update_arena, &revision_id) catch break :update) orelse break :update; 409 if (revision.removed) break :update; 410 const selected = evt.Patch.Revision.fromRecord(revision_id, revision); 411 if (published_patch.event.revision) |current| { 412 if (evt.fieldEqual(evt.Patch.Revision, current, selected)) break :update; 413 } 414 var patch = published_patch.event; 415 patch.revision = selected; 416 evt.consume(.server, .repo, .xit, repo_opts, io, allocator, target_repo, evt.events_ref, &.{.{ 417 .id = id.*, 418 .timestamp = timestamp, 419 .author = author, 420 .event = .{ .patch = patch }, 421 }}) catch |update_err| { 422 serve_common.logError(io, error_writer, "failed to update published patch {s}: {s}\n", .{ id, @errorName(update_err) }); 423 break :update; 424 }; 425 // consume also refreshed mergeability 426 refreshed = true; 427 } 428 429 if (!refreshed) pch.refreshMergeability(repo_opts, io, allocator, target_repo, patch_id); 430 sideband.run(io, .{ .complete_one = .writing_patch }) catch {}; 431 sideband.run(io, .{ .end = .writing_patch }) catch {}; 432 433 // the client may read either repo as soon as it gets the final response. 434 try response.finish(writer, null); 435 } 436