╭ changed ────╮ │build.zig.zon│ ╰─────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 3 .version = "0.0.0", │ │ 4 .dependencies = .{ │ │ 5 .xit = .{ │ │ 6 - .url = "git+https://github.com/xit-vcs/xit#5d56e3857a74ab8967480107026aa608fe93b498",│ │ 7 - .hash = "xit-0.0.0-Ub3nDAlRUQHFtsKESb90igWXICdQUvMdPQ4eW3ExH095", │ │ 6 + .url = "git+https://github.com/xit-vcs/xit#f73df683e8e96c67fffa6334c7f00992275f520d",│ │ 7 + .hash = "xit-0.0.0-Ub3nDDq-UgHURdPc5eJ4uICyD1iDQiWH1swXPtmlbZUP", │ │ 8 }, │ │ 9 }, │ │10 .paths = .{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │561 parent_oids = null; │ │562 } │ │563 } │ │564 + │ │565 +pub const PushProgress = struct { │ │566 + response: *xit.net_server_receive_pack.Response, │ │567 + writer: *std.Io.Writer, │ │568 + count: usize = 0, │ │569 + total: usize = 0, │ │570 + │ │571 + pub fn run(self: *PushProgress, _: std.Io, event: rp.ProgressEvent) !void { │ │572 + switch (event) { │ │573 + .start => |start| { │ │574 + if (start.kind != .writing_patch) return; │ │575 + self.total = start.estimated_total_items; │ │576 + self.count = 0; │ │577 + }, │ │578 + .complete_one => |kind| { │ │579 + if (kind != .writing_patch) return; │ │580 + self.count += 1; │ │581 + }, │ │582 + .end => |kind| { │ │583 + if (kind != .writing_patch) return; │ │584 + }, │ │585 + else => return, │ │586 + } │ │587 + var buffer: [128]u8 = undefined; │ │588 + const percent = if (self.total == 0) 100 else @as(u128, self.count) * 100 / self.total; │ │589 + const text = try std.fmt.bufPrint(&buffer, "Processing commits: {d}% ({d}/{d}){s}", .{ percent, self.count, self.total, if (event == .end) "\n" else "\r" });│ │590 + try self.response.progress(self.writer, text); │ │591 + } │ │592 +}; │ │593 │ │594 +// prepare all reachable commits, including history received before patch │ │595 +// generation was enabled. this shares the receive-pack transaction. │ │596 +pub fn writeReceivedPatches( │ │597 + comptime repo_opts: rp.RepoOpts(.xit), │ │598 + state: rp.Repo(.xit, repo_opts).State(.read_write), │ │599 + io: std.Io, │ │600 + allocator: std.mem.Allocator, │ │601 + response: *xit.net_server_receive_pack.Response, │ │602 + writer: *std.Io.Writer, │ │603 +) !void { │ │604 + try response.progress(writer, "Processing commits...\n"); │ │605 + var config = try xit.config.Config(.xit, repo_opts).init(state.readOnly(), io, allocator); │ │606 + defer config.deinit(); │ │607 + try config.add(state, io, .{ .name = "merge.algorithm", .value = "patch" }); │ │608 + │ │609 + var iter = try obj.ObjectIterator(.xit, repo_opts).init(state.readOnly(), io, allocator, .{ .kind = .commit }); │ │610 + defer iter.deinit(); │ │611 + var refs = try rf.AllRefIterator(.xit, repo_opts).init(state.readOnly(), allocator); │ │612 + defer refs.deinit(); │ │613 + while (try refs.next()) |ref| { │ │614 + if (try rf.readRecur(.xit, repo_opts, state.readOnly(), io, .{ .ref = ref })) |oid| { │ │615 + try iter.include(&oid); │ │616 + } │ │617 + } │ │618 + var progress = PushProgress{ .response = response, .writer = writer }; │ │619 + try xit.patch.writePatches(repo_opts, state, io, allocator, &iter, &progress); │ │620 +} │ │621 + │ │622 // serve a receive-pack and consume any events it pushed to the events branch, │ │623 // all in one transaction: the push and the views derived from it commit │ │624 // atomically, and a failed consume cancels the push │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │636 const DB = rp.Repo(.xit, repo_opts).DB; │ │637 const State = rp.Repo(.xit, repo_opts).State; │ │638 │ │639 + var response = xit.net_server_receive_pack.Response.init(allocator);│ │640 + defer response.deinit(); │ │641 + │ │642 const Ctx = struct { │ │643 core: *rp.Repo(.xit, repo_opts).Core, │ │644 io: std.Io, │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────╮ │646 reader: *std.Io.Reader, │ │647 writer: *std.Io.Writer, │ │648 options: xit.net_server_receive_pack.Options, │ │649 + response: *xit.net_server_receive_pack.Response,│ │650 repo_root_path: []const u8, │ │651 error_writer: *std.Io.Writer, │ │652 │ ╰─────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │657 defer updates.deinit(); │ │658 var receive_options = ctx.options; │ │659 receive_options.applied_ref_updates = &updates; │ │660 + receive_options.deferred_response = ctx.response; │ │661 try xit.net_server_receive_pack.run(.xit, repo_opts, state, ctx.io, ctx.allocator, ctx.reader, ctx.writer, receive_options);│ │662 + try writeReceivedPatches(repo_opts, state, ctx.io, ctx.allocator, ctx.response, ctx.writer); │ │663 │ │664 // a repo without an events branch has no events to consume. a │ │665 // no-op consume must not cancel here, since the push shares the │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────╮ │685 .reader = reader, │ │686 .writer = writer, │ │687 .options = options, │ │688 + .response = &response, │ │689 .repo_root_path = repo_root_path,│ │690 .error_writer = error_writer, │ │691 }, │ ╰──────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │628 - ) catch |err| switch (err) { │ │629 - error.CancelTransaction => {}, │ │630 - else => |e| return e, │ │692 + ) catch |err| { │ │693 + response.finish(writer, @errorName(err)) catch {}; │ │694 + if (err == error.CancelTransaction) return; │ │695 + return err; │ │696 }; │ │697 │ │633 - // pkt-line writes are buffered until the transaction settles, so the │ │634 - // report-status reaches the client only after the push truly committed│ │635 - try writer.flush(); │ │698 + try response.finish(writer, null); │ │699 } │ │700 │ │701 // a standalone event db holding events consumed from a local repo's events│ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───╮ │src/fork.zig│ ╰────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │237 │ │238 // execute a transaction that receives the push and materializes its revision│ │239 { │ │240 + var response = xit.net_server_receive_pack.Response.init(allocator); │ │241 + defer response.deinit(); │ │242 const DB = rp.Repo(.xit, repo_opts).DB; │ │243 const State = rp.Repo(.xit, repo_opts).State; │ │244 const Ctx = struct { │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────╮ │248 allocator: std.mem.Allocator, │ │249 reader: *std.Io.Reader, │ │250 writer: *std.Io.Writer, │ │251 + response: *xit.net_server_receive_pack.Response,│ │252 patch_id: [evt.event_id_size * 2]u8, │ │253 patch: evt.Patch.Record, │ │254 published: bool, │ ╰─────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │263 // receive the branch update │ │264 var moment = try DB.HashMap(.read_write).init(cursor.*); │ │265 const state = State(.read_write){ .core = ctx.core, .extra = .{ .moment = &moment } }; │ │263 - try xit.net_server_receive_pack.run(.xit, repo_opts, state, ctx.io, ctx.allocator, ctx.reader, ctx.writer, .{ .allowed_ref = ref_path }); │ │266 + try xit.net_server_receive_pack.run(.xit, repo_opts, state, ctx.io, ctx.allocator, ctx.reader, ctx.writer, .{ .allowed_ref = ref_path, .deferred_response = ctx.response });│ │267 + try evt.writeReceivedPatches(repo_opts, state, ctx.io, ctx.allocator, ctx.response, ctx.writer); │ │268 │ │269 // copy the target history needed to preserve the merge base │ │270 const source_oid = (try rf.readRecur(.xit, repo_opts, state.readOnly(), ctx.io, .{ .ref = ref })) orelse return error.CancelTransaction; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯