Zum Inhalt springen
Daniel Gietmann
← Blog
Coding

Vergessene GitHub-Repos aufräumen, ohne dich blind auf die Treffer zu verlassen

gitsorted zeigt alle deine GitHub-Repos mit Anlage- und Push-Datum, findet mögliche Duplikate und vergessene Repos und lässt dich mehrere auf einmal archivieren oder löschen. Nach welchen Regeln es sortiert, wo die Duplikat-Suche danebenliegt und was die App dafür von dir bekommt.

Titelbild: Vergessene GitHub-Repos aufräumen, ohne dich blind auf die Treffer zu verlassen

Wer ein paar Jahre auf GitHub unterwegs ist, sammelt Repos an: Experimente aus einem Nachmittag, Kopien mit -old oder -v2 im Namen, Forks, an denen nie jemand gearbeitet hat. In der Repo-Liste von GitHub siehst du davon nur Namen und das Datum der letzten Änderung. Was davon weg kann, musst du dir selbst zusammensuchen.

Dafür habe ich gitsorted gebaut. Du meldest dich mit GitHub an und bekommst alle Repos, die dir gehören oder die du über eine Organisation siehst, in einer Tabelle: wann sie angelegt und zuletzt gepusht wurden, Größe und Sterne, dazu ein Badge, ob das Repo privat oder öffentlich ist. Daneben zeigt die App mögliche Duplikate und Aufräum-Kandidaten, und du kannst mehrere auf einmal archivieren oder löschen. Die gehostete Version läuft unter gitsorted.eu, der Code steht unter MIT-Lizenz auf GitHub.

Was als Kandidat gilt

Die Regeln stehen in src/lib/cleanup.ts und sind ziemlich schlicht:

  • Leer: GitHub meldet eine Größe von 0 KB.
  • Fast leer: höchstens 10 KB und keine erkannte Programmiersprache, also meist nur README oder Lizenz.
  • Einmal hochgeladen: Der letzte Push lag weniger als zehn Minuten nach dem Anlegen, und das Repo ist älter als 90 Tage.
  • Unberührter Fork: Der letzte Push lag weniger als eine Minute nach dem Anlegen.
  • Lange still: Der letzte Push ist mehr als 730 Tage her. Archivierte Repos fallen hier raus.

Das sind Hinweise und kein Urteil. Ein kleines Repo mit einer einzigen Konfigurationsdatei oder ein Fork, den du nur als Lesezeichen behalten willst, landet genauso auf der Liste wie echter Müll.

Wie die Duplikat-Suche Namen vergleicht

Für Duplikate macht gitsorted aus jedem Namen einen Vergleichsschlüssel. Großschreibung und Trennzeichen fliegen raus, dazu typische Kopie-Marker am Ende wie copy, old, backup, test, v2 oder angehängte Ziffern und am Anfang my, old, new, test oder tmp. Generierte IDs wie app-03912df4c6de und Zeitstempel werden ebenfalls abgeschnitten. Haben zwei Repos denselben Schlüssel oder unterscheiden sich die Schlüssel ab sechs Zeichen Länge um genau einen Buchstaben, landen sie in einer Gruppe. So findet die App weather-app und wheather-app. Als zweiter Weg zählen Repos mit derselben Beschreibung, wenn die mindestens zwölf Zeichen lang ist.

Diese Regeln greifen auch dort, wo gar keine Kopie vorliegt. Ein paar Beispiele, die sich direkt aus dem Code ergeben:

  • web1, web2 und web3 werden alle zu web und bilden eine Gruppe, auch wenn es drei verschiedene Projekte sind.
  • project-2024 und project-2025 werden beide zu project. Wer pro Jahr ein eigenes Repo anlegt, bekommt sie als Duplikat angezeigt.
  • contest verliert das test am Ende und wird zu con. Gibt es daneben ein Repo con, hängen beide zusammen.
  • docker-pg-backup wird zu dockerpg, weil backup als Kopie-Marker gilt.

Schau dir eine Gruppe deshalb immer an, bevor du daraus etwas löschst. Die App sortiert die Mitglieder nach letztem Push, das neueste steht oben. Welches davon das Original ist, sagt dir das nicht.

Archivieren oder löschen

Für beides wählst du die Repos in der Tabelle aus. Archivieren macht ein Repo schreibgeschützt, es bleibt aber vollständig erhalten, und du kannst die Archivierung später wieder aufheben. Beim Löschen musst du im Dialog das Wort löschen eintippen. Die App arbeitet die Repos nacheinander ab, weil GitHub viele Schreibaufrufe in kurzer Zeit mit Limits beantwortet, und meldet für jedes Repo, ob es geklappt hat.

Gelöscht ist bei GitHub nicht sofort endgültig. Laut GitHub-Dokumentation lässt sich ein gelöschtes Repo 90 Tage lang wiederherstellen, aber nicht, wenn es Teil eines Fork-Netzwerks war, in dem noch andere Repos stecken. Über gitsorted selbst holst du nichts zurück, das geht nur in den GitHub-Einstellungen. Wenn du dir bei einem Repo nicht sicher bist, archivier es lieber erst.

Bei Organisationen gibt es noch einen Haken. gitsorted fragt die Repos mit affiliation=owner,organization_member ab. Laut GitHub-API enthält das jedes Repo, auf das du über eine Organisation Zugriff hast, also auch Repos, an denen du nur mitarbeitest. Archivieren oder Löschen geht dort nur mit Admin-Rechten. Fehlen sie, scheitert die Aktion für dieses Repo mit einer Meldung, und die anderen laufen weiter.

Was die App von dir bekommt

Zum Anmelden fragt gitsorted die GitHub-Berechtigungen repo und delete_repo an. Ohne repo sieht die App keine privaten Repos und kann nichts archivieren, ohne delete_repo kann sie nichts löschen. Das ist viel Zugriff, und du solltest wissen, dass du ihn gibst. Entziehen kannst du ihn jederzeit unter GitHub → Settings → Applications.

Eine Nutzerdatenbank gibt es nicht. Die Sitzung und der GitHub-Token liegen verschlüsselt in HTTP-only-Cookies in deinem Browser, die Sitzung ist auf sieben Tage eingestellt. Repo-Daten holt die App bei Bedarf von GitHub und speichert sie nicht. Wenn eine MongoDB angeschlossen ist, zählt sie nur anonyme Summen für die Startseite: gelöschte und archivierte Repos, freigegebenen Speicher und die Zahl der Accounts. Für die Accounts wird ein HMAC der GitHub-Nutzer-ID gespeichert, keine Namen und keine Repo-Namen.

Selbst betreiben

Wenn du deinen Token keiner fremden Instanz geben willst, betreibst du gitsorted selbst. Du brauchst eine GitHub OAuth App mit der Callback-Adresse https://deine-domain/api/auth/callback/github. Lass dort die Option „Expire user access tokens“ ausgeschaltet, sonst kann die App erneuerte Tokens nicht zurück ins Cookie schreiben. Dann setzt du die Umgebungsvariablen und startest das Image:

docker build -t gitsorted .
docker run -p 3000:3000 --env-file .env gitsorted

Pflicht sind BETTER_AUTH_SECRET (mindestens 32 Zeichen, zum Beispiel aus openssl rand -base64 32), BETTER_AUTH_URL, GITHUB_CLIENT_ID und GITHUB_CLIENT_SECRET. MONGODB_URL ist optional und schaltet nur die Zahlen auf der Startseite ein. Laut README startet MongoDB 8 nicht auf Linux-Kerneln ab 6.19, dort nimmst du mongo:7. Mit LEGAL_NAME, LEGAL_ADDRESS und LEGAL_EMAIL bekommt deine Instanz ein Impressum und eine Datenschutzseite. Das Image ist ein Next.js-Server auf Port 3000 und läuft so auch auf Coolify.

Die Kandidaten und Duplikat-Gruppen folgen festen Regeln, und die erwischen auch Repos, die du behalten willst. Geh die Liste durch, archivier im Zweifel und lösch erst, wenn du dir sicher bist.