╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │200 │ │201 // the "key:value" segments of a url tail, collected in any order │ │202 const Params = struct { │ │203 - const ParamKey = enum { start, line, branch, tag, object, base, patchrev, theirs, find, search, from, moment }; │ │203 + const ParamKey = enum { start, line, branch, tag, object, base, patchrev, theirs, find, search, from, moment, merge };│ │204 │ │205 // a branch:/tag:/object: param; the value stays url-encoded │ │206 const RefParam = struct { kind: RefOrOid, value: []const u8 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────╮ │288 const search_seg = @tagName(Params.ParamKey.search) ++ ":";│ │289 const from_seg = @tagName(Params.ParamKey.from) ++ ":"; │ │290 const base_seg = @tagName(Params.ParamKey.base) ++ ":"; │ │291 + const merge_seg = @tagName(Params.ParamKey.merge) ++ ":"; │ │292 const theirs_seg = @tagName(Params.ParamKey.theirs) ++ ":";│ │293 const issue_seg = "issue:"; │ │294 const patch_seg = "patch:"; │ ╰────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │388 search: Array(search_route_max_len) = .{}, │ │389 // the commit the list starts at ("" = the ref's own commit). │ │390 from: Array(ref_route_max_len) = .{}, │ │391 + // a merge commit whose brought-in commits the page lists, resolved to an│ │392 + // object and base on load. set alone. │ │393 + merge: Array(ref_route_max_len) = .{}, │ │394 // what the pane shows for the commit the log walks from. │ │395 content: Content = .{ .diff = .{} }, │ │396 │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────╮ │491 forkCommitsRoute(f.identity, f.id, value, start, path) │ │492 else │ │493 null, │ │494 + }; │ │495 + } │ │496 + │ │497 + // fork commits routes carry no base, so only a repo can list a merge's commits│ │498 + pub fn commitsMergeRoute(self: RepoLocation, oid: []const u8) ?RoutablePage { │ │499 + return switch (self) { │ │500 + .repo => |repo_identity| repoCommitsMergeRoute(repo_identity, oid), │ │501 + .fork => null, │ │502 }; │ │503 } │ │504 │ ╰────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │678 } }; │ │679 } │ │680 │ │681 + pub fn repoCommitsMergeRoute(identity: []const u8, oid: []const u8) ?RoutablePage { │ │682 + return .{ .repo_commits = .{ │ │683 + .name = Array(repo_identity_max_len).from(identity) orelse return null, │ │684 + .merge = Array(ref_route_max_len).from(oid) orelse return null, │ │685 + } }; │ │686 + } │ │687 + │ │688 pub fn repoDiffRoute(identity: []const u8, ref_or_oid: ?RefOrOid, value: []const u8, start: usize, path: []const u8, base_oid: []const u8) ?RoutablePage {│ │689 if (base_oid.len == 0 or (ref_or_oid == null and value.len != 0)) return null; │ │690 return .{ .repo_diff = .{ │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1050 if (c.base_oid.len != 0) try out.writer.print("/" ++ base_seg ++ "{s}", .{c.base_oid.slice()});│ │1051 if (c.search.len != 0) try out.writer.print("/" ++ search_seg ++ "{s}", .{c.search.slice()}); │ │1052 if (c.from.len != 0) try out.writer.print("/" ++ from_seg ++ "{s}", .{c.from.slice()}); │ │1053 + if (c.merge.len != 0) try out.writer.print("/" ++ merge_seg ++ "{s}", .{c.merge.slice()}); │ │1054 switch (c.content) { │ │1055 .diff => |d| { │ │1056 if (d.start != 0) try out.writer.print("/" ++ start_seg ++ "{d}", .{d.start}); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1640 } │ │1641 if (std.mem.eql(u8, tab, commits_seg)) { │ │1642 params.scanPairs(&segments) catch return null; │ │1643 + if (params.values.get(.merge)) |oid| { │ │1644 + if (!params.only(&.{.merge}) or segments.next() != null) return null; │ │1645 + return repoCommitsMergeRoute(pair, oid); │ │1646 + } │ │1647 if (!params.only(&.{ .start, .branch, .tag, .object, .base, .search, .from })) return null;│ │1648 const base_oid = params.values.get(.base) orelse ""; │ │1649 const search = params.values.get(.search) orelse ""; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1704 std.mem.eql(u8, a_c.base_oid.slice(), b.repo_commits.base_oid.slice()) and │ │1705 std.mem.eql(u8, a_c.search.slice(), b.repo_commits.search.slice()) and │ │1706 std.mem.eql(u8, a_c.from.slice(), b.repo_commits.from.slice()) and │ │1707 + std.mem.eql(u8, a_c.merge.slice(), b.repo_commits.merge.slice()) and │ │1708 switch (a_c.content) { │ │1709 .diff => |a_d| switch (b.repo_commits.content) { │ │1710 .diff => |b_d| a_d.start == b_d.start and std.mem.eql(u8, a_d.path.slice(), b_d.path.slice()),│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯