gites: colorful git tree viewer

Tauri 2 + Vue app that draws the commit graph of the repository given on
the command line. Branches can be hidden one by one or by patterns,
runs of plain commits fold into a dotted line, and everything is
reachable from the keyboard.
This commit is contained in:
2026-10-14 21:11:55 +02:00
commit c05edf7b74
58 changed files with 7716 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
import type { Ref } from "./types";
export interface TreeNode {
/** One `/`-separated part of the name. */
label: string;
/** The name up to and including this part; unique within one tree. */
path: string;
children: TreeNode[];
/** Set when a ref ends here. A node can be both a ref and a folder (`a` and `a/b`). */
ref?: Ref;
}
/** Splits short names by `/` into a tree. Folders come before refs, each sorted by name. */
export function buildTree(items: { short: string; ref: Ref }[]): TreeNode[] {
const root: TreeNode = { label: "", path: "", children: [] };
for (const { short, ref } of items) {
let node = root;
for (const part of short.split("/")) {
const path = node.path ? `${node.path}/${part}` : part;
let child = node.children.find((c) => c.label === part);
if (!child) {
child = { label: part, path, children: [] };
node.children.push(child);
}
node = child;
}
node.ref = ref;
}
const sort = (nodes: TreeNode[]) => {
nodes.sort(
(a, b) => Number(b.children.length > 0) - Number(a.children.length > 0) || a.label.localeCompare(b.label),
);
nodes.forEach((n) => sort(n.children));
};
sort(root.children);
return root.children;
}
/** Every ref in the subtree. */
export function refsUnder(node: TreeNode): Ref[] {
return [...(node.ref ? [node.ref] : []), ...node.children.flatMap(refsUnder)];
}