Find the files whose knowledge sits with a single person — the bus factor, made concrete.
Project description
orphan
The bus factor, made concrete.
Not an abstract score — the named list of files whose knowledge sits with a single person, sorted by how much is actually at risk.
What it does
Every mature codebase has files only one person has ever really touched. Some are harmless. Some are a terraform module or an nginx.conf holding up production, understood by exactly one engineer — who may have left months ago.
orphan reads your git history and surfaces those files by name, with the owner, the age of the last commit, and a risk category. It reads history only, never file contents, so it works on any repository regardless of language — application code and infrastructure alike.
The three categories
Age alone is misleading: a LICENSE written once and never touched looks exactly like a dangerous orphan, but it's the healthiest file in the repo. orphan separates stable from risky using two axes — how much a file has evolved (commit count, a proxy for accumulated knowledge) and whether its sole owner is still active.
| Category | Meaning | Signal |
|---|---|---|
| ◆ silo | Many commits, one owner, still active | Live, concentrated knowledge. One brain holds a growing area. |
| ✖ orphan | Many commits, one owner, now inactive | The knowledge has likely already walked out the door. |
| · stable | Few commits (a base or config file) | Low risk. Shown for completeness, not as an alarm. |
The stable bucket exists on purpose: it's where the classic false positives land (base files an author wrote once before moving on), so they stop drowning out the real risks.
Why you'll like it
- Zero dependencies. Pure Python standard library, 3.8+.
- Portable. A single script; drop
orphan.pyanywhere and run it. - Language-agnostic. Works on Terraform, Ansible, shell, YAML — anything git tracks, not just source code.
- Localized output. English, French, German, Spanish, Italian (see Languages).
- Scriptable.
--jsonfor pipelines and dashboards.
Installation
From PyPI
pip install orphan
Then, in any repository:
orphan
From source (no install)
The tool is a single file. Grab orphan.py (and locales.json if you want non-English output), then:
python3 orphan.py
That's it — nothing to build, nothing to install. orphan.py alone runs in English; add locales.json beside it to unlock the other languages.
Usage
orphan # analyze the current repository
orphan --lang fr # localized output (en, fr, de, es, it)
orphan --dense-commits 8 # raise the silo/orphan vs stable threshold
orphan --active-months 6 # "inactive owner" cutoff (default: 12 months)
orphan --threshold 2 # also consider files with up to 2 authors
orphan --top 40 # cap the displayed rows (0 = all)
orphan --path infra/ # restrict to a subdirectory
orphan --json # machine-readable output (never localized)
Options
| Option | Default | Description |
|---|---|---|
--lang |
en |
Output language: en, fr, de, es, it. |
--path |
(repo root) | Restrict analysis to a subdirectory. |
--threshold |
1 |
Max distinct authors for a file to be considered single-owned. |
--dense-commits |
5 |
Commits from which a file counts as knowledge-dense (silo/orphan vs stable). |
--active-months |
12 |
An author with no commit for N months counts as inactive. |
--top |
25 |
Show only the N riskiest files (0 for all). |
--json |
(off) | Emit raw JSON instead of the terminal report. |
--no-color |
(off) | Disable ANSI colors. |
JSON output
--json emits stable, language-neutral data — ideal for CI gates or a dashboard:
{
"summary": { "total": 12, "silo": 3, "orphan": 4, "stable": 5, "tracked_files": 210, "dense_commits": 5 },
"files": [
{
"path": "infra/pipeline.tf",
"kind": "infra",
"category": "orphan",
"owners": ["Bob"],
"owner_count": 1,
"commits": 8,
"last_commit": "2024-05-01T10:00:00+00:00",
"age_days": 800,
"departed": true
}
]
}
Languages
Switch the display language with --lang. English is built into the script; the other languages live in locales.json next to it. If that file is missing or a string is absent, the output falls back to English.
Adding a language (no code required)
Translations are pure data. To add, say, Portuguese:
- Open
locales.json. - Copy the
"en"block — it's the reference and lists every key. - Rename it
"pt"and translate the values. Keep the{placeholders}intact. - For the age units, note the
_one/_otherpairs — they exist so plurals read correctly (e.g.1 mêsvs6 meses). - Add
"pt"to the--langchoices inorphan.py(theLANGUAGEStuple). - Open a pull request. 🎉
How it works
A single git log pass over the whole repository collects, per file, the set of authors and the date of the last commit, plus each author's most recent activity anywhere in the repo. %aN applies your .mailmap, so one person committing under several emails counts once. Files are then bucketed into the three categories and sorted so the densest live silos rise to the top.
One git invocation for the whole repo — fast even on large histories.
Limitations
Honest by design:
- Commit count is a proxy for knowledge, not size. A large file written in a single commit reads as
stableeven if it holds a lot. - Renames aren't followed in a repo-wide log, so a freshly renamed file may look single-owner even if it had history under its old name.
- Ownership is per commit, not per line. "Who has ever touched this file" is the right orphan signal and far faster than running
git blameper file. - "Inactive" is inferred from git activity, not HR data. Someone working on another repo for a year will read as inactive. Tune
--active-monthsto your team's rhythm.
Contributing
Issues and pull requests welcome — a new translation, a smarter category heuristic, an extra infra file type. Adding a language needs no Python (see above).
License
MIT — do what you like, no warranty.
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 orphan-0.2.0.tar.gz.
File metadata
- Download URL: orphan-0.2.0.tar.gz
- Upload date:
- Size: 15.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ffba7fb82f95b3b7c21e02e3330f445914f963399638c62ffed7d9520cad2b1
|
|
| MD5 |
dbd2ff2253b27fc6124c61ab4fb1b9e3
|
|
| BLAKE2b-256 |
ae3c98717216ec5e545babe71725ef909af94d5e05cc22ac0ebce6c88e460bd3
|
File details
Details for the file orphan-0.2.0-py3-none-any.whl.
File metadata
- Download URL: orphan-0.2.0-py3-none-any.whl
- Upload date:
- Size: 14.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5c37d70a663d72de720260526702107f7c225b88f92a96f516c31b9cf3daef0
|
|
| MD5 |
e4a93db8392bd7280dd99ecd6daaf6c3
|
|
| BLAKE2b-256 |
d1f3a7ec4e7deb274d2807e1f6cc37f2d747fa7eb78df140dbbdd82520f520ca
|