Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,5 @@ generate-examples:
uv run git-graphable examples/repos/repo-pristine --engine mermaid -o examples/assets/pristine.mmd --highlight-critical --critical-branch main --highlight-authors --bare
uv run git-graphable examples/repos/repo-messy --engine mermaid -o examples/assets/messy.mmd --highlight-wip --highlight-direct-pushes --highlight-stale --bare
uv run git-graphable examples/repos/repo-features --engine mermaid -o examples/assets/features.mmd --highlight-orphans --highlight-diverging-from main --bare
uv run git-graphable examples/repos/repo-risk-silo --engine mermaid -o examples/assets/silo.mmd --highlight-silos --silo-threshold 20 --bare
uv run git-graphable examples/repos/repo-complex-hygiene --engine mermaid -o examples/assets/complex.mmd --highlight-back-merges --bare
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ git graphable .
- **Automatic Visualization**: Generates and opens an image (SVG/PNG) automatically if no output is specified.
- **Advanced Highlighting**: Visualize author patterns, topological distance, and specific merge paths.
- **GitHub Integration**: Highlight commits based on pull request status (Merged, Open, Closed, Draft) using the `gh` CLI.
- **Hygiene Analysis**: Automatically detect WIP commits, direct pushes to protected branches, and squashed PRs.
- **Hygiene Analysis**: Automatically detect WIP commits, direct pushes to protected branches, squashed PRs, back-merges, and contributor silos.
- **Health Scoring**: Get a numeric "Hygiene Score" (0-100%) with a color-coded grade and detailed breakdown of workflow anti-patterns.
- **CI Gating**: Use the `--check` flag to return a non-zero exit code if the hygiene score falls below a threshold (configurable via `--min-score`).
- **Flexible Input**: Works with local repository paths or remote Git URLs.
Expand Down
111 changes: 105 additions & 6 deletions examples/EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This page demonstrates the visual output and hygiene analysis of `git-graphable` using generated example repositories.

## 1. Pristine Repository (Score: 100%)
Demonstrates a clean, PR-based workflow with author highlighting and critical branch marking.
Demonstrates a clean, PR-based workflow with multi-author highlighting and critical branch marking.

**Command:**
```bash
Expand Down Expand Up @@ -45,8 +45,8 @@ git-graphable repo-messy --highlight-wip --highlight-direct-pushes --highlight-s

**Hygiene Report:**
- **Overall Score**: 76% (C)
- **Direct Pushes**: -15% (Non-merge commits on `main`)
- **WIP Commits**: -9% (3 commits with `WIP:` in message)
- **Direct Pushes**: -15%
- **WIP Commits**: -9%

**Output:**
```mermaid
Expand All @@ -73,8 +73,107 @@ f534dfb429831e547f418960a9c1091af158664d --> 147f97d5dd2c89f8078e0cc67aab5d49548

---

## 3. Special Features (Score: 93%)
Demonstrates topological analysis features like orphan/dangling commits and divergence (behind base).
## 3. Risk Analysis (Bus Factor)
Highlights branches with many commits but only one contributor. This indicates a "Review Risk" where large amounts of code are being written in isolation.

**Command:**
```bash
git-graphable repo-risk-silo --highlight-silos --silo-threshold 20
```

**Output:**
```mermaid
flowchart TD
3ea61173cf4a6c69db17b4c6501039d33a6982b0["3ea6117 - main - Demo User - 20260305105901"]
5efcb8d1f1ebb2795a738df951c5e06203f262b5["5efcb8d - Alice - 20260305105901"]
9195465c812ad54e5665012678f294b2f22d7184["9195465 - Alice - 20260305105901"]
d3a2ea8a3c8ed7409403d836f4280e7df24fe0a3["d3a2ea8 - Alice - 20260305105901"]
cd27397b8f88e090e5269311b26b77b10a61f954["cd27397 - Alice - 20260305105901"]
43458ccb4b4d435f1e726a71c36f4d74dfef3d88["43458cc - Alice - 20260305105901"]
5781a1dcd158780172ef530c7d3c4a5bbfddbb9b["5781a1d - Alice - 20260305105901"]
192391841cd43546840f9a73ca82d590a596f3d1["1923918 - Alice - 20260305105901"]
1123939f9dc7e8ac88c44560cea2854b25b8633a["1123939 - Alice - 20260305105901"]
3bb33732ee23b3c90eb56131d7c18461e635efab["3bb3373 - Alice - 20260305105901"]
92d7b93cd7f52406236dc1a771c18d3101097ac0["92d7b93 - Alice - 20260305105901"]
edc7e0989161e639f69a2e918315070a9b650257["edc7e09 - Alice - 20260305105901"]
aaa8545d80ce1c7f93f0ed635a642d52e537d418["aaa8545 - Alice - 20260305105901"]
44f32db29d6b3a917fe1f0493a78a563d164eccb["44f32db - Alice - 20260305105901"]
fcb262b2e3dce5b6d005cf5a2ee1e9d51a923053["fcb262b - Alice - 20260305105901"]
588cfa14d70f6679bb53588b4bcb8fa49adedb90["588cfa1 - Alice - 20260305105901"]
fc5736cf0a21cba3426fd0853c24f20592b2c925["fc5736c - Alice - 20260305105901"]
a4f8461065731a429ad9ae8c044827329d3083d5["a4f8461 - Alice - 20260305105901"]
543b5e4de434310b1f90817fd89ac19f735adaa8["543b5e4 - Alice - 20260305105901"]
b1fdd9163e15af3eb69105e1337bbe75813f61f7["b1fdd91 - Alice - 20260305105901"]
b1bba43af1237077c8d101b88d5a2e7aafdd3806["b1bba43 - Alice - 20260305105901"]
807f19736e0a1903dc83ec62cdf810bc5c043a1a["807f197 - Alice - 20260305105901"]
30c28e9ac75e9ca65360d6c000e3dd62b0ff48f7["30c28e9 - Alice - 20260305105901"]
f012b1d4971e5d6e9619396f9adbb32d311e0c80["f012b1d - Alice - 20260305105901"]
76310c4489cb4667e1f31e540b40b42c30b70190["76310c4 - Alice - 20260305105901"]
0007580446cab2af471bc90a29dd094175ef5617["0007580 [SILO] - feature/huge-silo - Alice - 20260305105901"]
style 0007580446cab2af471bc90a29dd094175ef5617 stroke:blue,stroke-width:6px
3ea61173cf4a6c69db17b4c6501039d33a6982b0 --> 5efcb8d1f1ebb2795a738df951c5e06203f262b5
5efcb8d1f1ebb2795a738df951c5e06203f262b5 --> 9195465c812ad54e5665012678f294b2f22d7184
9195465c812ad54e5665012678f294b2f22d7184 --> d3a2ea8a3c8ed7409403d836f4280e7df24fe0a3
d3a2ea8a3c8ed7409403d836f4280e7df24fe0a3 --> cd27397b8f88e090e5269311b26b77b10a61f954
cd27397b8f88e090e5269311b26b77b10a61f954 --> 43458ccb4b4d435f1e726a71c36f4d74dfef3d88
43458ccb4b4d435f1e726a71c36f4d74dfef3d88 --> 5781a1dcd158780172ef530c7d3c4a5bbfddbb9b
5781a1dcd158780172ef530c7d3c4a5bbfddbb9b --> 192391841cd43546840f9a73ca82d590a596f3d1
192391841cd43546840f9a73ca82d590a596f3d1 --> 1123939f9dc7e8ac88c44560cea2854b25b8633a
1123939f9dc7e8ac88c44560cea2854b25b8633a --> 3bb33732ee23b3c90eb56131d7c18461e635efab
3bb33732ee23b3c90eb56131d7c18461e635efab --> 92d7b93cd7f52406236dc1a771c18d3101097ac0
92d7b93cd7f52406236dc1a771c18d3101097ac0 --> edc7e0989161e639f69a2e918315070a9b650257
edc7e0989161e639f69a2e918315070a9b650257 --> aaa8545d80ce1c7f93f0ed635a642d52e537d418
aaa8545d80ce1c7f93f0ed635a642d52e537d418 --> 44f32db29d6b3a917fe1f0493a78a563d164eccb
44f32db29d6b3a917fe1f0493a78a563d164eccb --> fcb262b2e3dce5b6d005cf5a2ee1e9d51a923053
fcb262b2e3dce5b6d005cf5a2ee1e9d51a923053 --> 588cfa14d70f6679bb53588b4bcb8fa49adedb90
588cfa14d70f6679bb53588b4bcb8fa49adedb90 --> fc5736cf0a21cba3426fd0853c24f20592b2c925
fc5736cf0a21cba3426fd0853c24f20592b2c925 --> a4f8461065731a429ad9ae8c044827329d3083d5
a4f8461065731a429ad9ae8c044827329d3083d5 --> 543b5e4de434310b1f90817fd89ac19f735adaa8
543b5e4de434310b1f90817fd89ac19f735adaa8 --> b1fdd9163e15af3eb69105e1337bbe75813f61f7
b1fdd9163e15af3eb69105e1337bbe75813f61f7 --> b1bba43af1237077c8d101b88d5a2e7aafdd3806
b1bba43af1237077c8d101b88d5a2e7aafdd3806 --> 807f19736e0a1903dc83ec62cdf810bc5c043a1a
807f19736e0a1903dc83ec62cdf810bc5c043a1a --> 30c28e9ac75e9ca65360d6c000e3dd62b0ff48f7
30c28e9ac75e9ca65360d6c000e3dd62b0ff48f7 --> f012b1d4971e5d6e9619396f9adbb32d311e0c80
f012b1d4971e5d6e9619396f9adbb32d311e0c80 --> 76310c4489cb4667e1f31e540b40b42c30b70190
76310c4489cb4667e1f31e540b40b42c30b70190 --> 0007580446cab2af471bc90a29dd094175ef5617
```

---

## 4. Redundant History
Highlights redundant back-merges (merging `main` into a feature branch). This helps identify where a rebase strategy might have been cleaner.

**Command:**
```bash
git-graphable repo-complex-hygiene --highlight-back-merges
```

**Output:**
```mermaid
flowchart TD
366874227b985955d27323453b9e075f53065692["3668742 - Demo User - 20260305105901"]
89c693c037b2eab4256beab9ead6572e9c9d0f58["89c693c - Demo User - 20260305105901"]
71589446a700af13cb14ee4fea04c4ab1ad0a1e1["7158944 - Demo User - 20260305105901"]
11f210a834080631aeb343dcec09abde01f15e44["11f210a - Demo User - 20260305105901"]
62d326c80963476bf06537d224f66db9f48f30b4["62d326c [BACK-MERGE] - feature/noisy-history - Demo User - 20260305105901"]
style 62d326c80963476bf06537d224f66db9f48f30b4 stroke:orange,stroke-width:4px,stroke-dasharray: 2 2
96f27dee0ae8c3eb5abb9f9b04b4ead750a729ba["96f27de - main - Demo User - 20260305105901"]
99d822031a58c60594158beecfb254695af84db9["99d8220 - Demo User - 20260305105901"]
594597456b51ab193d76035dc5119f70b4cdaa7a["5945974 - feature/to-be-squashed - Demo User - 20260305105901"]
366874227b985955d27323453b9e075f53065692 --> 89c693c037b2eab4256beab9ead6572e9c9d0f58
366874227b985955d27323453b9e075f53065692 --> 71589446a700af13cb14ee4fea04c4ab1ad0a1e1
89c693c037b2eab4256beab9ead6572e9c9d0f58 --> 62d326c80963476bf06537d224f66db9f48f30b4
71589446a700af13cb14ee4fea04c4ab1ad0a1e1 --> 62d326c80963476bf06537d224f66db9f48f30b4
71589446a700af13cb14ee4fea04c4ab1ad0a1e1 --> 96f27dee0ae8c3eb5abb9f9b04b4ead750a729ba
71589446a700af13cb14ee4fea04c4ab1ad0a1e1 --> 11f210a834080631aeb343dcec09abde01f15e44
11f210a834080631aeb343dcec09abde01f15e44 --> 99d822031a58c60594158beecfb254695af84db9
99d822031a58c60594158beecfb254695af84db9 --> 594597456b51ab193d76035dc5119f70b4cdaa7a
```

---

## 5. Topological Analysis
Demonstrates features like orphan/dangling commits and divergence (behind base).

**Command:**
```bash
Expand All @@ -96,7 +195,7 @@ style ebeaa6afb7b85a2d84d8aa5279ca3ad50f54a987 stroke:orange,stroke-width:2px,st

---

## 4. CI Mode (Gating)
## 6. CI Mode (Gating)
Demonstrates how to use `git-graphable` as a CI gate. The tool returns a non-zero exit code if the hygiene score is below the threshold.

**Command (Fails):**
Expand Down
54 changes: 54 additions & 0 deletions examples/generate_demos.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,67 @@ def generate_features():
run_git(["branch", "-D", "detached-work"], path)


def generate_risk_silo():
print("Generating repo-risk-silo...")
path = create_base_repo("repo-risk-silo")

# Create a long-running branch with only one author (Alice)
run_git(["checkout", "-b", "feature/huge-silo"], path)
run_git(["config", "user.name", "Alice"], path)
run_git(["config", "user.email", "alice@example.com"], path)

for i in range(25):
(path / f"work_{i}.txt").write_text(f"work {i}")
run_git(["add", f"work_{i}.txt"], path)
run_git(["commit", "-m", f"chore: alice working hard {i}"], path)

run_git(["checkout", "main"], path)


def generate_squash_and_backmerge():
print("Generating repo-complex-hygiene...")
path = create_base_repo("repo-complex-hygiene")

# 1. A branch that was back-merged (main into feature)
run_git(["checkout", "-b", "feature/noisy-history"], path)
(path / "feat.txt").write_text("feat")
run_git(["add", "feat.txt"], path)
run_git(["commit", "-m", "feat: start working"], path)

run_git(["checkout", "main"], path)
(path / "main_work.txt").write_text("main")
run_git(["add", "main_work.txt"], path)
run_git(["commit", "-m", "chore: some main work"], path)

run_git(["checkout", "feature/noisy-history"], path)
run_git(
["merge", "main", "-m", "Merge branch 'main' into feature/noisy-history"], path
)

# 2. A branch intended for squashing (manual simulation)
run_git(["checkout", "main"], path)
run_git(["checkout", "-b", "feature/to-be-squashed"], path)
for i in range(3):
(path / f"part_{i}.txt").write_text(f"part {i}")
run_git(["add", f"part_{i}.txt"], path)
run_git(["commit", "-m", f"feat: part {i} of squashed work"], path)

# Simulate the squash commit on main (we won't link it here, highlighter does it via PR OIDs)
run_git(["checkout", "main"], path)
(path / "squashed_result.txt").write_text("all parts")
run_git(["add", "squashed_result.txt"], path)
run_git(["commit", "-m", "feat: implementing all parts (#123)"], path)


def main():
REPOS_DIR.mkdir(exist_ok=True)
ASSETS_DIR.mkdir(exist_ok=True)

generate_pristine()
generate_messy()
generate_features()
generate_risk_silo()
generate_squash_and_backmerge()

print("\nDone! Demo repositories created in examples/repos/")

Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,25 @@ authors = [
dependencies = [
"graphable>=0.6.0",
]
description = "A powerful tool to convert Git commit history into beautiful flowcharts."
description = "A powerful Git history visualizer and hygiene linter with CI gating."
keywords = [
"analysis",
"ci",
"d2",
"git",
"git-flow",
"github",
"graph",
"hygiene",
"lint",
"mermaid",
"topology",
"visualization",
]
name = "git-graphable"
readme = "README.md"
requires-python = ">=3.13"
version = "0.1.0"
version = "0.2.0"

[project.optional-dependencies]
cli = [
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.