╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───╮ │src/push.zig│ ╰────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │155 // the revisions the pushed branches cause belong to the push │ │156 _ = try pch.refreshBranchesInTransaction(.server, repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.updates.items.items, null, ctx.progress);│ │157 } │ │158 + │ │159 + // the indexes the pushed refs invalidate belong to the push too │ │160 + _ = try find.refreshInTransaction(repo_opts, state, &moment, ctx.io, ctx.allocator); │ │161 + _ = try srch_cmmt.refreshInTransaction(repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.updates.items.items); │ │162 + │ │163 try writeUndo(repo_opts, state, ctx.io, ctx.allocator, ctx.author); │ │164 } │ │165 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │197 serve_common.logError(io, error_writer, "failed to refresh branch patches: {s}\n", .{@errorName(err)});│ │198 pch.refreshOpenMergeability(repo_opts, io, allocator, repo, null, &progress); │ │199 }; │ │195 - find.refresh(repo_opts, io, allocator, repo) catch |err| { │ │196 - serve_common.logError(io, error_writer, "failed to refresh file index: {s}\n", .{@errorName(err)}); │ │197 - }; │ │198 - cms.refresh(repo_opts, io, allocator, repo, updates.items.items) catch |err| { │ │199 - serve_common.logError(io, error_writer, "failed to refresh commit index: {s}\n", .{@errorName(err)}); │ │200 - }; │ │200 try response.finish(writer, null); │ │201 } │ │202 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/search_commit.zig│ ╰─────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │62 return @sizeOf(u64) + hash.byteLen(hash_kind); │ │63 } │ │64 │ │65 -// reconcile the index versions with the repo's ref tips, in one transaction.│ │66 -// `updates` is the push that prompted this, whose old oids are the cheapest │ │67 -// bases to derive the new tips from. │ │65 +// the action a commit index refresh records when it runs on its own │ │66 +pub const undo_action = "haxy/commit-index"; │ │67 + │ │68 +// reconcile the index versions with the repo's ref tips, in a transaction of│ │69 +// its own │ │70 pub fn refresh( │ │71 comptime repo_opts: rp.RepoOpts(.xit), │ │72 io: std.Io, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 76 ) !void { │ │ 77 const Repo = rp.Repo(.xit, repo_opts); │ │ 78 const DB = Repo.DB; │ │ 79 + const Save = struct { │ │ 80 + core: *Repo.Core, │ │ 81 + io: std.Io, │ │ 82 + allocator: std.mem.Allocator, │ │ 83 + updates: ?[]const xit.net_server_receive_pack.AppliedRefUpdate, │ │ 84 + │ │ 85 + pub fn run(ctx: @This(), cursor: *DB.Cursor(.read_write)) !void { │ │ 86 + var moment = try DB.HashMap(.read_write).init(cursor.*); │ │ 87 + const state = Repo.State(.read_write){ .core = ctx.core, .extra = .{ .moment = &moment } }; │ │ 88 + if (!try refreshInTransaction(repo_opts, state, &moment, ctx.io, ctx.allocator, ctx.updates)) return error.CancelTransaction; │ │ 89 + │ │ 90 + // record the user action for undo │ │ 91 + try xit.undo.write(repo_opts, state, std.Io.Timestamp.now(ctx.io, .real).toSeconds(), .{ .custom = .{ .action_kind = undo_action } }); │ │ 92 + } │ │ 93 + }; │ │ 94 + │ │ 95 + // held across the reads and the writes, so a concurrent refresh can't │ │ 96 + // change the entries these decisions were made from. │ │ 97 + try repo.core.db_file.lock(io, .exclusive); │ │ 98 + defer repo.core.db_file.unlock(io); │ │ 99 + │ │100 + const history = try DB.ArrayList(.read_write).init(repo.core.db.rootCursor()); │ │101 + history.appendContext(.{ .slot = try history.getSlot(-1) }, Save{ .core = &repo.core, .io = io, .allocator = allocator, .updates = updates }) catch |err| switch (err) {│ │102 + error.CancelTransaction => {}, │ │103 + else => return err, │ │104 + }; │ │105 +} │ │106 + │ │107 +// reconcile them inside the caller's transaction, so the index lands with the │ │108 +// push that invalidated it. `updates` is that push, whose old oids are the │ │109 +// cheapest bases to derive the new tips from. reports whether anything was │ │110 +// written, so a caller that owns an otherwise empty transaction can cancel it │ │111 +pub fn refreshInTransaction( │ │112 + comptime repo_opts: rp.RepoOpts(.xit), │ │113 + state: rp.Repo(.xit, repo_opts).State(.read_write), │ │114 + moment: *rp.Repo(.xit, repo_opts).DB.HashMap(.read_write), │ │115 + io: std.Io, │ │116 + allocator: std.mem.Allocator, │ │117 + updates: ?[]const xit.net_server_receive_pack.AppliedRefUpdate, │ │118 +) !bool { │ │119 + const Repo = rp.Repo(.xit, repo_opts); │ │120 + const DB = Repo.DB; │ │121 const Oid = [hash.hexLen(repo_opts.hash)]u8; │ │122 const Tip = Commit(repo_opts.hash); │ │123 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │138 var default_tip: ?Oid = null; │ │139 // the versions a new tip can derive from │ │140 var versions: std.ArrayList(Tip) = .empty; │ │ 97 - │ │ 98 - // held across the reads and the writes, so a concurrent refresh can't │ │ 99 - // change the entries these decisions were made from. │ │100 - try repo.core.db_file.lock(io, .exclusive); │ │101 - defer repo.core.db_file.unlock(io); │ │141 │ │142 { │ │104 - var moment = try repo.core.latestMoment(); │ │105 - const state = Repo.State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } };│ │143 + const read_state = state.readOnly(); │ │144 │ │145 // the commits that already have a version │ │146 var existing: std.AutoArrayHashMapUnmanaged(Oid, void) = .empty; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │158 // what every head except the events branch and every tag points at │ │159 var tips: std.ArrayList(Oid) = .empty; │ │160 { │ │123 - var branches = try repo.listBranches(io, allocator, .beginning); │ │161 + var branches = try rf.RefIterator(.xit, repo_opts).init(read_state, io, allocator, .head, .beginning);│ │162 defer branches.deinit(); │ │163 while (try branches.next()) |ref| { │ │164 if (std.mem.eql(u8, ref.name, evt.events_ref.name)) continue; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │127 - try tips.append(aa, (try repo.readRef(io, .{ .kind = .head, .name = ref.name })) orelse continue); │ │165 + try tips.append(aa, (try rf.readRecur(.xit, repo_opts, read_state, io, .{ .ref = .{ .kind = .head, .name = ref.name } })) orelse continue);│ │166 } │ │167 } │ │168 { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │131 - var tags = try repo.listTags(io, allocator, .beginning); │ │169 + var tags = try rf.RefIterator(.xit, repo_opts).init(read_state, io, allocator, .tag, .beginning); │ │170 defer tags.deinit(); │ │171 while (try tags.next()) |ref| { │ │134 - try tips.append(aa, (try repo.readRef(io, .{ .kind = .tag, .name = ref.name })) orelse continue); │ │172 + try tips.append(aa, (try rf.readRecur(.xit, repo_opts, read_state, io, .{ .ref = .{ .kind = .tag, .name = ref.name } })) orelse continue);│ │173 } │ │174 } │ │175 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │183 try wanted.put(aa, tip, {}); │ │184 continue; │ │185 } │ │148 - var commit = obj.Object(.xit, repo_opts).initCommit(state, io, allocator, &tip) catch continue; │ │186 + var commit = obj.Object(.xit, repo_opts).initCommit(read_state, io, allocator, &tip) catch continue;│ │187 defer commit.deinit(); │ │188 try wanted.put(aa, commit.oid, {}); │ │189 if (existing.contains(commit.oid)) continue; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │197 } │ │198 │ │199 var head_buffer: [rf.MAX_REF_CONTENT_SIZE]u8 = undefined; │ │162 - if (repo.head(io, &head_buffer)) |head| switch (head) { │ │163 - .ref => |ref| default_tip = try repo.readRef(io, .{ .kind = .head, .name = ref.name }), │ │200 + if (rf.readHead(.xit, repo_opts, read_state, io, &head_buffer) catch null) |head| switch (head) { │ │201 + .ref => |ref| default_tip = try rf.readRecur(.xit, repo_opts, read_state, io, .{ .ref = .{ .kind = .head, .name = ref.name } }),│ │202 .oid => {}, │ │165 - } else |_| {} │ │203 + }; │ │204 │ │205 // an entry whose commit is gone can't be walked from, so it isn't a │ │206 // candidate. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯