git branch --merged main | grep -vE '^\*|main|develop' | xargs -r git branch -d löscht jeden lokalen Branch, der bereits in main gemergt ist, außer main und develop. Squash-gemergte Branches rührt der Befehl nicht an: git zeichnet ihre Commits nie als gemergt auf, also bleibt --merged für sie blind — prüfe stattdessen git branch -vv auf die Markierung gone.
1. Erst sehen, was als gemergt gilt
$ clai zeig mir die Branches, die in main gemergt sind→ git branch --merged main feature-merged* main
Das ist eine reine Leseliste. Der aktuelle Branch ist mit einem Sternchen markiert und kann nicht gelöscht werden — muss er auch nicht.
2. Löschen, aber die Hauptbranches schützen
$ clai lösche lokale Branches, die in main gemergt sind, außer main und develop→ git branch --merged main | grep -vE '^\*|main|develop' | xargs -r git branch -dDeleted branch feature-merged (was fb52fec).
-d ist sicher: git weigert sich, einen Branch mit nicht gemergten Commits zu löschen. Der Filter grep -vE ist notwendig, sonst landet main selbst in der Liste. -r verhindert, dass xargs bei einer leeren Liste überhaupt läuft.
3. Prüfen, dass nichts Wichtiges gelöscht wurde
$ clai zeig mir die verbleibenden Branches→ git branch feature-open* main
feature-open hat noch nicht gemergte Commits und blieb unangetastet. Das ist die Bestätigung, dass der Filter richtig funktioniert hat.
4. Die Falle: Squash Merge versteckt sich vor --merged
$ clai zeig mir Branches, deren Upstream auf dem Server gelöscht wurde→ git fetch --prune && git branch -vv | grep '\[.*: gone\]'
Das ist der entscheidende Teil. Ein Squash Merge bringt die Commits des Branches nie unverändert in die Historie von main, deshalb zeigt --merged ihn nie an — er bleibt für immer auf deiner Maschine. Aber sein Upstream auf dem Server verschwindet, sobald der Pull Request gemergt wird, und git branch -vv markiert diesen Branch als gone. Das ist das verlässliche Zeichen.
5. Branches mit verschwundenem Upstream löschen
$ clai lösche lokale Branches, deren Quell-Branch auf dem Server gelöscht wurde→ git fetch --prune && git branch --format '%(refname:short) %(upstream:track)' | awk '$2 == "[gone]" {print $1}' | xargs -r git branch -D⚠ CAUTION — this is -D, not -d: it also deletes unmerged branches
-D ist genau deshalb nötig, weil git Squash-gemergte Branches nicht als gemergt betrachtet und sich bei -d weigert. Der Preis dafür: Lies die Liste aus dem vorherigen Befehl, bevor du diesen ausführst.
6. Veraltete Remote-Referenzen aufräumen
$ clai entferne Referenzen auf Branches, die es auf dem Server nicht mehr gibt→ git remote prune origin
Das entfernt nur lokale origin/*-Referenzen — deine Branches bleiben unberührt. Praktisch, wenn die Autovervollständigung ständig Branches vorschlägt, die vor einem halben Jahr geschlossen wurden.
Fallstricke
-dund-Dhaben unterschiedliches Risiko. Klein löscht nur Gemergtes, groß löscht alles. Squash-gemergte Branches brauchen das große D, also erst die Liste, dann löschen.--mergedohne Argument zählt ab dem aktuellen Branch. Auf einem Feature-Branch bekommst du eine völlig andere Liste. Gib die Basis explizit an:--merged main.- Ein gelöschter lokaler Branch verschwindet nicht auf dem Server. Dafür braucht es
git push origin --delete <name>. Und umgekehrt: Löschen auf dem Server entfernt nicht deine lokale Kopie.
Verwandte Fragen
Wie stelle ich einen versehentlich gelöschten Branch wieder her? git reflog zeigt seinen letzten Commit; dann git branch <name> <hash>. Funktioniert, bis die Garbage Collection läuft.
Warum lässt sich ein Branch nicht löschen? Entweder du stehst gerade auf ihm — wechsle zu main —, oder er hat nicht gemergte Commits und -d schützt dich vor Verlust.
Wie sehe ich, wann ein Branch zuletzt aktualisiert wurde? git branch --sort=-committerdate -v sortiert nach Aktualität — praktisch, um verwaiste Branches zu finden.
Siehe auch
- Letzten Git-Commit rückgängig machen, Änderungen behalten
- Ein Secret aus der Git-Historie entfernen
- Git-Aufgaben, laut ausgesprochen
CliAI verwandelt die Formulierungen oben in den exakten Befehl und zeigt ihn, bevor irgendetwas läuft — der -D-Schritt bekommt ein CAUTION-Label statt eines stillen Laufs. In einer Zeile installieren.