Einrichtung und Konfiguration
Projekte holen und erstellen
Grundlegende Snapshots
Branching und Merging
Projekte teilen und aktualisieren
Inspektion und Vergleich
Patching
Debugging
Externe Systeme
Server-Administration
Anleitungen
- gitattributes
- Konventionen der Kommandozeile
- Tägliches Git
- Häufig gestellte Fragen (FAQ)
- Glossar
- Hooks
- gitignore
- gitmodules
- Revisionen
- Submodule
- Tutorial
- Workflows
- Alle Anleitungen...
Administration
Plumbing-Befehle
- 2.23.1 → 2.52.0 keine Änderungen
-
2.23.0
2019-08-16
- 2.13.7 → 2.22.5 keine Änderungen
-
2.12.5
2017-09-22
- 2.3.10 → 2.11.4 keine Änderungen
-
2.2.3
2015-09-04
- 2.1.4 keine Änderungen
-
2.0.5
2014-12-17
BESCHREIBUNG
Sie sollten gittutorial[7] durcharbeiten, bevor Sie dieses Tutorial lesen.
Das Ziel dieses Tutorials ist es, zwei grundlegende Bestandteile der Git-Architektur vorzustellen – die Objektdatenbank und die Indexdatei – und dem Leser alles Notwendige an die Hand zu geben, um den Rest der Git-Dokumentation zu verstehen.
Die Git-Objektdatenbank
Lassen Sie uns ein neues Projekt starten und eine kleine Historie erstellen
$ mkdir test-project $ cd test-project $ git init Initialized empty Git repository in .git/ $ echo 'hello world' > file.txt $ git add . $ git commit -a -m "initial commit" [master (root-commit) 54196cc] initial commit 1 file changed, 1 insertion(+) create mode 100644 file.txt $ echo 'hello world!' >file.txt $ git commit -a -m "add emphasis" [master c4d59f3] add emphasis 1 file changed, 1 insertion(+), 1 deletion(-)
Was sind die 7 Hex-Ziffern, mit denen Git auf den Commit geantwortet hat?
Wir haben in Teil Eins des Tutorials gesehen, dass Commits Namen wie diesen haben. Es stellt sich heraus, dass jedes Objekt in der Git-Historie unter einem 40-stelligen Hex-Namen gespeichert wird. Dieser Name ist der SHA-1-Hash des Inhalts des Objekts; unter anderem stellt dies sicher, dass Git niemals dieselben Daten doppelt speichert (da identische Daten einen identischen SHA-1-Namen erhalten) und dass der Inhalt eines Git-Objekts niemals geändert wird (da dies den Namen des Objekts ebenfalls ändern würde). Die 7 Zeichen langen Hex-Strings hier sind einfach die Abkürzung solcher 40 Zeichen langen Strings. Abkürzungen können überall dort verwendet werden, wo die 40 Zeichen langen Strings verwendet werden können, solange sie eindeutig sind.
Es ist zu erwarten, dass der Inhalt des Commit-Objekts, das Sie beim Befolgen des obigen Beispiels erstellt haben, einen anderen SHA-1-Hash generiert als der oben gezeigte, da das Commit-Objekt den Zeitpunkt seiner Erstellung und den Namen der Person, die den Commit durchführt, aufzeichnet.
Wir können Git mit dem Befehl cat-file nach diesem speziellen Objekt fragen. Kopieren Sie nicht die 40 Hex-Ziffern aus diesem Beispiel, sondern verwenden Sie die aus Ihrer eigenen Version. Beachten Sie, dass Sie sie auf nur wenige Zeichen kürzen können, um sich das Tippen aller 40 Hex-Ziffern zu ersparen
$ git cat-file -t 54196cc2 commit $ git cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 initial commit
Ein Baum kann sich auf ein oder mehrere "Blob"-Objekte beziehen, die jeweils einer Datei entsprechen. Darüber hinaus kann ein Baum auch auf andere Baum-Objekte verweisen und so eine Verzeichnisstruktur erstellen. Sie können den Inhalt eines jeden Baumes mit ls-tree untersuchen (denken Sie daran, dass ein ausreichend langer Anfangsteil des SHA-1 ebenfalls funktioniert)
$ git ls-tree 92b8b694 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad file.txt
Somit sehen wir, dass dieser Baum eine Datei enthält. Der SHA-1-Hash ist ein Verweis auf die Daten dieser Datei
$ git cat-file -t 3b18e512 blob
Ein "Blob" ist nur Dateidaten, die wir auch mit cat-file untersuchen können
$ git cat-file blob 3b18e512 hello world
Beachten Sie, dass dies die alten Dateidaten sind; das Objekt, das Git in seiner Antwort auf den ersten Baum benannt hat, war also ein Baum mit einem Schnappschuss des Verzeichniszustands, der durch den ersten Commit aufgezeichnet wurde.
Alle diese Objekte werden unter ihren SHA-1-Namen im Git-Verzeichnis gespeichert
$ find .git/objects/ .git/objects/ .git/objects/pack .git/objects/info .git/objects/3b .git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad .git/objects/92 .git/objects/92/b8b694ffb1675e5975148e1121810081dbdffe .git/objects/54 .git/objects/54/196cc2703dc165cbd373a65a4dcf22d50ae7f7 .git/objects/a0 .git/objects/a0/423896973644771497bdc03eb99d5281615b51 .git/objects/d0 .git/objects/d0/492b368b66bdabf2ac1fd8c92b39d3db916e59 .git/objects/c4 .git/objects/c4/d59f390b9cfd4318117afde11d601c1085f241
und der Inhalt dieser Dateien ist nur die komprimierten Daten plus ein Header, der ihre Länge und ihren Typ angibt. Der Typ ist entweder ein Blob, ein Baum, ein Commit oder ein Tag.
Der einfachste zu findende Commit ist der HEAD-Commit, den wir über .git/HEAD finden können
$ cat .git/HEAD ref: refs/heads/master
Wie Sie sehen können, teilt uns dies mit, auf welchem Zweig wir uns gerade befinden, und dies geschieht durch die Benennung einer Datei im .git-Verzeichnis, die selbst einen SHA-1-Namen enthält, der sich auf ein Commit-Objekt bezieht, das wir mit cat-file untersuchen können
$ cat .git/refs/heads/master c4d59f390b9cfd4318117afde11d601c1085f241 $ git cat-file -t c4d59f39 commit $ git cat-file commit c4d59f39 tree d0492b368b66bdabf2ac1fd8c92b39d3db916e59 parent 54196cc2703dc165cbd373a65a4dcf22d50ae7f7 author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500 committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143418702 -0500 add emphasis
Das "tree"-Objekt hier verweist auf den neuen Zustand des Baumes
$ git ls-tree d0492b36 100644 blob a0423896973644771497bdc03eb99d5281615b51 file.txt $ git cat-file blob a0423896 hello world!
und das "parent"-Objekt verweist auf den vorherigen Commit
$ git cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500 initial commit
Das Baumobjekt ist der Baum, den wir zuerst untersucht haben, und dieser Commit ist ungewöhnlich, da er keine Eltern hat.
Die meisten Commits haben nur einen Elternteil, aber es ist auch üblich, dass ein Commit mehrere Elternteile hat. In diesem Fall repräsentiert der Commit einen Merge, wobei die Elternreferenzen auf die Köpfe der zusammengeführten Branches zeigen.
Neben Blobs, Bäumen und Commits ist der einzige verbleibende Objekttyp ein "Tag", den wir hier nicht besprechen werden; siehe git-tag[1] für Details.
Somit wissen wir nun, wie Git die Objektdatenbank zur Darstellung der Historie eines Projekts verwendet
-
"commit"-Objekte verweisen auf "tree"-Objekte, die den Schnappschuss eines Verzeichnisbaums zu einem bestimmten Zeitpunkt in der Historie darstellen, und verweisen auf "parent"-Commits, um zu zeigen, wie sie in die Projekt-Historie eingebunden sind.
-
"tree"-Objekte stellen den Zustand eines einzelnen Verzeichnisses dar und ordnen Verzeichnisnamen "blob"-Objekten mit Dateidaten und "tree"-Objekten mit Unterverzeichnissen zu.
-
"blob"-Objekte enthalten Dateidaten ohne weitere Struktur.
-
Referenzen auf Commit-Objekte an der Spitze jedes Branches werden in Dateien unter .git/refs/heads/ gespeichert.
-
Der Name des aktuellen Branches wird in .git/HEAD gespeichert.
Beachten Sie übrigens, dass viele Befehle einen Baum als Argument entgegennehmen. Aber wie wir oben sehen können, kann ein Baum auf verschiedene Arten referenziert werden – über den SHA-1-Namen dieses Baumes, über den Namen eines Commits, der auf den Baum verweist, über den Namen eines Branches, dessen Kopf auf diesen Baum verweist, usw. – und die meisten solchen Befehle können jede dieser Namen akzeptieren.
In Befehlssynopsen wird das Wort "tree-ish" manchmal verwendet, um ein solches Argument zu bezeichnen.
Die Indexdatei
Das Hauptwerkzeug, das wir zum Erstellen von Commits verwendet haben, ist git-commit -a, das einen Commit erstellt, der alle Änderungen in Ihrem Arbeitsverzeichnis enthält. Aber was ist, wenn Sie nur Änderungen an bestimmten Dateien committen möchten? Oder nur bestimmte Änderungen an bestimmten Dateien?
Wenn wir uns ansehen, wie Commits im Hintergrund erstellt werden, werden wir sehen, dass es flexiblere Wege gibt, Commits zu erstellen.
Setzen wir unser Testprojekt fort, und modifizieren wir file.txt erneut
$ echo "hello world, again" >>file.txt
aber diesmal, anstatt sofort den Commit zu machen, machen wir einen Zwischenschritt und bitten um Diffs, um den Überblick zu behalten, was passiert
$ git diff --- a/file.txt +++ b/file.txt @@ -1 +1,2 @@ hello world! +hello world, again $ git add file.txt $ git diff
Der letzte Diff ist leer, aber es wurden keine neuen Commits gemacht, und der HEAD enthält immer noch nicht die neue Zeile
$ git diff HEAD diff --git a/file.txt b/file.txt index a042389..513feba 100644 --- a/file.txt +++ b/file.txt @@ -1 +1,2 @@ hello world! +hello world, again
Also vergleicht *git diff* etwas anderes als den HEAD. Das, womit es vergleicht, ist tatsächlich die Indexdatei, die in .git/index im Binärformat gespeichert ist, deren Inhalt wir aber mit ls-files untersuchen können
$ git ls-files --stage 100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt $ git cat-file -t 513feba2 blob $ git cat-file blob 513feba2 hello world! hello world, again
Was unser *git add* also getan hat, war, einen neuen Blob zu speichern und dann eine Referenz darauf in die Indexdatei zu legen. Wenn wir die Datei erneut modifizieren, werden wir sehen, dass die neuen Modifikationen in der *git diff*-Ausgabe reflektiert werden
$ echo 'again?' >>file.txt $ git diff index 513feba..ba3da7b 100644 --- a/file.txt +++ b/file.txt @@ -1,2 +1,3 @@ hello world! hello world, again +again?
Mit den richtigen Argumenten kann *git diff* uns auch den Unterschied zwischen dem Arbeitsverzeichnis und dem letzten Commit oder zwischen dem Index und dem letzten Commit anzeigen
$ git diff HEAD diff --git a/file.txt b/file.txt index a042389..ba3da7b 100644 --- a/file.txt +++ b/file.txt @@ -1 +1,3 @@ hello world! +hello world, again +again? $ git diff --cached diff --git a/file.txt b/file.txt index a042389..513feba 100644 --- a/file.txt +++ b/file.txt @@ -1 +1,2 @@ hello world! +hello world, again
Zu jedem Zeitpunkt können wir einen neuen Commit mit *git commit* (ohne die Option "-a") erstellen und verifizieren, dass der committete Zustand nur die im Index gespeicherten Änderungen enthält, nicht die zusätzliche Änderung, die sich noch nur in unserem Arbeitsverzeichnis befindet
$ git commit -m "repeat" $ git diff HEAD diff --git a/file.txt b/file.txt index 513feba..ba3da7b 100644 --- a/file.txt +++ b/file.txt @@ -1,2 +1,3 @@ hello world! hello world, again +again?
Standardmäßig verwendet *git commit* den Index zur Erstellung des Commits, nicht das Arbeitsverzeichnis; die Option "-a" zu commit weist es an, den Index zuerst mit allen Änderungen im Arbeitsverzeichnis zu aktualisieren.
Schließlich lohnt es sich, die Auswirkung von *git add* auf die Indexdatei zu betrachten
$ echo "goodbye, world" >closing.txt $ git add closing.txt
Die Auswirkung von *git add* war, einen Eintrag zur Indexdatei hinzuzufügen
$ git ls-files --stage 100644 8b9743b20d4b15be3955fc8d5cd2b09cd2336138 0 closing.txt 100644 513feba2e53ebbd2532419ded848ba19de88ba00 0 file.txt
Und, wie Sie mit cat-file sehen können, verweist dieser neue Eintrag auf den aktuellen Inhalt der Datei
$ git cat-file blob 8b9743b2 goodbye, world
Der Befehl "status" ist eine nützliche Möglichkeit, eine schnelle Zusammenfassung der Situation zu erhalten
$ git status On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: closing.txt Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: file.txt
Da der aktuelle Zustand von closing.txt im Index zwischengespeichert ist, wird er als "Changes to be committed" aufgeführt. Da file.txt Änderungen im Arbeitsverzeichnis hat, die sich nicht im Index widerspiegeln, wird es als "changed but not updated" markiert. Zu diesem Zeitpunkt würde die Ausführung von "git commit" einen Commit erstellen, der closing.txt (mit seinem neuen Inhalt) hinzufügt, aber file.txt nicht modifiziert.
Beachten Sie auch, dass ein reines git diff die Änderungen an file.txt anzeigt, aber nicht die Hinzufügung von closing.txt, da die Version von closing.txt in der Indexdatei mit der im Arbeitsverzeichnis identisch ist.
Neben der Tatsache, dass die Indexdatei als Staging-Bereich für neue Commits dient, wird sie auch beim Auschecken eines Branches aus der Objektdatenbank gefüllt und verwendet, um die an einem Merge-Vorgang beteiligten Bäume zu speichern. Siehe gitcore-tutorial[7] und die entsprechenden Manpages für Details.
Was nun?
An diesem Punkt sollten Sie alles wissen, was notwendig ist, um die Manpages für jeden der Git-Befehle zu lesen; ein guter Ausgangspunkt wären die Befehle, die in giteveryday[7] erwähnt werden. Sie sollten in gitglossary[7] unbekannte Fachbegriffe finden können.
Das Git User's Manual bietet eine umfassendere Einführung in Git.
gitcvs-migration[7] erklärt, wie man ein CVS-Repository in Git importiert und zeigt, wie man Git auf CVS-ähnliche Weise verwendet.
Einige interessante Beispiele für die Git-Nutzung finden Sie in den howtos.
Für Git-Entwickler geht gitcore-tutorial[7] detailliert auf die Mechanismen auf niedrigerer Ebene von Git ein, die beispielsweise bei der Erstellung eines neuen Commits eine Rolle spielen.
GIT
Teil der git[1] Suite