╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │102 // resolve the requested ref (or the default branch) to the commit oid to │ │103 // walk from. an explicitly named ref that doesn't resolve is a bad url │ │104 // (NotFound -> 404); the default-branch path falls through to empty. │ │103 - const resolved = (try ui.ResolvedRefOrOid(repo_kind, repo_opts).init(repo, io, aa, requested_ref_or_oid, requested_value)) orelse {│ │105 + var resolved = (try ui.ResolvedRefOrOid(repo_kind, repo_opts).init(repo, io, aa, requested_ref_or_oid, requested_value)) orelse { │ │106 if (requested_ref_or_oid != null) return error.NotFound; │ │105 - return emptyResult(aa, location, .branch, requested_value, content); │ │107 + return emptyResult(aa, location, .branch, requested_value, content, base_oid); │ │108 }; │ │107 - var start_arr = [1][hex_len]u8{resolved.oid}; │ │108 - const start_oids: []const [hex_len]u8 = start_arr[0..1]; │ │109 │ │110 // collect this page's commit metadata, plus a peek at the one after it (its │ │111 // oid is the next page's start). the diff for each is rendered afterward, so │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │115 var count: usize = 0; │ │116 var next_start: ?[]const u8 = null; │ │117 { │ │118 - var iter = repo.log(io, gpa, .{ .start_oids = start_oids, .first_parent = true }) catch return emptyResult(aa, location, resolved.ref_or_oid, resolved.value, content); │ │118 + var iter = repo.log(io, gpa, .{ .start_oids = &.{resolved.oid}, .first_parent = true }) catch return emptyResult(aa, location, resolved.ref_or_oid, resolved.value, content, base_oid);│ │119 defer iter.deinit(); │ │120 - // omit the base and its ancestors │ │121 - if (base_oid_maybe) |base_oid| try iter.exclude(&base_oid); │ │120 while (try iter.next(gpa)) |commit_object| { │ │121 defer commit_object.deinit(); │ │122 + // count from the commit itself when the tip is an annotated tag │ │123 + if (count == 0) resolved.oid = commit_object.oid; │ │124 + // the base is a stopping point, not an ancestry exclusion │ │125 + if (std.mem.eql(u8, &commit_object.oid, base_oid)) break; │ │126 if (count == page_size) { │ │127 next_start = try aa.dupe(u8, &commit_object.oid); │ │128 break; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │159 .location = try location.dupe(aa), │ │160 .ref_or_oid = resolved.ref_or_oid, │ │161 .ref_or_oid_value = resolved.value, │ │160 - .commit_count = if (repo_kind == .xit and base_oid_maybe == null) │ │161 - repo.commitCount(io, gpa, .{ .oid = &resolved.oid }) catch null │ │162 - else │ │163 - null, │ │162 + .base_oid = try aa.dupe(u8, base_oid), │ │163 + .commit_count = if (repo_kind == .xit and location == .repo) blk: { │ │164 + if (base_oid.len == 0) break :blk repo.commitCount(io, gpa, .{ .oid = &resolved.oid }) catch null; │ │165 + var moment = repo.core.latestMoment() catch break :blk null; │ │166 + const state = rp.Repo(.xit, repo_opts).State(.read_only){ .core = &repo.core, .extra = .{ .moment = &moment } };│ │167 + break :blk evt.PatchRev.commitCount(repo_kind, repo_opts, state, io, gpa, base_oid, &resolved.oid) catch null; │ │168 + } else null, │ │169 .commits = try aa.dupe(Commit, buf[0..count]), │ │170 .next_start = next_start, │ │171 .content = try pageContent(aa, content), │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │174 } │ │175 │ │176 // an empty listing pinned to a ref, for the wasm / no-repo / unresolved paths. │ │172 -pub fn emptyResult(aa: std.mem.Allocator, location: ui.RoutablePage.RepoLocation, ref_or_oid: ui.RoutablePage.RefOrOid, value: []const u8, content: ui.RoutablePage.RepoCommitsRoute.Content) !Self { │ │177 +pub fn emptyResult(aa: std.mem.Allocator, location: ui.RoutablePage.RepoLocation, ref_or_oid: ui.RoutablePage.RefOrOid, value: []const u8, content: ui.RoutablePage.RepoCommitsRoute.Content, base_oid: []const u8) !Self {│ │178 return .{ │ │179 .location = try location.dupe(aa), │ │180 .ref_or_oid = ref_or_oid, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────╮ │181 .ref_or_oid_value = try aa.dupe(u8, value),│ │182 + .base_oid = try aa.dupe(u8, base_oid), │ │183 .commit_count = null, │ │184 .commits = &.{}, │ │185 .next_start = null, │ ╰────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │214 const empty = Diff.Window{ .start = start }; │ │215 │ │216 // load the commit so we can diff it against its first parent. │ │211 - var start_oids = [_][xit.hash.hexLen(repo_opts.hash)]u8{oid}; │ │212 - var commit_iter = repo.log(io, gpa, .{ .start_oids = start_oids[0..1] }) catch return empty;│ │217 + var commit_iter = repo.log(io, gpa, .{ .start_oids = &.{oid} }) catch return empty; │ │218 defer commit_iter.deinit(); │ │219 const commit_object = (commit_iter.next(gpa) catch return empty) orelse return empty; │ │220 defer commit_object.deinit(); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │296 } │ │297 │ │298 pub const View = struct { │ │294 - // a vertical stack: the "viewing <ref>" banner on top, then a horizontal │ │299 + // a vertical stack: the ref banner on top, then a horizontal │ │300 // split with the commit list on the left and a diff pane on the right │ │301 // showing the selected commit's diff. │ │302 box: wgt.Box(ui.Widget), // vert: [header_index] = banner, [content_index] = split│ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │337 // js off (the browser follows it, rooting the list there); │ │338 // with wasm the click just selects it and swaps the diff pane. │ │339 const content = if (index == 0) data.content else Content{ .diff = .{} }; │ │335 - try addRow(allocator, &list_box, firstLine(commit.message), try commitRowLink(session.page_arena, data.location, commit, content)); │ │340 + try addRow(allocator, &list_box, firstLine(commit.message), try commitRowLink(session.page_arena, data, commit, content)); │ │341 } │ │342 if (data.next_start) |next| { │ │338 - try addRow(allocator, &list_box, "next →", try commitsLink(session.page_arena, data.location, next, 0, "")); │ │343 + try addRow(allocator, &list_box, "next →", try commitsLink(session.page_arena, data, next, 0, "")); │ │344 } │ │345 if (list_box.children.count() > 0) list_box.getFocus().child_id = list_box.children.keys()[0]; │ │346 break :blk try wgt.Scroll(ui.Widget).init(allocator, .{ .box = list_box }, .{ .direction = .vert, .web_native = !session.is_terminal, .fill = true });│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │395 // commit's route at `target_start`, so activating it (the host follows the │ │396 // "a:" link) reloads the page on the adjacent window — same on TUI and web. │ │397 fn addNavLink(self: *View, allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), label: []const u8, oid: []const u8, target_start: usize, path: []const u8) !void {│ │393 - const link = try commitsLink(self.session.page_arena, self.data.location, oid, target_start, path); │ │398 + const link = try commitsLink(self.session.page_arena, self.data, oid, target_start, path); │ │399 var tb = try wgt.TextBox.init(allocator, label, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │400 errdefer tb.deinit(allocator); │ │401 tb.getFocus().mode = .all; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │426 const cut_short = commit.message_truncated and kind == .preview; │ │427 var tb = try messageBox(allocator, commit.message, if (cut_short) " click or press enter to see more " else ""); │ │428 errdefer tb.deinit(allocator); │ │424 - if (cut_short) tb.getFocus().kind = .{ .custom = try messageLink(self.session.page_arena, self.data.location, commit.oid) };│ │429 + if (cut_short) tb.getFocus().kind = .{ .custom = try messageLink(self.session.page_arena, self.data, commit.oid) }; │ │430 try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); │ │431 } │ │432 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯