2012-04-16 17 views
13

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!Eine Verschmelzung rückgängig machen, die gedrückt wurde

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

es zu illustrieren, sieht das Netzwerk so etwas jetzt:

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

Was würde ich wirklich wollte, war, dass das C * auf den Ursprung begangen werden/entwickeln, anstatt die Zweige der Verschmelzung.

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

Zum Beispiel ich tun:

git reset --hard HEAD~1 

Ich bin nicht sicher, ob dies die Zusammenführung rückgängig gemacht, und ich habe zwei verschiedene entwickelt, und dann die Zusammenführung gelöscht etc ...?

Antwort

26

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

Es scheint wahrscheinlicher, dass Sie so etwas wie dies tun:

<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 dieser letzte Schritt, der die Zusammenführung (oben M) verpflichten schafft, weil pull bedeutet fetch (all diese neuen Commits bekommen, die jetzt in origin/develop), dann merge (nehmen Sie Ihre lokale develop und verschmelzen Sie Ihre Commit mit den neuen gerade geholt).

Wenn Sie nicht git push ed dieses neue Ergebnis, dann wird der Remote-Repo hat nicht entweder Ihrer lokalen Commits, die, die Sie haben beschriftet C* und M. In diesem Fall sind Sie in guter Verfassung! (Können Sie überprüfen, indem Sie git fetch laufen wieder um sicherzustellen, dass Ihre lokalen origin/develop Repo übereinstimmt, die in der Fernbedienung, dann git log origin/develop tun, um zu sehen, was drin ist.)

Es kann helfen, sich daran zu erinnern, dass es zwei völlig getrennte git repos hier : deine, mit deinen Sachen; und die, die du anrufst origin hier. (Lassen Sie uns diese Maschine X nennen.) Wenn Sie sich bei X anmelden, hat es seine eigenen separaten Commit-Verlauf und Zweig-Namen und so weiter. Dort drüben würden Sie das Verzeichnis in den Repo wechseln, führen Sie git log -1 develop, und sehen Sie, was an der Spitze dieser Filiale ist. Wenn Sie sich 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 gesehen haben, dann get fetch hat nichts zu aktualisieren, denn was tut, ist in der Tat (aber effizienter), melden Sie sich an X und schauen, was in develop dort ist. Alles, was X hat, haben Sie nicht in origin/develop, fetch bringt über und fügt origin/develop hinzu.Jetzt sind Sie mit X synchronisiert. X hat nicht Ihre Sachen, aber Sie haben ihre.

Wenn Sie dann den zusätzlichen Schritt zur Herstellung eines merge tun (einschließlich der implizierten pull), git wird, wenn es hat, eine Zusammenführung machen ... begeht aber all dies ist in Ihre Repo, bei die Spitze der Branche (in diesem Fall immer noch develop). Es sei denn, und bis Sie diese Verschmelzung Commit auf X (oder jemand auf X zieht Ihre Commits von Ihnen, aber lass uns das jetzt ignorieren :-)), X wird es nicht haben.

Wie auch immer, solange die Fernbedienung (X hier) nicht Ihre Zusammenführung haben, sind Sie golden. Seit sie haben es nicht, niemand sonst auch. Sie können einen rebase Ihres develop Zweigs tun, um Ihr Festschreiben (C*) auf origin/develop zu setzen. Dadurch wird der Zusammenführungs-Commit (M) gelöscht und Sie können einen einfachen Schnellvorlauf an origin/develop senden.

Wenn Xtut haben Ihre merge begehen, dh, wenn Sie, nachdem Sie pull ed gedrückt und bekam die merge-dann sind Sie stecken (in gewissem Maße), weil vermutlich andere Personen Zugang zu X haben und jetzt verwenden Ihr Zusammenführungs-Commit. Es ist möglich, das Repo auf X, ähnlich wie Sie dies in Ihrem eigenen Repo mit git reset und und so weiter tun können, Rollback, aber es ist in der Regel eine schlechte Idee. Nehmen wir an, Sie haben in der Tat auf den anderen Repo geschoben (auf Maschine X), aber Sie sind absolut sicher niemand hat Ihre Änderungen noch gesehen, und Sie werden sie definitiv zurücksetzen, anstatt rückgängig zu machen sie (das Zurückkehren von Werten zu "fessing up" und das Zurücklassen der screwup-Aufzeichnung hinterlässt, die jedermann sonst leicht davon zurückgewinnen lässt, aber auch sie Ihren Fehler sehen lässt :-)). 1

Hier ist der Trick: Sie erste Maschine X-Repo zu sagen brauchen, um die „Spitze des Zweiges devel wird C7 begehen“, wo C7 in Ihrem eigenen Diagramm ist von früher, gerade neu nummeriert, so dass ich kann jedes commit anders benennen:

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

Also, wie können Sie das tun? Nun, eine Möglichkeit besteht darin, sich unter X, cd in den Repo einzuloggen (auch wenn es --bare ist), und dort git update-ref zu verwenden. Nehmen wir an, der SHA1 für C7 ist eigentlich 50db850 (wie von "git log" gezeigt). Dann könnten Sie dies tun:

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

Aber wenn Sie nicht zu X anmelden können, oder auch nur wollen nicht, können Sie das gleiche mit git push -f tun. (Dies hat weitere Vorteile: Insbesondere werden Ihre git Repo wissen, dass origin/develop zurückgespult wurde, sobald die push -f erfolgreich abgeschlossen wurde.) Nur eine lokale Niederlassung-Spitze zeigt nach rechts machen begehen:

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 die push -f tun, wenn jemand anderes Commits auf der M gemacht hat, so würden diese auch unsichtbar werden (technisch in sie sind immer noch da, zusammen mit Ihrem merge begehen, aber sie sind " verloren "in der lost+found Sinn von git fsck --lost-found, und nach ein paar Monaten werden sie wirklich für immer weggehen."

Immer sehr wichtig: diese Art von „Rollback von Shared-Repo“ ist ein großer Schmerz für andere Benutzer dieses gemeinsamen Repo, so wirklich sicher sein, es ist OK, bevor Sie es tun.


Diese Art von trivialer merge nicht zurückkehrt nicht einmal benötigen. 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 die frühere Zusammenführung sehen und denken: OK, ich muss diese Änderungen nicht neu zusammenführen. Sie müssen dann stattdessen "Zurücksetzen" wiederherstellen.

Ich denke, das Recht zum Mitnehmen Lektion hier ist: Look (git log), bevor Sie, um sicherzustellen, drücken Sie das, was Sie im Begriff sind, zu drücken ist, was Sie schieben möchten.

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!

Die kommende git Version 2 Veröffentlichungen haben neue „Sicherheitsfunktionen“, die mit git push -f nutzbar sein werden. 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 dann, be wirklich vorsichtig hier: Sie sind gegen jeden anderen rennen, der versucht, neue Sachen in das gemeinsame Repository zu schieben, an dieser Stelle.

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

Die Speicherung und Verfall ist über Git "Reflogs". 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, d. H. Ungefähr ein Monat, für Commits, die nicht länger von der Verzweigungsspitze aus 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.

+0

wow, vielen Dank für diese umfassende Antwort. Unglücklicherweise habe ich es auf X geschoben. Da ich sicher bin, dass noch niemand das neue holen gemacht hat, habe ich mich gefragt, ob ich diese Änderungen rückgängig machen kann. Eine Sache, die mir wirklich nicht klar ist: Wenn ich zwei Schritte zurück setze, bin ich mit einem der Zweige oder mit beiden zurück? – dmeu

+0

OK, nun, nach dem Drücken, wenn du * wirklich sicher bist * :-) ... was du machen willst, ist, dass Xs Idee von der Spitze des Zweigs 'devel' ein wenig zurückgespult wird. Lass mich die Antwort bearbeiten ... – torek

+2

Oh, und: wenn du HEAD ~ 2' zurücksetzt, um "zwei Schritte zurück zu gehen", dann heißt das git: "setze die Spitze des Zweiges, auf dem ich gerade bin" - vermutlich wäre das dein ' Entwickeln Sie die "Commit-ID", indem Sie HEAD ~ 2 nachschlagen. Wenn du sehen willst, was das heißt, gib zuerst "HEAD ~ 2". Wenn Sie 'M' aus Ihrem obigen Diagramm übernehmen, ist' HEAD ~ 2' 'M ~ 2', das sowohl über' M' als auch über 'C *' läuft. Wahrscheinlich nicht was du willst. – torek