A blazingly fast, zero-config CLI tool to visualize your Git repository's code evolution by language over time. It parses your commit history and generates a beautiful, interactive stacked area chart embedded in a lightweight, standalone HTML report.
Built with Python and uv. This project is structured using the Chain of Responsibility design pattern for maximum maintainability.
- High Performance: Parses
git logmetrics using strict, non-regex string scanning for optimal CPU efficiency. - Interactive Visualization: Generates a Plotly-driven, dark-themed stacked area chart (
git-evolution.html) with unified hover tooltips. - Reliable on Small Repos: The X axis range is computed explicitly instead of relying on Plotly's autorange, so repositories with just 1-2 commits still render a visible, correctly-scaled chart (autorange collapses to a sub-millisecond window on very small date spans).
- Report Metadata: Every generated report is stamped with the repository name, generation timestamp, and total commit count, pulled straight from
git. - Polished Report Layout: The HTML output ships with a dark-themed header/footer and a card-style container around the chart, not just a bare
<div>. - Zero-Dependency Setup: Leverages
uvinline script metadata (PEP 723) to manage environments on the fly without polluting your system. - Standalone Binary: Can be compiled into a single self-contained executable with PyInstaller (orchestrated through
uv) — no Python oruvneeded at runtime, and the HTML template ships embedded inside the binary. - Clean Architecture: Built as an extensible execution pipeline (Fetch ➔ Parse ➔ Accumulate ➔ Plot ➔ Render).
You only need uv installed on your machine. Everything else (Python interpreter, plotly) is resolved automatically.
Run it from inside any Git repository you want to analyze:
uv run main.pyThis produces git-evolution.html in the current directory — open it in any browser.
If you built the standalone executable (see below), no uv/Python setup is required at all:
./git-evolutionJust run it from inside the target repository; it works exactly the same way.
build.sh orchestrates a full PyInstaller build using uv for environment management — no manual pip/venv juggling required.
./build.shUnder the hood it:
- Creates an isolated build environment with
uv venv .venv-build. - Installs
plotlyandpyinstallerinto it withuv pip install. - Runs PyInstaller in
--onefilemode, embeddingtemplate.htmldirectly into the executable (resolved at runtime viasys._MEIPASS) and collecting allplotlysubmodules.
The resulting binary is written to dist/git-evolution. It is platform-specific — PyInstaller does not cross-compile, so build on each target OS (Linux, macOS, Windows) separately.
Each generated report includes:
- Header: repository name, generation date/time, and total commits analyzed.
- Chart: a stacked area chart of cumulative lines of code per file extension over time, with zoom/pan controls and unified hover tooltips.
- Footer: attribution line.
├── main.py # Entry point; wires up and runs the handler chain
├── handler.py # Abstract base class for the Chain of Responsibility pattern
├── evolution_context.py # Shared state object passed through the pipeline
├── git_log_fetch.py # [1/5] Fetches git log, repo name, and commit count
├── git_log_parse.py # [2/5] Parses numstat output into per-extension, per-date deltas
├── metrics.py # [3/5] Computes cumulative time series per extension
├── plot_generator.py # [4/5] Builds the Plotly figure and fixes the small-dataset X axis range
├── html_renderer.py # [5/5] Injects the figure and metadata into template.html
├── template.html # HTML/CSS layout for the final report
├── build.sh # Builds a standalone binary via uv + PyInstaller
└── pyproject.toml # Project metadata and dependencies
The pipeline runs as a sequential Chain of Responsibility, each handler enriching a shared EvolutionContext:
Fetch ➔ Parse ➔ Accumulate ➔ Plot ➔ Render
- Fetch — runs
git log --numstatplusgit rev-parse/git rev-listto capture the raw log, repository name, and commit count. - Parse — scans the raw log line by line (no regex) to tally added/removed lines per file extension and date.
- Accumulate — turns per-date deltas into a running cumulative total per extension.
- Plot — builds a dark-themed Plotly stacked area chart, with an explicitly padded date range so the chart stays visible regardless of how few or how many commits exist.
- Render — fills
template.htmlwith the chart and the repo's metadata, producinggit-evolution.html.