╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │170 error.NotFound => break :collect, │ │171 else => return err, │ │172 }; │ │173 + │ │174 + // include all open server patches for mergeability checks │ │175 if (repo_kind == .xit and host_kind == .server) open: { │ │176 const statuses_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.status_to_id_set_key)) orelse break :open;│ │177 const statuses = try DB.SortedMap(.read_only).init(statuses_cursor); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │180 var iter = try open_patches.iteratorFromIndex(0); │ │181 while (try iter.next()) |cursor| try patches.put(allocator, try evt.readOrderKeyId(DB, cursor), null); │ │182 } │ │183 + │ │184 + // find tracked source branches and skip conflicting patches │ │185 const source_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.source_to_id_set_key)) orelse break :collect; │ │186 const sources = try DB.SortedMap(.read_only).init(source_cursor); │ │187 const conflicts = if (try moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.conflicts_key))) |cursor| try DB.SortedMap(.read_only).init(cursor) else null;│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────╮ │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, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯