1 == Git für Fortgeschrittene ==
3 Mittlerweile solltest Du Dich in den *git help* Seiten zurechtfinden und das
4 meiste verstanden haben. Trotzdem kann es langwierig sein, den exakten
5 Befehl zur Lösung einer bestimmten Aufgabe herauszufinden. Vielleicht kann
6 ich Dir etwas Zeit sparen: Nachfolgend findest Du ein paar Rezepte, die ich
7 in der Vergangenheit gebraucht habe.
9 === Quellcode veröffentlichen ===
11 Bei meinen Projekten verwaltet Git genau die Dateien, die ich archivieren
12 und für andere Benutzer veröffentlichen will. Um ein tarball-Archiv des
13 Quellcodes zu erzeugen, verwende ich den Befehl:
15 $ git archive --format=tar --prefix=proj-1.2.3/ HEAD
17 === 'Commite' Änderungen ===
19 Git mitzuteilen, welche Dateien man hinzugefügt, gelöscht und umbenannt hat,
20 ist für manche Projekte sehr mühsam. Stattdessen kann man folgendes
26 Git wird sich die Dateien im aktuellen Verzeichnis ansehen und sich die
27 Details selbst erarbeiten. Anstelle des zweiten Befehl kann man auch `git
28 commit -a` ausführen, falls man an dieser Stelle ohnehin 'comitten'
29 möchte. Siehe *git help ignore* um zu sehen, wie man Dateien definiert, die
30 ignoriert werden sollen.
32 Man kann das aber auch in einem einzigen Schritt ausführen mit:
34 $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove
36 Die *-z* und *-0* Optionen verhindern unerwünschte Nebeneffekte durch
37 Dateinamen mit ungewöhnlichen Zeichen. Da diese Anweisung aber auch zu
38 ignorierende Dateien hinzufügt, kann man noch die `-x` oder `-X` Option
41 === Mein 'Commit' ist zu groß! ===
43 Hast Du es zu lange versäumt zu 'comitten'? Hast Du so versessen
44 programmiert, daß Du darüber die Quellcodeverwaltung vergessen hast? Machst
45 Du eine Serie von unabhängigen Änderungen, weil es Dein Stil ist?
51 Für jede Änderung, die Du gemacht hast, zeigt Git Dir die Codepassagen, die
52 sich geändert haben und fragt ob sie Teil des nächsten 'Commit' sein
53 sollen. Antworte mit "y" für Ja oder "n" für Nein. Du hast auch noch andere
54 Optionen, z.B. den Aufschub der Entscheidung; drücke "?" um mehr zu
57 Wenn Du zufrieden bist, gib
61 ein um exakt die ausgewählten Änderungen zu 'comitten' (die "inszenierten"
62 Änderungen). Achte darauf, nicht die Option *-a* einzusetzen, anderenfalls
63 wird Git alle Änderungen 'comitten'.
65 Was ist, wenn Du viele Dateien an verschiedenen Orten bearbeitet hast? Jede
66 Datei einzeln nachzuprüfen ist frustrierend und ermüdend. In diesem Fall
67 verwende *git add -i*, dessen Bedienung ist nicht ganz einfach, dafür aber
68 sehr flexibel. Mit ein paar Tastendrücken kannst Du mehrere geänderte
69 Dateien für den 'Commit' hinzufügen ('stage') oder entfernen ('unstage')
70 oder Änderungen einzelner Dateien nachprüfen und hinzufügen. Alternativ
71 kannst Du *git commit \--interactive* verwenden, was dann automatisch die
72 ausgewählten Änderungen 'commited' nachdem Du fertig bist.
74 === Der Index: Git's Bereitstellungsraum ===
76 Bis jetzt haben wir Git's berühmten 'Index' gemieden, aber nun müssen wir
77 uns mit ihm auseinandersetzen um das bisherige zu erklären. Der Index ist
78 ein temporärer Bereitstellungsraum. Git tauscht selten Daten direkt zwischen
79 Deinem Projekt und seiner Versionsgeschichte aus. Vielmehr schreibt Git die
80 Daten zuerst in den Index, danach kopiert es die Daten aus dem Index an
81 ihren eigentlichen Bestimmungsort.
83 Zum Beispiel ist *commit -a* eigentlich ein zweistufiger Prozess. Der erste
84 Schritt erstellt einen Schnappschuß des aktuellen Status jeder überwachten
85 Datei im Index. Der zweite Schritt speichert dauerhaft den Schnappschuß, der
86 sich nun im Index befindet. Ein 'Commit' ohne die *-a* Option führt nur den
87 zweiten Schritt aus und macht nur wirklich Sinn, wenn zuvor eine Anweisung
88 angewendet wurde, welche den Index verändert, wie zum Beispiel *git add*.
90 Normalerweise können wir den Index ignorieren und so tun als würden wir
91 direkt aus der Versionsgeschichte lesen oder in sie schreiben. In diesem
92 Fall wollen wir aber mehr Kontrolle, also manipulieren wir den Index. Wir
93 erstellen einen Schnappschuß einiger, aber nicht aller unser Änderungen im
94 Index und speichern dann diesen sorgfältig zusammengestellten Schnappschuß
97 === Verliere nicht Deinen KOPF ===
99 Der HEAD Bezeichner ist wie ein Cursor, der normalerweise auf den jüngsten
100 'Commit' zeigt und mit jedem neuen 'Commit' voranschreitet. Einige Git
101 Anweisungen lassen Dich ihn manipulieren. Zum Beispiel:
105 bewegt den HEAD Bezeichner drei 'Commits' zurück. Dadurch agieren nun alle
106 Git Anweisungen als hätte es die drei letzten 'Commits' nicht gegeben,
107 während deine Dateien unverändert erhalten bleiben. Siehe auf der Git
108 Hilfeseite für einige Anwendungsbeispiele.
110 Aber wie kannst Du zurück in die Zukunft? Die vergangenen 'Commits' wissen
111 nichts von der Zukunft.
113 Wenn Du den SHA1 Schlüssel vom originalen HEAD hast, dann:
117 Aber stell Dir vor, Du hast ihn niemals notiert? Keine Sorge: Für solche
118 Anweisungen sichert Git den original HEAD als Bezeichner mit dem Namen
119 ORIG_HEAD und Du kannst gesund und munter zurückkehren mit:
121 $ git reset ORIG_HEAD
125 Möglicherweise reicht ORIG_HEAD nicht aus. Vielleicht hast Du gerade
126 bemerkt, dass Du einen kapitalen Fehler gemacht hast und nun musst Du zu
127 einem uralten 'Commit' in einem länst vergessenen 'Branch' zurück.
129 Standardmäßig behält Git einen 'Commit' für mindesten zwei Wochen, sogar
130 wenn Du Git anweist den 'Branch' zu zerstören, in dem er enthalten ist. Das
131 Problem ist, den entsprechenden SHA1-Wert zu finden. Du kannst Dir alle
132 SHA1-Werte in `.git/objects` vornehmen und ausprobieren ob Du den gesuchten
133 'Commit' findest. Aber es gibt einen viel einfacheren Weg.
135 Git speichert jeden errechneten SHA1-Wert eines 'Commits' in
136 `.git/logs`. Das Unterverzeichnis `refs` enthält den Verlauf aller
137 Aktivitäten auf allen 'Branches', während `HEAD` alle SHA1-Werte enthält,
138 die jemals diese Bezeichnung hatten. Die letztere kann verwendet werden um
139 SHA1-Werte von 'Commits' zu finden, die sich in einem 'Branch' befanden, der
140 versehentlich gestutzt wurde.
142 Die reflog Anweisung bietet eine benutzerfreundliche Schnittstelle zu diesen
147 Anstatt SHA1-Werte aus dem reflog zu kopieren und einzufügen, versuche:
149 $ git checkout "@{10 minutes ago}"
151 Oder rufe den fünftletzten 'Commit' ab, mit:
153 $ git checkout "@{5}"
155 Siehe in der ``Specifying Revisions'' Sektion von *git help rev-parse* für
158 Vielleicht möchtest Du eine längere Gnadenfrist für todgeweihte 'Commits'
159 konfigurieren. Zum Beispiel:
161 $ git config gc.pruneexpire "30 days"
163 bedeutet, ein gelöschter 'Commit' wird nur dann endgültig verloren sein,
164 nachdem 30 Tage vergangen sind und *git gc* ausgeführt wurde.
166 Du magst vielleicht auch das automatische Ausführen von *git gc* abstellen:
168 $ git config gc.auto 0
170 wodurch 'Commits' nur noch gelöscht werden, wenn Du *git gc* manuell
173 === Auf Git bauen ===
175 In echter UNIX Sitte erlaubt es Git's Design, dass es auf einfache Weise als
176 Low-Level-Komponente von anderen Programmen benutzt werden kann, wie zum
177 Beispiel grafischen Benutzeroberflächen und Internetanwendungen, alternative
178 Kommandozeilenanwendungen, Patch-Werkzeugen, Import- und
179 Konvertierungswerkzeugen und so weiter. Sogar einige Git Anweisungen selbst
180 sind nur winzige Skripte, wie Zwerge auf den Schultern von Riesen. Mit ein
181 bisschen Handarbeit kannst Du Git anpassen, damit es Deinen Anforderungen
184 Ein einfacher Trick ist es die in Git integrierte Aliasfunktion zu verwenden
185 um die am häufigsten benutzten Anweisungen zu verkürzen:
187 $ git config --global alias.co checkout
188 $ git config --global --get-regexp alias # display current aliases
190 $ git co foo # same as 'git checkout foo'
192 Etwas anderes ist der aktuelle 'Branch' im Prompt oder Fenstertitel. Die
195 $ git symbolic-ref HEAD
197 zeigt den Namen des aktuellen 'Branch'. In der Praxis möchtest Du aber das
198 "refs/heads/" entfernen und Fehler ignorieren:
200 $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-
202 Das +contrib+ Unterverzeichnis ist eine Fundgrube von Werkzeugen, die auf
203 Git aufbauen. Mit der Zeit können einige davon zu offiziellen Anweisungen
204 befördert werden. Auf Debian und Ubuntu, findet man dieses Verzeichnis unter
205 +/usr/share/doc/git-core/contrib+.
207 Ein beliebter Vertreter ist +workdir/git-new-workdir+. Durch cleveres
208 verlinken erzeugt dieses Skript ein neues Arbeitsverzeichis, das seine
209 Versionsgeschichte mit dem original 'Repository' teilt:
211 $ git-new-workdir ein/existierendes/repo neues/verzeichnis
213 Das neue Verzeichnis und die Dateien darin kann man sich als 'Clone'
214 vorstellen, mit dem Unterschied, dass durch die gemeinschaftliche
215 Versionsgeschichte die beiden Versionen automatisch synchron bleiben. Eine
216 Synchronisierung mittels 'merge', 'push' oder 'pull' ist nicht notwendig.
218 === Gewagte Kunststücke ===
220 Heutzutage macht es Git dem Anwender schwer versehentlich Daten zu
221 zerstören. Aber, wenn man weiß was man tut, kann man die Schutzmaßnahmen der
222 häufigsten Anweisungen umgehen.
224 *Checkout*: Nicht versionierte Änderungen lassen 'checkout' scheitern. Um trotzdem die Änderungen zu zerstören und einen vorhandenen 'Commit' abzurufen, benutzen wir die 'force' Option:
226 $ git checkout -f HEAD^
228 Auf der anderen Seite, wenn Du einen speziellen Pfad für 'checkout' angibst,
229 gibt es keinen Sicherheitsüberprüfungen mehr. Der angegebene Pfad wird
230 stillschweigend überschrieben. Sei vorsichtig, wenn Du 'checkout' auf diese
233 *Reset*: Reset versagt auch, wenn unversionierte Änderungen vorliegen. Um es zu erzwingen, verwende:
235 $ git reset --hard 1b6d
237 *Branch*: 'Branches' zu löschen scheitert ebenfalls, wenn dadurch Änderungen verloren gehen. Um das Löschen zu erzwingen, gib ein:
239 $ git branch -D dead_branch # instead of -d
241 Ebenso scheitert der Versuch einen 'Branch' durch ein 'move' zu
242 überschreiben, wenn das einen Datenverlust zur Folge hat. Um das Verschieben
243 zu erzwingen, gib ein:
245 $ git branch -M source target # instead of -m
247 Anders als bei 'checkout' und 'reset' verschieben diese beiden Anweisungen
248 das Zerstören der Daten. Die Änderungen bleiben im .git Unterverzeichnis
249 gespeichert und können wieder hergestellt werden, wenn der entsprechende
250 SHA1-Wert aus `.git/logs` ermittelt wird (siehe "KOPF-Jagd"
251 oben). Standardmäßig bleiben die Daten mindestens zwei Wochen erhalten.
253 *Clean*: Verschiedene git Anweisungen scheitern, weil sie Konflikte mit unversionierten Dateien vermuten. Wenn Du sicher bist, dass alle unversionierten Dateien und Verzeichnisse entbehrlich sind, dann lösche diese gnadenlos mit:
257 Beim nächsten Mal werden diese lästigen Anweisung gehorchen!
259 === Verhindere schlechte 'Commits' ===
261 Dumme Fehler verschmutzen meine 'Repositories'. Am schrecklichsten sind
262 fehlende Dateien wegen eines vergessenen *git add*. Kleinere Verfehlungen
263 sind Leerzeichen am Zeilenende und ungelöste 'merge'-Konflikte: obwohl sie
264 harmlos sind, wünschte ich, sie würden nie in der Öffentlichkeit erscheinen.
266 Wenn ich doch nur eine Trottelversicherung abgeschlossen hätte, durch
267 Verwendung eines _hook_, der mich bei solchen Problemen alarmiert.
270 $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit
272 Nun bricht Git einen 'Commit' ab, wenn es überflüssige Leerzeichen am
273 Zeilenende oder ungelöste 'merge'-Konflikte entdeckt.
275 Für diese Anleitung hätte ich vielleicht am Anfang des *pre-commit* 'hook'
276 folgendes hinzugefügt, zum Schutz vor Zerstreutheit:
278 if git ls-files -o | grep '\.txt$'; then
279 echo FAIL! Untracked .txt files.
283 Viele Git Operationen unterstützen 'hooks'; siehe *git help hooks*. Wir
284 haben den Beispiel 'hook' *post-update* aktiviert, weiter oben im Abschnitt
285 Git über HTTP. Dieser läuft immer, wenn der 'HEAD' sich bewegt. Das Beispiel
286 'post-update' Skript aktualisiert Dateien, welche Git für die Kommunikation
287 über 'Git-agnostic transports' wie z.B. HTTP benötigt.