Zwei sind gleich; man ist es nicht, außer unter besonderen Umständen.
Um dies zu verstehen, denken Sie daran:
- Ein Commit enthält eine Momentaufnahme aller Dateien, von denen Git wusste, in der Form, in der sie sich befanden, als Sie sagten, sie zu committen.
- der Schnappschuss wird gemacht von die Dateien, die sich in Gits Index befinden, auch bekannt als Staging-Area, auch bekannt als Cache (drei Begriffe für dasselbe); und
git add
meint Machen Sie die Kopie im Index/Staging-Bereich/Cache mit der Kopie in meinem Arbeitsbaum übereinstimmen (durch Kopieren aus dem Arbeitsbaum, wenn die Kopie des Arbeitsbaums aktualisiert wird, oder durch Entfernen aus dem Index, wenn die Kopie des Arbeitsbaums entfernt wird).
Der Index / Staging-Bereich enthält also zu jeder Zeit Ihre nächsten Commit vorgeschlagenund wurde ursprünglich von Ihrem ausgesät aktuelle Verpflichtung wenn du a gemacht hast git checkout
oder git switch
um dieses Commit zu erhalten.1 Ihr Arbeitsbaum enthält also a Dritter Kopieren2 jeder Datei, wobei die ersten beiden Kopien die in der sind aktuelle Verpflichtung auch bekannt HEAD
und die im Index.
In diesem Sinne macht jeder Ihrer Befehle Folgendes:
-
git rm --cached file
: Entfernt die Kopie der Datei aus dem Index / Staging-Bereich, ohne die Kopie des Arbeitsbaums zu berühren. Das vorgeschlagene nächste Commit jetzt fehlt die Datei. Wenn das aktuelle Commit hat die Datei, und Sie machen an dieser Stelle tatsächlich einen nächsten Commit, der Unterschied zwischen dem vorherigen Commit und dem neuen Commit besteht darin, dass die Datei weg ist.
-
git restore --staged file
: Git kopiert die Datei aus der HEAD
in den Index übernehmen, ohne die Kopie des Arbeitsbaums zu berühren. Die Indexkopie und die HEAD
Jetzt kopieren übereinstimmen, unabhängig davon, ob sie vorher übereinstimmten oder nicht. Ein neuer Commit, der jetzt gemacht wird, wird die haben gleich Kopie der Datei als aktuellen Commit.
Wenn das aktuelle Commit fehlt die Datei, dies hat die Wirkung von entfernen die Datei aus dem Index. So in diesem Fall es macht das gleiche wie git rm --cached
.
-
git reset file
: Dies kopiert die HEAD
Version der Datei in den Index, genau wie git restore --staged file
.
(Beachten Sie, dass git restore
im Gegensatz zu dieser besonderen Form von git reset
, kann Überschreiben Sie die Kopie des Arbeitsbaums einer Datei, wenn Sie dazu aufgefordert werden. Die --staged
Option, ohne die --worktree
Option, weist sie an, nur in den Index zu schreiben.)
Randnotiz: Viele Leute denken zunächst, dass der Index / Staging-Bereich nur Änderungen oder nur geänderte Dateien enthält. Dies ist nicht der Fall, aber wenn Sie so darüber nachdenken, git rm --cached
scheint das gleiche zu sein wie die anderen beiden. Da der Index nicht so funktioniert, ist es nicht so.
1Es gibt einige skurrile Randfälle, wenn Sie etwas inszenieren und dann etwas Neues machen git checkout
. Wenn es möglich ist, eine andere bereitgestellte Kopie beizubehalten, wird Git dies im Wesentlichen tun. Die blutigen Details finden Sie unter Anderen Zweig auschecken, wenn nicht festgeschriebene Änderungen im aktuellen Zweig vorhanden sind.
2Die festgeschriebene Kopie und jede bereitgestellte Kopie werden tatsächlich in Form eines internen Git aufbewahrt Blob-Objekt, die Inhalte dedupliziert. Wenn diese beiden also übereinstimmen, teilen sie sich buchstäblich nur eine zugrunde liegende Kopie. Wenn sich die bereitgestellte Kopie von der unterscheidet HEAD
kopieren, aber mit irgendeiner – vielleicht sogar vielen – anderen existierenden festgeschriebenen Kopie(n) übereinstimmt, teilt sich die bereitgestellte Kopie den zugrunde liegenden Speicher mit all diesen anderen Festschreibungen. Es ist also übertrieben, jeden als “Kopie” zu bezeichnen. Aber als mentales Modell funktioniert es gut genug: Keines kann jemals überschrieben werden; eine neue git add
erstellt bei Bedarf ein neues Blob-Objekt, und wenn am Ende niemand ein Blob-Objekt verwendet, verwirft Git es schließlich.
Ein konkretes Beispiel
In einem Kommentar sagt pavel_orekhov:
Mir ist immer noch nicht klar, wo sich “git rm –cached” und “git restore –staged” unterscheiden. Könnten Sie bitte eine Reihe von Befehlen mit diesen 2 zeigen, die sich unterschiedlich verhalten?
Schauen wir uns einen bestimmten Commit im Git-Repository für Git selbst an (klonen Sie ihn bei Bedarf zuerst, z https://github.com/git/git.git):
$ git switch --detach v2.35.1
HEAD is now at 4c53a8c20f Git 2.35.1
Ihr Arbeitsbaum enthält Dateien mit dem Namen Makefile
, README.md
, git.c
usw.
Lass uns jetzt Ändern Sie eine vorhandene Datei im Arbeitsbaum:
$ ed Makefile << end
> 1a
> foo
> .
> w
> q
> end
107604
107608
$ git status --short
M Makefile
Die >
Zeichen kommen von der Shell, die um Eingabe bittet; die beiden Zahlen sind die Byte-Anzahl der Datei Makefile
. Beachten Sie die Ausgabe von git status
ist SPACEMSPACEMakefile
was darauf hinweist, dass die Index oder Bühnenbereich Kopie von Makefile
entspricht dem HEAD
Kopie von Makefile
während Arbeitsbaum Kopie von Makefile
unterscheidet sich von der Index Kopie von Makefile
.
(Nebenbei: Ich habe versehentlich zwei hinzugefügt foo
Linien, während Sie den Text zum Ausschneiden und Einfügen vorbereiten. Ich werde nicht zurückgehen und es beheben, aber wenn Sie dieses Experiment selbst durchführen, erwarten Sie etwas andere Ergebnisse.)
Lass uns jetzt git add
diese aktualisierte Datei, dann ersetzen foo
in der ersten Zeile mit bar
:
$ git add Makefile
$ git status --short
M Makefile
Notiere dass der M
hat sich um eine Spalte nach links bewegt, M-Leerzeichen-Leerzeichen-Makefile, was darauf hinweist, dass die Index Kopie von Makefile
unterscheidet sich von der HEAD
kopieren, aber jetzt stimmen die Index- und Arbeitsbaumkopien überein. Jetzt machen wir den foo-to-bar-Ersatz:
$ ed Makefile << end
> 1s/foo/bar/
> w
> q
> end
107608
107608
$ git status --short
MM Makefile
Wir haben nun zwei M
s: die HEAD
Kopie von Makefile
unterscheidet sich von der Indexkopie von Makefile
die sich von der Arbeitsbaumkopie von unterscheidet Makefile
. Betrieb git diff --cached
und git diff
zeigt Ihnen genau, wie sich jede Paarung vergleicht.
$ git diff --cached
diff --git a/Makefile b/Makefile
index 5580859afd..8b8fc5a6d6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
-# The default target of this Makefile is...
+foo
+foo
all::
# Define V=1 to have a more verbose compile.
$ git diff
diff --git a/Makefile b/Makefile
index 8b8fc5a6d6..96a787d50d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-foo
+bar
foo
all::
Wenn wir jetzt laufen git rm --cached Makefile
dieser Wille Entfernen Sie die Indexkopie der Datei Makefile
vollständigund git status
wird sich entsprechend ändern. Da wir all diese Modifikationen haben, fordert Git auch das “force”-Flag:
$ git rm --cached Makefile
error: the following file has staged content different from both the
file and the HEAD:
Makefile
(use -f to force removal)
$ git rm --cached -f Makefile
rm 'Makefile'
$ git status --short
D Makefile
?? Makefile
Wir haben nun nein Datei benannt Makefile
in unserer nächsten Commit vorgeschlagen im Index / Staging-Bereich. Allerdings die Datei Makefile
erscheint immer noch (mit der ersten Zeile Lesung bar
) in der Arbeitsstruktur (überprüfen Sie die Datei selbst, um sie zu sehen). Diese Makefile
ist ein ungetrackte Datei so erhalten wir zwei Ausgabezeilen von git status --short
eine, um den bevorstehenden Untergang der Datei anzukündigen Makefile
im nächsten Commit und der andere, um die Existenz der nicht verfolgten Datei anzukündigen Makefile
.
Ohne ein Commit zu machen, verwenden wir jetzt git restore --staged Makefile
:
$ git restore --staged Makefile
$ git status --short
M Makefile
Der Status ist jetzt wieder Leerzeichen-M, was darauf hinweist Makefile
im Index vorhanden (und wird daher im nächsten Commit sein), und außerdem entspricht dem HEAD
Kopie des Makefilesso git diff --staged
– was eine andere Schreibweise ist git diff --cached
– wird es nicht zeigen (und wird tatsächlich nichts zeigen). Die Arbeitsbaum Kopie bleibt ungestört und enthält immer noch die zusätzliche Zeile bar
als git diff
zeigt an:
$ git diff --staged
$ git diff
diff --git a/Makefile b/Makefile
index 5580859afd..96a787d50d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
-# The default target of this Makefile is...
+bar
+foo
all::
# Define V=1 to have a more verbose compile.
Auch hier ist der Schlüssel zum Verständnis all dessen:
-
Jeder Commit enthält a vollständige Momentaufnahme jeder Datei, die Git kennt.
-
Dieser Snapshot ist jederzeit in Git vorhanden Indexdie Git auch als bezeichnet Bühnenbereichoder gelegentlich – jetzt meistens in der --cached
Flagge – die Zwischenspeicher. Die --staged
oder --cached
Flagge3 allgemein bedeutet machen Sie etwas mit diesem Index / Staging-Bereich. Befehle wie git reset
, git rm
und git add
implizit mit dem Index / Staging-Bereich arbeiten, obwohl Flags dieses Verhalten etwas modifizieren können; der git restore
Befehl hat die explizite --staged
und --worktree
Flaggen.
-
In der Zwischenzeit enthält Ihr Arbeitsbaum gewöhnliche Alltagsdateien. Dies sind die einzigen Dateien, die Sie direkt sehen und bearbeiten können (zum Beispiel mit Ihrem Editor); nur Git-Befehle kann die festgeschriebenen und indizierten Kopien von Dateien sehen und damit arbeiten.
-
Engagiert Kopien von Dateien können niemals geändert werden. Sie sind in diesen Commits für immer (oder solange diese Commits bestehen bleiben): Sie sind schreibgeschützt. Allerdings ist die Index Kopie einer Datei kann en gros ersetzt werden, mit git add
oder gepatcht, mit git add -p
oder ganz entfernt, mit git rm
oder git rm --cached
.
-
Gewöhnliche Dateien sind, nun ja, gewöhnliche Dateien: Alle Ihre gewöhnlichen Befehle funktionieren ganz normal mit den gewöhnlichen Dateien. (Und ist es nicht außergewöhnlich, wie amüsant das gewöhnliche Wort „gewöhnlich“ ist?)
-
Betrieb git commit
nimmt alles Index kopiert und friert sie in einen neuen Snapshot ein. Was Sie also tun, wenn Sie in Git arbeiten, ist:
- gewöhnliche Dateien auf gewöhnliche Weise manipulieren;
git add
sie, um die Indexkopie von Git zu aktualisieren und das Einfrieren vorzubereiten; und
git commit
das Ergebnis, sie für alle Zeit einzufrieren.
Dies ist der Prozess zur Herstellung von a neues Commitmentund wenn Sie Ihre Meinung ändern und entscheiden nicht um ein neues Commit zu machen, git restore --staged
oder git reset
kann benutzt werden um wieder extrahieren eine festgeschriebene Kopie in die Indexkopie. Aber git rm
entfernt eine Indexkopie vollständig.
Also wenn und nur wenn Entfernen Sie die Indexkopie vollständig stellt die Dinge wieder so her, wie sie waren (was passieren kann, wenn eine Datei Neu), dann “passen Sie die Indexkopie mit der nicht vorhandenen an HEAD
kopieren, indem Sie es entfernen” ist ein korrekter Weg, um das zu tun, was Sie wollen. Aber wenn die HEAD
commit enthält eine Kopie der betreffenden Datei, git rm --cached the-file
ist falsch.
3Beachten Sie, dass --cached
und --staged
habe den selbe Bedeutung Pro git diff
. Für git rm
aber es gibt einfach keine --staged
Möglichkeit überhaupt. Warum? Das ist eine Frage für die Git-Entwickler, aber wir können das historisch, in der fernen Vergangenheit, feststellen git diff
hatte nicht --staged
entweder. Meine beste Vermutung ist daher, dass es ein Versehen war: als wer auch immer hinzufügte --staged
zu git diff
getan, sie haben vergessen hinzuzufügen --staged
zu git rm
zu.