import type { Commit, FilterState, Ref, RefKind } from "./types"; export function refKind(name: string): RefKind { if (name.startsWith("refs/remotes/")) return "remote"; if (name.startsWith("refs/tags/")) return "tag"; return "local"; } export function shortName(name: string): string { return name.replace(/^refs\/(heads|remotes|tags)\//, ""); } /** `*` matches any run of characters (including `/`), `?` matches one character. */ export function globMatch(pattern: string, text: string): boolean { const re = pattern .split("") .map((ch) => (ch === "*" ? ".*" : ch === "?" ? "." : ch.replace(/[.+^${}()|[\]\\]/g, "\\$&"))) .join(""); return new RegExp(`^${re}$`).test(text); } export function isRefVisible(name: string, state: FilterState): boolean { if (state.hidden.includes(name)) return false; if (state.hideTags && name.startsWith("refs/tags/")) return false; const short = shortName(name); const only = state.rules.filter((r) => r.mode === "only"); if (only.length > 0 && !only.some((r) => globMatch(r.pattern, short))) return false; return !state.rules.some((r) => r.mode === "hide" && globMatch(r.pattern, short)); } /** Commits reachable from `starts`, in the original order. */ export function reachable(commits: Commit[], starts: Iterable): Commit[] { const byHash = new Map(commits.map((c) => [c.hash, c])); const seen = new Set(); const stack = [...starts]; while (stack.length > 0) { const h = stack.pop()!; if (seen.has(h)) continue; const c = byHash.get(h); if (!c) continue; seen.add(h); stack.push(...c.parents); } return commits.filter((c) => seen.has(c.hash)); } const kindOrder: Record = { local: 0, remote: 1, tag: 2 }; /** For each commit, the ref whose first-parent line it lies on. */ export function lineOwners(commits: Commit[], refs: Ref[], headRef: string | null): Map { const byHash = new Map(commits.map((c) => [c.hash, c])); const ordered = [...refs].sort( (a, b) => Number(b.name === headRef) - Number(a.name === headRef) || kindOrder[refKind(a.name)] - kindOrder[refKind(b.name)], ); const owners = new Map(); for (const ref of ordered) { let c = byHash.get(ref.target); while (c && !owners.has(c.hash)) { owners.set(c.hash, ref.name); c = byHash.get(c.parents[0]); } } return owners; }