Warum gibt es zwei Möglichkeiten, eine Datei in Git zu entfernen?

1169

Manchmal schlägt git vor git rm --cached, eine Datei zu entfernen, manchmal git reset HEAD file. Wann soll ich welche verwenden?

BEARBEITEN:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#
Senthess
quelle
20
Warum? Ich würde sagen, das liegt daran, dass sich die Kommandozeilenschnittstelle von git organisch weiterentwickelt hat und nie einer umfassenden Umstrukturierung unterzogen wurde, um die Konsistenz zu gewährleisten. (Wenn Sie nicht einverstanden sind , zur Kenntnis , wie git rmkönnen beide Stufe eine Löschung und auch unstage ein Zusatz )
Roman Starkov
3
@romkyns: Ich stimme zu, dass die Benutzeroberfläche von Git einige Kuriositäten aufweist, da sie sich organisch entwickelt hat, aber das Entfernen ist sicherlich eine umgekehrte Funktion einer Addition. Ist es also nicht logisch rm, sie rückgängig zu machen add? Wie solltest du dich rmverhalten?
Zaz
6
Die einzige tatsächliche Antwort auf Ihre Frage ist, dass direkt nach a git initkein HEADZurücksetzen möglich ist.
Miles Rout
Beste Dokumente dafür: help.github.com/articles/changing-a-remote-s-url
ScottyBlades
4
@Zaz, ich werde meine Meinung geben. rmimpliziert das Löschen in einem Unix-Kontext. Es ist nicht das Gegenteil von Hinzufügen zum Index. Eine Funktion zum Entfernen von Dateien sollte nicht mit Funktionen zum Ändern des Staging-Status überladen werden. Wenn es Implementierungsdetails gibt, die das Kombinieren vereinfachen, deutet dies einfach auf das Fehlen einer durchdachten Abstraktionsebene in git hin, was die Benutzerfreundlichkeit klar machen würde.
Joshua Goldberg

Antworten:

1893

git rm --cached <filePath> Löst eine Datei nicht ab, sondern entfernt tatsächlich die Datei (en) aus dem Repo (vorausgesetzt, sie wurde bereits zuvor festgeschrieben), belässt die Datei jedoch in Ihrem Arbeitsbaum (sodass Sie eine nicht verfolgte Datei erhalten).

git reset -- <filePath>wird unstage abgestufte Änderungen für die jeweilige Datei (en).

Wenn Sie jedoch git rm --cachedeine neue Datei verwenden, die bereitgestellt wird, sieht es im Grunde so aus, als hätten Sie sie gerade nicht bereitgestellt, da sie noch nie festgeschrieben wurde.

Update git 2.24
In dieser neueren Version von git können Sie git restore --stagedstattdessen verwenden git reset. Siehe Git-Dokumente .

Ryan Stewart
quelle
71
Ich würde sagen, git rm --cacheddass die Datei nicht bereitgestellt wird, aber nicht aus dem Arbeitsverzeichnis entfernt wird.
Pierre de LESPINAY
4
Das Entfernen einer zum Hinzufügen bereitgestellten Datei, damit sie nicht mehr bereitgestellt wird, kann sicherlich als "Aufheben der Bereitstellung einer zum Hinzufügen bereitgestellten Datei" bezeichnet werden, oder? Das Endergebnis ist keine inszenierte Löschung , das ist sicher, daher denke ich, dass das Missverständnis völlig verständlich ist.
Roman Starkov
4
In der Regel werden daher git rm --cached <filePath>einige Dateien aus dem Repo entfernt, nachdem festgestellt wurde, dass sie niemals im Repo enthalten sein sollten. Führen Sie diesen Befehl also höchstwahrscheinlich aus und fügen Sie dann die entsprechenden Dateien hinzu gitignore. Hab ich recht?
Adrien Be
13
Bei so vielen Stimmen zu Frage und Antwort würde ich sagen, dass wir anscheinend einen unstageBefehl haben wollen git.
Milosmns
4
"git status" rät jetzt: benutze "git restore --staged <file> ...", um die Bühne zu verlassen
yucer
334

git rm --cachedwird verwendet, um eine Datei aus dem Index zu entfernen. Wenn sich die Datei bereits im Repo befindet, git rm --cachedwird die Datei aus dem Index entfernt und im Arbeitsverzeichnis belassen. Durch ein Commit wird sie nun auch aus dem Repo entfernt. Grundsätzlich hätten Sie nach dem Festschreiben die Version deversioniert und eine lokale Kopie aufbewahrt.

git reset HEAD file(das standardmäßig das --mixedFlag verwendet) unterscheidet sich darin, dass in dem Fall, in dem sich die Datei bereits im Repo befindet, die Indexversion der Datei durch die aus dem Repo (HEAD) ersetzt wird, wodurch die Änderungen an der Datei effektiv aufgehoben werden .

Im Fall einer nicht versionierten Datei wird die gesamte Datei freigegeben, da die Datei nicht im HEAD vorhanden war. In dieser Hinsicht git reset HEAD fileund git rm --cachedsind gleich, aber sie sind nicht gleich (wie im Fall von Dateien erklärt, die bereits im Repo sind)

Auf die Frage Why are there 2 ways to unstage a file in git?- es gibt nie wirklich nur einen Weg, etwas in Git zu tun. das ist das Schöne daran :)

Manojlds
quelle
7
Sowohl die akzeptierte als auch die akzeptierte Antwort sind großartig und erklären, warum Sie eine gegen die andere verwenden würden. Aber sie beantworten nicht direkt die implizite Frage, warum Git zwei verschiedene Methoden vorschlägt. Im ersten Fall im Beispiel des OP wurde gerade ein Git-Init durchgeführt. In diesem Fall schlägt git "git rm --cached" vor, da zu diesem Zeitpunkt keine Commits im Repository vorhanden sind und HEAD daher nicht gültig ist. "git reset HEAD - a" erzeugt: "fatal: Fehler beim Auflösen von 'HEAD' als gültige Referenz."
Sootsnoot
5
Würden Sie mit 'git checkout' nicht alle Änderungen verlieren, die Sie an der Datei vorgenommen haben? Das ist nicht dasselbe wie das Aufheben der Bereitstellung einer Datei, es sei denn, ich habe ein Missverständnis.
John Deighan
there is never really only one way to do anything in git. that is the beauty of it- Hmm warum ? Es ist immer toll, wenn es nur einen offensichtlichen Weg gibt. Dies spart viel Zeit und Gedächtnis im Gehirn))
Oto Shavadze
128

Recht einfach:

  • git rm --cached <file> macht git auf, die Datei vollständig zu verfolgen (belässt sie im Gegensatz zu normalem git rm* im Dateisystem )
  • git reset HEAD <file> Stellt alle Änderungen fest, die seit dem letzten Festschreiben an der Datei vorgenommen wurden (setzt sie jedoch nicht im Dateisystem zurück, entgegen dem Befehlsnamen **). Die Datei bleibt unter Revisionskontrolle.

Wenn die Datei zuvor nicht in der Revisionskontrolle war (dh Sie stellen eine Datei wieder auf, die Sie gerade git addzum ersten Mal bearbeitet haben), haben die beiden Befehle den gleichen Effekt, sodass diese "zwei Möglichkeiten" sind, etwas zu tun ".

* Beachten Sie den Vorbehalt, den @DrewT in seiner Antwort in Bezug git rm --cachedauf eine Datei erwähnt, die zuvor im Repository festgeschrieben wurde . Im Zusammenhang mit dieser Frage, einer Datei, die gerade hinzugefügt und noch nicht festgeschrieben wurde, besteht kein Grund zur Sorge.

** Ich hatte eine peinlich lange Zeit Angst, den Befehl git reset wegen seines Namens zu verwenden - und noch heute schaue ich oft in der Syntax nach, um sicherzugehen, dass ich es nicht vermassle. ( Update : Ich habe mir endlich die Zeit genommen, die Verwendung von git resetauf einer tldr-Seite zusammenzufassen . Jetzt habe ich ein besseres mentales Modell der Funktionsweise und eine Kurzreferenz , wenn ich einige Details vergesse.)

waldyrious
quelle
Es istgit rm <file> --cached
Neonmate
8
Ich glaube wirklich nicht, dass die Bearbeitung dieser Antwort vom 4. August 2015 eine allgemeine Verbesserung war. Möglicherweise hat es die technische Korrektheit behoben (ich fühle mich nicht qualifiziert, dies zu bewerten), aber ich befürchte, es hat den Ton der Antwort viel weniger zugänglich gemacht, indem eine Sprache wie "Deaktiviert die Notwendigkeit, mit dem Verfolgen einer aktuell nicht verfolgten Datei zu beginnen" eingeführt wurde ", und mit Jargon wie" index "und" HEAD "genau das, was Anfänger abschreckt. Wenn jemand kann, bearbeiten Sie bitte, um eine neuere Sprache wiederherzustellen.
Waldyrious
5
Stimmen Sie mit @waldyrious überein. Die ursprüngliche Antwort stammt möglicherweise nicht direkt aus dem Git-Lehrbuch, beantwortete die Frage jedoch auf einem ausreichenden technischen Niveau. Technische Details sollten in Kommentaren präzisiert worden sein, nicht als Änderung, die die ursprüngliche Absicht verdeckte.
Simon Robb
Ich habe die Bearbeitung zurückgesetzt. Ich glaube, dass die Community (in den vorherigen Kommentaren und den Abstimmungen darüber) ausreichend bestätigt hat, dass die Bearbeitung die Klarheit der Antwort beeinträchtigte.
Waldyrious
Hinweis @DrewT warnt davor, dass bei Verwendung rm --cachedund Drücken von Daten, die denselben Zweig ziehen, die Dateien tatsächlich aus ihrem Arbeitsbaum entfernt werden.
Tom Hale
53

Dieser Thread ist etwas alt, aber ich möchte noch eine kleine Demonstration hinzufügen, da es sich immer noch nicht um ein intuitives Problem handelt:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(ohne -q) gibt eine Warnung über die geänderte Datei aus und ihr Exit-Code ist 1, was als Fehler in einem Skript betrachtet wird.

Bearbeiten: Funktioniert git checkout HEAD to-be-modified to-be-removedauch zum Aufheben der Bereitstellung, entfernt die Änderung jedoch vollständig aus dem Arbeitsbereich

Update Git 2.23.0: Von Zeit zu Zeit ändern sich die Befehle. Jetzt git statussagt:

  (use "git restore --staged <file>..." to unstage)

... was für alle drei Arten von Änderungen funktioniert

Daniel Alder
quelle
Vielen Dank, war aus den ersten beiden Antworten (wahrscheinlich nur meine Unkenntnis der Terminologie) nicht ganz klar, dass das Zurücksetzen von Git die Änderungen in der Datei lokal belassen hat (im Gegensatz zum Auschecken von Git, das sie zurücksetzen würde).
Suppenhund
Sie sollten am Anfang eine Warnung bezüglich der Version einfügen, da die alte Version die Dateien in den neuen Versionen löscht
Luis Mauricio vor
@LuisMauricio Welcher Befehl löscht Dateien?
Daniel Alder vor
@ DanielAlder sry, ich habe gerade erneut getestet, es löscht nicht, mein Fehler.
Luis Mauricio vor
36

Wenn Sie versehentlich Dateien bereitgestellt haben, die Sie nicht festschreiben möchten, und sicher sein möchten, dass Sie die Änderungen beibehalten, können Sie auch Folgendes verwenden:

git stash
git stash pop

Dadurch wird HEAD zurückgesetzt und Ihre Änderungen werden erneut angewendet, sodass Sie einzelne Dateien für das Festschreiben erneut bereitstellen können. Dies ist auch hilfreich, wenn Sie vergessen haben, einen Feature-Zweig für Pull-Anforderungen zu erstellen ( git stash ; git checkout -b <feature> ; git stash pop).

ives
quelle
3
Dies ist eine saubere Lösung und viel weniger besorgniserregend als die Eingabe von "git rm"
Subimage
1
git stashhat andere damit verbundene Vorteile, da es Einträge im Reflog erstellt, die dann in Zukunft verfügbar sind. Wenn Sie Zweifel haben, machen Sie ein git stash(z. B. git stash save -u "WIP notes to self"(das '-u' soll alle neuen / nicht verfolgten Dateien in das Stash-Commit aufnehmen) ... und versuchen Sie dann git reflog show stash, die Liste der Stash-Commits und ihrer shas anzuzeigen. Ich empfehle eine Shell Alias ​​wiealias grs="git reflog show stash"
zweiwöchentlich
15

Diese beiden Befehle weisen einige geringfügige Unterschiede auf, wenn sich die betreffende Datei bereits im Repo befindet und unter Versionskontrolle steht (zuvor festgeschrieben usw.):

  • git reset HEAD <file> Stellt die Datei im aktuellen Commit bereit.
  • git rm --cached <file>wird die Datei auch für zukünftige Commits freigeben. Es ist nicht inszeniert, bis es wieder hinzugefügt wird git add <file>.

Und es gibt noch einen wichtigen Unterschied:

  • Nach dem Ausführen git rm --cached <file>und Verschieben Ihres Zweigs auf die Fernbedienung wird die Datei von jedem, der Ihren Zweig von der Fernbedienung abruft, WIRKLICH aus seinem Ordner gelöscht, obwohl die Datei in Ihrem lokalen Arbeitssatz nur nicht mehr verfolgt wird (dh nicht physisch aus dem Ordner gelöscht wird).

Dieser letzte Unterschied ist wichtig für Projekte, die eine Konfigurationsdatei enthalten, in der jeder Entwickler im Team eine andere Konfiguration hat (dh eine andere Basis-URL, IP- oder Porteinstellung). Wenn Sie also git rm --cached <file>jemanden verwenden , der Ihren Zweig zieht, muss dieser manuell neu erstellt werden Erstellen Sie die Konfiguration, oder Sie können ihnen Ihre senden und sie können sie wieder auf ihre IP-Einstellungen (usw.) zurückarbeiten, da das Löschen nur Personen betrifft, die Ihren Zweig von der Fernbedienung abrufen.

DrewT
quelle
10

Angenommen , Sie haben stageein ganzes Verzeichnis über git add <folder>, aber Sie wollen eine Datei ausschließen aus der Liste aufgeführt (dh die Liste , die beim Laufen erzeugt git status) und halten Sie die Änderungen innerhalb der ausgeschlossenen Datei (Sie auf etwas gearbeitet haben und es ist nicht bereit für begehen, aber du willst deine Arbeit nicht verlieren ...). Sie könnten einfach verwenden:

git reset <file>

Wenn Sie ausführen git status, werden Sie sehen, welche Datei (en) Sie resetsind unstagedund welche anderen Dateien Sie addednoch in der stagedListe haben.

jiminikiz
quelle
10

1.

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(benutze "git rm --cached ..." um die Bühne zu verlassen)

  • Git ist ein System von Zeigern

  • Sie haben noch kein Commit, auf das Sie Ihren Zeiger ändern können

  • Die einzige Möglichkeit, Dateien aus dem Eimer zu entfernen , auf den verwiesen wird, besteht darin , Dateien zu entfernen, die Sie git angewiesen haben, auf Änderungen zu achten

2.

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

git commit -ma

  • Sie haben sich verpflichtet, " gerettet "

3.

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(benutze "git reset HEAD ..." um die Bühne zu verlassen)

  • Sie haben zu diesem Zeitpunkt ein Commit in Ihrem Code vorgenommen
  • Jetzt können Sie Ihren Zeiger auf Ihre commit ‚reset wieder zurück zum letzten save
Timothy LJ Stewart
quelle
1
Dies ist eigentlich die einzige Antwort, die die Frage richtig beantwortet, IMO. Es beantwortet tatsächlich die Frage: Was sind nicht die Unterschiede zwischen "git rm --cached" und "git reset HEAD", sondern "warum gibt git inkonsistent beide als Optionen an?". Die Antwort lautet, dass kein HEAD zurückgesetzt werden muss bis wann du git initzum ersten mal.
Miles Rout
5

Ich bin überrascht, dass niemand das Git-Reflog erwähnt hat ( http://git-scm.com/docs/git-reflog ):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

Das Reflog ist ein Git-Verlauf, der nicht nur die Änderungen am Repo verfolgt, sondern auch die Benutzeraktionen (z. B. Ziehen, Auschecken in einen anderen Zweig usw.) und das Rückgängigmachen dieser Aktionen ermöglicht. Anstatt die irrtümlich bereitgestellte Datei zu deaktivieren, können Sie zu dem Punkt zurückkehren, an dem Sie die Dateien nicht bereitgestellt haben.

Dies ist ähnlich wie git reset HEAD <file> jedoch in bestimmten Fällen detaillierter sein.

Tut mir leid - ich beantworte deine Frage nicht wirklich, sondern zeige nur einen anderen Weg, um Dateien, die ich ziemlich oft benutze, zu entfernen (ich mag Antworten von Ryan Stewart und waldyrious sehr.);) Ich hoffe, es hilft.

Alex
quelle
5

Benutz einfach:

git reset HEAD <filename>

Dadurch wird die Datei freigegeben und die Änderungen, die Sie daran vorgenommen haben, bleiben erhalten, sodass Sie wiederum die Zweige ändern können, wenn Sie möchten, und git adddiese Dateien stattdessen in einen anderen Zweig. Alle Änderungen bleiben erhalten.

Edgar Quintero
quelle
3

Es scheint mir, dass git rm --cached <file>die Datei aus dem Index entfernt wird, ohne sie aus dem Verzeichnis zu entfernen, in dem eine Ebene git rm <file>beides tun würde, genauso wie ein Betriebssystem rm <file>die Datei aus dem Verzeichnis entfernen würde, ohne ihre Versionierung zu entfernen.

ernie.cordell
quelle
1

Nur für Versionen 2.23 und höher

Anstelle dieser Vorschläge könnten Sie verwenden , git restore --staged <file>um unstagedie Datei (en).

Kaan Taha Köken
quelle
Es funktioniert sowohl mit den Optionen --stageals auch --staged.
dhana1310