╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │195 // render the window [start, start+len) of a commit's diff against its first│ │196 // parent into `arena`-owned hunks. has_prev/has_more flag adjacent windows.│ │197 fn renderCommitDiff( │ │195 - comptime repo_opts: rp.RepoOpts(.xit), │ │198 + comptime repo_kind: rp.RepoKind, │ │199 + comptime repo_opts: rp.RepoOpts(repo_kind), │ │200 io: std.Io, │ │201 gpa: std.mem.Allocator, │ │202 arena: std.mem.Allocator, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────╮ │199 - repo: *rp.Repo(.xit, repo_opts), │ │203 + repo: *rp.Repo(repo_kind, repo_opts), │ │204 oid: [xit.hash.hexLen(repo_opts.hash)]u8,│ │205 start: usize, │ │206 len: usize, │ ╰──────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │229 var pair = pair_val; │ │230 defer pair.deinit(); │ │231 │ │228 - var hunk_iter = df.HunkIterator(.xit, repo_opts).init(gpa, &pair.a, &pair.b) catch continue; │ │232 + var hunk_iter = df.HunkIterator(repo_kind, repo_opts).init(gpa, &pair.a, &pair.b) catch continue;│ │233 defer hunk_iter.deinit(gpa); │ │234 │ │235 // the path label rides on the first of this file's hunks we actually show. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │246 break :file_loop; │ │247 } │ │248 var lines: std.ArrayList([]const u8) = .empty; │ │245 - try appendHunkLines(repo_opts, arena, &lines, &hunk_iter, &hunk); │ │249 + try appendHunkLines(repo_kind, repo_opts, arena, &lines, &hunk_iter, &hunk);│ │250 try hunks.append(arena, .{ │ │251 .path = if (path_attached) null else try arena.dupe(u8, pair.path), │ │252 .lines = try lines.toOwnedSlice(arena), │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │261 // append a hunk's header and its edit lines, each prefixed with a right-aligned │ │262 // line number in a column wide enough for the hunk's largest number, then a space│ │263 fn appendHunkLines( │ │260 - comptime repo_opts: rp.RepoOpts(.xit), │ │264 + comptime repo_kind: rp.RepoKind, │ │265 + comptime repo_opts: rp.RepoOpts(repo_kind), │ │266 arena: std.mem.Allocator, │ │267 lines: *std.ArrayList([]const u8), │ │263 - hunk_iter: *df.HunkIterator(.xit, repo_opts), │ │264 - hunk: *df.Hunk(.xit, repo_opts), │ │268 + hunk_iter: *df.HunkIterator(repo_kind, repo_opts), │ │269 + hunk: *df.Hunk(repo_kind, repo_opts), │ │270 ) !void { │ │271 var max_num: usize = 1; │ │272 for (hunk.edits.items) |edit| { │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Repo/Files.zig│ ╰─────────────────────╯ ╭────────────────────────────────────────────────────────╮ │70 pub fn init( │ │71 arena: *std.heap.ArenaAllocator, │ │72 session: *ui.Session, │ │73 - event_id: *const [evt.event_id_size]u8, │ │73 + source_maybe: ?ui.RepoSource, │ │74 identity: []const u8, │ │75 requested_ref_or_oid: ?ui.RoutablePage.RefOrOid,│ │76 requested_value: []const u8, │ ╰────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │84 // ref the route asked for. the wasm path never calls init anyway — it │ │85 // rebuilds from the serialized snapshot. │ │86 const io = session.io orelse return emptyResult(aa, identity, requested_ref_or_oid orelse .branch, requested_value, path); │ │87 - const repos_dir = session.repos_dir orelse return emptyResult(aa, identity, requested_ref_or_oid orelse .branch, requested_value, path);│ │88 - │ │89 - // the repo's working copy lives at <repos_dir>/<hex event id>. │ │90 - const hex = std.fmt.bytesToHex(event_id.*, .lower); │ │91 - const repo_path = try std.fs.path.join(aa, &.{ repos_dir, &hex }); │ │87 + const source = source_maybe orelse return emptyResult(aa, identity, requested_ref_or_oid orelse .branch, requested_value, path); │ │88 │ │89 // open + read the committed file list with the arena's backing allocator │ │90 // (transient; freed before init returns); the listing is built into the │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 91 // page arena so it outlives them. AnyRepo opens both sha1 and sha256 repos. │ │ 92 const gpa = arena.child_allocator; │ │ 97 - var any_repo = rp.AnyRepo(.xit, .{}).open(io, gpa, .{ .path = repo_path }) catch return emptyResult(aa, identity, requested_ref_or_oid orelse .branch, requested_value, path); │ │ 98 - defer any_repo.deinit(io, gpa); │ │ 93 + switch (source.repo_kind) { │ │ 94 + inline else => |repo_kind| { │ │ 95 + var any_repo = rp.AnyRepo(repo_kind, .{}).open(io, gpa, .{ .path = source.path }) catch return emptyResult(aa, identity, requested_ref_or_oid orelse .branch, requested_value, path);│ │ 96 + defer any_repo.deinit(io, gpa); │ │ 97 │ │100 - return switch (any_repo) { │ │101 - inline else => |*repo| listing(repo.self_repo_opts, arena, repo, io, gpa, identity, requested_ref_or_oid, requested_value, path, after), │ │102 - }; │ │ 98 + return switch (any_repo) { │ │ 99 + inline else => |*repo| listing(repo_kind, repo.self_repo_opts, arena, repo, io, gpa, identity, requested_ref_or_oid, requested_value, path, after), │ │100 + }; │ │101 + }, │ │102 + } │ │103 } │ │104 │ │105 -// build the listing for an opened repo. generic over the repo's hash kind so │ │106 -// the tree/diff types it threads through match the repo's opts. │ │105 +// build the listing for an opened repo. generic over the repo's backend and │ │106 +// hash kind so the tree/diff types it threads through match the repo's opts. │ │107 fn listing( │ │108 - comptime repo_opts: rp.RepoOpts(.xit), │ │108 + comptime repo_kind: rp.RepoKind, │ │109 + comptime repo_opts: rp.RepoOpts(repo_kind), │ │110 arena: *std.heap.ArenaAllocator, │ │110 - repo: *rp.Repo(.xit, repo_opts), │ │111 + repo: *rp.Repo(repo_kind, repo_opts), │ │112 io: std.Io, │ │113 gpa: std.mem.Allocator, │ │114 identity: []const u8, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │122 // resolve the requested ref (or the default branch) to the commit oid whose │ │123 // tree we list. a ref the route named explicitly that doesn't resolve is a │ │124 // bad url (NotFound -> 404); the default-branch path falls through to empty. │ │124 - const resolved = (try ui.ResolvedRefOrOid(repo_opts).init(repo, io, aa, requested_ref_or_oid, requested_value)) orelse { │ │125 + const resolved = (try ui.ResolvedRefOrOid(repo_kind, repo_opts).init(repo, io, aa, requested_ref_or_oid, requested_value)) orelse {│ │126 if (requested_ref_or_oid != null) return error.NotFound; │ │127 return emptyResult(aa, identity, .branch, requested_value, path); │ │128 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │130 // read the tree at that commit. building the read-only state mirrors what │ │131 // repo.status does internally, but for an arbitrary commit rather than HEAD. │ │132 var moment = repo.core.latestMoment() catch return emptyResult(aa, identity, resolved.ref_or_oid, resolved.value, path); │ │132 - const state = rp.Repo(.xit, repo_opts).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } }; │ │133 - var tree = tr.Tree(.xit, repo_opts).init(state, io, gpa, &resolved.oid) catch return emptyResult(aa, identity, resolved.ref_or_oid, resolved.value, path); │ │133 + const state = rp.Repo(repo_kind, repo_opts).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } }; │ │134 + var tree = tr.Tree(repo_kind, repo_opts).init(state, io, gpa, &resolved.oid) catch return emptyResult(aa, identity, resolved.ref_or_oid, resolved.value, path);│ │135 defer tree.deinit(); │ │136 │ │137 // `path` names a file when it's an exact tree entry: list its parent │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯