╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │110 }; │ │111 } │ │112 │ │113 -// refresh tracked branches outside the caller's transaction │ │113 +// refresh tracked branches and, on the server, check open patches for merges.│ │114 +// this runs outside the caller's transaction. │ │115 pub fn refreshBranches( │ │116 host_kind: evt.HostKind, │ │117 comptime repo_kind: rp.RepoKind, │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────╮ │120 allocator: std.mem.Allocator, │ │121 repo: *rp.Repo(repo_kind, repo_opts), │ │122 updates: ?[]const xit.net_server_receive_pack.AppliedRefUpdate,│ │123 + progress_ctx_maybe: ?repo_opts.ProgressCtx, │ │124 ) !void { │ │125 const DB = evt.EventDB(repo_opts.hash); │ │126 var arena = std.heap.ArenaAllocator.init(allocator); │ ╰────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │127 defer arena.deinit(); │ │126 - var patches: std.ArrayList(struct { id: [evt.event_id_size]u8, record: evt.Patch.Record }) = .empty; │ │128 + var patches: std.AutoArrayHashMapUnmanaged([evt.event_id_size]u8, ?evt.Patch.Record) = .empty; │ │129 defer patches.deinit(allocator); │ │128 - { │ │130 + collect: { │ │131 var local_db: ?evt.LocalEventDB(repo_opts.hash) = if (repo_kind == .git) try evt.LocalEventDB(repo_opts.hash).openReadOnly(io, allocator, repo.core.repo_dir) else null; │ │132 defer if (local_db) |*db| db.deinit(io, allocator); │ │131 - const moment = (if (local_db) |*db| evt.currentMomentFromDb(repo_opts.hash, db.db) else if (repo_kind == .git) return else evt.currentMoment(repo_opts, repo)) catch |err| switch (err) { │ │132 - error.NotFound => return, │ │133 + const moment = (if (local_db) |*db| evt.currentMomentFromDb(repo_opts.hash, db.db) else if (repo_kind == .git) break :collect else evt.currentMoment(repo_opts, repo)) catch |err| switch (err) {│ │134 + error.NotFound => break :collect, │ │135 else => return err, │ │136 }; │ │135 - const source_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.source_to_id_set_key)) orelse return; │ │137 + if (repo_kind == .xit and host_kind == .server) open: { │ │138 + const statuses_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.status_to_id_set_key)) orelse break :open; │ │139 + const statuses = try DB.SortedMap(.read_only).init(statuses_cursor); │ │140 + const open_cursor = try statuses.getCursor("open") orelse break :open; │ │141 + const open_patches = try DB.SortedSet(.read_only).init(open_cursor); │ │142 + var iter = try open_patches.iteratorFromIndex(0); │ │143 + while (try iter.next()) |cursor| try patches.put(allocator, try evt.readOrderKeyId(DB, cursor), null); │ │144 + } │ │145 + const source_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.source_to_id_set_key)) orelse break :collect; │ │146 const sources = try DB.SortedMap(.read_only).init(source_cursor); │ │147 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; │ │148 var iter = try sources.iterator(); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │172 const record = (try evt.Patch.readById(DB, repo_opts.hash, moment, &arena, &id)) orelse continue; │ │173 if (conflicts) |map| if (try map.getCursor(&evt.orderKeyDesc(record.created_order, &id)) != null) continue; │ │174 if (record.event.revision) |revision| if (std.mem.eql(u8, revision.source_oid, &source)) continue; │ │165 - try patches.append(allocator, .{ .id = id, .record = record }); │ │175 + try patches.put(allocator, id, record); │ │176 } │ │177 } │ │178 + } │ │179 + if (repo_opts.ProgressCtx != void) { │ │180 + if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, .{ .start = .{ .kind = .writing_patch, .estimated_total_items = patches.count() } }) catch {}; │ │181 } │ │169 - for (patches.items) |patch| { │ │170 - const author = evt.CommitAuthor{ .name = "haxy", .email = patch.record.author_email orelse "user@haxy" }; │ │171 - writeBranchPatch(host_kind, repo_kind, repo_opts, io, allocator, repo, std.fmt.bytesToHex(patch.id, .lower), patch.record.event, patch.record.event, author) catch |err| {│ │172 - std.log.warn("failed to refresh branch patch: {s}", .{@errorName(err)}); │ │173 - }; │ │182 + for (patches.keys(), patches.values()) |id, record_maybe| { │ │183 + if (record_maybe) |record| { │ │184 + const author = evt.CommitAuthor{ .name = "haxy", .email = record.author_email orelse "user@haxy" }; │ │185 + writeBranchPatch(host_kind, repo_kind, repo_opts, io, allocator, repo, std.fmt.bytesToHex(id, .lower), record.event, record.event, author) catch |err| { │ │186 + std.log.warn("failed to refresh branch patch: {s}", .{@errorName(err)}); │ │187 + }; │ │188 + } │ │189 + if (repo_kind == .xit and host_kind == .server) refreshMergeability(repo_opts, io, allocator, repo, id); │ │190 + if (repo_opts.ProgressCtx != void) { │ │191 + if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, .{ .complete_one = .writing_patch }) catch {}; │ │192 + } │ │193 + } │ │194 + if (repo_opts.ProgressCtx != void) { │ │195 + if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, .{ .end = .writing_patch }) catch {}; │ │196 } │ │197 } │ │198 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯