Git-Push nach Feature-Branch-Rebase abgelehnt


919

OK, ich dachte, das wäre ein einfaches Git-Szenario. Was vermisse ich?

Ich habe einen masterZweig und einen featureZweig. Ich arbeite daran master, einige daran featureund dann noch mehr master. Am Ende habe ich so etwas (die lexikografische Reihenfolge impliziert die Reihenfolge der Commits):

A--B--C------F--G  (master)
       \    
        D--E  (feature)

Ich habe kein Problem damit git push origin master, die Fernbedienung auf dem neuesten masterStand zu halten oder eine Remote-Sicherung für meine Arbeit zu verwalten git push origin feature(wenn diese aktiviert ist feature) feature. Bis jetzt sind wir gut.

Aber jetzt möchte ich mich zusätzlich featurezu den F--GCommits auf Master neu gründen , also ich git checkout featureund git rebase master. Immer noch gut. Jetzt haben wir:

A--B--C------F--G  (master)
                 \
                  D'--E'  (feature)

Problem: In dem Moment, in dem ich die neue featureverzweigte Verzweigung sichern möchte git push origin feature, wird der Push abgelehnt, da sich der Baum aufgrund der Neubasierung geändert hat. Dies kann nur mit gelöst werden git push --force origin feature.

Ich hasse es zu benutzen, --forceohne sicher zu sein, dass ich es brauche. Also brauche ich es? Ist die Rebasieren unbedingt bedeuten , dass der nächste pushsein sollte --forceful?

Dieser Feature-Zweig wird nicht mit anderen Entwicklern geteilt, daher habe ich de facto kein Problem mit dem Force-Push. Ich werde keine Daten verlieren. Die Frage ist konzeptioneller.

Antworten:


682

Das Problem besteht darin, dass git pushdavon ausgegangen wird, dass der Remote-Zweig schnell zu Ihrem lokalen Zweig weitergeleitet werden kann. Das heißt, dass der Unterschied zwischen lokalen und Remote-Zweigen darin besteht, dass am Ende einige neue Commits wie folgt ausgeführt werden:

Z--X--R         <- origin/some-branch (can be fast-forwarded to Y commit)
       \        
        T--Y    <- some-branch

Wenn Sie git rebaseCommits ausführen , werden D und E auf eine neue Basis angewendet und neue Commits erstellt. Das heißt, nach dem Rebase haben Sie so etwas:

A--B--C------F--G--D'--E'   <- feature-branch
       \  
        D--E                <- origin/feature-branch

In dieser Situation kann der Remote-Zweig nicht schnell zum lokalen Zweig weitergeleitet werden. Theoretisch kann ein lokaler Zweig in einen Remote-Zweig zusammengeführt werden (in diesem Fall benötigen Sie ihn natürlich nicht), aber da git pushnur Schnellvorlauf-Zusammenführungen ausgeführt werden, werden Fehler und Fehler ausgelöst.

Die --forceOption besteht darin, den Status des Remote-Zweigs zu ignorieren und ihn auf das Commit zu setzen, das Sie in ihn verschieben. Also git push --force origin feature-brancheinfach origin/feature-branchmit local überschreiben feature-branch.

Meiner Meinung nach ist es in Ordnung, Feature-Zweige neu zu gründen masterund sie zwangsweise in das Remote-Repository zurückzuschieben, solange Sie der einzige sind, der an diesem Zweig arbeitet.


68
Um ehrlich zu sein, wird durch Ziehen und Zusammenführen der Originalversion des Feature-Zweigs in den neu basierten die gesamte Idee des erneuten Basierens eliminiert.
KL-7

24
Vielleicht habe ich Sie nicht richtig verstanden, aber wenn Sie den Feature-Zweig ziehen und auf einen neuen Master-Zweig zurücksetzen, können Sie ihn nicht ohne Gewalt zurückschieben, da die Remote-Version des Feature-Zweigs nicht schnell auf Ihren neuen vorgespult werden kann (neu basierte) Version des Feature-Zweigs. Genau das hat OP in seiner Frage beschrieben. Wenn Sie dies nach dem erneuten Basieren, jedoch vor dem Pushing tun git pull feature-branch, wird ein neues Zusammenführungs-Commit generiert (durch Zusammenführen von Remote- und lokalen Versionen des Feature-Zweigs). Entweder erhalten Sie nach dem erneuten Basieren eine unnötige Zusammenführung, oder Sie drücken mit --force.
KL-7

6
Ah, ich glaube ich habe es verstanden. Sie beschreiben den gleichen Ansatz wie in Mark Longairs Antwort. Es wird jedoch ein Merge-Commit generiert. In einigen Fällen kann dies nützlich sein, aber ich verwende Rebase hauptsächlich in meinen eigenen Feature-Zweigen (daher push --forceist dies kein Problem), um den Commit-Verlauf ohne Zusammenführungs-Commits linear zu halten.
KL-7

11
Das Problem mit „Force-Push“ ist, dass Sie tatsächlich „lose Sachen“ (vorherige Commits) können, was normalerweise NIEMALS in einem Versionskontrollsystem möglich sein sollte. Aus diesem Grund sollte mindestens ein „Master-ish“ -Zweig vorhanden sein die Einstellungen, um keine Kraftstöße zu akzeptieren , um möglichen Schaden zu begrenzen. (Nennen Sie eine der folgenden Angaben: mürrische / entlassene Mitarbeiter, eigene Idiotie, müde und überarbeitete "Entscheidungen" ...).
Frank Nocke

13
--force-with-leasewie @hardev vorgeschlagen ist eine großartige Option
Augustououza

466

Anstelle von -f oder --force sollten Entwickler verwenden

--force-with-lease

Warum? Weil es den Remote-Zweig auf Änderungen überprüft, was absolut eine gute Idee ist. Stellen wir uns vor, James und Lisa arbeiten an demselben Feature-Zweig und Lisa hat ein Commit vorangetrieben. James stößt jetzt seine lokale Niederlassung zurück und wird abgelehnt, wenn er versucht zu pushen. Natürlich glaubt James, dass dies auf Rebase zurückzuführen ist und verwendet --force und würde alle Änderungen von Lisa neu schreiben. Wenn James --force-with-lease verwendet hätte, hätte er eine Warnung erhalten, dass es Commits gibt, die von jemand anderem gemacht wurden. Ich verstehe nicht, warum irgendjemand --force anstelle von --force-with-lease verwenden würde, wenn er nach einer Rebase pusht.


33
Tolle Erklärung. git push --force-with-leasehat mir einen Haufen gerettet.
ckib16

5
Dies ist ein nützlicher Kommentar, aber keine wirkliche Antwort auf die Frage.
Dallin

4
Dies ist die Antwort. Die Umbasierung auf Master / Entwicklung führt zu einem Problem. Genau aus diesem Grund gibt es - Force-with-Lease.
Tamir Daniely

3
Dies sollte die akzeptierte Antwort sein. Löst genau das beschriebene Problem - Druck erzwingen ohne zu erzwingen, wenn sich in der Zwischenzeit jemand anderes verpflichtet hat.
Luzian

3
Ich denke, sowohl die akzeptierte als auch diese Antwort sprechen die Frage an. Die akzeptierte Antwort erklärt, warum Sie erzwingen müssen. Und dieser erklärt, warum --force-with-leasedas --force
Problem

48

Ich würde stattdessen "checkout -b" verwenden und es ist leichter zu verstehen.

git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature

Wenn Sie löschen, verhindern Sie, dass ein vorhandener Zweig mit einer anderen SHA-ID eingegeben wird. In diesem Fall lösche ich nur den Remote-Zweig.


6
Dies funktioniert hervorragend, insbesondere wenn Ihr Team über einen Git-Hook verfügt, der alle Git-Push-Force-Befehle ablehnt.
Ryan Thames

1
Danke dafür, dass es gut funktioniert hat. Hier sind weitere Details, die ich gelesen habe, um sie besser zu verstehen. Dies ist sehr nützlich, wenn Sie keinen Force-Push ausführen möchten oder können. Löschen entfernter Zweige und Wiederherstellen
RajKon

5
Dies hat das gleiche Ergebnis wie push --force, ist also nur ein Weg, um ein Git-Repo zu verhindern --force. Daher halte ich dies nicht für eine gute Idee - entweder das Repo erlaubt es push --forceoder aus gutem Grund deaktiviert es es. Die Antwort von Nabi ist besser geeignet, wenn sie --forceauf dem Remote-Repo deaktiviert ist, da nicht das Risiko besteht, dass Commits von anderen Entwicklern verloren gehen oder auf andere Weise Probleme verursachen.
Logan Pickup

19

Eine Lösung hierfür besteht darin, das zu tun, was das Rebasing-Merge- Skript von msysGit tut - nach dem Rebase den alten Kopf von featurewith zusammenzuführen -s ours. Am Ende erhalten Sie das Commit-Diagramm:

A--B--C------F--G (master)
       \         \
        \         D'--E' (feature)
         \           /
          \       --
           \    /
            D--E (old-feature)

... und Ihr Push-of featurewird ein schneller Vorlauf sein.

Mit anderen Worten, Sie können tun:

git checkout feature
git branch old-feature
git rebase master
git merge -s ours old-feature
git push origin feature

(Nicht getestet, aber ich denke das ist richtig ...)


26
Ich glaube, der häufigste Grund für die Verwendung git rebase(anstatt masterwieder in Ihren Feature-Zweig zu wechseln) ist die Erstellung eines sauberen Verlaufs linearer Commits. Mit Ihrem Ansatz wird die Geschichte noch schlimmer. Und da durch das erneute Basieren neue Commits ohne Bezugnahme auf ihre vorherigen Versionen erstellt werden, bin ich mir nicht einmal sicher, ob das Ergebnis dieser Zusammenführung angemessen sein wird.
KL-7

6
@ KL-7: Der springende Punkt merge -s oursist, dass künstlich ein übergeordneter Verweis auf die vorherige Version hinzugefügt wird. Sicher, die Geschichte sieht nicht sauber aus, aber der Fragesteller scheint besonders gestört zu sein, wenn er den Druck des featureZweigs erzwingen muss , und das umgeht das. Wenn Sie neu aufbauen möchten, ist es mehr oder weniger das eine oder andere. :) Generell finde ich es interessant, dass das msysgit-Projekt dies tut ....
Mark Longair

@ KL-7: Übrigens habe ich Ihre Antwort + 1 gegeben, was eindeutig die richtige ist - ich dachte nur, dass dies auch interessant sein könnte.
Mark Longair

Es ist definitiv interessant, zumindest für mich. Vielen Dank. Ich habe oursStrategie schon einmal gesehen , aber ich dachte, sie gilt nur für Konfliktsituationen, indem sie automatisch mithilfe von Änderungen in unserer Branche gelöst werden. Es stellte sich heraus, dass es anders funktioniert. Und auf diese Weise zu arbeiten ist sehr nützlich, wenn Sie eine neu basierte Version benötigen (z. B. damit der Repo-Betreuer sie sauber anwenden kann master), aber ein erzwungenes Drücken vermeiden möchten (wenn viele andere Personen aus irgendeinem Grund Ihren Feature-Zweig verwenden).
KL-7

15

Es kann sein, dass sich in diesem Zweig nur ein Entwickler befindet oder nicht, der jetzt (nach der Rebase) nicht mit dem Ursprung / der Funktion übereinstimmt.

Als solches würde ich vorschlagen, die folgende Sequenz zu verwenden:

git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2

Ja, neuer Zweig, dies sollte dies ohne --force lösen, was meiner Meinung nach im Allgemeinen ein großer Nachteil ist.


3
Tut mir leid zu sagen, aber: „Generieren Sie weiterhin Zweige“, um zu vermeiden, dass vorhandene erzwungen werden. Dies hilft weder „einsamen Feature-Entwicklern“ (die überschreiben können) noch mehreren Personen, die an einem Feature-Zweig arbeiten (müssen diesen Zweig „inkrementieren“ und mitteilen) um zu bewegen, Leute). - Es ist eher wie manuelle Versionierung ("Thesis_00.doc, Thesis_01.doc, ...") innerhalb eines Versionierungssystems ...
Frank Nocke

2
Außerdem hilft dies nicht, wenn Sie eine Github-PR für einen Filialnamen geöffnet haben. Sie müssen eine neue PR für den neuen Filialnamen erstellen, den Sie gepusht haben.
Gprasant

1
@frankee Halb wahr aus meiner Erfahrung. Für einen einsamen Entwickler ist es einfach genug, nur Druck auszuüben, aber es ist die Gewohnheit, die Sie später beißen könnte. + Ein neuer Entwickler ist gerade beigetreten? oder vielleicht ein CI-System, das --hard reset nicht verwendet? Für ein Team, das zusammenarbeitet, ist die Kommunikation des neuen Filialnamens meiner Meinung nach einfach genug. Dies kann auch problemlos per Skript erfolgen. Für ein Team würde ich empfehlen, die Basis lokal neu zu erstellen oder wenn die Filiale für die Zusammenführung bereit ist, nicht während der täglichen Arbeit Das zusätzliche Festschreiben ist weniger problematisch als der Umgang mit Rebase / Merge-Konflikten.
JAR.JAR.beans

@gprasant für PR, wieder denke ich, dass dies falsch wäre, um neu zu gründen, ich würde eigentlich die einzelnen Commits mit den PR-Fixes sehen wollen. Ein Rebase (Squash) sollte erst später im Rahmen der Zusammenführung zum Master erfolgen und wenn die PR abgeschlossen und bereit ist (daher muss keine neue PR geöffnet werden).
JAR.JAR.beans

13

Ich vermeide den Force Push, indem ich einen neuen Zweig erstelle und die Arbeit an diesem neuen Zweig fortsetze und nach einiger Stabilität den alten Zweig entferne, der neu basiert wurde:

  • Den ausgecheckten Zweig lokal wiederherstellen
  • Verzweigen von der neu basierten Verzweigung zu einer neuen Verzweigung
  • Verschieben dieses Zweigs als neuen Zweig auf Remote. und Löschen des alten Zweigs auf der Fernbedienung

1
Warum keine Liebe für diese Option? Es ist definitiv das sauberste, einfachste und sicherste.
CDMO

Da ich ungefähr 200 Systeme habe, die den Zweigstellennamen verfolgen, und es muss ein spezifischer Name für die Aufgabe sein, und wenn ich anfange, Zweigstellennamen bei jedem Push umzubenennen, werde ich meinen Verstand verlieren.
Tamir Daniely

@TamirDaniely Ich habe es nicht versucht, aber löst das Löschen des alten Zweigs (von der Fernbedienung) vor dem Verschieben und Verschieben des neuen Zweigs mit demselben alten Namen Ihr Problem?
Nabi

2
@Nabi Genau das macht --force-with-lease, außer dass auch überprüft wird, dass es keine neuen Commits gibt, die nicht Ihnen gehören.
Tamir Daniely

12

Andere haben Ihre Frage beantwortet. Wenn Sie einen Zweig neu gründen, müssen Sie erzwingen, um diesen Zweig zu verschieben.

Rebase und ein gemeinsam genutztes Repository verstehen sich im Allgemeinen nicht. Dies schreibt die Geschichte neu. Wenn andere diesen Zweig verwenden oder von diesem Zweig verzweigt haben, ist eine erneute Basis ziemlich unangenehm.

Im Allgemeinen funktioniert Rebase gut für das lokale Filialmanagement. Die Remote-Zweigstellenverwaltung funktioniert am besten mit expliziten Zusammenführungen (--no-ff).

Wir vermeiden auch, Master in einen Feature-Zweig zusammenzuführen. Stattdessen werden wir auf Master zurückgesetzt, jedoch mit einem neuen Zweignamen (z. B. Hinzufügen eines Versionssuffix). Dadurch wird das Problem der erneuten Basierung im gemeinsam genutzten Repository vermieden.


5
Könnten Sie bitte ein Beispiel hinzufügen?
Thermech

8

Was ist los mit einem git merge masterauf dem featureAst? Dadurch bleibt Ihre Arbeit erhalten, während Sie sie vom Hauptzweig trennen.

A--B--C------F--G
       \         \
        D--E------H

Edit: Ah sorry hat deine Problemstellung nicht gelesen. Sie benötigen Kraft, wenn Sie a rebase. Alle Befehle, die den Verlauf ändern, benötigen das --forceArgument. Dies ist ausfallsicher, um zu verhindern, dass Sie Arbeit verlieren (die alte Dund Ewürde verloren gehen).

Sie haben also eine ausgeführt, git rebasedie den Baum so aussehen ließ (obwohl er teilweise versteckt ist Dund Esich nicht mehr in einem benannten Zweig befindet):

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

Wenn Sie also versuchen, Ihren neuen featureZweig (mit D'und E'darin) zu verschieben, verlieren Sie Dund E.


3
Daran ist nichts auszusetzen, und ich weiß, dass es funktionieren wird. Es ist einfach nicht das, was ich brauche. Wie gesagt, die Frage ist eher konzeptionell als praktisch.
Yuval Adam

4

Für mich funktionieren folgende einfache Schritte:

1. git checkout myFeature
2. git rebase master
3. git push --force-with-lease
4. git branch -f master HEAD
5. git checkout master
6. git pull

Nachdem Sie alle oben genannten Schritte ausgeführt haben, können Sie auch den myFeature-Zweig mit dem folgenden Befehl löschen:

git push origin --delete myFeature

3

Folgendes funktioniert für mich:

git push -f origin branch_name

und es entfernt keinen meiner Codes.

Wenn Sie dies jedoch vermeiden möchten, können Sie Folgendes tun:

git checkout master
git pull --rebase
git checkout -b new_branch_name

Dann können Sie alle Ihre Commits für die neue Niederlassung auswählen. git cherry-pick COMMIT ID und dann schieben Sie Ihren neuen Zweig.


5
-fist ein Alias ​​für --force, was die Frage nach Möglichkeit zu vermeiden versucht.
Epochengine

1

Da das OP das Problem versteht, sucht es nur nach einer schöneren Lösung ...

Wie wäre es damit als Übung?

  • Haben Sie auf tatsächlichen Feature-Entwicklungszweig (wo Sie nie neu gründen und erzwingen, damit Ihre Kollegen Feature-Entwickler Sie nicht hassen). Hier können Sie diese Änderungen regelmäßig mit einer Zusammenführung von main abrufen. Messier Geschichte , ja, aber das Leben ist einfach und niemand wird in seiner Arbeit unterbrochen.

  • Haben Sie einen zweiten Zweig zur Entwicklung von Funktionen, in dem ein Mitglied des Feature-Teams alle Feature-Commits regelmäßig dazu drängt, tatsächlich neu zu gründen, tatsächlich zu erzwingen. Also fast sauber basierend auf einem relativ neuen Master Commit. Wenn die Funktion abgeschlossen ist, schieben Sie diesen Zweig auf den Master.

Möglicherweise gibt es bereits einen Musternamen für diese Methode.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.