Git: So entfernen Sie Dateien aus dem Index, ohne Dateien aus einem Repository zu löschen

163

Wenn Sie verwenden

git rm --cached myfile

Es wird nicht aus dem lokalen Dateisystem gelöscht, was das Ziel ist. Wenn Sie die Datei jedoch bereits versioniert und festgeschrieben, in ein zentrales Repository verschoben und vor Verwendung des Befehls in ein weiteres Repository verschoben haben, wird die Datei von diesem System gelöscht.

Gibt es eine Möglichkeit, die Datei einfach aus der Versionierung zu entfernen, ohne sie aus einem Dateisystem zu löschen?

Edit: Klar, hoffe ich.

Fletcher Moore
quelle
Vielleicht könnten Sie Ihren Anwendungsfall erweitern. Der Index ist der Staging-Bereich für das nächste Commit. Warum möchten Sie die Datei aus dem Index entfernen, wenn Sie sie nicht aus dem aktuellen Zweig entfernen möchten?
CB Bailey
1
Es tut mir Leid. Ich wollte damit sagen, dass myfile aus irgendeinem Grund vor langer Zeit festgeschrieben und verteilt wurde. Jetzt ist das Ziel, es aus der Versionierung zu entfernen, ohne es von den Systemen der Leute zu löschen. Der Grund dafür ist, dass ich versehentlich eine Konfigurationsdatei versioniert habe. Die Konfigurationsdatei ist erforderlich, daher möchte ich sie nicht von fremden Systemen entfernen.
Fletcher Moore
Ich habe die Frage bearbeitet, um klarer zu sein.
Fletcher Moore
3
git rm --cached entfernt die Datei nicht aus dem anderen Arbeitsverzeichnis. Die Datei wird nur entfernt, wenn jemand, der in diesem Verzeichnis arbeitet, einen Pull ausführt. Die Datei kann leicht mit "git checkout HEAD @ {1} foo" wiederhergestellt werden (wenn sie unmittelbar nach dem Ziehen ausgeführt wird)
William Pursell

Antworten:

119

Ich glaube nicht, dass ein Git-Commit eine Absicht wie "Verfolgen Sie die Verfolgung dieser Datei, aber löschen Sie sie nicht" aufzeichnen kann.

Um eine solche Absicht zu verwirklichen, muss außerhalb von Git in alle Repositorys eingegriffen werden, die ein Commit zusammenführen (oder neu aufbauen), bei dem die Datei gelöscht wird.


Kopie speichern, Löschung anwenden, Wiederherstellen

Am einfachsten ist es wahrscheinlich, Ihre nachgeschalteten Benutzer anzuweisen, eine Kopie der Datei zu speichern, Ihre Löschung abzurufen und die Datei dann wiederherzustellen. Wenn sie über Rebase ziehen und Änderungen an der Datei "tragen", kommt es zu Konflikten. Verwenden Sie zum Lösen solcher Konflikte git rm foo.conf && git rebase --continue(wenn das widersprüchliche Commit neben den Änderungen an der entfernten Datei Änderungen aufweist) oder git rebase --skip(wenn sich das widersprüchliche Commit nur in der entfernten Datei geändert hat).

Stellen Sie die Datei als nicht verfolgt wieder her, nachdem Sie einen Commit gezogen haben, der sie löscht

Wenn sie Ihr Lösch-Commit bereits gezogen haben, können sie die vorherige Version der Datei mit git show wiederherstellen :

git show @{1}:foo.conf >foo.conf

Oder mit Git Checkout (laut Kommentar von William Pursell; aber denken Sie daran, ihn wieder aus dem Index zu entfernen!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

Wenn sie seit dem Abrufen Ihrer Löschung andere Maßnahmen ergriffen haben (oder mit Rebase in einen abgetrennten HEAD ziehen), benötigen sie möglicherweise etwas anderes als @{1}. Sie können git log -gdas Commit verwenden, bevor sie Ihre Löschung vorgenommen haben.


In einem Kommentar erwähnen Sie, dass die Datei, die Sie "aufspüren, aber behalten" möchten, eine Art Konfigurationsdatei ist, die zum Ausführen der Software erforderlich ist (direkt aus einem Repository).

Behalten Sie die Datei als 'Standard' bei und aktivieren Sie sie manuell / automatisch

Wenn es nicht völlig inakzeptabel ist, den Inhalt der Konfigurationsdatei weiterhin im Repository zu verwalten, können Sie die verfolgte Datei möglicherweise von (z. B.) foo.confin umbenennen foo.conf.defaultund Ihre Benutzer cp foo.conf.default foo.confnach dem Anwenden des Umbenennungs-Commits anweisen . Wenn die Benutzer bereits einen vorhandenen Teil des Repositorys verwenden (z. B. ein Skript oder ein anderes Programm, das durch den Inhalt des Repositorys konfiguriert ist (z. B. Makefileoder ähnliches)), um Ihre Software zu starten / bereitzustellen, können Sie einen Standardmechanismus in den Start / integrieren. Bereitstellungsprozess:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

Mit einem solchen Standardmechanismus sollten Benutzer in der Lage sein, ein Commit abzurufen, foo.confin das umbenannt wird, foo.conf.defaultohne zusätzliche Arbeit leisten zu müssen. Außerdem müssen Sie eine Konfigurationsdatei nicht manuell kopieren, wenn Sie in Zukunft zusätzliche Installationen / Repositorys vornehmen.

Das Umschreiben des Verlaufs erfordert ohnehin manuelle Eingriffe…

Wenn es nicht akzeptabel ist, den Inhalt im Repository zu verwalten, möchten Sie ihn wahrscheinlich mit so etwas wie vollständig aus dem Verlauf entfernen git filter-branch --index-filter …. Dies entspricht einem Umschreiben des Verlaufs, für das für jeden Zweig / jedes Repository ein manueller Eingriff erforderlich ist (siehe Abschnitt „Wiederherstellen von Upstream-Rebase“ in der Manpage zur Git-Rebase ). Die spezielle Behandlung, die für Ihre Konfigurationsdatei erforderlich ist, ist nur ein weiterer Schritt, den Sie beim Wiederherstellen nach dem Umschreiben ausführen müssen:

  1. Speichern Sie eine Kopie der Konfigurationsdatei.
  2. Erholen Sie sich vom Umschreiben.
  3. Stellen Sie die Konfigurationsdatei wieder her.

Ignorieren Sie es, um Wiederholungen zu vermeiden

Unabhängig von der verwendeten Methode möchten Sie wahrscheinlich den Konfigurationsdateinamen in eine .gitignoreDatei im Repository aufnehmen, damit niemand versehentlich git add foo.confwieder darauf zugreifen kann (dies ist möglich, erfordert jedoch -f/ --force). Wenn Sie mehr als eine Konfigurationsdatei haben, können Sie sie alle in ein einziges Verzeichnis verschieben und das Ganze ignorieren (mit "Verschieben" meine ich das Ändern, wo das Programm seine Konfigurationsdateien erwartet, und das Abrufen der Benutzer (oder) der Start / deploy - Mechanismus) zum kopieren / die Dateien auf ihre neue Position verschieben, würden Sie wollen natürlich nicht auf mv git eine Datei in ein Verzeichnis , dass Sie ignorieren).

Chris Johnsen
quelle
5
Unglaublich gründliche Antwort. Danke dir!
Fletcher Moore
Die Antwort von Tom Power unten scheint Ihrem ersten Satz zu widersprechen.
Mike S
1
@MikeS: Der Effekt von --{,no-}assume-unchangedist rein lokal: Sein Status wird nicht direkt in Commits aufgezeichnet. Es kann verhindern, dass ein Repository neue Änderungen an der Datei festnimmt, entfernt sie jedoch nicht aus der Versionskontrolle. Wenn Sie es für alle relevanten, verwandten, nicht nackten Repositorys festlegen können, kann dies Ihrer Situation helfen, aber Sie können es nicht direkt in andere verwandte, nicht nackte Repositorys (insbesondere solche, die dies tun) drücken + ziehen / neu gründen Sie kontrollieren nicht, wie aus den klarstellenden Kommentaren des ursprünglichen Fragestellers zu der Frage hervorgeht: siehe „Volkssysteme“ / „fremde Systeme“).
Chris Johnsen
1
I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.- Jetzt kann es mitgit rm --cached foo.conf
Nick Volynkin
1
@NickVolynkin: Zeigt die Frage nicht bereits an, dass dies für den Zweck des Fragestellers nicht ausreichend ist: (automatisch) die Datei behalten, wenn das resultierende Commit in ein anderes Repository gezogen wird?
Chris Johnsen
86

Hatte diese Woche das gleiche Problem, als ich versehentlich einen Commit durchführte und dann versuchte, eine Build-Datei aus einem freigegebenen Repository zu entfernen.

http://gitready.com/intermediate/2009/02/18/temporARY-ignoring-files.html

hat gut für mich funktioniert und bisher nicht erwähnt.

git update-index --assume-unchanged <file>

Um die Datei, an der Sie interessiert sind, aus der Versionskontrolle zu entfernen, verwenden Sie wie gewohnt alle anderen Befehle.

git update-index --no-assume-unchanged <file>

Wenn Sie es jemals wieder einsetzen wollten.

Bearbeiten: Bitte beachten Sie die Kommentare von Chris Johnsen und KPM. Dies funktioniert nur lokal und die Datei bleibt für andere Benutzer unter Versionskontrolle, wenn sie dies nicht auch tun. Die akzeptierte Antwort enthält vollständigere / korrektere Methoden, um damit umzugehen. Auch einige Hinweise aus dem Link bei Verwendung dieser Methode:

Offensichtlich gibt es einige Einschränkungen, die damit ins Spiel kommen. Wenn Sie die Datei direkt hinzufügen, wird sie dem Index hinzugefügt. Wenn Sie ein Commit mit diesem Flag zusammenführen, schlägt die Zusammenführung ordnungsgemäß fehl, sodass Sie sie manuell verarbeiten können.

Tom Power
quelle
7
Dies ist keine Antwort auf das erwähnte spezifische Problem. Es funktioniert nur für Ihr eigenes lokales Repository, daher muss jeder Benutzer dies selbst tun. Es ist nervtötend.
KPM
28

Verwenden Sie Folgendes, um die Datei aus dem Index zu entfernen:

git reset myfile

Dies sollte sich nicht auf Ihre lokale Kopie oder die anderer Personen auswirken.

Armand
quelle
1
resetEntfernt die Datei nur aus dem Index, wenn sich die Datei nicht im aktuellen HEAD-Commit befindet. Andernfalls wird nur die Indexversion auf die aktuelle HEAD-Version zurückgesetzt.
CB Bailey
1
Vielleicht habe ich die Frage falsch verstanden, aber sie scheint sich auf das Entfernen einer Datei aus dem Index zu beziehen, ohne dass dies Auswirkungen auf Commits hat.
Armand
Wie Charles sagte, entfernt das Zurücksetzen keine "Datei". Geben Sie git help reset ein, um weitere Informationen zu erhalten.
Simon B.
4
Dies beantwortet die Frage mit dem Titel "So entfernen Sie Dateien aus dem Index, ohne Dateien aus einem Repository zu löschen". Obwohl das OP wirklich fragte: "Wie entferne ich eine Datei, ohne die lokale Kopie zu löschen?"
Hewsonism
@hewsonism das OP sagte "jedes Dateisystem" (noch vor Änderungen).
Jbobbins
19
  1. git rm --cached remove_file
  2. Datei zum Gitignore hinzufügen
  3. git add .gitignore
  4. git commit -m "Excluding"
  5. Habe Spaß ;)
Анастасия Ермолик
quelle
1
Das ist am einfachsten.
Julien
15

git rm --cachedVersuchen Sie myfilenach dem Ausführen des Befehls, der .gitignoreDatei etwas hinzuzufügen (erstellen Sie eine, falls sie nicht vorhanden ist). Dies sollte git anweisen, zu ignorieren myfile.

Die .gitignoreDatei ist versioniert, daher müssen Sie sie festschreiben und in das Remote-Repository übertragen.

awgy
quelle
1

Meine Lösung besteht darin, die andere Arbeitskopie abzurufen und dann Folgendes zu tun:

git log --pretty="format:" --name-only -n1 | xargs git checkout HEAD^1

Hier wird angegeben, dass alle Dateipfade im neuesten Kommentar abgerufen und vom übergeordneten Element von HEAD ausgecheckt werden sollen. Job erledigt.

Tom Viner
quelle
0

Die oben genannten Lösungen funktionieren in den meisten Fällen einwandfrei. Wenn Sie jedoch auch alle Spuren dieser Datei entfernen müssen (z. B. vertrauliche Daten wie Kennwörter), möchten Sie sie auch aus Ihrem gesamten Festschreibungsverlauf entfernen, da die Datei möglicherweise noch von dort abgerufen werden kann.

Hier ist eine Lösung, die alle Spuren der Datei aus Ihrem gesamten Festschreibungsverlauf entfernt, als ob sie nie existiert hätte, die Datei jedoch auf Ihrem System an Ort und Stelle hält.

https://help.github.com/articles/remove-sensitive-data/

Sie können tatsächlich mit Schritt 3 fortfahren, wenn Sie sich in Ihrem lokalen Git-Repository befinden und keinen Probelauf durchführen müssen. In meinem Fall brauchte ich nur die Schritte 3 und 6, da ich bereits meine .gitignore-Datei erstellt hatte und mich in dem Repository befand, an dem ich arbeiten wollte.

Um Ihre Änderungen anzuzeigen, müssen Sie möglicherweise zum GitHub-Stammverzeichnis Ihres Repositorys gehen und die Seite aktualisieren. Navigieren Sie dann durch die Links, um zu einem alten Commit zu gelangen, in dem sich die Datei befand, um festzustellen, ob sie jetzt entfernt wurde. Für mich zeigte das einfache Aktualisieren der alten Commit-Seite die Änderung nicht.

Anfangs sah es einschüchternd aus, aber es war wirklich einfach und wirkte wie ein Zauber! :-)

SherylHohman
quelle