diff --git a/.github/workflows/fame.yml b/.github/workflows/fame.yml
index 6d72c66f5..917cb7407 100644
--- a/.github/workflows/fame.yml
+++ b/.github/workflows/fame.yml
@@ -1,4 +1,5 @@
-# Refresh FAME.md, the contributor summary generated by scripts/fame.py.
+# Refresh FAME.md and FAME.svg, the contributor summary generated by
+# scripts/fame.py.
#
# The script needs the full history (it uses `git blame`), so this checks out
# with fetch-depth 0 and it always looks at master, no matter what triggered it.
@@ -6,14 +7,22 @@
# quiet week does not produce a pull request that only bumps the date.
#
# The pull request is created with GITHUB_TOKEN, so CI does not run on it - that
-# is fine for a generated markdown file, but keep it in mind if this workflow
-# ever starts touching code.
+# is fine for generated files, but keep it in mind if this workflow ever starts
+# touching code.
+#
+# Merging such a pull request pushes FAME.md and triggers this workflow again;
+# that run regenerates the same files, finds no diff and opens nothing, so it
+# just costs a minute of runner time.
name: FAME
on:
schedule:
- cron: '17 4 * * 1' # Mondays at 04:17 UTC
+ push:
+ branches: [ master ]
+ paths:
+ - 'FAME.md' # keep the chart in sync when the table changes
workflow_dispatch: # Allow manual trigger
concurrency:
@@ -51,13 +60,15 @@ jobs:
python -m pip install --upgrade pip
pip install 'git-fame ~= 3.1'
- - name: Regenerate FAME.md
+ - name: Regenerate FAME.md and FAME.svg
run: python scripts/fame.py
- name: Create pull request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
- add-paths: FAME.md
+ add-paths: |
+ FAME.md
+ FAME.svg
branch: auto/update-fame
delete-branch: true
commit-message: "FAME.md: update contributor statistics"
diff --git a/FAME.md b/FAME.md
index b810dc903..2492e98e2 100644
--- a/FAME.md
+++ b/FAME.md
@@ -1,9 +1,11 @@
# Contributors
-370 people have contributed to Borg, with 10,581 commits in total.
+370 people have contributed to Borg, with 10,585 commits in total.
Thanks to everyone who helped!
-Generated on 2026-08-01 by `scripts/fame.py`, which computes the statistics with
+
+
+Generated on 2026-08-02 by `scripts/fame.py`, which computes the statistics with
[git-fame](https://github.com/casperdcl/git-fame), from the `master` branch
only - commits that exist solely on other branches or in unmerged pull requests
are not counted.
@@ -19,64 +21,64 @@ show up there, so the number understates early contributions. Generated files
| Contributor | Commits | Lines | Files |
|:---|---:|---:|---:|
-| Thomas Waldmann | 7,020 | 73,353 | 451 |
-| Marian Beermann | 1,140 | 9,016 | 158 |
-| Jonas Borgström | 560 | 1,080 | 38 |
-| Antoine Beaupré | 285 | 456 | 35 |
+| Thomas Waldmann | 7,024 | 75,119 | 454 |
+| Marian Beermann | 1,140 | 8,400 | 157 |
+| Jonas Borgström | 560 | 1,029 | 38 |
+| Antoine Beaupré | 285 | 441 | 33 |
| Mrityunjay Raj | 198 | 7,988 | 102 |
-| Andrey Bienkowski | 72 | 243 | 17 |
+| Andrey Bienkowski | 72 | 210 | 16 |
| Ted Lawson | 71 | 4,606 | 39 |
-| Thalian | 63 | 771 | 33 |
-| Martin Hostettler | 46 | 2,589 | 10 |
-| Milkey Mouse | 42 | 253 | 14 |
+| Thalian | 63 | 594 | 32 |
+| Martin Hostettler | 46 | 2,559 | 9 |
+| Milkey Mouse | 42 | 248 | 14 |
| Dan Christensen | 40 | 5 | 2 |
| dependabot[bot] | 37 | 46 | 7 |
| Rayyan Ansari | 34 | 91 | 12 |
| Abdel-Rahman | 30 | 4 | 3 |
-| Manuel Riel | 28 | 107 | 7 |
-| Nehalenniæ Oudin | 26 | 55 | 11 |
+| Manuel Riel | 28 | 94 | 6 |
+| Nehalenniæ Oudin | 26 | 49 | 11 |
| anarcat | 24 | 34 | 4 |
| Hugo Wallenburg | 23 | 1,189 | 13 |
-| Michael Hanselmann | 22 | 151 | 3 |
+| Michael Hanselmann | 22 | 71 | 3 |
| Björn Ketelaars | 17 | 33 | 6 |
| SanskritFritz | 17 | 258 | 1 |
-| Lee Bousfield | 14 | 169 | 10 |
-| Alan Jenkins | 14 | 47 | 2 |
+| Lee Bousfield | 14 | 160 | 9 |
+| Alan Jenkins | 14 | 38 | 2 |
| Robin Schneider | 13 | 19 | 8 |
| Daniel Rudolf | 13 | 230 | 14 |
-| Jürg Rast | 12 | 78 | 11 |
-| Elmar Hoffmann | 12 | 98 | 9 |
-| Ronny Pfannschmidt | 12 | 22 | 4 |
+| Jürg Rast | 12 | 73 | 11 |
+| Elmar Hoffmann | 12 | 90 | 6 |
+| Ronny Pfannschmidt | 12 | 20 | 4 |
| mh4ckt3mh4ckt1c4s | 11 | 0 | 0 |
| Teemu Toivanen | 10 | 16 | 2 |
| Lauri Niskanen | 10 | 5 | 2 |
| finefoot | 10 | 18 | 3 |
-| James Buren | 10 | 10 | 2 |
+| James Buren | 10 | 2 | 2 |
| Per Guth | 9 | 1 | 1 |
| rugk | 9 | 13 | 5 |
-| Ed Blackman | 9 | 14 | 3 |
+| Ed Blackman | 9 | 13 | 3 |
| Carlo Teubner | 9 | 3 | 3 |
-| Emmo Emminghaus | 9 | 87 | 11 |
+| Emmo Emminghaus | 9 | 80 | 10 |
| Josh Soref | 8 | 3 | 3 |
| Jakob Schnitzer | 8 | 35 | 1 |
| Gianfranco Costamagna | 7 | 10 | 6 |
-| Frank Sachsenheim | 7 | 3 | 1 |
+| Frank Sachsenheim | 7 | 2 | 1 |
| Hartmut Goebel | 7 | 0 | 0 |
| Michael Deyaso | 6 | 170 | 14 |
| Alf Mikula | 6 | 19 | 1 |
| William D. Jones | 6 | 250 | 6 |
-| Felix Schwarz | 6 | 5 | 2 |
-| Peter Gerber | 6 | 105 | 10 |
+| Felix Schwarz | 6 | 4 | 2 |
+| Peter Gerber | 6 | 103 | 9 |
| Alexander-N | 6 | 0 | 0 |
| Paul D | 5 | 185 | 46 |
-| Mark Edgington | 5 | 11 | 3 |
+| Mark Edgington | 5 | 10 | 3 |
| Dominik Stadler | 5 | 77 | 1 |
| infectormp | 5 | 18 | 2 |
| remyabel | 5 | 25 | 3 |
-| Alexander 'Leo' Bergolth | 5 | 26 | 3 |
-| Simon Frei | 5 | 213 | 3 |
+| Alexander 'Leo' Bergolth | 5 | 24 | 3 |
+| Simon Frei | 5 | 174 | 3 |
| elandorr | 5 | 40 | 3 |
-| step21 | 5 | 2 | 2 |
+| step21 | 5 | 1 | 1 |
| Radu Ciorba | 5 | 0 | 0 |
| Suryansh Pal | 4 | 36 | 9 |
| 8bit | 4 | 30 | 5 |
@@ -92,7 +94,7 @@ show up there, so the number understates early contributions. Generated files
| Artem Sheremet | 4 | 10 | 1 |
| Narendra Vardi | 4 | 3 | 1 |
| Alexander Pyhalov | 4 | 0 | 0 |
-| Will | 3 | 22 | 4 |
+| Will | 3 | 21 | 4 |
| Lukas Fleischer | 3 | 23 | 2 |
| Daniel Reichelt | 3 | 25 | 5 |
| Stephen | 3 | 10 | 2 |
@@ -106,7 +108,7 @@ show up there, so the number understates early contributions. Generated files
| Michael Gajda | 3 | 23 | 2 |
| jungle-boogie | 3 | 13 | 1 |
| Thomas Harold | 3 | 1 | 1 |
-| Gregor Kleen | 3 | 10 | 4 |
+| Gregor Kleen | 3 | 8 | 3 |
| kmq | 3 | 15 | 1 |
| Luke Dashjr | 3 | 4 | 1 |
| Divyesh | 3 | 1 | 1 |
@@ -134,7 +136,7 @@ show up there, so the number understates early contributions. Generated files
| Ioannis Cherouvim | 2 | 32 | 2 |
| AJ Jordan | 2 | 2 | 1 |
| Markus Engelbrecht | 2 | 3 | 1 |
-| Thomas Portmann | 2 | 257 | 5 |
+| Thomas Portmann | 2 | 206 | 5 |
| Leo Famulari | 2 | 1 | 1 |
| Fabian Fröhlich | 2 | 4 | 1 |
| Andreas Gruhler | 2 | 3 | 1 |
@@ -143,7 +145,7 @@ show up there, so the number understates early contributions. Generated files
| Kurt Yoder | 2 | 8 | 1 |
| Jonas Schäfer | 2 | 13 | 1 |
| a1346054 | 2 | 4 | 3 |
-| Sophie Herold | 2 | 3 | 2 |
+| Sophie Herold | 2 | 2 | 2 |
| ReethuVinta | 2 | 20 | 2 |
| textshell | 2 | 1 | 1 |
| Saurav Sachidanand | 2 | 2 | 1 |
@@ -153,7 +155,7 @@ show up there, so the number understates early contributions. Generated files
| Matthew Glazar | 2 | 18 | 3 |
| Max-Julian Pogner | 2 | 15 | 1 |
| Atharva Varpe | 2 | 23 | 1 |
-| Lapinot | 2 | 30 | 4 |
+| Lapinot | 2 | 24 | 4 |
| Soumik Dutta | 2 | 85 | 3 |
| Eric Wolf | 2 | 51 | 4 |
| snsmac | 2 | 6 | 2 |
@@ -192,9 +194,9 @@ show up there, so the number understates early contributions. Generated files
| Daniel Dent | 1 | 2 | 1 |
| Lars K.W. Gohlke | 1 | 1 | 1 |
| Kevin Yin | 1 | 3 | 1 |
-| Benedikt Seidl | 1 | 117 | 2 |
| Eddie Carswell | 1 | 4 | 1 |
| vancheese | 1 | 28 | 8 |
+| Benedikt Seidl | 1 | 116 | 1 |
| Michael Bauer | 1 | 2 | 1 |
| Aleksey Korol | 1 | 95 | 1 |
| Coenraad Loubser | 1 | 1 | 1 |
@@ -236,7 +238,7 @@ show up there, so the number understates early contributions. Generated files
| Stefan Majewsky | 1 | 1 | 1 |
| Atemu | 1 | 11 | 2 |
| Dominik Tugend | 1 | 3 | 1 |
-| Reiko Asakura | 1 | 12 | 2 |
+| Reiko Asakura | 1 | 11 | 2 |
| Mher Kazandjian | 1 | 6 | 1 |
| Mike | 1 | 23 | 1 |
| Ceesjan Luiten | 1 | 1 | 1 |
@@ -247,9 +249,8 @@ show up there, so the number understates early contributions. Generated files
| Jim Paris | 1 | 2 | 1 |
| vhadzhiev | 1 | 2 | 1 |
| Tarrailt | 1 | 371 | 8 |
-| Romain Vimont | 1 | 4 | 3 |
-| Sam H | 1 | 27 | 2 |
-| Leo Antunes | 1 | 8 | 1 |
+| Sam H | 1 | 24 | 2 |
+| Leo Antunes | 1 | 5 | 1 |
| Matt Van Horn | 1 | 1 | 1 |
| Mike Mason | 1 | 38 | 3 |
| edvatar | 1 | 16 | 2 |
@@ -258,18 +259,16 @@ show up there, so the number understates early contributions. Generated files
| nyuszika7h | 1 | 3 | 1 |
| Divyansh Singh | 1 | 1 | 1 |
| Phil Kulin | 1 | 4 | 1 |
-| Félix Sipma | 1 | 14 | 1 |
-| Graham Stockton | 1 | 13 | 1 |
+| Félix Sipma | 1 | 13 | 1 |
+| Graham Stockton | 1 | 7 | 1 |
| Peter Zangerl | 1 | 1 | 1 |
| lexa-a | 1 | 31 | 1 |
| Nikolaus Rath | 1 | 7 | 1 |
-| axapaxa | 1 | 117 | 2 |
-| Swanand01 | 1 | 1 | 1 |
+| axapaxa | 1 | 105 | 2 |
| Abhijeet | 1 | 1 | 1 |
-| Dark Dragon | 1 | 7 | 1 |
-| Emil M George | 1 | 2 | 1 |
+| Dark Dragon | 1 | 6 | 1 |
+| Emil M George | 1 | 1 | 1 |
| Hauke Rehfeld | 1 | 1 | 1 |
-| Henry-Joseph Audéoud | 1 | 1 | 1 |
| Ivan Shapovalov | 1 | 1 | 1 |
| Zhou Qiankang | 1 | 3 | 1 |
| Adam Kouse | 1 | 0 | 0 |
@@ -304,6 +303,7 @@ show up there, so the number understates early contributions. Generated files
| Flachz | 1 | 0 | 0 |
| Florian Klink | 1 | 0 | 0 |
| Hans-Peter Jansen | 1 | 0 | 0 |
+| Henry-Joseph Audéoud | 1 | 0 | 0 |
| Jakub Wilk | 1 | 0 | 0 |
| Jan | 1 | 0 | 0 |
| Jens Rantil | 1 | 0 | 0 |
@@ -345,9 +345,11 @@ show up there, so the number understates early contributions. Generated files
| RR | 1 | 0 | 0 |
| Ricardo M. Correia | 1 | 0 | 0 |
| Roberto Polli | 1 | 0 | 0 |
+| Romain Vimont | 1 | 0 | 0 |
| Ruben Rodriguez | 1 | 0 | 0 |
| Sebastiaan Lokhorst | 1 | 0 | 0 |
| Serghei Iakovlev | 1 | 0 | 0 |
+| Swanand01 | 1 | 0 | 0 |
| Thiago Macieira | 1 | 0 | 0 |
| Thomas Deutschmann | 1 | 0 | 0 |
| Thomas Schwinge | 1 | 0 | 0 |
diff --git a/FAME.svg b/FAME.svg
new file mode 100644
index 000000000..08a108e82
--- /dev/null
+++ b/FAME.svg
@@ -0,0 +1,81 @@
+
diff --git a/scripts/fame.py b/scripts/fame.py
index 3a18a2b91..e7ffa282c 100644
--- a/scripts/fame.py
+++ b/scripts/fame.py
@@ -1,7 +1,13 @@
#!/usr/bin/env python3
-"""Regenerate FAME.md, a contributor summary produced by git-fame.
+"""Regenerate FAME.md and FAME.svg, a contributor summary produced by git-fame.
-Usage: python scripts/fame.py [--top N]
+FAME.md is the full table, FAME.svg a bar chart of the busiest contributors that
+FAME.md embeds. The chart is drawn here rather than taken from git-fame's own
+`--format=svg`, which renders the whole table as monospaced text - fine for a
+handful of contributors, unreadable for a few hundred. It leaves out bots,
+which do appear in the table.
+
+Usage: python scripts/fame.py [--top N] [--svg-top N]
Requires `git-fame` (pip install git-fame) and a full (non-shallow) clone,
because the line counts come from `git blame` over the whole history.
@@ -17,9 +23,9 @@ later refactored away, so it is not what we rank by.
Author identities are merged via .mailmap, so fix duplicates there rather than
here.
-FAME.md is left alone if only the generation date would change, so that running
-this regularly (see .github/workflows/fame.yml) does not produce a diff for a
-week in which the statistics did not move.
+Both files are left alone if nothing but the generation date would change, so
+that running this regularly (see .github/workflows/fame.yml) does not produce a
+diff for a week in which the statistics did not move.
"""
import argparse
@@ -28,11 +34,18 @@ import re
import subprocess
from datetime import date
from pathlib import Path
+from xml.sax.saxutils import escape
BRANCH = "master"
DATE_LINE = re.compile(r"^Generated on \d{4}-\d{2}-\d{2} ", re.MULTILINE)
+# Bots keep their row in the table - they did make those commits - but they
+# would take a slot away from a person in the chart, which only has room for a
+# few. GitHub appends "[bot]" to the account name of a bot, e.g.
+# "dependabot[bot]"; matching that is safer than guessing from the name.
+BOT = re.compile(r"\[bot\]$")
+
# Generated and binary-ish files would drown out hand-written code. Excalidraw
# scenes are the worst offenders: they are pretty-printed JSON, so a single
# diagram of a few boxes and arrows is over 10000 lines. Image formats are
@@ -48,6 +61,8 @@ PREAMBLE = """\
{count} people have contributed to Borg, with {commits} commits in total.
Thanks to everyone who helped!
+
+
Generated on {date} by `scripts/fame.py`, which computes the statistics with
[git-fame](https://github.com/casperdcl/git-fame), from the `{branch}` branch
only - commits that exist solely on other branches or in unmerged pull requests
@@ -65,6 +80,39 @@ show up there, so the number understates early contributions. Generated files
"""
+# Chart geometry, in user units. The viewport is given in user units plus a
+# viewBox rather than in em, because em on the root