╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │85 .repo_files => |f| f.line, │ │86 else => 0, │ │87 }; │ │88 - // the hunk the commits view's selected commit's diff window starts at. │ │89 - const commits_start = switch (route) { │ │90 - .repo_commits => |c| c.start, │ │91 - else => 0, │ │88 + // what the commits view's pane shows for the commit it walks from: a diff │ │89 + // window (with the file it's filtered to) or that commit's message. │ │90 + const commits_content: ui.RoutablePage.RepoCommitsRoute.Content = switch (route) {│ │91 + .repo_commits => |c| c.content, │ │92 + else => .{ .diff = .{} }, │ │93 }; │ │93 - // the file the commits view's diff pane is filtered to ("" = every file). │ │94 - const commits_path = switch (route) { │ │95 - .repo_commits => |c| c.path.slice(), │ │96 - else => "", │ │94 + const commits_start = switch (commits_content) { │ │95 + .diff => |d| d.start, │ │96 + .message => 0, │ │97 }; │ │98 // the refs tab windows one column at a time: `refs_from` (a url-encoded │ │99 // ref name) roots `refs_kind`'s column, the other stays at its first window. │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │176 if (session.local != null) try evt.consume(repo_kind, opened.self_repo_opts, io, gpa, opened, evt.events_ref); │ │177 break :blk .{ │ │178 try Files.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, repo_identity.identity, requested_ref_or_oid, requested_ref_value, files_dir, files_line), │ │179 - try Commits.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, repo_identity.identity, requested_ref_or_oid, requested_ref_value, commits_start, commits_path),│ │179 + try Commits.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, repo_identity.identity, requested_ref_or_oid, requested_ref_value, commits_content), │ │180 try Refs.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, repo_identity.identity, refs_kind, refs_from), │ │181 try Issues.init(repo_kind, opened.self_repo_opts, arena, opened, io, repo_identity.identity, issues_tag, issues_selected, issues_view), │ │182 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │188 const aa = arena.allocator(); │ │189 break :blk .{ │ │190 try Files.emptyResult(aa, repo_identity.identity, requested_ref_or_oid orelse .branch, requested_ref_value, files_dir), │ │191 - try Commits.emptyResult(aa, repo_identity.identity, requested_ref_or_oid orelse .branch, requested_ref_value, commits_path), │ │191 + try Commits.emptyResult(aa, repo_identity.identity, requested_ref_or_oid orelse .branch, requested_ref_value, commits_content),│ │192 try Refs.emptyResult(arena, repo_identity.identity, refs_kind, refs_from), │ │193 try Issues.emptyResult(aa, repo_identity.identity, issues_tag, issues_selected, issues_view), │ │194 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │204 // directory route drops it. │ │205 const files_route_line = if (files.selected_file != null) files_line else 0; │ │206 const route_name = (ui.RoutablePage.repoFilesRoute(repo_identity.identity, files.ref_or_oid, files.ref_or_oid_value, files_path, files_route_line) orelse return error.NotFound).repo_files; │ │207 - const commits_route_name = (ui.RoutablePage.repoCommitsRoute(repo_identity.identity, commits.ref_or_oid, commits.ref_or_oid_value, commits_start, commits.path) orelse return error.NotFound).repo_commits;│ │207 + // the message page mirrors its own url, not the log's │ │208 + const commits_route = switch (commits.content) { │ │209 + .message => ui.RoutablePage.repoCommitMessageRoute(repo_identity.identity, commits.ref_or_oid, commits.ref_or_oid_value), │ │210 + .diff => |d| ui.RoutablePage.repoCommitsRoute(repo_identity.identity, commits.ref_or_oid, commits.ref_or_oid_value, commits_start, d.path), │ │211 + }; │ │212 + const commits_route_name = (commits_route orelse return error.NotFound).repo_commits; │ │213 │ │214 return .{ │ │215 // files and commits resolve the same ref, so either's serves the header, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │18 // how many diff hunks one window of a commit's diff shows; "next"/"previous"│ │19 // move to the adjacent window. │ │20 const diff_page = 10; │ │21 -// the largest commit message the detail view will read in full. │ │22 -const max_message_size = 64 * 1024; │ │21 +// the largest commit message a log page's detail view reads; a longer one is│ │22 +// cut here, with the message page carrying the whole thing. │ │23 +pub const max_message_size = 2 * 1024; │ │24 +// the same for the message page, which only reads the one commit. │ │25 +const max_full_message_size = 100 * 1024; │ │26 │ │27 pub const Hunk = struct { │ │28 path: ?[]const u8 = null, // the file this hunk belongs to │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │35 oid: []const u8, │ │36 date: []const u8, // "YYYY-MM-DD" │ │37 message: []const u8, // trimmed, may be multi-line │ │38 + // whether `message` is only the first line of a message too big to read.│ │39 + message_truncated: bool = false, │ │40 hunks: []const Hunk, │ │41 // the hunk index this window starts at (0 = the first window). │ │42 window_start: usize, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │51 // didn't name one), so the page can canonicalize its url to it. │ │52 ref_or_oid: ui.RoutablePage.RefOrOid, │ │53 ref_or_oid_value: []const u8, │ │49 -// the file the top commit's diff is filtered to ("" = every file). │ │50 -path: []const u8 = "", │ │54 commits: []const Commit, │ │55 // the first oid of the next page, or null when this is the last page.│ │56 next_start: ?[]const u8, │ ╰──────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │57 +// what the pane shows for the commit the log walks from. │ │58 +content: Content = .{ .diff = .{} }, │ │59 // the "viewing <ref> <value>" banner shown above the log.│ │60 header: Header, │ │61 │ ╰──────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │62 const Self = @This(); │ │63 │ │64 +// the diff and the message are alternatives, so a filtered diff can't also be│ │65 +// a message. the message is read whole, however big, since it's all its pane │ │66 +// shows. │ │67 +pub const Content = union(enum) { │ │68 + diff: struct { │ │69 + // the file the top commit's diff is filtered to ("" = every file). │ │70 + path: []const u8 = "", │ │71 + }, │ │72 + message, │ │73 +}; │ │74 + │ │75 // walk the log for an opened repo. generic over the repo's backend and hash │ │76 // kind so the oid buffers and diff types it threads through match the repo's │ │61 -// opts. `start` is the hunk index the selected commit's (the first one, the │ │62 -// walk root) diff window starts at; 0 is the first window, moved by │ │63 -// "next"/"previous". walks with the arena's backing allocator (transient; the│ │77 +// opts. `content` is what the walk root's pane shows, the only commit any of │ │78 +// it applies to. walks with the arena's backing allocator (transient; the │ │79 // commits we keep are duped into the page arena so they outlive it). │ │80 pub fn init( │ │81 comptime repo_kind: rp.RepoKind, │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │87 identity: []const u8, │ │88 requested_ref_or_oid: ?ui.RoutablePage.RefOrOid, │ │89 requested_value: []const u8, │ │75 - start: usize, │ │76 - path: []const u8, │ │90 + content: ui.RoutablePage.RepoCommitsRoute.Content, │ │91 ) !Self { │ │92 const aa = arena.allocator(); │ │93 const hex_len = ui.ResolvedRefOrOid(repo_kind, repo_opts).hex_len;│ ╰──────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯