Git-Snippets für TYPO3-Projekte
- Kernbegriffe & Commit-Regeln
- Branches im Griff (Merge vs. Rebase)
Rollbacks & Erste Hilfe bei den häufigsten Notfällen
Begriffe in je einem Satz
| Repository | Der Projektordner samt vollständiger Historie im Unterordner .git. |
| Working Tree | Die Dateien, wie sie gerade auf der Platte liegen. |
| Staging / Index | Die Vorstufe: was beim nächsten commit mitkommt. git add legt hier ab. |
| Commit | Ein eingefrorener Stand mit Autor, Datum und Nachricht. Unveränderlich, identifiziert durch seinen Hash. |
| HEAD | Zeiger auf den Commit, auf dem du gerade stehst — meist die Spitze des aktuellen Branches. |
| Branch | Ein wandernder Zeiger auf einen Commit. Billig, schnell, jederzeit wegwerfbar. |
| Remote / origin | Ein anderes Repository, meist auf GitHub. origin ist der übliche Name dafür. |
| Tag | Fester Name für einen Commit, typischerweise eine Release-Nummer wie v5.3.57. |
| Merge | Zwei Entwicklungslinien zusammenführen. Die Historie beider bleibt sichtbar. |
| Rebase | Die eigenen Commits auf einen neuen Ausgangspunkt umhängen. Erzeugt neue Commits. |
| Stash | Zwischenablage für unfertige Änderungen, wenn der Branch gewechselt werden muss. |
| Reflog | Protokoll aller HEAD-Bewegungen der letzten Wochen. Der Rettungsanker. |
Was in ein TYPO3-Repository gehört — und was nicht
Der wichtigste Punkt überhaupt, weil ein falsch eingecheckter Ordner das Repository dauerhaft aufbläht und Zugangsdaten nicht wieder aus der Historie verschwinden.
# .gitignore einer Composer-Installation
/vendor/
/var/
/public/index.php
/public/typo3
/public/_assets/
/public/fileadmin/
/public/uploads/
/public/typo3temp/
.envEingecheckt wird:composer.json und composer.lock, config/ mit Site-Konfiguration und system/settings.php, das eigene Sitepackage unter packages/, die .gitignore selbst und die .ddev/-Konfiguration.
Zugangsdaten gehören in .env, nicht in die Historie. Einmal committed, bleibt ein Passwort in jedem Klon — auch wenn du es im nächsten Commit entfernst. Dann hilft nur noch: Passwort ändern.
Commits: Format und Regeln
Betreffzeile höchstens 50 Zeichen, im Imperativ, ohne Punkt am Ende. Dann eine Leerzeile, dann der Flißtext mit höchstens 72 Zeichen je Zeile — und der beantwortet warum, nicht was. Das Was steht im Diff.
[BUGFIX] Hoehe des Background-Wrappers bei disableInlineJs
Die Zusatzhoehe wurde als Inline-Script-Fragment registriert, das erst
durch den Asset-Renderer gueltiges JavaScript wurde. Mit disableInlineJs
steigt dieser aus, das Fragment landet roh in der Seite und wirft einen
Syntaxfehler.
Die Hoehe reist jetzt als data-addheight am Wrapper mit.
Resolves: #534Die Präfixe stammen aus dem TYPO3-Core und lassen die Historie auf einen Blick lesen:
[FEATURE] | Neue Funktion. |
[BUGFIX] | Fehler behoben. |
[TASK] | Aufräumen, Umbau, alles ohne sichtbare Änderung. |
[DOCS] | Nur Dokumentation. |
[!!!] | Breaking Change — wird dem eigentlichen Präfix vorangestellt. |
Wann committen: Ein Gedanke, ein Commit. Sobald ein Stand für sich allein Sinn ergibt und läuft. Immer vor einem Branch-Wechsel.
Was nicht in einen Commit gehört: mehrere Themen gleichzeitig, auskommentierter Code, Debug-Ausgaben, generierte Dateien, Zugangsdaten — und Nachrichten wie „wip“ oder „Fix“, die in sechs Monaten niemandem mehr etwas sagen.
Branch-Konzept
main | Immer deploybar. Hier wird nicht entwickelt, hier wird gemerged. |
feature/… | Eine Aufgabe, ein Branch, z. B. feature/534-wrapper-hoehe. Nach dem Merge gelöscht. |
hotfix/… | Dringender Fix direkt auf Basis von main, wenn ein Feature-Branch zu lange bräuchte. |
# Branch anlegen und hinwechseln
git switch -c feature/534-wrapper-hoehe
# zwischen Branches wechseln
git switch main
# erstmals hochladen und Verbindung merken
git push -u origin feature/534-wrapper-hoehe
# alle Branches mit ihrem Remote-Stand
git branch -vv
# gemergten Branch loeschen, lokal und remote
git branch -d feature/534-wrapper-hoehe
git push origin --delete feature/534-wrapper-hoehe
# geloeschte Remote-Branches auch lokal aufraeumen
git fetch -pgit switch und git restore haben git checkout abgelöst. checkout funktioniert weiter, machte aber drei verschiedene Dinge — daher die Trennung.
Repository aufsetzen
git init
git add .
git commit -m "[TASK] Initial commit"
git remote add origin git@github.com:t3solution/projekt.git
git push -u origin mainStatus, Historie, Remote
# kompakter Status mit Branch-Zeile
git status -sb
# was ist geaendert, was ist vorgemerkt
git diff
git diff --staged
# Historie als Graph, eine Zeile je Commit
git log --oneline --graph --decorate --all
# Historie einer einzelnen Datei, mit Diffs
git log -p Classes/Utility/BackgroundImageUtility.php
# wer hat welche Zeile geschrieben
git blame Classes/DataProcessing/ConfigProcessor.php
# Remote-Stand holen, ohne etwas zu veraendern
git fetch
# holen und einarbeiten, aber nur wenn es ohne Merge-Commit geht
git pull --ff-onlygit pull --ff-only statt git pull: Es bricht lieber ab, als dir ungefragt einen Merge-Commit in die Historie zu setzen. Dann entscheidest du bewusst zwischen Merge und Rebase.
Merge oder Rebase — Entscheidungsbaum
| Situation | Was du nimmst |
|---|---|
| Die Commits sind schon gepusht und jemand anders hat sie | merge — immer |
| Der Branch liegt nur bei dir, nichts ist geteilt | rebase — saubere, gerade Historie |
Fertiges Feature nach main übernehmen | merge --no-ff — der Merge-Commit zeigt, was zusammengehörte |
Neue main-Änderungen ins laufende Feature holen | rebase, solange der Branch nicht geteilt ist — sonst merge |
# Feature in main uebernehmen
git switch main
git pull --ff-only
git merge --no-ff feature/534-wrapper-hoehe
# main-Stand ins Feature holen
git switch feature/534-wrapper-hoehe
git fetch
git rebase origin/mainDer Unterschied in einem Satz:merge schreibt einen neuen Commit, der beide Linien verbindet und die Historie so lässt, wie sie war. rebase nimmt deine Commits, hängt sie hinter den neuen Stand und erzeugt sie dabei neu — mit neuen Hashes.
Die eiserne Regel: Schreibe nie Commits um, die schon jemand anders hat. Rebase, commit --amend und reset sind für das, was nur bei dir liegt. Sobald etwas gepusht ist, wird korrigiert statt umgeschrieben.
Rollback — revert oder reset
| Situation | Befehl |
|---|---|
| Commit ist schon gepusht | git revert <sha> |
| Nur lokal, Änderungen im Editor behalten | git reset --soft HEAD~1 |
| Nur lokal, Änderungen behalten, Staging leeren | git reset HEAD~1 |
| Nur lokal, alles verwerfen | git reset --hard HEAD~1 |
| Eine einzelne Datei zurücksetzen | git restore <datei> |
| Eine Datei aus dem Staging nehmen | git restore --staged <datei> |
revert löscht nichts, sondern schreibt einen neuen Commit, der die Änderung rückgängig macht. Genau deshalb ist er der richtige Weg für alles, was schon veröffentlicht ist.
Quick Reference
| Szenario | Befehl |
|---|---|
| Falsche Commit-Message, noch nicht gepusht | git commit --amend |
| Datei im letzten Commit vergessen | git add <datei>git commit --amend --no-edit |
| Im falschen Branch gearbeitet | git stashgit switch <richtiger>git stash pop |
| Einen fremden Commit hierüber holen | git cherry-pick <sha> |
| Datei nachträglich aus der Versionierung nehmen | git rm --cached <datei> + .gitignore |
| Alle lokalen Änderungen verwerfen | git restore . |
| Was habe ich da gerade getan? | git reflog |
| Versehentlich gelöschten Branch zurückholen | git branch <name> <sha aus reflog> |
| Push nötig, aber Historie hat sich geändert | git push --force-with-lease |
--force-with-lease statt --force: Es bricht ab, wenn in der Zwischenzeit jemand anders gepusht hat, statt dessen Arbeit zu überschreiben.
Merge-Konflikte lösen
# welche Dateien betroffen sind
git status
# Datei oeffnen, die Marker aufloesen:
# <<<<<<< HEAD dein Stand
# ======= Trennlinie
# >>>>>>> origin/main der andere Stand
git add <datei>
git commit # beim Merge
git rebase --continue # beim Rebase
# doch lieber zurueck auf Anfang
git merge --abort
git rebase --abortKonflikt in composer.lock: nicht von Hand auflösen. Erst den Konflikt in composer.json klären, dann die Lock-Datei neu erzeugen lassen:
git checkout --ours composer.lock
composer update --lock
git add composer.json composer.lock