git - create - Rückgängigmachen einer Zusammenführung, die gedrückt wurde



git reset tag (2)

Hier die Antworten von Linus Torvals Hier die Antworten von Linus Torvals http://opensource.apple.com/source/Git/Git-26/src/git-htmldocs/howto/revert-a-faulty-merge.txt

OK, ich habe ein bisschen Chaos gemacht. Anscheinend wurde auf meiner Maschine zu Hause der Entwicklungszweig nicht aktualisiert. Ich habe einen Commit gemacht und gedrängt. Das Ergebnis war, dass der eigentliche Ursprung / Entwicklung Zweig in meine lokale Entwicklung Zweig verschmolzen wurde - die aus irgendeinem Grund als verschiedene Zweige betrachtet wurden!

Zum einen verstehe ich wirklich nicht, wie das passiert ist und zum anderen, kann ich das rückgängig machen?

Zur Veranschaulichung sieht das Netzwerk jetzt so aus:

 local-develop ---------------------------------- C*--- M -
origin/develop --- C --- C --- C --- C --- C --------- / 

Was ich wirklich wollte, war, dass das C * dem Ursprung verpflichtet wäre, anstatt die Zweige zu verschmelzen.

Wie ich schon sagte, wurde dies bereits vorangetrieben. Gibt es eine Möglichkeit, die Änderungen zu löschen und sie so zu übernehmen, wie ich es wollte?

Zum Beispiel mache ich:

git reset --hard HEAD~1

Ich bin mir nicht sicher, ob dies die Zusammenführung rückgängig macht und ich habe zwei verschiedene Entwicklungen, und dann löscht die Zusammenführung etc ...?


Zusammenführungen passieren nicht auf Push, sie passieren auf git merge (na ja, und pull aber das ist wirklich nur fetch + merge, daher Merge passiert bei der Zusammenführung).

Es scheint wahrscheinlicher, dass Sie so etwas getan haben:

<get copy from origin/develop>
<wait around for remote's origin/develop to acquire new commits>
git checkout develop      # get onto (local) develop branch
<edit>
git commit -m message     # create some new commit(s)
git push origin develop   # attempt to push -- but this fails!
git pull

Es ist der letzte Schritt, der den Merge-Commit ( M oben) erzeugt, weil pull fetch bedeutet (alle neuen Commits fetch , die jetzt im origin/develop ) und dann merge (nehmen Sie Ihre lokale develop und verschmelzen Sie Ihre Commit mit den eben geholten neuen ).

Wenn Sie dieses neue Ergebnis nicht git push haben, verfügt das Remote-Repo nicht über Ihre lokalen Commits, die Sie mit C* und M . In diesem Fall sind Sie in guter Verfassung! (Sie können dies überprüfen, indem Sie git fetch erneut git fetch , um sicherzustellen, dass der origin/develop Ihres lokalen Repos mit dem in der Remote übereinstimmt, und dann git log origin/develop , git log origin/develop zu sehen, was darin enthalten ist.)

Es mag helfen, sich daran zu erinnern, dass es hier zwei völlig getrennte Git-Repos gibt: deine, mit deinen Sachen; und der, den du anrufst, stammt von hier. (Lassen Sie uns diese Maschine X .) Wenn Sie sich bei X anmelden X , hat es seine eigenen Commit-History- und Branch-Namen und so weiter. Dort drüben würden Sie das Verzeichnis in den Repo wechseln, git log -1 develop ausführen und sehen, was sich an der Spitze dieses Zweigs befindet.

Wenn Sie sich jetzt von X abmelden und wieder auf Ihrem eigenen Computer sind, können Sie git log -1 origin/develop ausführen. Wenn das das gleiche wie das ist, was Sie auf X , dann hat get fetch nichts zu aktualisieren, denn was git fetch tut, ist in der Tat (aber effizienter), sich bei X anzumelden und zu schauen, was develop dort develop . Alles, was X hat, das du nicht im origin/develop hast origin/develop , bringt fetch und fügt origin/develop . Jetzt sind Sie mit X synchronisiert. X hat deine Sachen nicht, aber du hast ihre.

Wenn Sie dann den zusätzlichen Schritt machen, eine merge (einschließlich der, die durch pull angedeutet wird), wird git, wenn es sein muss, einen Zusammenführungs-Commit machen ... aber das alles ist in Ihrem Repo, an der Spitze der Verzweigung ( develop in diesem Fall). Es sei denn, und bis Sie diese Zusammenführung Commit zu X (oder jemand auf X zieht Ihre Commits von Ihnen, aber lassen Sie uns das jetzt ignorieren) :-), X wird es nicht haben.

Wie auch immer, solange die Fernbedienung ( X hier) nicht Ihren Merge Commit hat, sind Sie golden. Da sie es nicht haben, tut es auch keiner. Sie können eine rebase Ihres develop rebase , um Ihr Commit ( C* ) auf den origin/develop . Das wird den Merge-Commit ( M ) los, und dann können Sie einen einfachen Schnellvorlauf zum origin/develop schieben.

Wenn X Ihren Merge-Commit hat - dh, nachdem Sie ed gedrückt haben und diesen Merge bekommen haben - dann stecken Sie (bis zu einem gewissen Grad) fest, weil vermutlich andere Leute Zugriff auf X und jetzt Ihren Merge-Commit verwenden. Es ist möglich, den Repo auf X , ähnlich wie Sie dies in Ihrem eigenen Repo mit git reset und git rebase tun können, aber es ist generell eine schlechte Idee.

Nehmen wir nun an, Sie hätten tatsächlich in den anderen Repo (auf Maschine X ) gedrängt, aber Sie sind sich absolut sicher, dass noch niemand Ihre Änderungen gesehen hat, und Sie werden sie definitiv zurücksetzen, anstatt sie rückgängig zu machen "fessing up" und hinterlässt den "screwup-record", der alle anderen leicht davon ablösen lässt, aber auch den Fehler sieht :-)). 1

Hier ist der Trick: Zuerst müssen Sie den Repo von Maschine X holen, um zu sagen, dass "Tipp der Verzweigung devel commit C7" ist, wo C7 in Ihrem eigenen Diagramm von früher ist, neu nummeriert, so dass ich jede Commit unterschiedlich benennen kann:

--------------------------- C*--- M
--- C4 --- C5 --- C6 --- C7 ---- /

Also, wie kannst du das tun? Nun, eine Möglichkeit besteht darin, sich auf X , 2 cd in das Repo --bare (auch wenn es --bare ) und dort git update-ref zu verwenden. Nehmen wir an, der SHA1 für C7 ist tatsächlich 50db850 (wie durch "git log" gezeigt). Dann könntest du das tun:

localhost$ ssh X
X$ cd /path/to/repo.git
X$ git update-ref refs/heads/develop 50db850

Aber wenn du dich nicht bei X anmelden kannst oder es einfach nicht willst, kannst du dasselbe mit git push -f . 3 (Das hat noch andere Vorteile: Ihr git-Repo wird wissen, dass origin/develop push -f nach dem erfolgreichen Ausführen des push -f .) Machen Sie einfach einen lokalen Zweig-Tipp, der auf das richtige Commit zeigt: 4

localhost$ git branch resetter 50db850
localhost$ git log resetter         # make sure it looks right
...
localhost$ git push -f origin resetter:develop
Total 0 (delta 0), reused 0 (delta 0)
To ssh://[redacted]
 + 21011c9...50db850 resetter -> develop (forced update)
localhost$ git branch -d resetter   # we don't need it anymore

Sobald Sie dies getan haben, ist Maschine X wieder in dem Zustand, den Sie wollten, und Sie können fortfahren, als ob Sie nie die Zusammenführung gedrückt haben, die Sie nicht mögen.

Beachten Sie, dass wenn Sie den push -f , wenn jemand anderes neue Commits auf M , diese auch unsichtbar werden (technisch gesehen sind sie immer noch da, zusammen mit Ihrem Merge Commit, aber sie sind in der "verloren") lost+found Gefühl von git fsck --lost-found , und nach ein paar Monaten werden sie wirklich für immer weggehen. 5

Nochmal und sehr wichtig : Diese Art von "Rollback von geteilten Repos" ist ein großer Schmerz für andere Benutzer dieses geteilten Repos, also sei dir wirklich sicher, dass es in Ordnung ist, bevor du es tust.

1 Diese Art von trivialem Merge muss nicht einmal zurückgesetzt werden. Es ist grundsätzlich nicht falsch, die Zusammenführung dort zu lassen. Wenn es sich jedoch um eine ernsthaftere fehlerhafte Zusammenführung handelt, gibt es neben der Aufzeichnung Ihrer "oops" noch einen weiteren Nachteil beim Zurücksetzen einer Zusammenführung: Sie macht die Änderungen später "etwas" schwieriger, indem Sie später "zusammenführen" absichtlich "wird das frühere Zusammenführen sehen und denken: OK, ich muss diese Änderungen nicht erneut zusammenführen. Sie müssen dann stattdessen "Zurücksetzen" wiederherstellen.

Ich denke, die richtige Lektion zum Mitnehmen ist: Schau ( git log ), bevor du drückst , um sicherzustellen, dass das, was du gerade drückst, das ist, was du schieben willst.

2 Oder einfacher und einfacher: git ls-remote . Dies verwendet den Fetch-Protokoll-Code, um zu sehen, was die Fernbedienung hat. Aber es verbirgt das Thema, für das ich hier bin, nämlich: Die Fernbedienung ist ein Repo genau wie deine!

3 Die kommenden Versionen von git version 2 haben neue "Sicherheitsmerkmale", die mit git push -f verwendbar sind. Aber sie sind noch nicht draußen, also hilft dir das nicht. Ich bearbeite das später, wenn sie sind, um sie zu notieren. Bis dahin, sei hier sehr vorsichtig: Du rennst an diesem Punkt gegen jeden, der versucht, neue Sachen in das gemeinsame Repository zu pushen.

4 Sie können dies auch mit roher SHA-1 ID tun. Der Name des Zweiges kann jedoch mehrmals hintereinander richtig eingegeben werden.

5 Die Speicherung und Verfallszeit erfolgt über die "Reflogs" von git. Der gemeinsam genutzte Server protokolliert möglicherweise nicht alle ref-Aktualisierungen. Wenn nicht, behalten nur private Repos, die bereits die neuen Commits hatten, diese zurück. Der kürzeste Standardverfall ist 30 Tage, dh etwa ein Monat, für die Commits, die nicht mehr von der Zweigstelle erreichbar sind. Aber bedenken Sie, es ist ein spaßiges, verrücktes Scramble, um jeden in seinen Repositories nach "verlorenen" Commits durchsuchen zu lassen, nachdem Force-Pushs "Arbeit" aus einem gemeinsamen Repository verloren haben.





revert