╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────╮ │189 while (try iter.next()) |cursor| { │ │190 const pair = try cursor.readKeyValuePair(); │ │191 const branch = try pair.key_cursor.readBytesAlloc(arena.allocator(), null);│ │192 + │ │193 + // limit source refreshes to branches changed by this push │ │194 if (updates) |changed| { │ │195 var found = false; │ │196 for (changed) |update| { │ ╰────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │201 } │ │202 if (!found) continue; │ │203 } │ │204 + │ │205 + // read the source tip before comparing stored revisions │ │206 const source = (try repo.readRef(io, .{ .kind = .head, .name = branch })) orelse { │ │207 if (host_kind == .server) std.log.warn("patch source branch missing: {s}", .{branch});│ │208 continue; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │209 }; │ │210 + │ │211 + // collect open patches whose stored source tip is outdated │ │212 const ids = try DB.CountedHashSet(.read_only).init(pair.value_cursor);│ │213 var ids_iter = try ids.iterator(); │ │214 while (try ids_iter.next()) |id_cursor| { │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │223 } │ │224 } │ │225 } │ │226 + │ │227 + // start progress reporting after the patch count is known │ │228 if (repo_opts.ProgressCtx != void) { │ │229 if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = patches.count() } }) catch {};│ │230 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │231 + │ │232 + // refresh each patch and report its completion │ │233 for (patches.keys(), patches.values()) |id, record_maybe| { │ │234 if (record_maybe) |record| { │ │235 const author = evt.CommitAuthor{ .name = "haxy", .email = record.author_email orelse "user@haxy" };│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │242 if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, .{ .complete_one = .writing_patch }) catch {};│ │243 } │ │244 } │ │245 + │ │246 + // finish progress reporting after all patches have been visited │ │247 if (repo_opts.ProgressCtx != void) { │ │248 if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, .{ .end = .writing_patch }) catch {}; │ │249 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │309 id: *const [evt.event_id_size]u8, │ │310 ) !?MergeCheck { │ │311 const DB = evt.EventDB(repo_opts.hash); │ │312 + │ │313 + // find the cached check for this patch │ │314 const map_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.patch_id_to_mergeability_key)) orelse return null;│ │315 const map = try DB.HashMap(.read_only).init(map_cursor); │ │316 const cursor = try map.getCursor(hash.hashInt(repo_opts.hash, id)) orelse return null; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │317 + │ │318 + // treat unrecognized cached statuses as a cache miss │ │319 return evt.read(MergeCheck, DB, repo_opts.hash, arena, try DB.HashMap(.read_only).init(cursor)) catch |err| switch (err) {│ │320 error.InvalidEnumTag => return null, │ │321 else => return err, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │331 id: *const [evt.event_id_size]u8, │ │332 patch: evt.Patch, │ │333 ) !Mergeability { │ │334 + // reject checks for an outdated source revision │ │335 if (patch.source_branch != null and !try branchCurrent(repo_opts, state, io, arena, patch)) return .{}; │ │336 + │ │337 + // use the cached result only when the target still matches │ │338 const target_oid = try rf.readRecur(.xit, repo_opts, state, io, .{ .ref = .{ .kind = .head, .name = patch.target_branch } }) orelse return .{};│ │339 const haxy_moment = evt.currentMomentFromRepoMoment(repo_opts.hash, state.extra.moment.*) catch |err| switch (err) { │ │340 error.NotFound => return .{}, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │366 id_maybe: ?[evt.event_id_size]u8, │ │367 ) !void { │ │368 if (id_maybe) |id| return refreshMergeCheck(repo_opts, io, allocator, target_repo, &id);│ │369 + │ │370 + // collect open patch ids before any refresh changes the database │ │371 const DB = evt.EventDB(repo_opts.hash); │ │372 const moment = evt.currentMoment(repo_opts, target_repo) catch |err| switch (err) { │ │373 error.NotFound => return, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯