Wie kann ich mehrere Git-Commits (bereits gepusht) in ein veröffentlichtes Repository zurücksetzen?

75

Neu bei git und schon durcheinander.

Ich habe einige Änderungen an einem Remote-Entwicklungscomputer vorgenommen und vorgenommen. Ich muss eine ältere Version wiederherstellen, aber den bisherigen "schlechten Fortschritt" beibehalten, um weiterhin an einem separaten Zweig zu arbeiten.

Ich dachte, es so zu machen:

  1. Erstellen Sie einen lokalen Zweig mit dem Namen: "getestete Sache"
  2. Setzen Sie das lokale Repository auf den Zustand zurück, in dem es funktioniert hat (hoffentlich helfen sinnvolle Commits) .
  3. Zur Fernbedienung drücken

  4. Beende die Tests an der getesteten Sache

  5. Füge "getestetes Ding" in dev ein
  6. Zur Fernbedienung drücken

Zwischen den Schritten 3 und 5 können sich andere Entwickler verpflichten und pushen, und ich befürchte, dass dies zu einer "Merge-Tragödie" führen kann.

AKTUALISIEREN:

Das Hauptproblem liegt hier bei 2)

Hier zum Thema: "Arbeit in einen Themenzweig aufteilen" http://learn.github.com/p/undoing.html

Sie schlagen vor:

  1. $ git Branch Test
  2. $ git reset --hard a6b4c974

Auf diese Weise könnten andere Entwickler noch:

$ git commit (auf dem Dev-Zweig)

und ich kann auschecken, um es zu testen und bis zur Zusammenführungszeit auszuarbeiten .

Trotz all Ihrer Möglichkeiten scheint dies ein guter Ansatz zu sein. Es ist jedoch nicht angegeben, ob dies möglich ist, nachdem wir gepusht haben?

Bitte beachten Sie Folgendes: Seit ich diese Änderungen vorgenommen habe und alles durcheinander gebracht habe, hat bisher noch niemand am Repository gearbeitet . Wenn ich also das Arbeitsverzeichnis zurücksetze, wird es niemand bemerken.

MEM
quelle

Antworten:

145

Das Problem

Es gibt eine Reihe von Workflows, die Sie verwenden können. Der Hauptpunkt besteht darin, die Geschichte in einem veröffentlichten Zweig nicht zu brechen, es sei denn, Sie haben mit allen kommuniziert, die den Zweig möglicherweise konsumieren und bereit sind, alle Klone zu operieren. Es ist am besten, das nicht zu tun, wenn Sie es vermeiden können.

Lösungen für veröffentlichte Branchen

Ihre beschriebenen Schritte haben ihre Berechtigung. Wenn der Entwicklungszweig sofort stabil sein soll, tun Sie dies auf diese Weise. Sie haben eine Reihe von Tools zum Debuggen mit Git , mit denen Sie den richtigen Verzweigungspunkt finden können. Anschließend können Sie alle Commits zwischen Ihrem letzten stabilen Commit und HEAD zurücksetzen.

Entweder die Commits einzeln in umgekehrter Reihenfolge zurücksetzen oder den <first_bad_commit>..<last_bad_commit>Bereich verwenden. Hashes sind die einfachste Möglichkeit, den Festschreibungsbereich anzugeben, es gibt jedoch auch andere Notationen. Wenn Sie beispielsweise 5 fehlerhafte Commits gepusht haben, können Sie diese zurücksetzen mit:

# Revert a series using ancestor notation.
git revert --no-edit dev~5..dev

# Revert a series using commit hashes.
git revert --no-edit ffffffff..12345678

Dadurch werden nacheinander umgekehrte Patches auf Ihr Arbeitsverzeichnis angewendet, die rückwärts auf Ihr bekanntermaßen funktionsfähiges Commit hinarbeiten. Mit dem Flag --no-edit werden die Änderungen an Ihrem Arbeitsverzeichnis automatisch festgeschrieben, nachdem jeder umgekehrte Patch angewendet wurde.

Siehe man 1 git-revertfür mehr Optionen, und man 7 gitrevisionsfür verschiedene Möglichkeiten , die Commits angeben , rückgängig gemacht werden.

Alternativ können Sie Ihren HEAD abzweigen, die Dinge so reparieren, wie sie sein müssen, und sie erneut zusammenführen. Ihr Build wird in der Zwischenzeit beschädigt, dies kann jedoch in einigen Situationen sinnvoll sein.

Die Gefahrenzone

Wenn Sie absolut sicher sind, dass seit Ihren fehlerhaften Pushs niemand mehr aus dem Repository gezogen wurde, und wenn es sich bei der Fernbedienung um ein reines Repository handelt , können Sie natürlich ein Commit ohne schnellen Vorlauf durchführen.

git reset --hard <last_good_commit>
git push --force

Dadurch bleibt das Reflog auf Ihrem System und dem Upstream-Host intakt, aber Ihre fehlerhaften Commits verschwinden aus dem direkt zugänglichen Verlauf und verbreiten sich nicht bei Pulls. Ihre alten Änderungen bleiben bestehen, bis die Repositorys gelöscht werden, aber nur Git-Ninjas können die versehentlich vorgenommenen Commits anzeigen oder wiederherstellen.

Todd A. Jacobs
quelle
Wenn Leute bereits schlechte Pushs geklont haben und benachrichtigt wurden - was sollten sie tun, um denselben Zweig wie auf der Fernbedienung zu haben? Müssen sie dasselbe tun git reset --hard <last_good_commit>?
Andrybak
5
Wenn Sie Commit-Tags verwenden, ist es nicht <first_bad_commit>.. <last_bad_commit>, sondern <last_good_commit>..<last_bad_commit>
Yerke
32

Wenn Sie bereits Dinge auf einen Remote-Server übertragen haben (und andere Entwickler an demselben Remote-Zweig arbeiten), ist es wichtig zu beachten, dass Sie den Verlauf nicht neu schreiben möchten

Verwenden Sie nicht git reset --hard

Sie müssen Änderungen rückgängig machen, andernfalls werden sie beim Auschecken, bei dem die Commits entfernt wurden, beim nächsten Push wieder zum Remote-Repository hinzugefügt. und jede andere Kasse zieht sie beim nächsten Zug danach ein.

Wenn Sie keine Änderungen an eine Fernbedienung übertragen haben, können Sie diese verwenden

git reset --hard <hash>

Wenn Sie haben gedrückt Veränderungen, aber sind sicher , niemand sie gezogen hat , kann man verwenden

git reset --hard
git push -f

Wenn Sie haben Änderungen gedrückt, und jemand hat sie in ihre Kasse gezogen können Sie es immer noch , aber das andere Team-Mitglied / Kasse collaborate brauchen würde:

(you) git reset --hard <hash>
(you) git push -f

(them) git fetch
(them) git reset --hard origin/branch

Aber im Allgemeinen wird das zu einem Chaos. Also zurücksetzen:

Die zu entfernenden Commits sind die letzten

Dies ist möglicherweise der häufigste Fall. Sie haben etwas getan - Sie haben sie herausgeschoben und dann festgestellt, dass sie nicht existieren sollten.

Zuerst müssen Sie das Commit identifizieren, zu dem Sie zurückkehren möchten. Dies können Sie tun mit:

git log

Suchen Sie einfach vor Ihren Änderungen nach dem Commit und notieren Sie sich den Commit-Hash. Sie können das Protokoll mithilfe des -nFlags auf die am häufigsten erneut gesendeten Commits beschränken :git log -n 5

Setzen Sie dann Ihren Zweig auf den Status zurück, den Ihre anderen Entwickler sehen sollen:

git revert  <hash of first borked commit>..HEAD

Der letzte Schritt besteht darin, einen eigenen lokalen Zweig zu erstellen, in dem die zurückgesetzten Änderungen erneut angewendet werden:

git branch my-new-branch
git checkout my-new-branch
git revert <hash of each revert commit> .

Arbeiten my-new-branchSie weiter, bis Sie fertig sind, und führen Sie es dann in Ihrem Hauptentwicklungszweig zusammen.

Die zu entfernenden Commits werden mit anderen Commits vermischt

Wenn die Commits, die Sie zurücksetzen möchten, nicht alle zusammen sind, ist es wahrscheinlich am einfachsten, sie einzeln zurückzusetzen. Verwenden Sie erneut git logfind die Commits, die Sie entfernen möchten, und dann:

git revert <hash>
git revert <another hash>
..

Erstellen Sie dann erneut Ihren Zweig, um Ihre Arbeit fortzusetzen:

git branch my-new-branch
git checkout my-new-branch
git revert <hash of each revert commit> .

Dann hacken Sie weg und verschmelzen Sie, wenn Sie fertig sind.

Sie sollten am Ende einen Commit-Verlauf haben, der so aussieht my-new-branch

2012-05-28 10:11 AD7six             o [my-new-branch] Revert "Revert "another mistake""
2012-05-28 10:11 AD7six             o Revert "Revert "committing a mistake""
2012-05-28 10:09 AD7six             o [master] Revert "committing a mistake"
2012-05-28 10:09 AD7six             o Revert "another mistake"
2012-05-28 10:08 AD7six             o another mistake
2012-05-28 10:08 AD7six             o committing a mistake
2012-05-28 10:05 Bob                I XYZ nearly works

Better way®

Insbesondere jetzt, da Sie sich der Gefahren mehrerer Entwickler bewusst sind, die in derselben Branche arbeiten, sollten Sie Feature-Zweige immer für Ihre Arbeit verwenden. Das bedeutet nur, dass Sie in einem Zweig arbeiten, bis etwas fertig ist, und es erst dann mit Ihrem Hauptzweig zusammenführen. Erwägen Sie auch die Verwendung von Tools wie git-flow, um die Erstellung von Zweigen auf konsistente Weise zu automatisieren.

AD7six
quelle
+1 für git resetohne das Flag --hard, aber ich glaube nicht, dass die meisten Leute verstehen werden, dass Sie sowohl das Arbeitsverzeichnis als auch HEAD zurücksetzen, was zu einem ! [rejected] master -> master (non-fast-forward)Push führen wird. Wenn viele Dateien geändert wurden, ist dies ein wirklich umfassender Patch, selbst wenn Sie das Festschreiben erzwingen.
Todd A. Jacobs
@ AD7Six: Vielen Dank für das von Better Way registrierte Marken-Add-On. Eigentlich sind Filialen so billig, dass ich gleich nach diesem dummen Zug darüber nachdenke. Wenn Sie wissen, dass niemand außer mir das Repository berührt, da ich das alles durcheinander gebracht habe, kann es Ihnen helfen, den Ansatz für mein Fragen-Update zu verwenden?
MEM
@ CodeGnome Sie haben Recht, es ist so lange her, dass ich mich in dieser Position befand:]
AD7six
@MEM Es spielt keine Rolle, ob niemand anderes das Repo aktualisiert hat - es ist wichtig, ob jemand seine Kaufabwicklung aktualisiert hat, seit die "Fehler" begangen wurden. Wenn dies der Fall ist, enthält die Kasse beim Ziehen weiterhin die Commits, die Sie zwangsweise entfernt und mit neuen Änderungen zusammengeführt haben. Wenn Sie git reset --hardin Ihrem Feature-Zweig verwenden, werden Sie beim Zusammenführen höchstwahrscheinlich Konflikte bekommen, wenn die von Ihnen entfernten Commits erneut angewendet werden - im Grunde ist es wahrscheinlich keine gute Idee, dies zu tun.
AD7six
-1
git revert HEAD -m 1

In der obigen Codezeile. "Letztes Argument repräsentiert"

  • 1 - kehrt ein Commit zurück. 2 - Setzt die letzten Commits zurück. n - setzt die letzten n Commits zurück

oder

git reset --hard siriwjdd

Sireesh Yarlagadda
quelle
1
löst Fehler aus: Commit a17dc423957324b2b537c1caa9d9476c9b443f48 hat kein übergeordnetes n, wobei n das letzte Argument ist
MyrionSC2
Der Code funktioniert, wenn HEAD das Merge-Commit ist. Die Beschreibung dessen, was dies tut, ist jedoch falsch. -mtatsächlich gibt an Mutter welche Commit vom m ainline Zweig. -m 2würde Sie auf die Version an der Spitze Ihres Feature-Zweigs zurücksetzen und alle Änderungen an der Hauptlinie verwerfen, da Ihr Feature-Zweig von der Hauptlinie abweicht.
Chaim Leib Halbert