Make git graphs from text
Overview
A version-control graph draws a commit history as parallel branch lanes — the git branch-and-merge picture you see in GitKraken, git log --graph, or a GitHub network view. It’s the right diagram for explaining a branching strategy (GitFlow, trunk-based, release trains), onboarding docs, and pull-request walkthroughs.
The name is deliberately tool-neutral: commits, branches, and merges are universal to git, Mercurial, and SVN. It shows topology — who branched from whom and what merged where — not wall-clock time. For real dates use timeline or gantt.
The grammar is keyword-less: a bare top-level line is a branch, a bare indented line is a commit. Re-naming a branch resumes it, so there is no checkout to track. Only merge and cherry-pick are verbs you have to type.
Syntax
version-control Title
// a bare top-level line is a BRANCH (the first is the trunk)
main
// a bare indented line is a COMMIT — the text is the message
Initial commit
Add README
// `from` sets the branch point
develop from main
Set up CI
// naming a branch again RESUMES it (this replaces `checkout`)
main
merge develop tag: v1.0.0
- Branch — a bare top-level line:
name [from parent] [order: N] [color]. The first branch (or any commit before one) is the trunk. There is nobranchkeyword. - Commit — a bare indented line; the text is the message. There is no
commitkeyword (it survives only as the opt-in form for an empty commit). - Resume / “checkout” — naming an existing branch again appends to it. Re-opening a branch later in the file places its commits later in time, so you can interleave (e.g. a hotfix on
mainmid-feature).
Commit metadata
Add same-line metadata after a commit message:
id: <sha>— show a short SHA on the commit (e.g. a real hash). Withoutid:no hash is drawn, so the source matches the picture exactly.tag: <label>— a release/ref marker, rendered as a colored pill badge.type: normal | highlight | reverse—highlightis a filled square (emphasis);reverseis a crossed dot (a backed-out commit).
Merge and cherry-pick
merge and cherry-pick are the only required verbs; write them indented, inside the active branch:
merge <branch> [tag:] [id:] [type:] [squash | ff | no-ff]— joins a branch’s tip back into the active lane.squashcollapses the source into one commit;ffis a fast-forward (no merge node).cherry-pick <commit>— copies a commit (referenced by message orid:) onto the active lane. Addparent:when the source is a merge commit.
Direction
direction-lr (default) draws horizontal lanes with the newest commit on the right — the iconic git-graph look. direction-tb draws column lanes, the git log view.
HEAD, remotes, and ahead/behind
The ref verb drops a labeled pointer at a commit — one mechanism for remote-tracking branches, extra tags, and a detached HEAD:
ref <name> at <commit> [remote]— a pointer pill. Anorigin/…name is ghosted automatically, and when a local branchXand anorigin/Xref both exist the renderer labels ahead/behind (↑2) from the commit graph.- HEAD auto-sits on the active branch’s tip; write
ref HEAD at <commit>for a detached HEAD, orno-headto hide it.
Operations: rebase, reset, revert, squash
These draw the operations that confuse people — git’s hardest concepts. Abandoned commits render faded (“ghosts”).
rebase <branch> onto <target>— replays the branch’s commits onto a new base; the originals fade and dashed arrows point to the solid copies.reset <branch> to <commit>— moves the branch pointer back; later commits fade (orphaned).revert <commit>— adds an inverse commit (crossed) with a dashed link to what it undoes.
Step notes
note <text> adds a numbered callout on the current commit — handy for narrating a strategy:
Directives
| Directive | Effect |
|---|---|
direction-lr | direction-tb | Lane orientation. direction-lr (default) is newest-right; direction-tb is the git-log column view. |
no-labels | Hide commit messages. |
no-lanes | Hide the branch lane lines. |
no-head | Hide the auto HEAD marker. |
When to use
version-control— branch/merge topology: how work flows across branches, splitting into parallel tracks that later rejoin.timeline— the events run in one line and the real dates between them matter.version-controlis topology, not a calendar.gantt— real durations and dependencies on a schedule.flowchart— the rules a person follows, not an actual commit history. A flowchart cannot express parallel lanes rejoining.sequence— parties passing messages to each other, not commits landing on branches.
Appearance
Each branch gets its own lane color automatically, assigned in declaration order. Set one explicitly with a trailing color token on the branch line — after from and order: if present:
Commits, merge nodes, tag pills, and ref pointers inherit their lane’s color; ghosted commits and origin/… refs are drawn faded against it. Colors come from the active palette — see Colors. Set the palette and light/dark theme at render time with --palette <name> and --theme light|dark|transparent.
The universal fill family (fill-tint / fill-solid / fill-outline) and no-title do not apply here — a commit graph draws dots and lanes rather than filled shapes. Use no-labels, no-lanes, and no-head above to subtract detail.
Next
- Related:
timeline·gantt·flowchart·sequence - Then: Colors & palettes