• Kernbegriffe & Commit-Regeln
  • Branches im Griff (Merge vs. Rebase)
  • Rollbacks & Erste Hilfe bei den häufigsten Notfällen

Begriffe in je einem Satz

RepositoryDer Projektordner samt vollständiger Historie im Unterordner .git.
Working TreeDie Dateien, wie sie gerade auf der Platte liegen.
Staging / IndexDie Vorstufe: was beim nächsten commit mitkommt. git add legt hier ab.
CommitEin eingefrorener Stand mit Autor, Datum und Nachricht. Unveränderlich, identifiziert durch seinen Hash.
HEADZeiger auf den Commit, auf dem du gerade stehst — meist die Spitze des aktuellen Branches.
BranchEin wandernder Zeiger auf einen Commit. Billig, schnell, jederzeit wegwerfbar.
Remote / originEin anderes Repository, meist auf GitHub. origin ist der übliche Name dafür.
TagFester Name für einen Commit, typischerweise eine Release-Nummer wie v5.3.57.
MergeZwei Entwicklungslinien zusammenführen. Die Historie beider bleibt sichtbar.
RebaseDie eigenen Commits auf einen neuen Ausgangspunkt umhängen. Erzeugt neue Commits.
StashZwischenablage für unfertige Änderungen, wenn der Branch gewechselt werden muss.
ReflogProtokoll 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/
.env

Eingecheckt 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: #534

Die 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

mainImmer 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 -p

git 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 main

Status, 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-only

git 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

SituationWas du nimmst
Die Commits sind schon gepusht und jemand anders hat siemerge — immer
Der Branch liegt nur bei dir, nichts ist geteiltrebase — saubere, gerade Historie
Fertiges Feature nach main übernehmenmerge --no-ff — der Merge-Commit zeigt, was zusammengehörte
Neue main-Änderungen ins laufende Feature holenrebase, 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/main

Der 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

SituationBefehl
Commit ist schon gepushtgit revert <sha>
Nur lokal, Änderungen im Editor behaltengit reset --soft HEAD~1
Nur lokal, Änderungen behalten, Staging leerengit reset HEAD~1
Nur lokal, alles verwerfengit reset --hard HEAD~1
Eine einzelne Datei zurücksetzengit restore <datei>
Eine Datei aus dem Staging nehmengit 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

SzenarioBefehl
Falsche Commit-Message, noch nicht gepushtgit commit --amend
Datei im letzten Commit vergessengit add <datei>
git commit --amend --no-edit
Im falschen Branch gearbeitetgit stash
git switch <richtiger>
git stash pop
Einen fremden Commit hierüber holengit cherry-pick <sha>
Datei nachträglich aus der Versionierung nehmengit rm --cached <datei> + .gitignore
Alle lokalen Änderungen verwerfengit restore .
Was habe ich da gerade getan?git reflog
Versehentlich gelöschten Branch zurückholengit branch <name> <sha aus reflog>
Push nötig, aber Historie hat sich geändertgit 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 --abort

Konflikt 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