╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───╮ │src/fork.zig│ ╰────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │88 if (existing != null) return error.InvalidPatchDraft; │ │89 │ │90 // get the target repo │ │75 - const target_id = std.fmt.bytesToHex(input.repo_id, .lower); │ │76 - const target_path = try std.fs.path.join(allocator, &.{ repo_root_path, &target_id }); │ │91 + const target_path = try evt.repoPath(allocator, users_dir, &input.repo_user_id, &input.repo_id); │ │92 defer allocator.free(target_path); │ │93 var target_repo = try rp.Repo(.xit, repo_opts).open(io, allocator, .{ .path = target_path, .require_repo_root = true });│ │94 defer target_repo.deinit(io, allocator); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │137 core: *rp.Repo(.xit, repo_opts).Core, │ │138 io: std.Io, │ │139 allocator: std.mem.Allocator, │ │125 - forker_id: [evt.event_id_size * 2]u8, │ │140 │ │141 pub fn run(ctx: @This(), cursor: *DB.Cursor(.read_write)) !void {│ │142 var moment = try DB.HashMap(.read_write).init(cursor.*); │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │171 var config = try xit.config.Config(.xit, repo_opts).init(state.readOnly(), ctx.io, ctx.allocator); │ │172 defer config.deinit(); │ │173 try config.add(state, ctx.io, .{ .name = "receive.denydeletes", .value = "true" }); │ │160 - try config.add(state, ctx.io, .{ .name = "haxy.forker", .value = &ctx.forker_id }); │ │174 + │ │175 + // the copied db carries the target's forker entries │ │176 + if (config.local_sections.get(forker_section)) |section| { │ │177 + var names_arena = std.heap.ArenaAllocator.init(ctx.allocator); │ │178 + defer names_arena.deinit(); │ │179 + const na = names_arena.allocator(); │ │180 + var names: std.ArrayList([]const u8) = .empty; │ │181 + for (section.keys()) |key| { │ │182 + if (std.mem.startsWith(u8, key, forker_prefix)) try names.append(na, try std.fmt.allocPrint(na, forker_section ++ ".{s}", .{key}));│ │183 + } │ │184 + for (names.items) |name| try config.remove(state, ctx.io, .{ .name = name }); │ │185 + } │ │186 │ │187 // the copied db carries the target's file index, so the patch │ │188 // branch's entry starts from one of those rather than walking │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │199 const history = try DB.ArrayList(.read_write).init(fork_repo.core.db.rootCursor()); │ │200 try history.appendContext( │ │201 .{ .slot = try history.getSlot(-1) }, │ │177 - Ctx{ .core = &fork_repo.core, .io = io, .allocator = allocator, .forker_id = std.fmt.bytesToHex(input.user_id, .lower) },│ │202 + Ctx{ .core = &fork_repo.core, .io = io, .allocator = allocator }, │ │203 ); │ │204 } │ │205 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │217 }}); │ │218 │ │219 // create the fork event │ │195 - try evt.consume(.server, .user, .xit, evt.user_repo_opts, io, allocator, user_repo, evt.events_ref, &.{.{ │ │220 + try evt.consume(.server, .user, .xit, evt.user_repo_opts, io, allocator, &user_repo, evt.events_ref, &.{.{│ │221 .id = input.id, │ │222 .timestamp = input.timestamp, │ │223 .author = input.author, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────╮ │234 pub fn remove( │ │235 io: std.Io, │ │236 allocator: std.mem.Allocator, │ │212 - repo_root_path: []const u8, │ │213 - user_repo: *rp.Repo(.xit, evt.user_repo_opts),│ │237 + users_dir: []const u8, │ │238 + forker_id: *const [evt.event_id_size]u8, │ │239 id: *const [evt.event_id_size * 2]u8, │ │240 author: evt.CommitAuthor, │ │241 ) !void { │ ╰───────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │242 const fork_id = try evt.parseEventId(id); │ │243 + var user_repo = (try evt.openUserRepo(io, allocator, users_dir, forker_id)) orelse return error.InvalidPatchDraft; │ │244 + defer user_repo.deinit(io, allocator); │ │245 var arena = std.heap.ArenaAllocator.init(allocator); │ │246 defer arena.deinit(); │ │220 - const moment = (try evt.userMoment(user_repo)) orelse return error.InvalidPatchDraft; │ │247 + const moment = (try evt.userMoment(&user_repo)) orelse return error.InvalidPatchDraft; │ │248 const record = (try evt.Fork.readById(evt.UserDB, evt.user_repo_opts.hash, moment, &arena, &fork_id)) orelse return error.InvalidPatchDraft;│ │222 - if (!record.removed) try evt.remove(.server, .user, .xit, evt.user_repo_opts, io, allocator, user_repo, &fork_id, .fork, author); │ │249 + if (!record.removed) try evt.remove(.server, .user, .xit, evt.user_repo_opts, io, allocator, &user_repo, &fork_id, .fork, author); │ │250 │ │224 - const path = try forkPath(allocator, repo_root_path, id); │ │251 + const path = try forkPath(allocator, users_dir, forker_id, &fork_id); │ │252 defer allocator.free(path); │ │253 try std.Io.Dir.cwd().deleteTree(io, path); │ │254 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │255 │ │229 -// the id of the user who created the fork at `fork_path`, or null when it's gone │ │230 -pub fn readForkerId(io: std.Io, allocator: std.mem.Allocator, fork_path: []const u8) !?[evt.event_id_size]u8 { │ │231 - var fork_repo = rp.Repo(.xit, .{}).open(io, allocator, .{ .path = fork_path, .require_repo_root = true }) catch |err| switch (err) {│ │232 - error.RepoNotFound, error.FileNotFound => return null, │ │233 - else => |e| return e, │ │234 - }; │ │235 - defer fork_repo.deinit(io, allocator); │ │236 - var config = try fork_repo.listConfig(io, allocator); │ │237 - defer config.deinit(); │ │238 - const section = config.sections.get("haxy") orelse return error.InvalidPatchDraft; │ │239 - return try evt.parseEventId(section.get("forker") orelse return error.InvalidPatchDraft); │ │240 -} │ │241 - │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │86 // a branch patch has no fork, so this only names the patch to check │ │87 var checks: MergeCheckInputs(repo_opts) = .{}; │ │88 defer checks.deinit(io, allocator); │ │89 - if (host_kind == .server) try checks.add(io, allocator, repo.core.work_path, try evt.parseEventId(&id), patch);│ │89 + if (host_kind == .server) try checks.add(io, allocator, &repo.core, try evt.parseEventId(&id), patch); │ │90 │ │91 try repo.core.db_file.lock(io, .exclusive); │ │92 defer repo.core.db_file.unlock(io); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │278 // a branch patch opens no fork, so its checks need no lock of their own │ │279 var checks: MergeCheckInputs(repo_opts) = .{}; │ │280 defer checks.deinit(io, allocator); │ │281 - if (host_kind == .server) try checks.add(io, allocator, state.core.work_path, id, record.event); │ │281 + if (host_kind == .server) try checks.add(io, allocator, state.core, id, record.event); │ │282 │ │283 const author = evt.CommitAuthor{ .name = "haxy", .email = record.author_email orelse "user@haxy" }; │ │284 writeBranchPatchInTransaction(repo_opts, state, moment, io, &arena, std.fmt.bytesToHex(id, .lower), record.event, record.event, author, first_parent, &checks) catch |err| {│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯