Wie können Sie eine fehlerhafte Zusammenführung beheben und Ihre guten Commits auf einer festen Zusammenführung wiedergeben?

StackOverflow https://stackoverflow.com/questions/307828

Frage

Ich habe versehentlich eine unerwünschte Datei übertragen (filename.orig beim Auflösen einer Zusammenführung) vor einigen Commits in mein Repository kopiert, ohne dass ich es bis jetzt bemerkt habe.Ich möchte die Datei vollständig aus dem Repository-Verlauf löschen.

Ist es möglich, den Änderungsverlauf so umzuschreiben? filename.orig wurde überhaupt nicht zum Repository hinzugefügt?

War es hilfreich?

Lösung

Bitte nicht dieses Rezept verwenden, wenn Ihre Situation nicht die in der Frage beschrieben ist. Dieses Rezept ist für eine schlechte merge Fixierung und Ihre guten Commits auf einem festen merge Wiedergabe.

Obwohl filter-branch wird tun, was Sie wollen, ist es ein ziemlich komplexer Befehl und ich würde wahrscheinlich wählt diese mit git rebase zu tun. Es ist wahrscheinlich eine persönliche Präferenz. filter-branch kann es in einem einzigen, etwas komplexen Befehl, während die rebase Lösung wird zu einer Zeit, den entsprechenden logische Operationen eines Schritt durchgeführt wird.

Versuchen Sie das folgende Rezept:

(Beachten Sie, dass Sie nicht wirklich benötigen ein temporärer Zweig, kann Sie dies mit einem ‚abgelöst HEAD‘, aber Sie müssen eine Notiz von der Commit-ID durch den git commit --amend Schritt erzeugt ergreifen, um den git rebase Befehl liefern eher als die temporären Zweignamen verwenden.)

Andere Tipps

Einführung:Ihnen stehen 5 Lösungen zur Verfügung

Auf dem Originalplakat heißt es:

Ich habe versehentlich eine unerwünschte Datei begangen ... in meinem Repository vor einigen Commits ... Ich möchte die Datei vollständig aus dem Repository -Verlauf löschen.

Ist es möglich, die Veränderungsgeschichte so umzuschreiben, dass filename.orig Wurde in erster Linie nie zum Repository hinzugefügt?

Es gibt viele verschiedene Möglichkeiten, die Geschichte einer Datei vollständig aus Git zu entfernen:

  1. Commits ändern.
  2. Hard-Resets (ggf. plus Rebase).
  3. Nicht interaktive Rebase.
  4. Interaktive Rebases.
  5. Zweige filtern.

Im Falle des ursprünglichen Plakats ist die Änderung des Commits für sich nicht wirklich eine Option, da er danach mehrere zusätzliche Commits gemacht hat, aber aus Gründen der Vollständigkeit werde ich auch erklären, wie es geht, für alle anderen, die Gerechte wollen ihr früheres Commit zu ändern.

Beachten Sie, dass alle diese Lösungen Folgendes beinhalten ändern/neu schreiben Geschichte/Commits in gewisser Weise auf eine andere Weise, also muss jeder mit alten Kopien der Commits zusätzliche Arbeit leisten, um seine Geschichte mit der neuen Geschichte neu zu synchronisieren.


Lösung 1:Änderungsverpflichtungen

Wenn Sie in Ihrem vorherigen Commit versehentlich eine Änderung vorgenommen haben (z. B. eine Datei), und Sie möchten nicht, dass die Geschichte dieser Änderung mehr existiert, können Sie einfach das vorherige Commit ändern, um die Datei daraus zu entfernen:

git rm <file>
git commit --amend --no-edit

Lösung 2:Hard Reset (möglicherweise plus Rebase)

Wie Lösung Nr. 1, wenn Sie nur Ihr vorheriges Komitee loswerden möchten, haben Sie auch die Möglichkeit, einfach einen harten Reset für seinen Elternteil durchzuführen:

git reset --hard HEAD^

Dieser Befehl setzt Ihren Zweig hart auf den vorherigen 1 zurückst Elternbefestigung.

Jedoch, Wenn Sie wie das ursprüngliche Poster nach dem Commit, wie Sie die Änderung rückgängig machen möchten, mehrere Commits gemacht haben, können Sie weiterhin harte Resets verwenden, um sie zu ändern.Hier sind die Schritte, mit denen Sie einen Commit weiter in die Geschichte ändern können:


Lösung 3:Nicht interaktives Rebase

Dies funktioniert, wenn Sie einen Commit nur vollständig aus dem Verlauf entfernen möchten:


Lösung 4:Interaktive Rebases

Mit dieser Lösung können Sie die gleichen Dinge wie die Lösungen Nr. 2 und Nr. 3 erledigen, dhÄndern oder entfernen Sie die Commits weiter zurück in die Geschichte als Ihr unmittelbares Vorgänger. Welche Lösung Sie verwenden, ist eine Art bis zu Ihnen.Interaktive Neubasen sind nicht gut geeignet, um Hunderte von Commits aus Leistungsgründen neu zu reservieren. Daher würde ich in solchen Situationen nicht-interaktive Rebasen oder die Filterzweiglösung (siehe unten) verwenden.

Um mit der interaktiven Rebase zu beginnen, verwenden Sie Folgendes:

Dies führt dazu, dass Git die Verschingungsgeschichte an den Elternteil des Commits zurückspulen, das Sie ändern oder entfernen möchten.Anschließend werden Sie eine Liste der Rewound -Commits in umgekehrter Reihenfolge in dem verwendet, in dem Git verwendet wird, in dem die Verwendung von Editor eingestellt ist (dies ist standardmäßig VIM):

pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)

Der Commit, den Sie ändern oder entfernen möchten, steht ganz oben in dieser Liste.Um es zu entfernen, löschen Sie einfach seine Zeile in der Liste.Andernfalls ersetzen Sie "Auswahl" durch "Bearbeiten" auf der 1st Zeile, etwa so:

edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`

Als nächstes geben Sie ein git rebase --continue.Wenn Sie sich für die vollständige Entfernung entschieden haben, müssen Sie alles, was Sie tun müssen (außer Überprüfung, den letzten Schritt für diese Lösung).Wenn Sie andererseits das Komitee ändern wollten, wird Git das Commit erneut anwenden und dann die Rebase pausieren.

Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

Zu diesem Zeitpunkt können Sie die Datei entfernen und das Commit ändern und dann die Rebase fortsetzen:

git rm <file>
git commit --amend --no-edit
git rebase --continue

Das ist es.Unabhängig davon, ob Sie das Komitee geändert oder vollständig entfernt haben, ist es immer eine gute Idee, zu überprüfen, ob keine anderen unerwarteten Änderungen an Ihrem Zweig vorgenommen wurden, indem Sie ihn vor der Rebase mit seinem Zustand verbreiten:

git diff master@{1}

Lösung 5:Zweige filtern

Schließlich ist diese Lösung am besten, wenn Sie alle Spuren der Existenz einer Datei aus der Geschichte vollständig auslöschen möchten, und keine der anderen Lösungen ist der Aufgabe ziemlich hoch.

Das wird entfernt <file> von allen Commits, beginnend mit dem Root-Commit.Wenn Sie stattdessen nur den Commit -Bereich umschreiben möchten HEAD~5..HEAD, dann können Sie das als zusätzliches Argument an übergeben filter-branch, wie in ausgeführtdiese Antwort:

Nochmals nach dem filter-branch Es ist vollständig, es ist normalerweise eine gute Idee zu überprüfen, ob es keine anderen unerwarteten Änderungen gibt, indem Sie Ihren Zweig mit seinem vorherigen Zustand vor dem Filterbetrieb verzichten:

git diff master@{1}

Filterzweig-Alternative:BFG Repo Cleaner

Ich habe gehört, dass das BFG Repo Cleaner Werkzeug läuft schneller als git filter-branch, also sollten Sie das vielleicht auch als Option ausprobieren. Es wird sogar offiziell im erwähnt Filterzweigdokumentation als sinnvolle Alternative:

Mit Git-Filter-Branch können Sie komplexe Shell-Script-Umschreibungen Ihrer Git-Geschichte vornehmen, aber Sie brauchen diese Flexibilität wahrscheinlich nicht, wenn Sie einfach sind Entfernen unerwünschter Daten wie große Dateien oder Passwörter.Für diese Vorgänge sollten Sie vielleicht in Betracht ziehen Der BFG Repo-Cleaner, Eine JVM-basierte Alternative zur Git-Filter-Branch, die für diese Anwendungsfälle in der Regel mindestens 10-50x schneller und mit ganz unterschiedlichen Eigenschaften schneller ist:

  • Jede bestimmte Version einer Datei wird genau bereinigt einmal.Im Gegensatz zu Git-Filter-Branch gibt Ihnen das BFG nicht die Möglichkeit, eine Datei unterschiedlich zu verarbeiten, basierend darauf, wo oder wann sie in Ihrer Geschichte begangen wurde.Diese Einschränkung bietet den Kernleistung des BFG und ist für die Aufgabe, schlechte Daten zu reinigen, gut geeignet - Sie kümmern sich nicht darum Wo Die schlechten Daten sind, dass Sie es einfach wollen gegangen.

  • Standardmäßig nutzt die BFG die Vorteile von Multi-Core-Maschinen voll aus und bereinigt parallel Commit-Dateibäume.Git-Filter-Branch-Reinigungen verpflichtet sich nacheinander (dh in einer einzigen Thread-Weise), obwohl es es Istmöglich, Filter zu schreiben, die ihren eigenen Parallellismus in die Skripte enthalten, die gegen jeden Commit ausgeführt werden.

  • Der Befehlsoptionen sind viel restriktiver als Git-Filter-Zweig und nur den Aufgaben, unerwünschte Daten zu entfernen- z. B.: --strip-blobs-bigger-than 1M.

Zusätzliche Ressourcen

  1. Pro Git § 6.4 Git Tools – Geschichte neu schreiben.
  2. git-filter-branch(1) Handbuchseite.
  3. git-commit(1) Handbuchseite.
  4. git-reset(1) Handbuchseite.
  5. git-rebase(1) Handbuchseite.
  6. Der BFG Repo Cleaner (siehe auch Diese Antwort stammt vom Schöpfer selbst).

Wenn Sie haben nichts begangen, da git rm einfach die Datei und git commit --amend.

Wenn Sie

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

wird von merge-point durch jede Änderung gehen HEAD, löschen filename.orig und die Änderung neu schreiben. --ignore-unmatch Mit der Benutzung wird der Befehl nicht fehlschlagen, wenn aus irgendeinem Grunde filename.orig aus einer Änderung fehlt. Das ist die empfohlene Art und Weise aus dem Beispielabschnitt in dem git-filter-branch Mann .

Hinweis für Windows-Benutzer: Der Dateipfad muss verwenden Schrägstriche

Dies ist die beste Art und Weise:
http://github.com/guides/completely-remove-a -file-von-allen-Revisionen

Seien Sie sicher, dass die Kopien der Dateien zu sichern zuerst.

Bearbeiten

Das Bearbeiten von Neon leider während der Überprüfung abgelehnt wurde.
Siehe Neon unten schreiben, könnte es nützliche Informationen enthält!


z. Alle *.gz Dateien versehentlich in Git Repository begangen zu entfernen:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

Das hat noch nicht für mich arbeiten? (Ich bin derzeit bei git Version 1.7.6.1)

$ du -sh .git ==> e.g. 100M

Nicht sicher, warum, da ich nur einen Master-Zweig hatte. Wie auch immer, ich habe endlich mein git Repo truely durch Drücken in eine neue leere und kahle git-Repository bereinigt, z.

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(ja!)

Dann klonen ich, dass in ein neues Verzeichnis und bewegt über sie .git Ordner, in diesem ist. z.

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(ja endlich aufgeräumt!)

Nach Überprüfung, ob alles in Ordnung ist, dann können Sie die ../large_dot_git und ../tmpdir Verzeichnisse löschen (vielleicht in ein paar Wochen oder Monaten ab jetzt, nur für den Fall ...)

Umschreiben Git Geschichte Anforderungen verändern sich ids verpflichten betroffen, und so jeder, der an dem Projekt arbeitet, müssen ihre alte Kopien des Repo löschen und machen einen frischen Klon, nachdem Sie die Geschichte gereinigt haben. Je mehr Menschen es Unannehmlichkeiten, desto mehr müssen Sie einen guten Grund, es zu tun - Ihre überflüssige Datei ist nicht wirklich ein Problem verursacht, aber wenn nur Sie sind an dem Projekt arbeiten, könnte man genauso gut sauber up der Git Geschichte, wenn Sie wollen!

es so einfach wie möglich zu machen, würde ich empfehlen, die BFG Repo-Cleaner mit , eine einfachere, schnellere Alternative zum Entfernen von Dateien von Git Geschichte speziell entwickelt, um git-filter-branch. Eine Möglichkeit, wie es Ihr Leben macht hier leichter ist, dass es tatsächlich Griffe alle Refs standardmäßig (alle Tags, Zweige, usw.), aber es ist auch 10 -?. 50x schneller

Sie sollten sorgfältig die Schritte folgen hier: http://rtyley.github.com / bfg-Repo-Reiniger / # Nutzung - aber das Kern-Bit ist nur dies: Download der BFG jar (erfordert Java 6 oder höher) und diesen Befehl ausführen:

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

Ihre gesamte Repository-Historie wird gescannt, und jede Datei mit dem Namen filename.orig (das ist nicht in den letzte verpflichten ) werden entfernt. Dies ist wesentlich einfacher als git-filter-branch mit der gleichen Sache zu tun!

Vollständige Offenlegung:. Ich bin der Autor des BFG Repo-Cleaner

You should probably clone your repository first.

Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all

Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all

nur hinzufügen, dass Charles Bailey-Lösung, habe ich nur ein git rebase -i unerwünschte Dateien aus einem früheren entfernen begehen und es funktionierte wie ein Charme. Die Schritte:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue

Der einfachste Weg, fand ich von leontalbot vorgeschlagen wurde (als Kommentar), das ist ein Beitrag von Anoopjohn veröffentlicht. Ich denke, es lohnt seinen eigenen Raum als Antwort:

(I umgewandelt es zu einem Bash-Skript)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: $0 FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

geht Alle Kredite an Annopjohn, und es leontalbot für den Hinweis auf.

Hinweis

Beachten Sie, dass das Skript nicht Validierungen enthält, so sicher, dass Sie keine Fehler machen und dass Sie ein Backup, falls etwas schief geht. Er arbeitete für mich, aber es kann in Ihrer Situation nicht funktionieren. Verwenden Sie es mit VORSICHT (folgen Sie dem Link, wenn Sie wollen wissen, was los ist).

Auf jeden Fall, git filter-branch ist der Weg zu gehen.

Leider wird dies nicht ausreicht, um vollständig filename.orig aus dem Repo zu entfernen, da es noch von Tags, reflog Einträgen, Fernbedienungen usw. Bezug genommen werden wird.

Ich empfehle all diese Referenzen auch, zu entfernen und dann den Garbage Collector aufrufen. Sie können die git forget-blob Skript von dieser Webseite all dies in einem Schritt zu tun.

git forget-blob filename.orig

Wenn es das letzte verpflichten Sie bereinigen möchten, habe ich versucht, mit git Version 2.14.3 (Apple-Git-98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git

Dies ist, was git filter-branch wurde konzipiert.

Sie können auch verwendet werden:

git reset HEAD file/path

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top