# Repository, Commit und Branch

## Drei Grundbegriffe für die tägliche Git-Arbeit

Fast jede Git-Aktion dreht sich um drei Begriffe:

- **Repository**: der verwaltete Projektbestand inklusive Geschichte
- **Commit**: ein dauerhaft gespeicherter, beschrifteter Projektzustand
- **Branch**: eine bewegliche Entwicklungslinie innerhalb eines Repositorys

Wer diese Begriffe sauber voneinander trennt, versteht viele Git-Befehle nicht mehr als einzelne Kommandos, sondern als nachvollziehbare Zustandsänderungen.

---

## Das Repository: Projekt und Geschichte an einem Ort

Ein **Repository** ist der Bereich, den Git verwaltet. Es enthält nicht nur die aktuellen Dateien eines Projekts, sondern auch deren vollständige nachvollziehbare Entwicklung.

In einem Repository speichert Git unter anderem:

- Dateien des Projekts,
- frühere Versionen dieser Dateien,
- Commits mit Autor, Zeitpunkt und Nachricht,
- Branches und Tags,
- Einstellungen für dieses konkrete Projekt,
- Informationen über verbundene Server, etwa GitHub.

Ein lokales Git-Repository entsteht beispielsweise mit:

```bash
git init
```

Danach legt Git im Projektordner einen versteckten Unterordner namens `.git` an. Dieser Ordner ist das technische Herz des Repositorys.

```text
mein-projekt/
├── .git/
├── src/
├── tests/
├── README.md
└── composer.json
```

> **Wichtig:** Der Ordner `.git` enthält die Git-Datenbank mit der Historie. Wird er gelöscht, bleiben zwar die Projektdateien erhalten, aber Git „vergisst“ Commits, Branches, Einstellungen und die gesamte Versionsgeschichte.

### Lokale und entfernte Repositories

Ein Repository kann ausschließlich auf dem eigenen Computer liegen. Das ist ein **lokales Repository**.

Später kann es mit einem Server verbunden werden, zum Beispiel mit GitHub. Das dortige Repository wird als **entferntes Repository** oder *Remote Repository* bezeichnet.

```text
Lokaler Computer                   GitHub
────────────────                   ──────
Arbeitskopie                       Entferntes Repository
Git-Historie        ←── Sync ──→   Gemeinsame Git-Historie
Eigene Branches                    Veröffentlichte Branches
```

Git funktioniert aber bereits vollständig ohne GitHub und ohne Internetverbindung. GitHub erweitert Git um Zusammenarbeit, Hosting, Reviews, Issues und Automatisierung. Es ersetzt Git nicht.

---

## Der Commit: ein nachvollziehbarer Projektzustand

Ein **Commit** ist ein gespeicherter Zustand des Projekts zu einem bestimmten Zeitpunkt. Er hält fest, welche Dateien und Inhalte zu diesem Zeitpunkt zum Projekt gehören.

Ein Commit ist daher **kein bloßer Speichern-Knopf**. Er ist eine bewusste Aussage über die Entwicklung des Projekts.

Beispiele für sinnvolle Commit-Aussagen:

- „Registrierung mit E-Mail-Validierung hinzugefügt“
- „Fehlerhafte Berechnung der Mehrwertsteuer korrigiert“
- „API-Dokumentation für Benutzerendpunkt ergänzt“
- „PHPUnit-Tests für Passwort-Reset hinzugefügt“

Weniger hilfreich sind Nachrichten wie:

- „Änderungen“
- „Update“
- „Fix“
- „WIP“
- „asdf“

Eine gute Commit-Nachricht beantwortet knapp die Frage:

> *Was wurde in diesem Schritt fachlich oder technisch verändert?*

### Was Git in einem Commit speichert

Ein Commit enthält mehr als den Inhalt der Dateien. Vereinfacht gehören dazu:

- ein eindeutiger Bezeichner, meist als Hash dargestellt,
- der gespeicherte Projektzustand,
- eine Commit-Nachricht,
- Autor und Zeitpunkt,
- Committer und Zeitpunkt der tatsächlichen Speicherung,
- ein oder mehrere Vorgänger-Commits.

Ein Commit könnte in einer Kurzansicht so aussehen:

```text
Commit: 8f3c1a2
Autor:  Maria Beispiel
Datum:  2025-03-08

Validierung für Registrierungsformular hinzugefügt
```

Der lange technische Bezeichner ist ein Hash, zum Beispiel:

```text
8f3c1a2d7e4b0c4af95e3b8d1e6f9a2c7b4d5e6f
```

Im Alltag genügt meist ein eindeutiger Anfang dieses Hashes, etwa `8f3c1a2`.

### Commits bilden eine Geschichte

Jeder neue Commit verweist normalerweise auf seinen Vorgänger. Dadurch entsteht eine chronologische, technisch präzise Projektgeschichte.

```text
A ─── B ─── C
```

Dabei könnte gelten:

```text
A  Projektgrundstruktur erstellt
B  Benutzeranmeldung implementiert
C  Anmeldung mit Tests abgesichert
```

Der aktuelle Projektstand ist dann Commit `C`. Git kann aber jederzeit die Zustände von `A` oder `B` untersuchen, vergleichen oder bei Bedarf wiederherstellen.

### Commits sind lokale Sicherungspunkte

Ein Commit wird zunächst nur im lokalen Repository gespeichert. Er ist also noch nicht automatisch auf GitHub sichtbar und auch nicht für Kolleginnen und Kollegen verfügbar.

```text
Dateien ändern
      ↓
Commit lokal erstellen
      ↓
Commit zu GitHub übertragen
```

Das Übertragen zu einem Remote erfolgt später mit `git push`.

Diese Trennung ist bewusst: Du kannst lokal sauber arbeiten, Commits vorbereiten, korrigieren und testen, bevor du Änderungen mit anderen teilst.

---

## Ein Commit ist kein Dateiarchiv

Ein verbreitetes Denkmodell lautet: Git speichere bei jedem Commit einfach eine vollständige ZIP-Datei des Projekts. Das ist als erste Vorstellung nützlich, aber technisch nicht ganz richtig.

Git behandelt jeden Commit als vollständigen **Snapshot** des Projektzustands. Intern speichert Git identische Inhalte jedoch effizient nur einmal und verknüpft sie wieder.

Das entscheidende mentale Modell lautet:

> Ein Commit beschreibt, wie das gesamte Projekt zu einem bestimmten Zeitpunkt aussieht.

Git speichert also nicht primär die Anweisung „Zeile 12 wurde geändert“, sondern einen neuen Projektzustand, der auf bereits bekannten Inhalten aufbaut.

Das erklärt später unter anderem, warum Git:

- Änderungen sehr zuverlässig vergleichen kann,
- Branches schnell erstellt,
- alte Zustände einfach wiederherstellen kann,
- mehrere Entwicklungswege effizient verwaltet.

---

## Der Branch: eine Entwicklungslinie

Ein **Branch** ist eine benannte Entwicklungslinie. Er zeigt auf einen bestimmten Commit und bewegt sich weiter, wenn auf diesem Branch neue Commits entstehen.

Ein frisch initialisiertes Repository besitzt meist einen Standard-Branch namens `main`.

```text
main
  ↓
A ─── B ─── C
```

Der Name `main` zeigt hier auf den neuesten Commit `C`.

Wenn ein weiterer Commit entsteht, verschiebt Git den Branch-Zeiger automatisch:

```text
A ─── B ─── C ─── D
                  ↑
                main
```

Der Branch enthält dabei nicht selbst alle Dateien und auch keine Kopie der Commits. Er ist vielmehr ein **beweglicher Verweis** auf den neuesten Commit einer Entwicklungslinie.

> **Merksatz:** Ein Branch ist kein Projektordner und keine vollständige Projektkopie. Er ist ein Name, der auf einen Commit zeigt.

---

## Warum Branches wichtig sind

Branches ermöglichen es, mehrere Arbeiten parallel durchzuführen, ohne den stabilen Hauptstand direkt zu gefährden.

Angenommen, die produktive Anwendung befindet sich auf `main`:

```text
A ─── B ─── C
            ↑
          main
```

Nun soll eine neue Passwort-Reset-Funktion entwickelt werden. Dafür wird ein Feature-Branch angelegt:

```text
A ─── B ─── C
            ↑
          main
            ↑
   feature/password-reset
```

Zu Beginn zeigen beide Branches auf denselben Commit `C`. Danach entstehen die neuen Commits nur auf dem Feature-Branch:

```text
A ─── B ─── C ─── D ─── E
            ↑           ↑
          main   feature/password-reset
```

Die neue Funktion kann entwickelt, getestet und geprüft werden, ohne den Stand auf `main` zu verändern.

Erst wenn die Arbeit fertig ist, wird der Feature-Branch wieder mit `main` zusammengeführt. Dieser Vorgang heißt **Merge**.

```text
A ─── B ─── C ───────────── F
            │               ↑
            D ─── E       main
                ↑
      feature/password-reset
```

Die genaue Form der Historie hängt von der gewählten Merge-Strategie ab. Das Prinzip bleibt jedoch gleich: Branches erlauben isolierte Entwicklung und kontrollierte Integration.

---

## HEAD: Wo arbeite ich gerade?

Git muss wissen, auf welchem Branch du gerade arbeitest. Dafür verwendet Git die Referenz **HEAD**.

HEAD zeigt normalerweise auf den aktuell ausgecheckten Branch:

```text
A ─── B ─── C
            ↑
          main
            ↑
           HEAD
```

Wenn du den Branch wechselst, folgt HEAD diesem Branch:

```text
A ─── B ─── C ─── D
            ↑           ↑
          main   feature/login
                        ↑
                       HEAD
```

Neue Commits werden dann auf `feature/login` erstellt. Der Branch `main` bleibt unverändert auf Commit `C`.

In der Kommandozeile zeigt dieser Befehl die vorhandenen lokalen Branches an:

```bash
git branch
```

Der Stern markiert den aktuell aktiven Branch:

```text
* main
  feature/login
  feature/password-reset
```

Mit modernen Git-Versionen wechselst du einen Branch meist so:

```bash
git switch feature/login
```

Die Arbeit mit Branches wird in einem späteren Kapitel ausführlich behandelt. Für den Einstieg genügt dieses Modell:

- **HEAD** markiert deine aktuelle Arbeitsposition.
- Der aktive **Branch** bestimmt, wohin neue Commits geschrieben werden.
- Ein neuer Commit bewegt den aktiven Branch weiter.

---

## Repository, Commit und Branch im Zusammenhang

Die drei Begriffe gehören zusammen, erfüllen aber unterschiedliche Aufgaben:

| Begriff | Aufgabe | Beispiel |
|---|---|---|
| **Repository** | Verwaltet Projektdateien, Historie und Git-Metadaten | Das gesamte PHP-Projekt mit allen Commits |
| **Commit** | Speichert einen nachvollziehbaren Projektzustand | „Login-Validierung ergänzt“ |
| **Branch** | Kennzeichnet eine Entwicklungslinie und zeigt auf ihren neuesten Commit | `main`, `feature/login` |

Eine vereinfachte Darstellung:

```text
Repository
│
├── Branch main
│   └── A ─── B ─── C
│
└── Branch feature/login
    └── A ─── B ─── C ─── D ─── E
```

Die Commits `A`, `B` und `C` werden von beiden Branches geteilt. Erst ab `D` entwickelt sich der Feature-Branch unabhängig weiter.

---

## Praxisbeispiel: Entwicklung einer kleinen Funktion

Stell dir ein PHP-Projekt für eine Aufgabenverwaltung vor. Das Repository enthält zunächst drei Commits:

```text
A  Projekt mit Composer eingerichtet
B  Aufgabenliste implementiert
C  PHPUnit-Grundkonfiguration ergänzt
```

Der Standard-Branch `main` zeigt auf den letzten Commit:

```text
A ─── B ─── C
            ↑
          main
```

Nun soll eine Funktion zum Markieren erledigter Aufgaben entstehen.

### 1. Feature-Branch erstellen

```bash
git switch -c feature/complete-task
```

Git erstellt den Branch und wechselt sofort dorthin:

```text
A ─── B ─── C
            ↑
          main
            ↑
feature/complete-task
            ↑
           HEAD
```

### 2. Funktion implementieren und committen

Nach der Implementierung und passenden Tests wird ein Commit erstellt:

```bash
git add src/Task.php tests/TaskTest.php
git commit -m "Erledigte Aufgaben markieren"
```

Die Historie sieht danach so aus:

```text
A ─── B ─── C ─── D
            ↑       ↑
          main  feature/complete-task
```

### 3. Weiterarbeiten, ohne `main` zu verändern

Ein weiterer Commit ergänzt die Darstellung im Benutzerinterface:

```bash
git add templates/tasks.php
git commit -m "Erledigte Aufgaben visuell hervorheben"
```

```text
A ─── B ─── C ─── D ─── E
            ↑               ↑
          main  feature/complete-task
```

Währenddessen bleibt `main` auf dem getesteten Stand `C`. Andere Teammitglieder können dort arbeiten, ohne unvollständige Änderungen an der neuen Funktion zu übernehmen.

---

## Häufige Missverständnisse

### „Ein Branch ist eine Kopie des Projekts“

Nicht im üblichen Sinn. Git kopiert nicht bei jedem neuen Branch das gesamte Projektverzeichnis. Ein Branch ist hauptsächlich ein zusätzlicher Zeiger auf einen Commit. Deshalb sind Branches in Git schnell und günstig.

### „Ein Commit wird automatisch auf GitHub gespeichert“

Nein. Ein Commit entsteht zunächst lokal. Erst ein Push überträgt ihn an ein entferntes Repository.

```bash
git push
```

### „Ich brauche für jede Änderung einen neuen Branch“

Nicht zwingend. Für kleine persönliche Experimente kann Arbeit direkt auf `main` sinnvoll sein. In Teamprojekten und bei größeren Änderungen sind separate Feature-Branches jedoch meist der sichere Standard.

### „Ein Branch muss nach dem Zusammenführen bleiben“

Nein. Ist eine Funktion integriert, wird ihr Feature-Branch häufig gelöscht. Die enthaltenen Commits bleiben erhalten, sofern sie in die Zielhistorie übernommen wurden.

### „Ein Commit muss möglichst viele Änderungen enthalten“

Das Gegenteil ist meist besser: Ein Commit sollte eine **abgeschlossene, logisch zusammengehörige Änderung** darstellen. Kleine, verständliche Commits erleichtern Reviews, Fehlersuche und spätere Korrekturen.

---

## Praktische Leitlinien

Für den Einstieg helfen diese Regeln:

1. **Ein Repository pro klar abgegrenztem Projekt**  
   Ein Repository sollte eine zusammenhängende Anwendung, Bibliothek oder Konfiguration verwalten.

2. **Commits klein und verständlich halten**  
   Ein Commit sollte idealerweise eine Aufgabe, Fehlerbehebung oder fachliche Änderung beschreiben.

3. **Aussagekräftige Commit-Nachrichten schreiben**  
   Die Nachricht soll auch Wochen später ohne erneutes Lesen des Codes verständlich sein.

4. **Für neue Funktionen eigene Branches verwenden**  
   Namen wie `feature/login`, `fix/tax-calculation` oder `docs/setup-guide` machen den Zweck sichtbar.

5. **Den stabilen Hauptbranch schützen**  
   `main` sollte in Teamprojekten möglichst nur getestete und überprüfte Änderungen enthalten.

6. **Vor einer Änderung den aktuellen Branch prüfen**  
   Viele Fehler entstehen, weil versehentlich auf dem falschen Branch gearbeitet wird.

```bash
git status
```

Git zeigt dabei unter anderem an, auf welchem Branch du dich befindest.

---

## Zusammenfassung

Ein Git-Repository ist das verwaltete Projekt mitsamt seiner vollständigen Historie. Ein Commit speichert einen bewusst gewählten, beschrifteten Projektzustand. Ein Branch benennt eine Entwicklungslinie und zeigt auf ihren aktuellsten Commit.

Das zentrale Bild lautet:

```text
Repository
├── enthält die gesamte Historie
├── besteht aus vielen Commits
└── organisiert parallele Entwicklung über Branches
```

Oder noch kürzer:

> **Das Repository ist der Speicherort, der Commit ist der Zustand, der Branch ist die Entwicklungslinie.**

Mit diesem Modell lassen sich die nächsten Git-Schritte — insbesondere Staging, Commit-Erstellung, Branch-Wechsel und Zusammenführen — systematisch verstehen.