Git ist seit 2005 das weltweit dominierende Werkzeug für verteilte Versionsverwaltung. Geschrieben hat es Linus Torvalds, ursprünglich für die Linux-Kernel-Entwicklung; heute wird es weit über Quellcode hinaus eingesetzt. In unserem Alltag versionieren wir mit Git auch TEI-Editionsdaten, Server-Konfigurationen und strukturierte Markdown-Inhalte.

Verteilt heisst: jede Kopie ist vollständig

Anders als die alten zentralen Versionssysteme (CVS, Subversion) hat in Git jede:r Mitwirkende eine vollständige Kopie der gesamten Historie. Es gibt deshalb keinen «Master-Server», sondern nur Konventionen: Wir vereinbaren ein Repo (z. B. auf GitHub) als gemeinsamen Synchronisationspunkt, aber technisch sind alle Kopien gleichwertig. Das macht Branching billig, Offline-Arbeit selbstverständlich und vor allem die Historie selbst zur Datenstruktur — jeder Commit ist ein Snapshot, identifiziert durch einen kryptografischen Hash, verkettet mit seinen Vorgängern.

Die vier Datenspeicher

Lokal hält Git die Daten an drei Orten parallel — und tauscht sie mit einem (oder mehreren) entfernten Repos aus:

Diagramm: Working Tree, Stage, Local Repository und Remote Repository — mit den Befehlen add, commit, push, restore, fetch, pull, clone, die Daten zwischen diesen vier Stores bewegen.
  • Working Tree — die Dateien, mit denen du gerade arbeitest.
  • Stage (auch Index) — eine Zwischenstufe, in der man auswählt, welche Änderungen in den nächsten Commit gehen sollen.
  • Local Repo — die Historie aller Commits dieses Klons, gespeichert im .git-Ordner.
  • Remote Repo — eine andere Kopie, typischerweise auf einem Server (GitHub, GitLab, eigener Host).

Die Befehle in der Grafik sind die häufigsten Bewegungen zwischen diesen Stores. Wer das Modell einmal verinnerlicht hat, versteht alle weiteren Git-Befehle als Variationen davon.

Alltäglich gebrauchte Kommandos

git status -s                              # was hat sich geändert?
git diff                                   # unstaged Änderungen
git diff --staged                          # staged Änderungen
git add <datei>                            # zur nächsten Aufnahme markieren
git add -p                                 # interaktiv, hunk für hunk
git commit -m "Beschreibung"
git log --oneline --graph --decorate -20   # kompakte Historie
git push                                   # eigene Commits zum Remote
git pull --rebase                          # Remote-Stand übernehmen

Branches

Branches sind in Git billig — ein Branch ist nicht mehr als ein Pointer auf einen Commit. Daher: lieber häufiger neue Branches anlegen als versuchen, alles im Hauptbranch zu jonglieren.

git switch -c feature/abc                  # Branch erstellen + wechseln
git switch main                            # zurück
git merge feature/abc                      # zurückführen
git branch -d feature/abc                  # nach Merge aufräumen

git switch und git restore (seit 2.23) sind die modernen Alternativen zum überladenen git checkout.

Rückgängig machen

Drei Mechanismen mit unterschiedlicher Tragweite:

  • git restore <datei> — Datei aus dem Working Tree zurücksetzen (Änderungen verwerfen).
  • git reset --soft HEAD~1 — letzten Commit aus dem Branch nehmen, Änderungen behalten. Nur lokal!
  • git revert <commit> — sicherer Weg für Geschichte, die schon publiziert ist: ein neuer Commit, der den alten rückwärts anwendet.

Faustregel: reset ist destruktiv für nicht-publizierte Arbeit; revert ist die sichere Variante für Geschichte, die schon hochgeladen ist.

Stash — Arbeit zwischenparken

Wenn du mitten in einer Änderung schnell etwas anderes machen musst:

git stash                                  # alles wegpacken (inkl. -u für untracked)
git switch andere-aufgabe

git switch zurück
git stash pop                              # zurückholen

Konfiguration, die viel Ärger spart

git config --global init.defaultBranch main
git config --global pull.rebase true               # statt Merge-Commits beim Pull
git config --global push.autoSetupRemote true      # neuer Branch wird beim ersten Push gleich getracked
git config --global rerere.enabled true            # Konflikt-Lösungen merken

.gitignore-Pattern

Eine kleine Falle, die uns selbst kürzlich erwischt hat: Pattern ohne führenden / matchen überall im Repo. wp-content/ ignoriert also auch site/public/wp-content/. Ausnahmen mit ! regeln:

wp-content/
!site/public/wp-content/