╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │203 const empty = RenderedDiff{ .hunks = &.{}, .has_prev = false, .has_more = false }; │ │204 │ │205 // load the commit so we can diff it against its first parent. │ │186 - var start_oids = [_][xit.hash.hexLen(.sha1)]u8{oid}; │ │206 + var start_oids = [_][xit.hash.hexLen(repo_opts.hash)]u8{oid}; │ │207 var commit_iter = repo.log(io, gpa, start_oids[0..1]) catch return empty; │ │208 defer commit_iter.deinit(); │ │209 const commit_object = (commit_iter.next(gpa) catch return empty) orelse return empty;│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │224 var pair = pair_val; │ │225 defer pair.deinit(); │ │226 │ │207 - var hunk_iter = df.HunkIterator(.xit, .{}).init(gpa, &pair.a, &pair.b) catch continue; │ │227 + var hunk_iter = df.HunkIterator(.xit, repo_opts).init(gpa, &pair.a, &pair.b) catch continue;│ │228 defer hunk_iter.deinit(gpa); │ │229 │ │230 // the path label rides on the first of this file's hunks we actually show. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │241 break :file_loop; │ │242 } │ │243 var lines: std.ArrayList([]const u8) = .empty; │ │224 - try appendHunkLines(arena, &lines, &hunk_iter, &hunk); │ │244 + try appendHunkLines(repo_opts, arena, &lines, &hunk_iter, &hunk); │ │245 try hunks.append(arena, .{ │ │246 .path = if (path_attached) null else try arena.dupe(u8, pair.path),│ │247 .lines = try lines.toOwnedSlice(arena), │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │256 // append a hunk's header and its edit lines, each prefixed with a right-aligned │ │257 // line number in a column wide enough for the hunk's largest number, then a space│ │258 fn appendHunkLines( │ │259 + comptime repo_opts: rp.RepoOpts(.xit), │ │260 arena: std.mem.Allocator, │ │261 lines: *std.ArrayList([]const u8), │ │241 - hunk_iter: *df.HunkIterator(.xit, .{}), │ │242 - hunk: *df.Hunk(.xit, .{}), │ │262 + hunk_iter: *df.HunkIterator(.xit, repo_opts), │ │263 + hunk: *df.Hunk(.xit, repo_opts), │ │264 ) !void { │ │265 var max_num: usize = 1; │ │266 for (hunk.edits.items) |edit| { │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Repo/Files.zig│ ╰─────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │13 const Focus = xitui.focus.Focus; │ │14 │ │15 const SubHeader = @import("SubHeader.zig"); │ │16 - │ │17 -const RefOrOid = ui.RoutablePage.RefOrOid; │ │16 │ │17 // how many lines of a file's content one window shows before a "next" link.│ │18 const file_page = 2000; │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │52 identity: []const u8, │ │53 // the resolved ref this listing is read at (the default branch when the route│ │54 // didn't name one), so the view's directory links stay pinned to it. │ │57 -ref_or_oid: RefOrOid, │ │55 +ref_or_oid: ui.RoutablePage.RefOrOid, │ │56 ref_or_oid_value: []const u8, │ │57 // the directory being viewed, relative to the repo root ("" at the root). │ │58 dir: []const u8, │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │71 session: *ui.Session, │ │72 event_id: *const [evt.event_id_size]u8, │ │73 identity: []const u8, │ │76 - requested_ref_or_oid: ?RefOrOid, │ │74 + requested_ref_or_oid: ?ui.RoutablePage.RefOrOid, │ │75 requested_value: []const u8, │ │76 path: []const u8, │ │77 // the line offset the selected file's content window starts at (0 = first).│ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 91 │ │ 92 // open + read the committed file list with the arena's backing allocator │ │ 93 // (transient; freed before init returns); the listing is built into the │ │ 96 - // page arena so it outlives them. │ │ 94 + // page arena so it outlives them. AnyRepo opens both sha1 and sha256 repos. │ │ 95 const gpa = arena.child_allocator; │ │ 98 - var repo = rp.Repo(.xit, .{}).open(io, gpa, .{ .path = repo_path }) catch return emptyResult(aa, identity, requested_ref_or_oid orelse .branch, requested_value, path); │ │ 99 - defer repo.deinit(io, gpa); │ │ 96 + 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);│ │ 97 + defer any_repo.deinit(io, gpa); │ │ 98 + │ │ 99 + return switch (any_repo) { │ │100 + inline else => |*repo| listing(repo.self_repo_opts, arena, repo, io, gpa, identity, requested_ref_or_oid, requested_value, path, after), │ │101 + }; │ │102 +} │ │103 + │ │104 +// build the listing for an opened repo. generic over the repo's hash kind so │ │105 +// the tree/diff types it threads through match the repo's opts. │ │106 +fn listing( │ │107 + comptime repo_opts: rp.RepoOpts(.xit), │ │108 + arena: *std.heap.ArenaAllocator, │ │109 + repo: *rp.Repo(.xit, repo_opts), │ │110 + io: std.Io, │ │111 + gpa: std.mem.Allocator, │ │112 + identity: []const u8, │ │113 + requested_ref_or_oid: ?ui.RoutablePage.RefOrOid, │ │114 + requested_value: []const u8, │ │115 + path: []const u8, │ │116 + after: usize, │ │117 +) !Self { │ │118 + const aa = arena.allocator(); │ │119 │ │120 // resolve the requested ref (or the default branch) to the commit oid whose │ │121 // tree we list. a ref the route named explicitly that doesn't resolve is a │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │122 // bad url (NotFound -> 404); the default-branch path falls through to empty. │ │104 - const resolved = (try ui.ResolvedRefOrOid.init(&repo, io, aa, requested_ref_or_oid, requested_value)) orelse { │ │123 + const resolved = (try ui.ResolvedRefOrOid(repo_opts).init(repo, io, aa, requested_ref_or_oid, requested_value)) orelse {│ │124 if (requested_ref_or_oid != null) return error.NotFound; │ │125 return emptyResult(aa, identity, .branch, requested_value, path); │ │126 }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │128 // read the tree at that commit. building the read-only state mirrors what │ │129 // repo.status does internally, but for an arbitrary commit rather than HEAD. │ │130 var moment = repo.core.latestMoment() catch return emptyResult(aa, identity, resolved.ref_or_oid, resolved.value, path); │ │112 - const state = rp.Repo(.xit, .{}).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } }; │ │113 - var tree = tr.Tree(.xit, .{}).init(state, io, gpa, &resolved.oid) catch return emptyResult(aa, identity, resolved.ref_or_oid, resolved.value, path); │ │131 + const state = rp.Repo(.xit, repo_opts).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } }; │ │132 + 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 defer tree.deinit(); │ │134 │ │135 // `path` names a file when it's an exact tree entry: list its parent │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯