fix(vcs): link filtered Git Log commits past the commits a filter hides (#1094)

With a User or text filter, the Git Log graph opened a lane for every commit whose parent the filter hid; the lanes never closed and filled the graph with arrows, since git keeps the real parents for these filters.

The log now links each shown commit to its nearest shown ancestors, dashed where the link passes hidden commits (the date range and No Merges count as hidden there too); a page's last commits are linked by walking on past the page. Collapse Linear Branches keeps those dashed links.

Fixes #872
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-11-04 03:57:08 +08:00
1 джерело a6c5574194
коміт 23e468252f
6 files changed
+170 -20

No files matched your search

+12
Переглянути файл
@@ -44,6 +44,18 @@ describe("Collapse Linear Branches", () => {
expect(rows[2]!.up.some((s) => s.dashed)).toBe(true);
});
it("keeps the dashed links of a filtered log", () => {
// A user filter already linked y past hidden commits.
const commits = [
c("z", ["y"], ["main"]),
{ ...c("y", ["x"], ["tag: v2"]), dashed: [true] },
c("x", [], ["tag: v1"]),
];
const { graph } = collapseLinear(commits, new Set());
expect(graph.find((g) => g.id === "y")).toEqual({ id: "y", parents: ["x"], dashed: [true] });
expect(computeGraph(commits).rows[1]!.down.some((s) => s.dashed)).toBe(true);
});
it("keeps referenced and kept commits", () => {
const commits = [c("d", ["c"]), c("c", ["b"], ["tag: v1"]), c("b", ["a"]), c("a")];
expect(collapseLinear(commits, new Set()).commits.map((x) => x.id)).toEqual(["d", "c", "a"]);
+6 -6
Переглянути файл
@@ -133,10 +133,9 @@ export function computeGraph(
* no references) so that only branch points, merges, tips and referenced commits stay,
* linked by dashed edges. Commits in `keep` (HEAD) stay visible.
*/
export function collapseLinear<C extends { id: string; parents: readonly string[]; refs: readonly string[] }>(
commits: readonly C[],
keep: ReadonlySet<string>,
): { commits: C[]; graph: GraphCommit[] } {
export function collapseLinear<
C extends { id: string; parents: readonly string[]; refs: readonly string[]; dashed?: readonly boolean[] },
>(commits: readonly C[], keep: ReadonlySet<string>): { commits: C[]; graph: GraphCommit[] } {
const byId = new Map(commits.map((c) => [c.id, c]));
const children = new Map<string, number>();
for (const c of commits) for (const p of c.parents) children.set(p, (children.get(p) ?? 0) + 1);
@@ -149,9 +148,10 @@ export function collapseLinear<C extends { id: string; parents: readonly string[
if (hidden(c)) continue;
const dashed: boolean[] = [];
const parents: string[] = [];
for (const p of c.parents) {
for (const [i, p] of c.parents.entries()) {
let cur = p;
let skipped = false;
// A filter may already link past hidden commits.
let skipped = c.dashed?.[i] ?? false;
for (let next = byId.get(cur), guard = 0; next && guard < commits.length && hidden(next); guard++) {
cur = next.parents[0]!;
next = byId.get(cur);