-
1. Erste Schritte
- 1.1 Über Versionskontrolle
- 1.2 Eine kurze Geschichte von Git
- 1.3 Was ist Git?
- 1.4 Die Kommandozeile
- 1.5 Git installieren
- 1.6 Erstmalige Git-Einrichtung
- 1.7 Hilfe bekommen
- 1.8 Zusammenfassung
-
2. Git Grundlagen
-
3. Git Branching
- 3.1 Branches im Überblick
- 3.2 Grundlegendes Branching und Merging
- 3.3 Branch-Management
- 3.4 Branching-Workflows
- 3.5 Remote-Branches
- 3.6 Rebasing
- 3.7 Zusammenfassung
-
4. Git auf dem Server
- 4.1 Die Protokolle
- 4.2 Git auf einem Server einrichten
- 4.3 Generieren Ihres SSH-Public-Keys
- 4.4 Einrichten des Servers
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Drittanbieter-Hosting-Optionen
- 4.10 Zusammenfassung
-
5. Verteiltes Git
-
6. GitHub
-
7. Git-Werkzeuge
- 7.1 Revisionsauswahl
- 7.2 Interaktives Staging
- 7.3 Stashing und Bereinigen
- 7.4 Ihre Arbeit signieren
- 7.5 Suchen
- 7.6 Historie umschreiben
- 7.7 Reset entmystifiziert
- 7.8 Fortgeschrittenes Merging
- 7.9 Rerere
- 7.10 Debugging mit Git
- 7.11 Submodule
- 7.12 Bundling
- 7.13 Ersetzen
- 7.14 Credential-Speicher
- 7.15 Zusammenfassung
-
8. Git anpassen
-
9. Git und andere Systeme
- 9.1 Git als Client
- 9.2 Migration zu Git
- 9.3 Zusammenfassung
-
10. Git-Interna
- 10.1 Plumbing und Porcelain
- 10.2 Git-Objekte
- 10.3 Git-Referenzen
- 10.4 Packfiles
- 10.5 Die Refspec
- 10.6 Übertragungsprotokolle
- 10.7 Wartung und Datenwiederherstellung
- 10.8 Umgebungsvariablen
- 10.9 Zusammenfassung
-
Anhang A: Git in anderen Umgebungen
- A1.1 Grafische Oberflächen
- A1.2 Git in Visual Studio
- A1.3 Git in Visual Studio Code
- A1.4 Git in IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git in Sublime Text
- A1.6 Git in Bash
- A1.7 Git in Zsh
- A1.8 Git in PowerShell
- A1.9 Zusammenfassung
-
Anhang B: Git in Ihre Anwendungen einbetten
- A2.1 Kommandozeilen-Git
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
Anhang C: Git-Befehle
- A3.1 Einrichtung und Konfiguration
- A3.2 Projekte abrufen und erstellen
- A3.3 Grundlegendes Snapshotting
- A3.4 Branching und Merging
- A3.5 Projekte teilen und aktualisieren
- A3.6 Inspektion und Vergleich
- A3.7 Debugging
- A3.8 Patching
- A3.9 E-Mail
- A3.10 Externe Systeme
- A3.11 Administration
- A3.12 Plumbing-Befehle
7.1 Git Tools - Revisionsauswahl
Bis jetzt haben Sie die meisten täglichen Befehle und Arbeitsabläufe gelernt, die Sie benötigen, um ein Git-Repository für die Quellcodeverwaltung zu verwalten. Sie haben die grundlegenden Aufgaben des Trackings und Commitens von Dateien erledigt und die Leistung der Staging-Area sowie des leichten Topic-Branching und Mergens genutzt.
Nun werden Sie eine Reihe sehr mächtiger Dinge erkunden, die Git tun kann und die Sie vielleicht nicht unbedingt täglich verwenden, aber zu einem bestimmten Zeitpunkt benötigen könnten.
Revisionsauswahl
Git ermöglicht es Ihnen, auf einzelne Commits, Gruppen von Commits oder Bereiche von Commits auf verschiedene Weise zu verweisen. Diese sind nicht unbedingt offensichtlich, aber es ist hilfreich, sie zu kennen.
Einzelne Revisionen
Sie können offensichtlich auf jeden einzelnen Commit über seinen vollständigen, 40-stelligen SHA-1-Hash verweisen, aber es gibt auch benutzerfreundlichere Möglichkeiten, auf Commits zu verweisen. Dieser Abschnitt beschreibt die verschiedenen Möglichkeiten, wie Sie auf jeden Commit verweisen können.
Kurzer SHA-1
Git ist intelligent genug, um herauszufinden, auf welchen Commit Sie sich beziehen, wenn Sie die ersten paar Zeichen des SHA-1-Hashs angeben, solange dieser Teil-Hash mindestens vier Zeichen lang und eindeutig ist; das heißt, kein anderes Objekt in der Objektdatenbank kann einen Hash haben, der mit demselben Präfix beginnt.
Um beispielsweise einen bestimmten Commit zu untersuchen, bei dem Sie wissen, dass Sie eine bestimmte Funktionalität hinzugefügt haben, könnten Sie zuerst den Befehl git log ausführen, um den Commit zu finden
$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
Fix refs handling, add gc auto, update tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
Add some blame and merge stuff
In diesem Fall, sagen wir, Sie interessieren sich für den Commit, dessen Hash mit 1c002dd… beginnt. Sie können diesen Commit mit einer der folgenden Varianten von git show inspizieren (vorausgesetzt, die kürzeren Versionen sind eindeutig)
$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d
Git kann eine kurze, eindeutige Abkürzung für Ihre SHA-1-Werte ermitteln. Wenn Sie --abbrev-commit an den Befehl git log übergeben, verwendet die Ausgabe kürzere Werte, behält sie aber eindeutig bei; standardmäßig werden sieben Zeichen verwendet, die aber bei Bedarf länger gemacht werden, um den SHA-1 eindeutig zu halten.
$ git log --abbrev-commit --pretty=oneline
ca82a6d Change the version number
085bb3b Remove unnecessary test code
a11bef0 Initial commit
Im Allgemeinen sind acht bis zehn Zeichen mehr als genug, um in einem Projekt eindeutig zu sein. Zum Beispiel hat der Linux-Kernel (ein ziemlich großes Projekt) im Februar 2019 über 875.000 Commits und fast sieben Millionen Objekte in seiner Objektdatenbank, wobei keine zwei Objekte identische SHA-1s in den ersten 12 Zeichen haben.
|
Hinweis
|
EINE KURZE ANMERKUNG ZU SHA-1
Viele Leute werden irgendwann besorgt sein, dass sie durch zufälliges Zusammentreffen zwei verschiedene Objekte in ihrem Repository haben, die denselben SHA-1-Wert ergeben. Was dann? Wenn Sie einen Commit mit einem Objekt durchführen, das denselben SHA-1-Wert wie ein früheres, *anderes* Objekt in Ihrem Repository ergibt, wird Git das frühere Objekt bereits in Ihrer Git-Datenbank sehen, annehmen, dass es bereits geschrieben wurde, und es einfach wiederverwenden. Wenn Sie versuchen, dieses Objekt später wieder auszuchecken, erhalten Sie immer die Daten des ersten Objekts. Sie sollten sich jedoch bewusst sein, wie lächerlich unwahrscheinlich dieses Szenario ist. Der SHA-1-Digest ist 20 Bytes oder 160 Bits. Die Anzahl der zufällig gehashten Objekte, die benötigt werden, um eine 50%ige Wahrscheinlichkeit einer einzelnen Kollision sicherzustellen, beträgt etwa 280 (die Formel zur Bestimmung der Kollisionswahrscheinlichkeit lautet Hier ist ein Beispiel, das Ihnen eine Vorstellung davon gibt, was es dauern würde, eine SHA-1-Kollision zu erzeugen. Wenn alle 6,5 Milliarden Menschen auf der Erde programmieren würden und jede Sekunde jeder von ihnen Code produzieren würde, der der gesamten Linux-Kernel-Historie entspricht (6,5 Millionen Git-Objekte) und ihn in ein riesiges Git-Repository pushen würde, würde es ungefähr 2 Jahre dauern, bis dieses Repository genügend Objekte enthält, um eine 50%ige Wahrscheinlichkeit einer einzelnen SHA-1-Objektkollision zu haben. Daher ist eine organische SHA-1-Kollision unwahrscheinlicher als dass jedes Mitglied Ihres Programmierteams angegriffen und von Wölfen in unabhängigen Vorfällen in derselben Nacht getötet wird. Wenn Sie mehrere Tausend Dollar an Rechenleistung dafür aufwenden, ist es möglich, zwei Dateien mit demselben Hash zu synthetisieren, wie am https://shattered.io/ im Februar 2017 bewiesen wurde. Git bewegt sich in Richtung der Verwendung von SHA256 als Standard-Hashing-Algorithmus, der deutlich widerstandsfähiger gegen Kollisionsangriffe ist und über Code verfügt, der hilft, diesen Angriff abzumildern (obwohl er ihn nicht vollständig beseitigen kann). |
Branch-Referenzen
Eine einfache Möglichkeit, auf einen bestimmten Commit zu verweisen, ist, wenn er der aktuelle Commit eines Branches ist; in diesem Fall können Sie einfach den Branch-Namen in jedem Git-Befehl verwenden, der eine Referenz auf einen Commit erwartet. Wenn Sie zum Beispiel den letzten Commit-Objekt in einem Branch untersuchen möchten, sind die folgenden Befehle äquivalent, vorausgesetzt, der Branch topic1 zeigt auf den Commit ca82a6d…
$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1
Wenn Sie sehen möchten, auf welchen spezifischen SHA-1 ein Branch zeigt, oder wenn Sie sehen möchten, worauf all diese Beispiele in Bezug auf SHA-1s hinauslaufen, können Sie ein Git-Plumbing-Tool namens rev-parse verwenden. Mehr Informationen über Plumbing-Tools finden Sie unter Git Internals; im Grunde genommen existiert rev-parse für niedrigstufige Operationen und ist nicht für den täglichen Gebrauch gedacht. Es kann jedoch manchmal hilfreich sein, wenn Sie sehen müssen, was wirklich vor sich geht. Hier können Sie rev-parse auf Ihren Branch anwenden.
$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949
RefLog-Kurznamen
Eine der Dinge, die Git im Hintergrund tut, während Sie arbeiten, ist das Führen eines "Reflogs" – ein Protokoll darüber, wo sich Ihre HEAD- und Branch-Referenzen in den letzten Monaten befunden haben.
Sie können Ihr Reflog mit git reflog einsehen
$ git reflog
734713b HEAD@{0}: commit: Fix refs handling, add gc auto, update tests
d921970 HEAD@{1}: merge phedders/rdocs: Merge made by the 'recursive' strategy.
1c002dd HEAD@{2}: commit: Add some blame and merge stuff
1c36188 HEAD@{3}: rebase -i (squash): updating HEAD
95df984 HEAD@{4}: commit: # This is a combination of two commits.
1c36188 HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD
Jedes Mal, wenn die Spitze Ihres Branches aus irgendeinem Grund aktualisiert wird, speichert Git diese Information für Sie in dieser temporären Historie. Sie können Ihre Reflog-Daten auch verwenden, um auf ältere Commits zu verweisen. Wenn Sie beispielsweise den fünften vorherigen Wert des HEAD Ihres Repositorys sehen möchten, können Sie die Referenz @{5} verwenden, die Sie in der Reflog-Ausgabe sehen
$ git show HEAD@{5}
Sie können diese Syntax auch verwenden, um zu sehen, wo sich ein Branch vor einer bestimmten Zeit befand. Um zum Beispiel zu sehen, wo sich Ihr master-Branch gestern befand, können Sie eingeben
$ git show master@{yesterday}
Dies würde Ihnen zeigen, wo sich die Spitze Ihres master-Branches gestern befand. Diese Technik funktioniert nur für Daten, die sich noch in Ihrem Reflog befinden, daher können Sie sie nicht verwenden, um nach Commits älter als ein paar Monate zu suchen.
Um Reflog-Informationen formatiert wie die git log-Ausgabe anzuzeigen, können Sie git log -g ausführen
$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon <schacon@gmail.com>)
Reflog message: commit: Fix refs handling, add gc auto, update tests
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
Fix refs handling, add gc auto, update tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
Es ist wichtig zu beachten, dass Reflog-Informationen streng lokal sind – es ist ein Protokoll nur dessen, was *Sie* in *Ihrem* Repository getan haben. Die Referenzen werden auf der Kopie des Repositorys von jemand anderem nicht dieselben sein; auch unmittelbar nach dem anfänglichen Klonen eines Repositorys haben Sie ein leeres Reflog, da in Ihrem Repository noch keine Aktivität stattgefunden hat. Das Ausführen von git show HEAD@{2.months.ago} zeigt Ihnen den übereinstimmenden Commit nur dann, wenn Sie das Projekt vor mindestens zwei Monaten geklont haben – wenn Sie es erst kürzlich geklont haben, sehen Sie nur Ihren ersten lokalen Commit.
|
Tipp
|
Betrachten Sie das Reflog als Git's Version der Shell-Historie
Wenn Sie einen UNIX- oder Linux-Hintergrund haben, können Sie sich das Reflog als Git's Version der Shell-Historie vorstellen, was betont, dass das, was dort ist, eindeutig für Sie und Ihre "Sitzung" relevant ist und nichts mit anderen zu tun hat, die möglicherweise an derselben Maschine arbeiten. |
|
Hinweis
|
Escaping von geschweiften Klammern in PowerShell
Bei der Verwendung von PowerShell sind geschweifte Klammern wie
|
Vorfahren-Referenzen
Die andere Hauptmethode, einen Commit anzugeben, ist über seine Vorfahren. Wenn Sie ein ^ (Caret) am Ende einer Referenz platzieren, löst Git dies als den Elternteil dieses Commits auf. Angenommen, Sie betrachten die Historie Ihres Projekts
$ git log --pretty=format:'%h %s' --graph
* 734713b Fix refs handling, add gc auto, update tests
* d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd Add some blame and merge stuff
|/
* 1c36188 Ignore *.gem
* 9b29157 Add open3_detach to gemspec file list
Dann können Sie den vorherigen Commit sehen, indem Sie HEAD^ angeben, was "der Elternteil von HEAD" bedeutet
$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
|
Hinweis
|
Caret auf Windows escapen
Unter Windows in
|
Sie können auch eine Zahl nach dem ^ angeben, um *welchen* Elternteil Sie wünschen, zu identifizieren; zum Beispiel bedeutet d921970^2 "der zweite Elternteil von d921970". Diese Syntax ist nur für Merge-Commits nützlich, die mehr als einen Elternteil haben – der *erste* Elternteil eines Merge-Commits stammt vom Branch, auf dem Sie sich beim Mergen befanden (häufig master), während der *zweite* Elternteil eines Merge-Commits vom Branch stammt, der gemergt wurde (sagen wir, topic).
$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
Add some blame and merge stuff
$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly <paul+git@mjr.org>
Date: Wed Dec 10 22:22:03 2008 +0000
Some rdoc changes
Die andere Haupt-Vorfahren-Spezifikation ist die ~ (Tilde). Dies bezieht sich ebenfalls auf den ersten Elternteil, sodass HEAD~ und HEAD^ äquivalent sind. Der Unterschied wird deutlich, wenn Sie eine Zahl angeben. HEAD~2 bedeutet "der erste Elternteil des ersten Elternteils" oder "der Großelternteil" – es durchläuft die ersten Elternteile so oft, wie Sie angeben. Zum Beispiel würde in der zuvor gezeigten Historie HEAD~3 sein
$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
Ignore *.gem
Dies kann auch als HEAD~~~ geschrieben werden, was wieder der erste Elternteil des ersten Elternteils des ersten Elternteils ist
$ git show HEAD~~~
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
Ignore *.gem
Sie können diese Syntaxen auch kombinieren – Sie können den zweiten Elternteil der vorherigen Referenz (vorausgesetzt, es war ein Merge-Commit) abrufen, indem Sie HEAD~3^2 usw. verwenden.
Commit-Bereiche
Nachdem Sie nun einzelne Commits spezifizieren können, sehen wir uns an, wie Sie Bereiche von Commits spezifizieren können. Dies ist besonders nützlich für die Verwaltung Ihrer Branches – wenn Sie viele Branches haben, können Sie Bereichsspezifikationen verwenden, um Fragen zu beantworten wie: "Welche Arbeit ist auf diesem Branch, die ich noch nicht in meinen Hauptbranch gemergt habe?"
Doppelpunkt
Die gebräuchlichste Bereichsspezifikation ist die Doppelpunkt-Syntax. Dies fragt Git im Grunde, ob es einen Bereich von Commits auflösen soll, die von einem Commit erreichbar, aber von einem anderen nicht erreichbar sind. Angenommen, Sie haben eine Commit-Historie, die so aussieht: Beispielhistorie für Bereichsauswahl.
Angenommen, Sie möchten sehen, was sich in Ihrem experiment-Branch befindet, das noch nicht in Ihren master-Branch gemergt wurde. Sie können Git bitten, Ihnen nur diese Commits mit master..experiment anzuzeigen – das bedeutet "alle Commits, die von experiment erreichbar sind, aber nicht von master". Der Kürze und Klarheit halber werden in diesen Beispielen die Buchstaben der Commit-Objekte aus dem Diagramm anstelle der tatsächlichen Log-Ausgabe verwendet, in der Reihenfolge, in der sie angezeigt würden
$ git log master..experiment
D
C
Wenn Sie umgekehrt das Gegenteil sehen möchten – alle Commits in master, die nicht in experiment sind –, können Sie die Branch-Namen umkehren. experiment..master zeigt Ihnen alles in master, was nicht von experiment erreichbar ist
$ git log experiment..master
F
E
Dies ist nützlich, wenn Sie den experiment-Branch auf dem neuesten Stand halten und Vorschau auf das sehen möchten, was Sie gleich mergen werden. Ein weiterer häufiger Anwendungsfall dieser Syntax ist es, zu sehen, was Sie zu einem Remote pushen werden
$ git log origin/master..HEAD
Dieser Befehl zeigt Ihnen alle Commits in Ihrem aktuellen Branch, die nicht im master-Branch auf Ihrem origin-Remote vorhanden sind. Wenn Sie git push ausführen und Ihr aktueller Branch origin/master verfolgt, sind die von git log origin/master..HEAD aufgelisteten Commits die Commits, die auf den Server übertragen werden. Sie können auch eine Seite der Syntax weglassen, damit Git HEAD annimmt. Sie können beispielsweise dieselben Ergebnisse wie im vorherigen Beispiel erhalten, indem Sie git log origin/master.. eingeben – Git ersetzt HEAD, wenn eine Seite fehlt.
Mehrere Punkte
Die Doppelpunkt-Syntax ist als Kurzform nützlich, aber vielleicht möchten Sie mehr als zwei Branches angeben, um Ihre Revision zu definieren, z. B. um zu sehen, welche Commits in mehreren Branches vorhanden sind, die nicht in dem Branch sind, auf dem Sie sich gerade befinden. Git erlaubt Ihnen dies, indem Sie entweder das ^-Zeichen oder --not vor jede Referenz setzen, von der Sie keine erreichbaren Commits sehen möchten. Somit sind die folgenden drei Befehle äquivalent
$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA
Dies ist gut, weil Sie mit dieser Syntax mehr als zwei Referenzen in Ihrer Abfrage angeben können, was Sie mit der Doppelpunkt-Syntax nicht können. Wenn Sie beispielsweise alle Commits sehen möchten, die von refA oder refB erreichbar sind, aber nicht von refC, können Sie eine der folgenden Optionen verwenden
$ git log refA refB ^refC
$ git log refA refB --not refC
Dies ergibt ein sehr leistungsfähiges Revisionsabfragesystem, das Ihnen helfen sollte, herauszufinden, was sich in Ihren Branches befindet.
Dreipunkt
Die letzte Hauptsyntax für die Bereichsauswahl ist die Dreipunkt-Syntax, die alle Commits angibt, die von *entweder* zwei Referenzen erreichbar sind, aber nicht von beiden. Schauen Sie zurück auf die Beispiel-Commit-Historie in Beispielhistorie für Bereichsauswahl. Wenn Sie sehen möchten, was sich in master oder experiment befindet, aber nicht in gemeinsamen Referenzen, können Sie ausführen
$ git log master...experiment
F
E
D
C
Auch dies gibt Ihnen eine normale log-Ausgabe, zeigt Ihnen aber nur die Commit-Informationen für diese vier Commits, sortiert nach dem traditionellen Commit-Datum.
Ein häufiger Schalter, der in diesem Fall mit dem log-Befehl verwendet wird, ist --left-right, der Ihnen anzeigt, auf welcher Seite des Bereichs sich jeder Commit befindet. Dies macht die Ausgabe nützlicher
$ git log --left-right master...experiment
< F
< E
> D
> C
Mit diesen Werkzeugen können Sie Git viel einfacher mitteilen, welchen Commit oder welche Commits Sie untersuchen möchten.