it-swarm.com.de

Bestimmte Festschreibung entfernen

Ich habe mit einem Freund an einem Projekt gearbeitet, und er hat eine Reihe von Dateien bearbeitet, die nicht hätten bearbeitet werden dürfen. Irgendwie habe ich seine Arbeit in meine verschmolzen, entweder als ich sie gezogen habe oder als ich versucht habe, nur die spezifischen Dateien herauszusuchen, die ich wollte. Ich habe lange gesucht und gespielt und versucht, herauszufinden, wie die Commits entfernt werden können, die die Änderungen an diesen Dateien enthalten. Es scheint sich um einen Fehler zwischen Zurücksetzen und Zurücksetzen zu handeln, und es gibt keine eindeutigen Beispiele docs gehen davon aus, dass ich mehr weiß als ich.

Hier ist also eine vereinfachte Version der Frage:

Wie entferne ich Commit 2 in dem folgenden Szenario?

$ mkdir git_revert_test && cd git_revert_test

$ git init
Initialized empty Git repository in /Users/josh/deleteme/git_revert_test/.git/

$ echo "line 1" > myfile

$ git add -A

$ git commit -m "commit 1"
[master (root-commit) 8230fa3] commit 1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 myfile

$ echo "line 2" >> myfile

$ git commit -am "commit 2"
[master 342f9bb] commit 2
 1 files changed, 1 insertions(+), 0 deletions(-)

$ echo "line 3" >> myfile

$ git commit -am "commit 3"
[master 1bcb872] commit 3
 1 files changed, 1 insertions(+), 0 deletions(-)

Das erwartete Ergebnis ist

$ cat myfile
line 1
line 3

Hier ist ein Beispiel, wie ich versucht habe, zurückzusetzen

$ git revert 342f9bb
Automatic revert failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>'
and commit the result.
243
Joshua Cheek

Der Algorithmus, den Git bei der Berechnung der wiederherzustellenden Diffs verwendet, erfordert dies

  1. die zurückgesetzten Zeilen werden durch spätere Festschreibungen nicht geändert.
  2. dass es später in der Geschichte keine anderen "angrenzenden" Commits gibt.

Die Definition von "benachbart" basiert auf der Standardanzahl von Zeilen aus einem Kontextdifferential (3). Wenn also 'myfile' wie folgt konstruiert wurde:

$ cat >myfile <<EOF
line 1
junk
junk
junk
junk
line 2
junk
junk
junk
junk
line 3
EOF
$ git add myfile
$ git commit -m "initial check-in"
 1 files changed, 11 insertions(+), 0 deletions(-)
 create mode 100644 myfile

$ Perl -p -i -e 's/line 2/this is the second line/;' myfile
$ git commit -am "changed line 2 to second line"
[master d6cbb19] changed line 2
 1 files changed, 1 insertions(+), 1 deletions(-)

$ Perl -p -i -e 's/line 3/this is the third line/;' myfile
$ git commit -am "changed line 3 to third line"
[master dd054fe] changed line 3
 1 files changed, 1 insertions(+), 1 deletions(-)

$ git revert d6cbb19
Finished one revert.
[master 2db5c47] Revert "changed line 2"
 1 files changed, 1 insertions(+), 1 deletions(-)

Dann funktioniert alles wie erwartet.

Die zweite Antwort war sehr interessant. Es gibt eine Funktion namens Revert Strategy, die noch nicht offiziell veröffentlicht wurde (obwohl sie in Git 1.7.2-rc2 verfügbar ist). Sie können git folgendermaßen aufrufen:

git revert - strategieauflösung <commit>

und es sollte einen besseren Job machen, um herauszufinden, was Sie meinten. Ich kenne weder die Liste der verfügbaren Strategien noch die Definition einer Strategie.

58
Hal Eisen

Es gibt vier Möglichkeiten, dies zu tun:

  • Sauberer Weg, rückgängig machen, aber die Rückgängigmachung im Log behalten:

    git revert --strategy resolve <commit>
    
  • Harte Weise, entfernen Sie insgesamt nur das letzte Festschreiben:

    git reset --soft "HEAD^"
    

Hinweis: Vermeiden Sie git reset --hard, da auch alle Änderungen in Dateien seit dem letzten Festschreiben verworfen werden. Wenn --soft funktioniert nicht, versuche es lieber mit --mixed oder --keep.

  • Neu starten (zeigen Sie das Protokoll der letzten 5 Festschreibungen an und löschen Sie die Zeilen, die Sie nicht möchten, oder ordnen Sie mehrere Festschreibungen neu an oder drücken Sie sie zusammen, oder tun Sie alles, was Sie möchten. Dies ist ein sehr vielseitiges Tool.)

    git rebase -i HEAD~5
    

Und wenn ein Fehler gemacht wird:

git rebase --abort
  • Quick Rebase: Entferne nur ein bestimmtes Commit mit seiner ID:

    git rebase --onto commit-id^ commit-id
    
  • Alternativen: Sie könnten auch versuchen:

    git cherry-pick commit-id
    
  • Noch eine Alternative:

    git revert --no-commit
    
  • Als letzten Ausweg können Sie diese sehr schnelle Open-Source-Anwendung verwenden, wenn Sie die volle Freiheit beim Bearbeiten des Verlaufs benötigen (z. B. weil Sie mit git nicht bearbeiten können, was Sie möchten): - reposurgeon .

Hinweis: Natürlich werden alle diese Änderungen lokal vorgenommen. Sie sollten git Push, um die Änderungen anschließend auf die Fernbedienung anzuwenden. Und falls Ihr Repo das Commit nicht entfernen möchte ("kein schneller Vorlauf erlaubt", was passiert, wenn Sie ein Commit entfernen möchten, das Sie bereits gepusht haben), können Sie git Push -f, um die Änderungen zu erzwingen.

Hinweis 2: Wenn Sie an einem Zweig arbeiten und Push erzwingen müssen, sollten Sie git Push --force, da dies möglicherweise andere Zweige überschreibt (wenn Sie Änderungen daran vorgenommen haben, auch wenn sich Ihre aktuelle Kasse in einem anderen Zweig befindet). Bevorzugen Sie es, immer den entfernten Zweig anzugeben, wenn Sie Push erzwingen: git Push --force Origin your_branch.

246
gaborous

Sehr einfacher Weg

git-Basis -i HEAD ~ x

(x = Anzahl der Commits)

beim Ausführen der Notepad-Datei wird neben Ihrem Festschreiben ein 'Put'-Drop geöffnet enter image description here

und fertig ... synchronisiere einfach das Git-Dashboard und die Änderungen werden auf die Fernbedienung übertragen. Wenn das Commit, das Sie verwerfen, bereits auf der Fernbedienung ausgeführt wurde, müssen Sie das Update erzwingen. Da --force als schädlich gilt, verwenden Sie git Push --force-with-lease.

80
JD-V

Sie haben die Wahl zwischen

  1. beibehaltung des Fehlers und Einführung eines Fixes und
  2. beheben des Fehlers und Ändern des Verlaufs.

Sie sollten (1) wählen, wenn die fehlerhafte Änderung von einer anderen Person übernommen wurde, und (2), wenn der Fehler auf einen privaten, nicht weitergeleiteten Zweig beschränkt ist.

Git Revert ist ein automatisiertes Werkzeug (1), das ein neues Commit erstellt, das ein vorheriges Commit rückgängig macht. Sie werden den Fehler und die Entfernung im Projektverlauf sehen, aber Personen, die aus Ihrem Repository ziehen, werden beim Aktualisieren nicht auf Probleme stoßen. Es funktioniert in Ihrem Beispiel nicht automatisch, daher müssen Sie 'myfile' bearbeiten (um Zeile 2 zu entfernen), und git add myfile und git commit mit dem Konflikt umzugehen. Sie erhalten dann vier Festschreibungen in Ihrem Verlauf, wobei Festschreibung 4 Festschreibung 2 rückgängig macht.

Wenn es niemanden interessiert, dass sich Ihr Verlauf ändert, können Sie ihn umschreiben und Commit 2 entfernen (Auswahl 2). Der einfache Weg dies zu tun ist git rebase -i 8230fa3. Dadurch gelangen Sie in einen Editor, und Sie können festlegen, dass die fehlerhafte Festschreibung nicht übernommen wird, indem Sie die Festschreibung entfernen (und "pick" neben den anderen Festschreibungsmeldungen belassen. Lesen Sie die Konsequenzen hierfür .

36
Andrew Walker

Approch 1

Holen Sie sich zuerst den Commit-Hash (z. B. 1406cd61), den Sie zurücksetzen müssen. einfache Lösung wird unter Befehl sein,

$ git revert 1406cd61

wenn Sie nach dem Festschreiben von 1406cd61 weitere Änderungen in Bezug auf 1406cd61-Dateien festgeschrieben haben, funktioniert der obige einfache Befehl nicht. Dann müssen Sie die folgenden Schritte ausführen, bei denen es sich um Kirschenpflücken handelt.

Approch 2

Bitte befolgen Sie die folgenden Schritte: Da wir --force verwenden, müssen Sie Administratorrechte über dem Git Repo haben, um dies zu tun.

Schritt 1: Finde das Commit vor dem Commit, das du entfernen möchtest git log

Schritt 2: Checkout mit Commit git checkout <commit hash>

Schritt 3: Erstelle einen neuen Zweig mit deinem aktuellen Checkout Commit git checkout -b <new branch>

Schritt 4: Jetzt müssen Sie das Commit nach dem entfernten Commit hinzufügen git cherry-pick <commit hash>

Schritt 5: Wiederholen Sie nun Schritt 4 für alle anderen Commits, die Sie behalten möchten.

Schritt 6: Sobald alle Festschreibungen zu Ihrer neuen Niederlassung hinzugefügt und festgeschrieben wurden. Überprüfen Sie, ob alles im richtigen Zustand ist und wie vorgesehen funktioniert. Überprüfen Sie, ob alles festgeschrieben wurde: git status

Schritt 7: Wechseln Sie zu Ihrem kaputten Zweig git checkout <broken branch>

Schritt 8: Führen Sie nun einen Hard-Reset für den unterbrochenen Zweig des Commits durch, bevor Sie den zu entfernenden Zweig git reset --hard <commit hash> Ausführen.

Schritt 9: Füge deinen festen Zweig in diesen Zweig ein git merge <branch name>

Schritt 10: Verschieben Sie die zusammengeführten Änderungen zurück zu Origin. WARNUNG: Dadurch wird das Remote-Repo überschrieben! git Push --force Origin <branch name>

Sie können den Vorgang ausführen, ohne eine neue Verzweigung zu erstellen, indem Sie Schritt 2 und 3 durch Schritt 8 ersetzen und dann Schritt 7 und 9 nicht ausführen.

22
tk_

Sie können unerwünschte Commits mit git rebase Entfernen. Angenommen, Sie haben einige Commits aus dem Zweig eines Kollegen in den Zweig Ihres Themas aufgenommen, aber Sie möchten diese Commits später nicht mehr.

git checkout -b tmp-branch my-topic-branch  # Use a temporary branch to be safe.
git rebase -i master  # Interactively rebase against master branch.

An diesem Punkt öffnet Ihr Texteditor die interaktive Rebase-Ansicht. Zum Beispiel

git-rebase-todo

  1. Entfernen Sie die Commits, die Sie nicht möchten, indem Sie ihre Zeilen löschen
  2. Speichern und Beenden

Wenn der Rebase nicht erfolgreich war, löschen Sie den temporären Zweig und versuchen Sie es mit einer anderen Strategie. Fahren Sie andernfalls mit den folgenden Anweisungen fort.

git checkout my-topic-branch
git reset --hard tmp-branch  # Overwrite your topic branch with the temp branch.
git branch -d tmp-branch  # Delete the temporary branch.

Wenn Sie Ihren Topic-Zweig auf eine Remote-Site verschieben, müssen Sie möglicherweise Push erzwingen, da sich der Commit-Verlauf geändert hat. Wenn andere an demselben Zweig arbeiten, geben Sie ihnen einen Hinweis.

17
Dennis

Aufgrund der anderen Antworten hier war ich irgendwie verwirrt, wie git rebase -i Zum Entfernen eines Commits verwendet werden konnte. Ich hoffe, dass es in Ordnung ist, meinen Testfall hier niederzuschreiben (sehr ähnlich wie beim OP).

Hier ist ein bash Skript, das Sie einfügen können, um ein Test-Repository im Ordner /tmp Zu erstellen:

set -x

rm -rf /tmp/myrepo*
cd /tmp

mkdir myrepo_git
cd myrepo_git
git init
git config user.name me
git config user.email [email protected]

mkdir folder
echo aaaa >> folder/file.txt
git add folder/file.txt
git commit -m "1st git commit"

echo bbbb >> folder/file.txt
git add folder/file.txt
git commit -m "2nd git commit"

echo cccc >> folder/file.txt
git add folder/file.txt
git commit -m "3rd git commit"

echo dddd >> folder/file.txt
git add folder/file.txt
git commit -m "4th git commit"

echo eeee >> folder/file.txt
git add folder/file.txt
git commit -m "5th git commit"

Zu diesem Zeitpunkt haben wir einen file.txt Mit diesen Inhalten:

aaaa
bbbb
cccc
dddd
eeee

Zu diesem Zeitpunkt ist HEAD beim 5. Festschreiben, HEAD ~ 1 wäre das 4. - und HEAD ~ 4 wäre das 1. Festschreiben (also würde HEAD ~ 5 nicht existieren) Wir möchten das 3. Commit entfernen - wir können diesen Befehl im Verzeichnis myrepo_git eingeben:

git rebase -i HEAD~4

( Beachten Sie, dass git rebase -i HEAD~5 Zu "fatal: Benötigte eine einzelne Revision; ungültiger Upstream-HEAD ~ 5" führt. ) Ein Texteditor (siehe Der Screenshot in @ Dennis 'Antwort ) wird mit folgendem Inhalt geöffnet:

pick 5978582 2nd git commit
pick 448c212 3rd git commit
pick b50213c 4th git commit
pick a9c8fa1 5th git commit

# Rebase b916e7f..a9c8fa1 onto b916e7f
# ...

Wir erhalten also alle Commits seit (aber ohne ) unseren angeforderten HEAD ~ 4. Löschen Sie die Zeile pick 448c212 3rd git commit Und speichern Sie die Datei. Sie erhalten diese Antwort von git rebase:

error: could not apply b50213c... 4th git commit

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To check out the original branch and stop rebasing run "git rebase --abort".
Could not apply b50213c... 4th git commit

Öffnen Sie jetzt myrepo_git/folder/file.txt In einem Texteditor. Sie werden sehen, dass es geändert wurde:

aaaa
bbbb
<<<<<<< HEAD
=======
cccc
dddd
>>>>>>> b50213c... 4th git commit

Grundsätzlich sieht git, dass es bei HEAD zum 2. Commit kam, Inhalt von aaaa + bbbb gab, und dass es dann einen Patch gibt von hinzugefügtem cccc + dddd, das nicht weiß, wie es an den vorhandenen Inhalt angehängt wird.

Hier kann sich git also nicht für Sie entscheiden - es ist Sie , der eine Entscheidung treffen muss: Durch Entfernen des 3. Commits behalten Sie entweder die dadurch eingeführten Änderungen (hier die Zeile cccc) - oder Sie nicht. Wenn nicht, entfernen Sie einfach die zusätzlichen Zeilen - einschließlich des cccc - in folder/file.txt Mit einem Texteditor. So sieht es aus:

aaaa
bbbb
dddd

... und speichern Sie dann folder/file.txt. Jetzt können Sie die folgenden Befehle im Verzeichnis myrepo_git Ausführen:

$ nano folder/file.txt  # text editor - edit, save
$ git rebase --continue
folder/file.txt: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add

Ah - also, um zu markieren, dass wir den Konflikt gelöst haben, müssen wir git add Den folder/file.txt Vor macht git rebase --continue:

$ git add folder/file.txt
$ git rebase --continue

Hier öffnet sich wieder ein Texteditor mit der Zeile 4th git commit - hier haben wir die Möglichkeit, die Commit-Meldung zu ändern (die in diesem Fall sinnvollerweise in 4th (and removed 3rd) commit oder ähnliches geändert werden könnte). Nehmen wir an, Sie möchten nicht - beenden Sie einfach den Texteditor, ohne zu speichern. Wenn Sie das tun, erhalten Sie:

$ git rebase --continue
[detached HEAD b8275fc] 4th git commit
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master.

An diesem Punkt haben Sie jetzt eine solche Historie (die Sie auch mit gitk . Oder anderen Werkzeugen einsehen können) des Inhalts von folder/file.txt (Mit anscheinend unveränderten Zeitstempeln der ursprünglichen Commits) ):

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   dddd
                |  +eeee

Und wenn wir uns zuvor entschlossen hätten, die Zeile cccc (den Inhalt des dritten git-Commits, den wir entfernt haben) beizubehalten, hätten wir Folgendes gehabt:

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +cccc
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   cccc
                |   dddd
                |  +eeee

Nun, das war die Art von Lektüre, von der ich gehofft hatte, dass ich herausgefunden hätte, wie git rebase Funktioniert, wenn man Commits/Revisionen löscht. also hoffe, es könnte auch anderen helfen ...

7
sdaau

Es hört sich also so an, als ob das fehlerhafte Commit irgendwann in einem Merge-Commit enthalten war. Wurde Ihr Merge-Commit bereits ausgeführt? Wenn ja, sollten Sie git revert Verwenden. Sie müssen die Zähne zusammenbeißen und die Konflikte bewältigen. Wenn nein, dann könnten Sie möglicherweise entweder zurücksetzen oder zurücksetzen, aber Sie können dies tun vorher das Zusammenführungs-Commit, dann wiederholen Sie das Zusammenführen.

Es gibt nicht viel Hilfe, die wir Ihnen für den ersten Fall wirklich geben können. Nachdem Sie die Wiederherstellung versucht haben und festgestellt haben, dass die automatische Wiederherstellung fehlgeschlagen ist, müssen Sie die Konflikte untersuchen und sie entsprechend beheben. Dies ist genau der gleiche Vorgang wie das Beheben von Zusammenführungskonflikten. Mit git status können Sie feststellen, wo sich die Konflikte befinden, die nicht zusammengeführten Dateien bearbeiten, die in Konflikt stehenden Hunks finden, herausfinden, wie sie gelöst werden können, die in Konflikt stehenden Dateien hinzufügen und schließlich ein Commit ausführen. Wenn Sie git commit Alleine verwenden (nein -m <message>), Sollte die Nachricht, die in Ihrem Editor angezeigt wird, die von git revert Erstellte Vorlagennachricht sein. Sie können eine Notiz hinzufügen, wie Sie die Konflikte behoben haben, speichern und beenden, um einen Commit durchzuführen.

Für den zweiten Fall, der das Problem behebt vor Ihrer Zusammenführung, gibt es zwei Unterfälle, je nachdem, ob Sie seit der Zusammenführung mehr Arbeit geleistet haben. Wenn Sie dies nicht getan haben, können Sie einfach git reset --hard HEAD^, Um die Zusammenführung abzubrechen, den Vorgang rückgängig zu machen und dann die Zusammenführung zu wiederholen. Aber ich vermute, Sie haben. So werden Sie am Ende so etwas tun:

  • erstelle einen temporären Zweig kurz vor dem Zusammenführen und checke ihn aus
  • machen Sie das Zurücksetzen (oder verwenden Sie git rebase -i <something before the bad commit> <temporary branch>, um das fehlerhafte Commit zu entfernen)
  • wiederholen Sie die Zusammenführung
  • stütze deine nachfolgende Arbeit wieder auf: git rebase --onto <temporary branch> <old merge commit> <real branch>
  • entfernen Sie den temporären Zweig
2
Cascabel

Sie haben also einige Arbeit geleistet und es vorangetrieben. Nennen wir sie Commits A und B. Ihr Mitarbeiter hat auch einige Arbeit geleistet, Commits C und D. Sie haben die Arbeit Ihrer Mitarbeiter mit Ihrer Arbeit zusammengeführt (Merge Commit E). Dann haben Sie weitergearbeitet. Auch (Commit F), und festgestellt, dass Ihr Mitarbeiter einige Dinge geändert hat, die er nicht haben sollte.

Ihr Commit-Verlauf sieht also folgendermaßen aus:

A -- B -- C -- D -- D' -- E -- F

Sie wollen wirklich C, D und D 'loswerden. Da Sie sagen, Sie haben Ihre Kollegen mit Ihrer zusammengeführt, sind diese Commits bereits "da draußen". Entfernen Sie die Commits also z. Git Rebase ist ein No-No. Glaub mir, ich habe es versucht.

Jetzt sehe ich zwei Auswege:

  • wenn Sie E und F noch nicht an Ihren Kollegen oder eine andere Person (normalerweise Ihren "Origin" -Server) weitergeleitet haben, können Sie diese vorerst noch aus dem Verlauf entfernen. Dies ist Ihre Arbeit, die Sie speichern möchten. Dies kann mit a erfolgen

    git reset D'
    

    (Ersetzen Sie D 'durch den tatsächlichen Commit-Hash, den Sie aus einem git log

    Zu diesem Zeitpunkt sind die Festschreibungen E und F nicht mehr vorhanden und die Änderungen sind wieder nicht festgeschriebene Änderungen in Ihrem lokalen Arbeitsbereich. An diesem Punkt würde ich sie in einen Zweig verschieben oder sie in einen Patch verwandeln und für später speichern. Setzen Sie jetzt die Arbeit Ihres Mitarbeiters zurück, entweder automatisch mit einem git revert Oder manuell. Wenn Sie das getan haben, spielen Sie Ihre Arbeit noch einmal ab. Möglicherweise haben Sie Zusammenführungskonflikte, aber zumindest werden sie im Code Sie geschrieben, anstatt im Code Ihres Kollegen.

  • Wenn Sie die Arbeit, die Sie nach dem Commit Ihres Mitarbeiters geleistet haben, bereits vorangetrieben haben, können Sie weiterhin versuchen, einen "Reverse Patch" entweder manuell oder mit git revert Abzurufen, da Ihre Arbeit jedoch "im Weg" ist Sie werden wahrscheinlich mehr Zusammenführungskonflikte und verwirrendere Konflikte bekommen. Sieht so aus, als wären Sie in ... gelandet.

1
Frans