git blame tells you who. git-why tells you why — explains code from local git history, offline or with AI.
Project description
git-why
git blame tells you who broke it. git-why tells you why it's there in the first place.
It's 11pm. You've found a six-line guard clause that looks paranoid, maybe even wrong. git blame says Dave wrote it three years ago. Dave doesn't work here anymore. Do you delete it and hope, or burn twenty minutes in git log -p praying the commit message isn't just "fix"?
git-why does that archaeology for you — in under a second, completely offline, no API key, no setup beyond pip install.
git-why src/auth.py:42
It reads the actual commits and diffs around that line and reconstructs the story nobody wrote down anywhere else — or hands the same context to Claude/GPT/Gemini/Ollama/OpenRouter if you want a richer, AI-narrated version.
⭐ If this saves you a Slack message to a teammate who left the company two years ago, consider starring it — it helps other people stumbling through the same archaeology find this instead of reinventing it.
Install
pip install git-why
Or with an AI provider extra:
pip install "git-why[claude]" # Anthropic Claude
pip install "git-why[openai]" # OpenAI GPT
pip install "git-why[gemini]" # Google Gemini
pip install "git-why[all]" # all providers
Quick Start
git-why src/auth.py
git-why src/auth.py:42
git-why src/auth.py:42-60
git-why src/auth.py:42 --depth 30
git-why src/auth.py:42 --context 15
git-why src/auth.py:42 --verbose
git-why src/auth.py:42 --provider offline
Targets use FILE, FILE:LINE, or FILE:START-END. See the demo above for what the output actually looks like.
Offline And Free Mode
Offline mode is always available:
git-why src/auth.py:42 --provider offline
It reads the current file, runs local git commands, summarizes commit messages and diffs, and produces a non-AI markdown explanation. It does not need an API key and does not make internet calls.
The default provider is auto. Auto tries configured AI providers first and always falls back to offline.
Provider Setup
Provider order in auto mode:
- Claude when
ANTHROPIC_API_KEYexists andanthropicis installed - OpenAI when
OPENAI_API_KEYexists andopenaiis installed - Gemini when
GEMINI_API_KEYexists andgoogle-genaiis installed - OpenRouter when
OPENROUTER_API_KEYexists - Ollama when local Ollama is running
- Offline
Claude:
pip install "git-why[claude]"
set ANTHROPIC_API_KEY=your-key
git-why src/auth.py:42 --provider claude
OpenAI:
pip install "git-why[openai]"
set OPENAI_API_KEY=your-key
git-why src/auth.py:42 --provider openai
Gemini:
pip install "git-why[gemini]"
set GEMINI_API_KEY=your-key
git-why src/auth.py:42 --provider gemini
OpenRouter:
set OPENROUTER_API_KEY=your-key
git-why src/auth.py:42 --provider openrouter
Ollama:
ollama serve
ollama pull qwen2.5-coder
git-why src/auth.py:42 --provider ollama
PowerShell examples use set for brevity. Use your shell's normal environment variable syntax if needed.
Models
Override the model with --model:
git-why src/auth.py:42 --provider openai --model gpt-5.4-mini
git-why src/auth.py:42 --provider ollama --model qwen2.5-coder
Default models:
| Provider | Default model |
|---|---|
| Claude | claude-sonnet-4-6 |
| OpenAI | gpt-5.4-mini |
| Gemini | gemini-3.5-flash |
| OpenRouter | openrouter/free |
| Ollama | qwen2.5-coder |
Model names change over time. Defaults are centralized in git_why/ai/defaults.py. If a model fails, pass --model.
Windows PATH Note
On Windows, pip install -e . may install git-why.exe into a Scripts directory that is not on PATH, for example:
%LOCALAPPDATA%\Programs\Python\Python313\Scripts
Add that Scripts directory to PATH, or run the script with its full path.
Examples
Explain a whole file:
git-why src/auth.py
Explain one line:
git-why src/auth.py:42 --provider offline
Explain a range with more history:
git-why src/auth.py:42-60 --depth 30
Show raw git context:
git-why src/auth.py:42 --verbose
Development
pip install -e ".[dev]"
python -m pytest
Limitations
- Offline explanations are heuristic and based only on local git history.
- Deleted or renamed files depend on what git can resolve from the current working tree.
- Whole-file mode caps the displayed current code preview.
- Binary files are detected and shown as a placeholder rather than raw bytes; commit history still comes from
git log, but line-levelgit blameis skipped since line numbers aren't meaningful for binary content. - AI providers require their respective SDKs, keys, or local services only when explicitly selected or available in
auto.
Roadmap
- Publish to PyPI.
- Add CI for tests and packaging checks.
- Improve provider response parsing and error reporting.
- Add more git-history context, such as issue or PR links when present in commit messages.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file git_why-0.1.0.tar.gz.
File metadata
- Download URL: git_why-0.1.0.tar.gz
- Upload date:
- Size: 19.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42a1a5223966db34f2f2177a6a410b0c184154be2c409580844b5b7fd2e746fa
|
|
| MD5 |
ac04951a7ea8ab94dc20ca0b0a98df1c
|
|
| BLAKE2b-256 |
8b8ae8041d786447fe7d0f386bd00bf88aabcd134f8557a3827237d8fde86b08
|
File details
Details for the file git_why-0.1.0-py3-none-any.whl.
File metadata
- Download URL: git_why-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e77f64e663af2eb0fa1b0c04557d2c9aed8da8ccbe4fa9b4a0119979e248662
|
|
| MD5 |
27091651dd925f67572f038d0842288f
|
|
| BLAKE2b-256 |
4cd64ad437503a6af21d7ac25286fe4b45c41ccc2a8767d90a4820861bf9b6e0
|