A terminal interactive viewer for the Git commit graph. It walks the history with git itself (never re-implements git), lays the DAG out into lanes, and renders it either as a full-screen TUI or as a one-shot listing.
- Six views:
classic,compact,lanes,desktop,table,log - Full-screen interactive mode on
prompt_toolkit(alternate screen) - Deterministic lane engine: stable columns, correct parent connections
- Ref decorations (
HEAD, branches, remotes, tags) parsed from git itself - Unicode box drawing with an
--asciifallback - Search (
/), details pane, mouse scrolling, incremental loading --plainoutput suitable for pipes, scripts and CI
Requires Python 3.14+ and the git executable on PATH.
uv sync --extra dev
uv run git-treegit-tree # interactive, full screen
git-tree -V classic # "long" style: * -- hash subject decoration
git-tree -V lanes --plain # plain text, one line per commit
git-tree --ascii --plain # ASCII-only glyphs
git-tree --view log -n 30 # compact one-liners
git-tree --all # walk every ref instead of HEAD
git-tree main..feature # revision/range passed to git verbatim
git-tree --repo /path/to/repo # operate on another repositorygit-tree --plain --view lanes
● ╯ 6f8db44 [HEAD -> main] [main] merge feature
│ ● 7745c48 [feature] feature work
● ┤ b2bbe19 main work
● 4c7c5dc initial
--view |
Description |
|---|---|
lanes |
Default. Wide graph columns, refs on the right |
classic |
git log --graph style, one lane per column |
compact |
Classic but denser |
desktop |
Refs aligned to the left, graph on the right |
table |
Graph, then hash / refs / subject / date / author |
log |
No graph; hash, refs, subject, date, author |
| Key | Action |
|---|---|
j / k, arrows |
Move the selection up / down |
PgDn / PgUp |
Page forward / back |
g / G |
Jump to top / bottom |
Enter |
Toggle the commit details pane |
v, 1-6 |
Cycle / jump between views |
/, n, N |
Search, next / previous match |
h / l |
Scroll the graph horizontally |
q, Ctrl-C |
Quit |
--view/--all/--branch/-b/--max-count/-n/--skip/-N/--author/--grep/-g/--since/--until/--reverse/--first-parent/--no-merges/--merges/--topo-order/--date-order/--author-date-order/--no-color/--plain/--ascii/--no-interactive/--no-status/--details/--repo/--hash-length/--date/--debug/--version
git-tree --help lists every flag.
The same small merge history rendered by every view:
# classic # compact # lanes
●╯ 6f8db4 merge ●╯ 6f8db4 merge ● ╯ 6f8db4 merge
│● 7745c4 feature │● 7745c4 feature │ ● 7745c4 feature
●┤ b2bbe1 main ●┤ b2bbe1 main ● ┤ b2bbe1 main
● 4c7c5d initial ● 4c7c5d initial ● 4c7c5d initial
# desktop # table # log
[HEAD -> main] ● ╯ merge ●╯ merge 14m Dev 6f8db4 merge 14m Dev
[feature] │ ● feature │● feature 14m Dev 7745c4 feature 14m Dev
● ┤ main ●┤ main 14m Dev b2bbe1 main 14m Dev
● initial ● initial 14m Dev 4c7c5d initial 14m DevSame history with --ascii:
git-tree --plain --ascii
* \ 6f8db4 merge feature
| * 7745c4 feature work
* + b2bbe1 main work
* 4c7c5d initialThe revision is handed to git log verbatim, so every git revision syntax just
works: branches, tags, HEAD~3, feature@{yesterday}, ranges main..topic,
main...topic. Only the usual caveats of git apply; everything the terminal
does is read-only.
git-tree HEAD~10..HEAD # last ten commits
git-tree --all --view log # every ref, one line each
git-tree -b topic # same as passing "topic"
git-tree --merges # only merge commits
git-tree --since 2.weeks # since/until/author/grep passthroughEnter opens a details pane with the full body and a per-file stat summary.
/ searches the loaded history across subject, hashes, authors and ref names;
n / N jump between matches. The pane and the status line never touch the
repository.
Repository -> git commands -> parser -> commits -> graph builder
-> lane engine -> renderer -> segments -> viewport -> terminal
git_tree/git— commands,-zparsing, refs, repository discoverygit_tree/graph— DAG model, deterministic lane allocation, glyph topologygit_tree/render— renderers emitSegment(text, style)lists; no ANSI gluegit_tree/app—prompt_toolkitapplication, viewport, key bindingsgit_tree/theme,git_tree/config— style tokens and settings
Git is the single source of truth; the tool never writes to the repository. Renderers never talk to git; they only consume the graph model.
The lane engine walks rows newest-first and assigns each commit one column
(the "lane"). A merge never guesses where its parents are: it either continues
its own lane into the first parent, opens fresh lanes for the others, or walks
a line sideways to a parent that already occupies a column. Lanes are
append-only; freed slots are reused, so history with many short-lived branches
stays narrow instead of growing fat. Parent connections are decided before any
glyph is drawn, which is why the graph in every view agrees with git log
itself.
src/git_tree/
cli.py # typer entry point and option handling
app/ # prompt_toolkit app, viewport, key bindings, state
config/ # runtime settings
git/ # command building, -z parsing, refs, repository access
graph/ # model, lane engine, symbol tables, topology helpers
render/ # six renderers + shared segment pipeline
theme/ # style tokens (the only place colors are named)
util/ # subprocess, terminal size, unicode width helpers
tests/
unit/ # pure-python tests (parsing, lanes, rendering, scrolling)
integration/ # runs against real git repositories
The project targets Python 3.14 and is managed with uv.
uv sync --extra dev # create the 3.14 environment with dev tools
uv run git-tree # run the tool
uv run pytest # 79 tests: unit + integration against real git repos
uv run ruff check . # lint
uv run mypy src # type checkThe integration tests build throwaway git repositories with git itself, so
the suite doubles as a compatibility check against a real installation.
MIT — see LICENSE.