Recipe: filtering commits

Pass a filter predicate to <GitGraph> and the layout engine removes commits for which it returns false. Edges are rewritten transitively: if a visible commit had a hidden parent, the edge skips through the hidden chain to the nearest visible ancestor. The visible graph stays connected wherever the underlying graph is.

Basic — author filter

The simplest filter. The predicate runs on every commit; only commits where it returns true appear in the layout, and edges between visible commits are rewritten through the hidden ones.

import GitGraph from "@/components/git-graph/git-graph";

<GitGraph
  commits={commits}
  filter={(c) => c.author.name === "Alice"}
/>

Working-tree-aware filter

When showWorkingTreeRow is true, the synthesized working-tree commit is passed to your predicate just like any other commit. Most filters should keep it visible by special-casing WORKING_TREE_SHA.

import GitGraph from "@/components/git-graph/git-graph";
import { WORKING_TREE_SHA } from "@/components/git-graph/lib/working-tree";

const keep = (c) => c.message.includes("WIP");

<GitGraph
  commits={commits}
  showWorkingTreeRow
  // Always keep the synthetic working-tree row visible — it's subject
  // to the filter by default.
  filter={(c) => c.sha === WORKING_TREE_SHA || keep(c)}
/>

Branch-ancestry filter

To show only commits reachable from a branch tip, precompute the reachable set with a BFS over parents, then check membership in the predicate. Memoize the set so the predicate stays cheap on large inputs.

"use client";
import { useMemo } from "react";
import GitGraph from "@/components/git-graph/git-graph";

// Consumer-supplied: BFS up the parents from a tip sha.
function computeReachable(commits, tipSha) {
  const bySha = new Map(commits.map((c) => [c.sha, c]));
  const reached = new Set();
  const queue = [tipSha];
  while (queue.length) {
    const sha = queue.shift();
    if (reached.has(sha)) continue;
    const c = bySha.get(sha);
    if (!c) continue;
    reached.add(sha);
    for (const p of c.parents) queue.push(p);
  }
  return reached;
}

export default function MainAncestryGraph({ commits, mainTipSha }) {
  const reachable = useMemo(
    () => computeReachable(commits, mainTipSha),
    [commits, mainTipSha],
  );
  return (
    <GitGraph
      commits={commits}
      filter={(c) => reachable.has(c.sha)}
    />
  );
}

Selection survives filtering

If a commit is selected and a filter change hides it, the row disappears from the listbox but renderDetail (if used) still receives the remembered commit looked up from the unfiltered input. Useful when a search-box filter narrows the graph while a drawer is open — the drawer's contents stay put.