╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │428 // consuming an existing ref does not provide the set of changed patches │ │429 // explicit patch writes already saved their checks in the event transaction │ │430 if (role == .repo and repo_kind == .xit and host_kind == .server and events.len == 0) {│ │431 - pch.refreshOpenMergeability(repo_opts, io, allocator, repo, null); │ │431 + pch.refreshOpenMergeability(repo_opts, io, allocator, repo, null, null); │ │432 } │ │433 } │ │434 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │13 │ │14 // the action a patch merge records │ │15 pub const merge_undo_action = "haxy/merge"; │ │16 + │ │17 +// progress is optional and best effort: a failed report must not fail a refresh │ │18 +fn reportProgress(comptime repo_opts: rp.RepoOpts(.xit), io: std.Io, progress_ctx_maybe: ?repo_opts.ProgressCtx, event: rp.ProgressEvent) void {│ │19 + if (repo_opts.ProgressCtx != void) { │ │20 + if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, event) catch {}; │ │21 + } │ │22 +} │ │23 + │ │24 +// whether a push moved this branch │ │25 +fn branchUpdated(updates: []const xit.net_server_receive_pack.AppliedRefUpdate, branch: []const u8) bool { │ │26 + const prefix = "refs/heads/"; │ │27 + for (updates) |update| { │ │28 + if (std.mem.startsWith(u8, update.ref_name, prefix) and std.mem.eql(u8, update.ref_name[prefix.len..], branch)) return true; │ │29 + } │ │30 + return false; │ │31 +} │ │32 │ │33 // capture an existing branch without creating a fork │ │34 pub fn writeBranchPatch( │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │217 const branch = try pair.key_cursor.readBytesAlloc(arena.allocator(), null); │ │218 │ │219 // limit source refreshes to branches changed by this push │ │204 - if (updates) |changed| { │ │205 - var found = false; │ │206 - for (changed) |update| { │ │207 - if (std.mem.startsWith(u8, update.ref_name, "refs/heads/") and std.mem.eql(u8, update.ref_name["refs/heads/".len..], branch)) {│ │208 - found = true; │ │209 - break; │ │210 - } │ │211 - } │ │212 - if (!found) continue; │ │213 - } │ │220 + if (updates) |changed| if (!branchUpdated(changed, branch)) continue; │ │221 │ │222 // read the source tip before comparing stored revisions │ │223 const source = (try rf.readRecur(repo_kind, repo_opts, state, io, .{ .ref = .{ .kind = .head, .name = branch } })) orelse { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────╮ │255 allocator: std.mem.Allocator, │ │256 updates: ?[]const xit.net_server_receive_pack.AppliedRefUpdate,│ │257 first_parent: ?[1][hash.hexLen(repo_opts.hash)]u8, │ │258 + progress_ctx_maybe: ?repo_opts.ProgressCtx, │ │259 ) !bool { │ │260 var arena = std.heap.ArenaAllocator.init(allocator); │ │261 defer arena.deinit(); │ ╰────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │267 else => return err, │ │268 }; │ │269 try collectOutdatedBranchPatches(.xit, repo_opts, io, allocator, &arena, state.readOnly(), events_moment, updates, host_kind, &patches);│ │270 + │ │271 + const total = patches.count(); │ │272 + if (total > 0) { │ │273 + reportProgress(repo_opts, io, progress_ctx_maybe, .{ .text = "Updating patches" }); │ │274 + reportProgress(repo_opts, io, progress_ctx_maybe, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = total } }); │ │275 + } │ │276 │ │277 var refreshed = false; │ │278 for (patches.keys(), patches.values()) |id, record| { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │279 + defer reportProgress(repo_opts, io, progress_ctx_maybe, .{ .complete_one = .writing_patch });│ │280 + │ │281 // a branch patch opens no fork, so its checks need no lock of their own │ │282 var checks: MergeCheckInputs(repo_opts) = .{}; │ │283 defer checks.deinit(io, allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │290 }; │ │291 refreshed = true; │ │292 } │ │293 + if (total > 0) reportProgress(repo_opts, io, progress_ctx_maybe, .{ .end = .writing_patch });│ │294 return refreshed; │ │295 } │ │296 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │321 host_kind: evt.HostKind, │ │322 updates: ?[]const xit.net_server_receive_pack.AppliedRefUpdate, │ │323 first_parent: ?[1][hash.hexLen(repo_opts.hash)]u8, │ │324 + progress_ctx_maybe: ?repo_opts.ProgressCtx, │ │325 │ │326 pub fn run(ctx: @This(), cursor: *DB.Cursor(.read_write)) !void {│ │327 var moment = try DB.HashMap(.read_write).init(cursor.*); │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │328 const state = rp.Repo(.xit, repo_opts).State(.read_write){ .core = ctx.core, .extra = .{ .moment = &moment } }; │ │311 - if (!try refreshBranchesInTransaction(ctx.host_kind, repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.updates, ctx.first_parent)) return error.CancelTransaction; │ │329 + if (!try refreshBranchesInTransaction(ctx.host_kind, repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.updates, ctx.first_parent, ctx.progress_ctx_maybe)) return error.CancelTransaction;│ │330 │ │331 // record the user action for undo │ │332 try xit.undo.write(repo_opts, state, std.Io.Timestamp.now(ctx.io, .real).toSeconds(), .{ .custom = .{ .action = evt.undo_action } }); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │346 .host_kind = host_kind, │ │347 .updates = updates, │ │348 .first_parent = first_parent, │ │349 + .progress_ctx_maybe = progress_ctx_maybe,│ │350 }) catch |err| switch (err) { │ │351 error.CancelTransaction => {}, │ │352 else => return err, │ ╰──────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯