This file provides guidance for Claude Code when working on this project.
git-graph is a native macOS CLI tool that opens a window rendering a git repository's commit history as a beautiful, interactive graph (colored branch lanes, merge curves, ref pills, click-to-inspect diffs). It uses Swift Package Manager (not an Xcode project) and is modeled on the sibling project MarkdownRender — same AppKit-window-hosting-a-WKWebView approach.
# Debug build
swift build
# Release build
swift build -c release
# Run against the current directory's repo
swift run git-graph
# Run against another repo, with options
swift run git-graph --all --theme dark /path/to/repoThe release binary is at .build/release/git-graph.
Sources/git-graph/
├── main.swift # CLI entry, argument parsing with ArgumentParser
├── App/
│ └── AppDelegate.swift # NSApplication delegate, window, WebView, diff bridge, watch
├── Git/
│ ├── GitModels.swift # Commit / Ref / GraphData — Codable, serialized to JSON
│ └── GitRepository.swift # Process-based git runner + log parser + diff fetch
└── Rendering/
├── GraphLayout.swift # Lane/color assignment algorithm
└── HTMLGenerator.swift # HTML template, CSS, and the SVG-drawing JS
main.swiftresolves the repo (GitRepository.discover) and launches the app.AppDelegate.loadGraph()callsGitRepository.loadCommits()(onegit log), runsGraphLayout.assign()to fill in lanes/colors/parent-edges, encodes theGraphDatato JSON, and hands it toHTMLGenerator.generateHTML().- JS in the WebView reads the embedded JSON and draws the SVG graph + commit rows.
- Clicking a commit posts its hash to Swift via a
WKScriptMessageHandler(commithandler);AppDelegaterunsgit showand callswindow.renderDiff.
- No Xcode project — SPM only. Open
Package.swiftin Xcode for IDE features. - System
git, not libgit2 — already installed, always current, handles every repo quirk. Onegit logprocess keeps load fast. --topo-orderis mandatory —GraphLayoutrequires each commit to be emitted before any of its parents. Plain date order breaks this when timestamps tie or clocks are skewed, corrupting merge edges. Do not remove it.- Hand-drawn SVG, no CDN — git data is small and we control the layout, so the tool works fully offline (unlike MarkdownRender, which loads diagram libs from CDN).
- Lazy diffs — the embedded JSON has no patch text; diffs are fetched on click
via
git show, keeping initial load fast on large repos. - Theme support — CSS uses
prefers-color-scheme;--themesets theNSWindow.appearanceto override, same as MarkdownRender.
Sweeps commits newest→oldest tracking "active lanes" (columns reserved by a child for one of its parents). A commit takes the leftmost lane reserved for it; its first parent inherits that lane (straight history), additional parents open/merge lanes. A final post-pass rewrites every parent edge to the parent's real assigned lane so edges always land on their node even when branches converge. See the file's doc comment for the full description.
- Add it to the relevant struct in
GitModels.swift(keep itCodable). - Populate it in
GitRepository.parseLog(extend the--pretty=format:string and the field indices) or inGraphLayout. - Read it from the
GRAPH_DATAobject inHTMLGenerator.generateJS().
Edit HTMLGenerator.generateCSS(). Theming uses CSS variables
(--bg-color, --text-color, --lane-0…--lane-7, …) with dark-mode overrides
under @media (prefers-color-scheme: dark). Drawing constants (lane width, row
height, dot radius) live at the top of generateJS().
The graph rendering can be verified headlessly without opening a window:
- Build a repo with branches and a merge (so lanes/merges are exercised), e.g.
git init, a couple of commits, a feature branch, a--no-ffmerge, a tag. - Render its HTML via the real
HTMLGeneratorand open it in headless Chrome to execute the JS and screenshot it:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \ --headless --disable-gpu --screenshot=out.png file:///path/to/rendered.html - Confirm: every commit has a
.commit-row, each has a<circle>, parent links have a<path>, and merge/branch edges curve into the correct lanes.
Then launch the real app: swift run git-graph --all /path/to/repo.
Edit Package.swift: platforms: [.macOS(.v13)].
Edit Package.swift and add to both dependencies and the target's dependencies.
Add an @Option/@Flag in main.swift's GitGraph command, thread it through to
AppDelegate, and use it in loadGraph() or GitRepository.