Startseite

Vorwort
1. An wen richtet sich dieses Buch?
2. Wie ist das Buch zu lesen?
3. Konventionen
4. Installation und „das Git-Repository“
5. Dokumentation und Hilfe
6. Downloads und Kontakt
7. Danksagungen
8. Vorwort zur 2. Auflage
9. Vorwort zur CreativeCommons-Ausgabe
1. Einführung und erste Schritte
1.1. Grundbegriffe
1.2. Erste Schritte mit Git
1.3. Git konfigurieren
2. Grundlagen
2.1. Git-Kommandos
2.2. Das Objektmodell
3. Praktische Versionsverwaltung
3.1. Referenzen: Branches und Tags
3.2. Versionen wiederherstellen
3.3. Branches zusammenführen: Merges
3.4. Merge-Konflikte lösen
3.5. Einzelne Commits übernehmen: Cherry-Pick
3.6. Visualisierung von Repositories
3.7. Reflog
4. Fortgeschrittene Konzepte
4.1. Commits verschieben – Rebase
4.2. Die Geschichte umschreiben – Interaktives Rebase
4.3. Wer hat diese Änderungen gemacht? – git blame
4.4. Dateien ignorieren
4.5. Veränderungen auslagern – git stash
4.6. Commits annotieren – git notes
4.7. Mehrere Root-Commits
4.8. Regressionen finden – git bisect
5. Verteiltes Git
5.1. Wie funktioniert verteilte Versionsverwaltung?
5.2. Repositories klonen
5.3. Commits herunterladen
5.4. Commits hochladen: git push
5.5. Remotes untersuchen
5.6. Verteilter Workflow mit mehreren Remotes
5.7. Remotes verwalten
5.8. Tags austauschen
5.9. Patches per E-Mail
5.10. Ein verteilter, hierarchischer Workflow
5.11. Unterprojekte verwalten
6. Workflows
6.1. Anwender
6.2. Ein Branching-Modell
6.3. Releases-Management
7. Git auf dem Server
7.1. Einen Git-Server hosten
7.2. Gitolite: Git einfach hosten
7.3. Git-Daemon: Anonymer, lesender Zugriff
7.4. Gitweb: Das integrierte Web-Frontend
7.5. CGit – CGI for Git
8. Git automatisieren
8.1. Git-Attribute – Dateien gesondert behandeln
8.2. Hooks
8.3. Eigene Git-Kommandos schreiben
8.4. Versionsgeschichte umschreiben
9. Zusammenspiel mit anderen Versionsverwaltungssystemen
9.1. Subversion
9.2. Eigene Importer
10. Shell-Integration
10.1. Git und die Bash
10.2. Git und die Z-Shell
11. Github
A. Installation
A.1. Linux
A.2. Mac OS X
A.3. Windows
B. Struktur eines Repositorys
B.1. Aufräumen
B.2. Performance

Anhang B. Struktur eines Repositorys

Git speichert die Objektdatenbank, die zugehörigen Referenzen usw. im sogenannten Git-Directory, oft auch als $GIT_DIR bezeichnet. Standardmäßig ist dies .git/. Es existiert für jedes Git-Repository nur einmal, d.h. es werden keine zusätzlichen .git/-Verzeichnisse in Unterverzeichnissen angelegt.[154] Es enthält unter anderem folgende Einträge:

HEAD
Der HEAD, siehe Abschnitt 3.1.1, „HEAD und andere symbolische Referenzen“. Neben HEAD liegen ggf. auch andere wichtige symbolische Referenzen auf oberster Ebene, z.B. ORIG_HEAD oder FETCH_HEAD.
config
Die Konfigurationsdatei des Repositorys, siehe Abschnitt 1.3, „Git konfigurieren“.
hooks/
Enthält die für dieses Repository gesetzten Hooks, siehe Abschnitt 8.2, „Hooks“.
index
Der Index bzw. Stage, siehe Abschnitt 2.1.1, „Index“.
info/
Zusätzliche Repository-Informationen, z.B. zu ignorierende Muster (siehe Abschnitt 4.4, „Dateien ignorieren“) und auch Grafts (siehe Abschnitt 8.4.3, „Grafts: Nachträgliche Merges“). Sie können eigene Informationen dort ablegen, wenn andere Tools damit umgehen können (siehe z.B. der Abschnitt über Caching von CGit, Abschnitt 7.5.4, „Caching ausnutzen“).

Abbildung B.1. Die wichtigsten Einträge in .git

bilder_ebook/git-dir-crop.png

logs/
Protokoll der Veränderungen an Referenzen; zugänglich über das Reflog, siehe Abschnitt 3.7, „Reflog“. Enthält eine Logdatei für jede Referenz unter refs/ sowie HEAD.
objects/
Die Objektdatenbank, siehe Abschnitt 2.2.3, „Die Objektdatenbank“. Aus Performance-Gründen sind die Objekte in Unterverzeichnisse, die einem Zwei-Zeichen-Präfix ihrer SHA-1-Summe entsprechen, einsortiert (der Commit 0a7ba55... liegt also unter 0a/7ba55...). Im Unterverzeichnis pack/ finden Sie die Packfiles und zugehörigen Indizes, die u.a. von der Garbage-Collection (s.u.) erstellt wird. Im Unterverzeichnis info/ legt Git bei Bedarf eine Auflistung vorhandener Packfiles ab.
refs/
Alle Referenzen, unter anderem Branches in refs/heads/, siehe Abschnitt 3.1.1, „HEAD und andere symbolische Referenzen“, Tags in refs/tags/, siehe Abschnitt 3.1.3, „Tags – Wichtige Versionen markieren“ sowie Remote-Tracking-Branches unter refs/remotes/, siehe Abschnitt 5.2.2, „Remote-Tracking-Branches“.

Eine ausführliche technische Beschreibung finden Sie in der Man-Page gitrepository-layout(5).

B.1. Aufräumen

Wie beispielsweise schon in Abschnitt 3.1.2, „Branches verwalten“ erwähnt, sind Commits, die nicht mehr referenziert werden (sei es durch Branches oder andere Commits), nicht mehr zu erreichen. In der Regel ist das der Fall, wenn Sie einen Commit löschen wollten (oder Commits mit Rebase umgebaut haben). Git löscht diese nicht sofort aus der Objektdatenbank, sondern belässt sie per Default zwei Wochen dort, auch wenn sie nicht mehr erreichbar sind.

Intern verwendet Git die Kommandos prune, prune-packed, fsck, repack u.a. Allerdings werden die Tools mit entsprechenden Optionen automatisch von der Garbage Collection („Müllabfuhr“) ausgeführt: git gc. Folgende Aufgaben erledigt das Tool:

  • Dangling und Unreachable Objects löschen. Diese entstehen bei diversen Operationen und können in der Regel nach einiger Zeit gelöscht werden, um Platz zu sparen (Default: nach zwei Wochen).
  • Loose Objects neu packen. Git verwendet sog. Packfiles, um mehrere Git-Objekte zusammenzuschnüren. (Dann existiert nicht mehr eine Datei unterhalb von .git/objects/ pro Blob, Tree und Commit – diese werden in einer großen, zlib-komprimierten Datei zusammengefasst).
  • Existierende Packfiles nach alten (unerreichbaren) Objekten durchsuchen und die Packfiles entsprechend „ausdünnen“. Ggf. werden mehrere kleine Packfiles zu großen kombiniert.
  • Referenzen packen. Es entstehen sog. Packed Refs, siehe auch Abschnitt 3.1, „Referenzen: Branches und Tags“.
  • Alte Reflog-Einträge löschen. Das geschieht per Default nach 90 Tagen.
  • Alte Konflikt-Resolutionen (siehe Rerere, Abschnitt 3.4.2, „rerere: Reuse Recorded Resolution“) werden entsorgt (15/60 Tage Haltezeit für ungelöst/gelöst).

Die Garbage Collection kennt drei Modi: automatisch, normal und aggressiv. Den automatischen Modus rufen Sie per git gc --auto auf – der Modus überprüft, ob es wirklich eklatante Mängel im Repository gibt. Was „eklatant“ bedeutet, ist konfigurierbar. Über folgende Konfigurationseinstellungen können Sie (global oder per Repository) bestimmen, ab wann, d.h. ab welcher Anzahl „kleiner“ Dateien der automatische Modus aufräumt, also diese in große Archive zusammenfasst.

gc.auto (Default: 6700 Objekte)
Objekte zu einem Packfile zusammenfassen
gc.autopacklimit (Default: 50 Packs)
Packs zu einem großen Packfile zusammenfassen

Der automatische Modus wird häufig aufgerufen, u.a. von receive-pack und rebase (interaktiv). In den meisten Fällen tut der automatische Modus allerdings nichts, da die Defaults sehr konservativ sind. Wenn doch, sieht das so aus:

$ git gc --auto
Auto packing the repository for optimum performance. You may also
run "git gc" manually. See "git help gc" for more information.
...

B.2. Performance

Sie sollten entweder die Schwellen, ab denen die automatische Garbage Collection greift, deutlich herabsetzen, oder von Zeit zu Zeit git gc aufrufen. Dies hat einen offensichtlichen Vorteil, nämlich dass Plattenplatz gespart wird:

$ du -sh .git
20M     .git
$ git gc
Counting objects: 3726, done.
Compressing objects: 100% (1639/1639), done.
Writing objects: 100% (3726/3726), done.
Total 3726 (delta 1961), reused 2341 (delta 1279)
Removing duplicate objects: 100% (256/256), done.
$ du -sh .git
6.3M    .git

Einzelne Objekte unterhalb von .git/objects/ wurden zu einem Packfile zusammengefasst:

$ ls -lh .git/objects/pack/pack-a97624dd23<...>.pack
-r-------- 1 feh feh 4.6M Jun  1 10:20 .git/objects/pack/pack-a97624dd23<...>.pack
$ file .git/objects/pack/pack-a97624dd23<...>.pack
.git/objects/pack/pack-a97624dd23<...>.pack: Git pack, version 2, 3726 objects

Sie können sich per git count-objects ausgeben lassen, aus wie vielen Dateien die Objektdatenbank besteht. Hier nebeneinander vor und nach dem obigen Packvorgang:

$ git count-objects -v
count: 1905                             count: 58
size: 12700                             size: 456
in-pack: 3550                           in-pack: 3726
packs: 7                                packs: 1
size-pack: 4842                         size-pack: 4716
prune-packable: 97                      prune-packable: 0
garbage: 0                              garbage: 0

Nun ist Plattenplatz billig, ein auf 30% komprimiertes Repository also kein großer Gewinn. Der Performance-Gewinn ist allerdings nicht zu verachten. In der Regel zieht ein Objekt (z.B. ein Commit) weitere Objekte nach sich (Blobs, Trees). Wenn Git also pro Objekt eine Datei öffnen muss (bei n verwalteten Dateien also mindestens n Blob-Objekte), dann sind dies n Lese-Vorgänge auf dem Dateisystem.

Packfiles haben zwei wesentliche Vorteile: Erstens legt Git zu jedem Packfile eine Indizierung an, die angibt, welches Objekt in welchem Offset der Datei zu finden ist. Zusätzlich hat die Packroutine noch eine gewisse Heuristik um die Objektplatzierung innerhalb der Datei zu optimieren (so dass bspw. ein Tree-Object und die davon referenzierten Blob-Objekte „nah“ beieinander liegen). Dadurch kann Git einfach das Packfile in den Speicher mappen (Stichwort: „sliding mmap“). Die Operation „suche Objekt X“ ist dann nichts weiter als eine Lookup-Operation im Pack-Index und ein entsprechendes Auslesen der Stelle im Packfile, d.h. im Speicher. Dies entlastet das Datei- und Betriebssystem erheblich.

Der zweite Vorteil der Packfiles liegt in der Delta-Kompression. So werden Objekte möglichst als Deltas (Veränderungen) anderer Objekte gespeichert.[155] Das spart Speicherplatz, ermöglicht aber andererseits auch Kommandos wie git blame, „kostengünstig“, also ohne großen Rechenaufwand, Kopien von Code-Stücken zwischen Dateien zu entdecken.

Der aggressive Modus sollte nur in begründeten Ausnahmefällen eingesetzt werden.[156]

Tipp

Lassen Sie auf Ihren öffentlich zugänglichen Repositories auch regelmäßig, z.B. per Cron, ein git gc laufen. Commits werden über das Git-Protokoll immer als Packfiles übertragen, die on demand, das heißt zum Zeitpunkt des Abrufs, erzeugt werden. Wenn das gesamte Repository schon als ein großes Packfile vorliegt, können Teile daraus schneller extrahiert werden, und ein kompletter Clone des Repositorys benötigt keine zusätzlichen Rechenoperationen (es muss kein riesiges Packfile gepackt werden). Eine regelmäßige Garbage Collection kann also die Auslastung Ihres Servers senken, außerdem wird der Clone-Vorgang der Nutzer beschleunigt.

Ist das Repository besonders groß, kann es bei einem git clone sehr lange dauern, bis der Server alle Objekte gezählt hat. Dies können Sie beschleunigen, indem Sie regelmäßig per Cron-Job git repack -A -d -b aufrufen: Git erstellt dann zusätzlich zu den Pack-Files eine Bitmap-Datei, die diesen Vorgang um ein bis zwei Größenordnungen beschleunigt.



[154] Da ein Bare-Repository (siehe Abschnitt 7.1.3, „Bare Repositories – Repositories ohne Working Tree“) keinen Working Tree besitzt, bilden die Inhalte, die normalerweise in .git liegen, die oberste Ebene in der Verzeichnisstruktur, und es gibt kein zusätzliches Verzeichnis .git.

[155] Das ist nicht zu verwechseln mit Versionskontrollsystemen, die inkrementelle Versionen einer Datei speichern. Innerhalb von Packfiles werden die Objekte unabhängig von ihrem semantischen Zusammenhang, d.h. speziell ihrer zeitlichen Abfolge, gepackt.

[156] Eine ausführliche Auseinandersetzung mit dem Thema finden Sie unter http://metalinguist.wordpress.com/2007/12/06/the-woes-of-git-gc-aggressive-and-how-git-deltas-work/


Creative Commons License
Lizensiert unter der Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.