-
1. Erste Schritte
-
2. Git Grundlagen
-
3. Git Branching
- 3.1 Branches auf einen Blick
- 3.2 Einfaches 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 Erstellung eines SSH-Public-Keys
- 4.4 Einrichten des Servers
- 4.5 Git-Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Von Drittanbietern gehostete Optionen
- 4.10 Zusammenfassung
-
5. Verteiltes Git
-
6. GitHub
-
7. Git Tools
- 7.1 Revisions-Auswahl
- 7.2 Interaktives Stagen
- 7.3 Stashen und Bereinigen
- 7.4 Deine Arbeit signieren
- 7.5 Suchen
- 7.6 Den Verlauf umschreiben
- 7.7 Reset entzaubert
- 7.8 Fortgeschrittenes Merging
- 7.9 Rerere
- 7.10 Debuggen mit Git
- 7.11 Submodule
- 7.12 Bundling
- 7.13 Replace (Ersetzen)
- 7.14 Anmeldeinformationen speichern
- 7.15 Zusammenfassung
-
8. Git einrichten
- 8.1 Git Konfiguration
- 8.2 Git-Attribute
- 8.3 Git Hooks
- 8.4 Beispiel für Git-forcierte Regeln
- 8.5 Zusammenfassung
-
9. Git und andere VCS-Systeme
- 9.1 Git als Client
- 9.2 Migration zu Git
- 9.3 Zusammenfassung
-
10. Git Interna
-
A1. Anhang A: Git in anderen Umgebungen
- A1.1 Grafische Schnittstellen
- 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
-
A2. Anhang B: Git in deine Anwendungen einbetten
- A2.1 Die Git-Kommandozeile
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Anhang C: Git Kommandos
- A3.1 Setup und Konfiguration
- A3.2 Projekte importieren und erstellen
- A3.3 Einfache Snapshot-Funktionen
- A3.4 Branching und Merging
- A3.5 Projekte gemeinsam nutzen und aktualisieren
- A3.6 Kontrollieren und Vergleichen
- A3.7 Debugging
- A3.8 Patchen bzw. Fehlerkorrektur
- A3.9 E-mails
- A3.10 Externe Systeme
- A3.11 Administration
- A3.12 Basisbefehle
10.6 Git Interna - Transfer Protokolle
Transfer Protokolle
Git kann Daten zwischen zwei Repositorys hauptsächlich auf zwei Arten übertragen: mittels dem „dummen“ Protokoll und dem „intelligenten“ Protokoll. In diesem Abschnitt wird kurz erläutert, wie diese beiden Hauptprotokolle funktionieren.
Das dumme Protokoll
Wenn du ein Repository einrichtest, das schreibgeschützt über HTTP bereitgestellt wird, wird wahrscheinlich das dumme Protokoll verwendet.
Dieses Protokoll wird als „dumm“ bezeichnet, da es während des Transportvorgangs keinen Git-spezifischen Code auf der Serverseite erfordert. Der Abrufprozess besteht aus einer Reihe von HTTP GET
Abfragen, bei denen der Client das Layout des Git-Repositorys auf dem Server übernehmen kann.
Anmerkung
|
Das dumme Protokoll wird heutzutage ziemlich selten verwendet. Es ist schwierig, es sicher oder privat einzurichten, daher lehnen die meisten Git-Hosts (sowohl cloudbasiert als auch on-premise) die Verwendung ab. Es wird generell empfohlen, das smarte Protokoll zu verwenden, welches wir weiter unten beschreiben. |
Folgen wir dem http-fetch
Prozess für die simplegit-Bibliothek:
$ git clone http://server/simplegit-progit.git
Das erste, was dieser Befehl tut, ist das pullen der Datei info/refs
.
Diese Datei wird mit dem Befehl update-server-info
geschrieben. Aus diesem Grund musst du diesen als post-receive
Hook aktivieren, damit der HTTP-Transport ordnungsgemäß funktioniert:
=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
Jetzt hast du eine Liste der remote Referenzen und der SHA-1s. Als Nächstes suchst du nach der HEAD-Referenz, damit du weißt, was du abschließend überprüfen musst:
=> GET HEAD
ref: refs/heads/master
Du musst den 'master'-Branch auschecken, wenn du den Vorgang abgeschlossen hast.
Jetzt kannst du mit dem laufenden Prozess beginnen.
Da dein Ausgangspunkt das Commit-Objekt ca82a6
ist, das du in der Datei info/refs
gesehen hast, rufst du zunächst Folgendes ab:
=> GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)
Du erhältst ein Objekt zurück – das Objekt befindet sich in losem Format auf dem Server und du hast es über einen statischen HTTP-GET-Aufruf abgerufen. Du kannst es zlib-dekomprimieren, den Header entfernen und den Commit-Inhalt anzeigen:
$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <schacon@gmail.com> 1205815931 -0700
committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
Change version number
Als nächstes musst du zwei weitere Objekte abrufen – cfda3b
, das ist der Inhaltsbaum, auf den das gerade abgerufene Commit verweist; und 085bb3
, welches das übergeordnete Commit ist:
=> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)
Damit hast du dein nächstes Commit-Objekt. Hole dir nun das Baumobjekt:
=> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)
Hoppla – es sieht so aus, als ob das Baum-Objekt auf dem Server nicht im losen Format vorliegt, sodass du eine 404-Antwort erhältst. Dafür gibt es mehrere Gründe: Das Objekt befindet sich möglicherweise in einem alternativen Repository oder in einemr Paketdatei (engl. packfile) in diesem Repository. Git sucht zuerst nach allen gelisteten Alternativen:
=> GET objects/info/http-alternates
(empty file)
Falls dies eine Liste alternativer URLs zurückgibt, sucht Git dort nach losen Dateien und packfiles. Dies ist ein nützlicher Mechanismus für Projekte, die sich gegenseitig forken, um Objekte auf der Festplatte zu teilen.
Da in diesem Fall jedoch keine Alternativen aufgeführt sind, muss sich dein Objekt in einem packfile befinden.
Um zu sehen, welche packfiles auf diesem Server verfügbar sind, musst du die Datei objects/info/packs
abrufen, die eine Liste dieser Dateien enthält (dies wird ebenfalls mittels update-server-info
generiert):
=> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
Es gibt nur ein packfile auf dem Server, sodass sich dein Objekt offensichtlich dort befindet. Überprüfe jedoch den Index, um dies auch sicherzustellen. Dies ist ebenfalls nützlich, wenn sich mehrere packfiles auf dem Server befinden. Du kannst so prüfen, welches packfile das gewünschte Objekt enthält:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)
Nachdem du nun den packfile-Index hast, kannst du sehen, ob sich dein Objekt darin befindet. Der Index listet die SHA-1-Werte der in dem packfile enthaltenen Objekte und die Offsets zu diesen Objekten. Dein Objekt ist vorhanden, also holen dir das ganze packfile:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)
Nun hast du dein Baumobjekt und kannst deine Commits weiter durchgehen.
Sie befinden sich alle in dem gerade heruntergeladenen packfile, sodass du keine weiteren Anfragen an deinen Server stellen musst.
Git checkt eine Arbeitskopie des master
Branches aus, auf die in der HEAD-Referenz verwiesen wurde, die du zu Beginn heruntergeladen hast.
Das smarte Protokoll
Das dumme Protokoll ist einfach, aber ein bisschen ineffizient. Es kann keine Daten vom Client auf den Server schreiben. Das Smart-Protokoll wird oft zum Übertragen von Daten genutzt. Es erfordert jedoch einen intelligenten Git-Prozess auf der Remote-Seite. Es kann lokale Daten lesen, herausfinden, was der Client hat und benötigt, und eine benutzerdefiniertes packfile dafür generieren. Es gibt zwei Arten von Prozessen um Daten zu übertragen: zwei zum Hochladen von Daten und zwei zum Herunterladen von Daten.
Daten hochladen
Um Daten remote hochzuladen, verwendet Git die Prozesse send-pack
und receive-pack
.
Der send-pack
Prozess wird auf dem Client ausgeführt und stellt eine Verbindung zu einem receive-pack
Prozess auf der Remote-Seite her.
SSH
Angenommen, du führst in deinem Projekt git push origin master
aus, und origin
ist als URL definiert, die das SSH-Protokoll verwendet.
Git startet den send-pack
Prozess, der eine Verbindung über SSH zu deinem Server aufbaut.
Es wird versucht, einen Befehl auf dem Remote-Server über einen SSH-Aufruf auszuführen, der in etwa so aussieht:
$ ssh -x git@server "git-receive-pack 'simplegit-progit.git'"
00a5ca82a6dff817ec66f4437202690a93763949 refs/heads/master□report-status \
delete-refs side-band-64k quiet ofs-delta \
agent=git/2:2.1.1+github-607-gfba4028 delete-refs
0000
Der Befehl git-receive-pack
antwortet sofort mit einer Zeile für jede Referenz, die er derzeit hat – in diesem Fall nur den master
Branch und seinen SHA-1.
Die erste Zeile enthält auch eine Liste der Serverfunktionen (hier report-status
, delete-refs
und einige andere, einschließlich der Client-ID).
Die Daten werden in Paketen (eng. chunks) übertragen. Jeder Chunk beginnt mit einem 4-stelligen Hex-Wert, der angibt, wie lang der Chunk ist (einschließlich der 4 Bytes der Gesamtlänge). Die Pakete enthalten in der Regel eine einzige Zeile mit Daten und einen abschließenden Zeilenvorschub. Dein erster Chunk beginnt mit 00a5, also hexadezimal für 165, womit das Paket 165 Bytes lang ist. Der nächste Chunk ist 0000, d.h. der Server ist mit seiner Referenzliste fertig.
Jetzt, da der Server-Status bekannt ist, bestimmt dein send-pack
Prozess, welche Commits er hat, die der Server nicht hat.
Für jede Referenz, die durch diesen Push aktualisiert wird, teilt der send-pack
Prozess dem receive-pack
Prozess diese Informationen mit.
Wenn du beispielsweise den master
Branch aktualisieren und einen experiment
Branch hinzufügst, sieht die Antwort von send-pack
möglicherweise so aus:
0076ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6 \
refs/heads/master report-status
006c0000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d \
refs/heads/experiment
0000
Git sendet eine Zeile für jede Referenz, die du aktualisierst, mit der Länge der Zeile, dem alten SHA-1, dem neuen SHA-1 und der Referenz, die aktualisiert wird. Die erste Zeile enthält auch die Funktionen des Clients. Der SHA-1-Wert aller Nullen bedeutet, dass zuvor nichts vorhanden war, da du die experiment-Referenz hinzufügst. Wenn du eine Referenz löschst, siehst du das Gegenteil: Alle Nullen auf der rechten Seite.
Als nächstes sendet der Client ein packfile mit allen Objekten, die der Server noch nicht hat. Schließlich antwortet der Server mit einer Erfolgs- oder Fehlermeldung:
000eunpack ok
HTTP(S)
Der Prozess über HTTP ist fast derselbe, nur das Handshaking ist ein wenig anders. Die Verbindung wird mit folgender Anfrage initiiert:
=> GET http://server/simplegit-progit.git/info/refs?service=git-receive-pack
001f# service=git-receive-pack
00ab6c5f0e45abd7832bf23074a333f739977c9e8188 refs/heads/master□report-status \
delete-refs side-band-64k quiet ofs-delta \
agent=git/2:2.1.1~vmg-bitmaps-bugaloo-608-g116744e
0000
Das ist das Ende der ersten Client-Server-Unterhaltung.
Der Client stellt dann eine weitere Anfrage, diesmal einen POST
, mit den Daten, die send-pack
liefert.
=> POST http://server/simplegit-progit.git/git-receive-pack
Die POST
Abfrage enthält die send-pack
Ausgabe und das packfile als Nutzdaten.
Der Server zeigt dann mit seiner HTTP-Antwort Erfolg oder Fehler an.
Denke daran, dass das HTTP-Protokoll diese Daten möglicherweise zusätzlich in einen „chunked transfer“ Code verpackt.
Daten herunterladen
Wenn du Daten herunterlädst, sind die Prozesse fetch-pack
und upload-pack
beteiligt.
Der Client initiiert einen fetch-Pack
Prozess, der eine Verbindung zu einem upload-pack
Prozess auf der Remote-Seite herstellt, um auszuhandeln, welche Daten nach übertragen werden sollen.
SSH
Wenn du den Abruf über SSH ausführst, führt fetch-pack
in etwa Folgendes aus:
$ ssh -x git@server "git-upload-pack 'simplegit-progit.git'"
Nachdem fetch-pack
eine Verbindung hergestellt hat, sendet upload-pack
in etwas Folgendes zurück:
00dfca82a6dff817ec66f44342007202690a93763949 HEAD□multi_ack thin-pack \
side-band side-band-64k ofs-delta shallow no-progress include-tag \
multi_ack_detailed symref=HEAD:refs/heads/master \
agent=git/2:2.1.1+github-607-gfba4028
003fe2409a098dc3e53539a9028a94b6224db9d6a6b6 refs/heads/master
0000
Dies ist sehr ähnlich zu dem, was receive-pack
antwortet, aber die Einsatzmöglichkeiten sind unterschiedlich.
Außerdem wird zurückgesendet, worauf HEAD verweist (symref=HEAD:refs/heads/master
), sodass der Client weiß, was er überprüfen muss, wenn es sich um einen Klon handelt.
Zu diesem Punkt prüft der fetch-pack
Prozess, über welche Objekte er verfügt, und antwortet mit den Objekten, die er benötigt, indem er „want“ und dann den gewünschten SHA-1 sendet.
Es sendet alle Objekte, die es bereits hat, mit „have“ und dann den SHA-1.
Am Ende dieser Liste wird „done“ geschrieben, um den `upload-pack“-Prozess einzuleiten, der das packfile mit den benötigten Daten sendet:
003cwant ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0009done
0000
HTTP(S)
Der Handshake für einen Abrufvorgang benötigt zwei HTTP Aufrufe.
Das erste ist ein GET
zum selben Endpunkt, der im dummen Protokoll verwendet wird:
=> GET $GIT_URL/info/refs?service=git-upload-pack
001e# service=git-upload-pack
00e7ca82a6dff817ec66f44342007202690a93763949 HEAD□multi_ack thin-pack \
side-band side-band-64k ofs-delta shallow no-progress include-tag \
multi_ack_detailed no-done symref=HEAD:refs/heads/master \
agent=git/2:2.1.1+github-607-gfba4028
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
0000
Dies ist dem Aufrufen von git-upload-pack
über eine SSH-Verbindung sehr ähnlich. Der zweite Austausch wird als separater Aufruf ausgeführt:
=> POST $GIT_URL/git-upload-pack HTTP/1.0
0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7
0032have 441b40d833fdfa93eb2908e52742248faf0ee993
0000
Auch dies ist das gleiche Format wie oben. Die Antwort auf diese Anfrage zeigt Erfolg oder Fehler an und enthält das packfile.
Zusammenfassung der Protokolle
Dieser Abschnitt enthält eine sehr grundlegende Übersicht über die Transfer Protokolle.
Das Protokoll enthält viele andere Funktionen, wie z.B. multi_ack
oder side-band
, die jedoch nicht in diesem Buch behandelt werden.
Wir haben versucht, dir ein Gefühl für das allgemeine Hin und Her zwischen Client und Server zu vermitteln. Wenn du mehr Informationen diesbezüglich benötigst, solltest du dir den Git-Quellcode ansehen.