Hg: Wie man eine Rebase wie die Rebase von Git macht


207

In Git kann ich das machen:

1. Arbeiten Sie an einer neuen Funktion:
$ git co -b newfeature-123 # (ein Zweig für die Entwicklung lokaler Features)
mache ein paar Commits (M, N, O)

Meister A --- B --- C.
                \.
newfeature-123 M --- N --- O.

2. Ziehen Sie neue Änderungen vom Upstream-Master ab:
$ git pull
(Master aktualisiert mit ff-Commits)

Meister A --- B --- C --- D --- E --- F.
                \.
newfeature-123 M --- N --- O.

3. Master ausbauen, damit meine neue Funktion 
kann gegen die neuesten vorgelagerten Änderungen entwickelt werden:
(von newfeature-123)
$ git Rebase Master

Meister A --- B --- C --- D --- E --- F.
                            \.
newfeature-123 M --- N --- O.


Ich möchte wissen, wie man dasselbe in Mercurial macht, und ich habe das Web nach einer Antwort durchsucht , aber das Beste, was ich finden konnte, war: git rebase - kann ich das?

Dieser Link enthält zwei Beispiele:
1. Ich gebe zu, dass dies: (Ersetzen der Revisionen aus dem Beispiel durch die aus meinem eigenen Beispiel)

hg up -CF  
hg branch -f newfeature-123  
hg Transplantation -a -b neue Funktion-123 

ist nicht schlecht, außer dass es das MNO vor dem Rebase als nicht zusammengeführten Kopf zurücklässt und 3 neue Commits M ', N', O 'erstellt, die sie darstellen, die von der aktualisierten Hauptlinie abzweigen.

Grundsätzlich ist das Problem, dass ich am Ende folgendes habe:

Meister A --- B --- C --- D --- E --- F.
                \ \
newfeature-123 \ M '--- N' --- O '
                  \.
newfeature-123 M --- N --- O.

Dies ist nicht gut, da es lokale, unerwünschte Commits hinterlässt, die gelöscht werden sollten.

  1. Die andere Option über denselben Link ist
hg qimport -r M: O.
hg qpop -a
hg up F.
hg branch newfeature-123
hg qpush -a
hg qdel -r qbase: qtip

und dies führt zu dem gewünschten Graphen:

Meister A --- B --- C --- D --- E --- F.
                            \.
newfeature-123 M --- N --- O.

Aber diese Befehle (alle 6!) scheinen so viel komplizierter zu sein als

$ git Rebase Master

Ich möchte wissen, ob dies das einzige Äquivalent in Hg ist oder ob es einen anderen Weg gibt, der so einfach ist wie Git.


7
"Das ist nicht gut, weil es lokale, unerwünschte Commits hinterlässt, die fallengelassen werden sollten." - Eigentlich macht Git das Gleiche. Die Commits im ursprünglichen Zweig werden nicht geändert oder entfernt, sondern nur neue, die dieselben Änderungen auf den Master anwenden. Sie können immer noch mit auf die alten zugreifen git reflogund sie sind nicht vollständig verschwunden, bis sie Müll gesammelt haben. Wenn Sie sie in einem benannten Zweig behalten möchten, damit Sie das Reflog nicht verwenden müssen, tun Sie dies einfach git branch feature-123_originalvor dem erneuten Basieren.
MatrixFrog

5
Zufällige Frage: Haben Sie die Änderungssätze / Zweige selbst gezeichnet oder gibt es ein Tool, das dies tut?
Amir Rachum

2
Habe sie gerade selbst mit TextWrangler auf "überschreiben" gesetzt.
JPSWain

Antworten:


235

VonC hat die Antwort, die Sie suchen , die Rebase-Erweiterung. Es lohnt sich jedoch, ein oder zwei Sekunden darüber nachzudenken, warum in mercurial standardmäßig weder mq noch rebase aktiviert sind: Bei mercurial dreht sich alles um unauslöschliche Änderungssätze. Wenn ich so arbeite, wie Sie es beschreiben, was fast täglich geschieht, dann ist hier das Muster, das ich nehme:

1. Start working on a new feature:
$ hg clone mainline-repo newfeature-123
do a few commits (M, N, O)

master A---B---C
                \
newfeature-123   M---N---O

2. Pull new changes from upstream mainline:
$ hg pull

master A---B---C---D---E---F
                \
newfeature-123   M---N---O

3. merge master into my clone so that my new feature 
can be developed against the latest upstream changes:
(from newfeature-123)
$ hg merge F

master A---B---C---D---E---F
                \           \
newfeature-123   M---N---O---P

und das ist wirklich alles was nötig ist. Am Ende habe ich einen neuen Feature-123-Klon, den ich leicht zur Hauptlinie zurückschieben kann, wenn ich damit zufrieden bin. Vor allem aber habe ich die Geschichte nie verändert . Jemand kann sich meine Csets ansehen und sehen, gegen was sie ursprünglich codiert wurden und wie ich während meiner Arbeit auf Änderungen in der Hauptzeile reagiert habe. Nicht jeder glaubt, dass dies einen Wert hat, aber ich bin fest davon überzeugt, dass es die Aufgabe der Quellcodeverwaltung ist, uns nicht zu zeigen, was wir uns gewünscht haben, sondern was tatsächlich passiert ist - jede Sackgasse und jeder Refaktor sollte eine unauslöschliche Spur hinterlassen und neu basieren und andere Techniken zur Bearbeitung des Verlaufs verbergen dies.

Wählen Sie jetzt VonCs Antwort aus, während ich meine Seifenkiste wegstelle. :) :)


18
Hinweis: Natürlich erlaubt Git Ihnen nicht , den Verlauf neu zu schreiben, sondern nur einfach einen neuen zu erstellen ( utcc.utoronto.ca/~cks/space/blog/tech/GitNewHistory ). Durch Hinzufügen der RebaseExtension bietet Mercurial genau die gleiche bequeme Möglichkeit, einen alten Verlauf durch einen neuen zu ersetzen. Warum? Weil eine Zusammenführung nicht immer die richtige Antwort ist, insbesondere wenn Ihr Änderungssatz als Entwicklung über F und nicht umgekehrt angesehen werden sollte (P über O zusammengeführt)
VonC

19
VonC, ich stimme zu, eine Zusammenführung ist nicht immer die richtige Wahl, aber ich denke, der Unterschied besteht darin, was man möchte, dass die VCS-Geschichte es ihnen sagen kann. Ich denke, die Geschichte sollte immer in der Lage sein, Fragen wie "Wie habe ich versucht, es zu integrieren, das hat nicht geklappt und ich dachte, es war zu der Zeit nutzlos" zu beantworten. Wissenschaftler halten Logbücher mit nummerierten Seiten im Stift, und AFAIC-Softwareentwickler sollten jedes Byte speichern, das sie jemals eingegeben haben. Das Umschreiben des Verlaufs, sogar der Cset-Abstammung, aber sicherlich CollapseExtension, HistEdit usw. verstoßen dagegen. Es ist völlig eine Frage der persönlichen Wahl.
Ry4an Brase

14
+1 für persönliche Wahl. Mit Git verwende ich folglich Rebase für triviale Abweichungen und Merge für nicht triviale. Auf diese Weise kann ich den Zusammenführungsverlauf dort beibehalten, wo ich es für wichtig halte, aber das Protokoll die meiste Zeit sauber und linear halten.
Kos

16
Außerdem mache ich Tonnen von interaktiven Rebases, weil ich dazu neige, zuerst viele kleine Commits zu machen, sie dann zu verbinden, zu beschriften und zu bereinigen und sie dann wieder mit dem Hauptzweig zusammenzuführen (oder darüber zu gründen). Ich mag es, Änderungen als separate Schritte zu codieren und zu verwalten.
Kos

6
Die Philosophie "Jedes Byte auf dem Weg der Entwicklung speichern" eignet sich nicht wirklich für große Open-Source-Projekte, bei denen Mitwirkende eine Reihe von Patches vorschlagen und diese dann auf der Grundlage des Feedbacks der Betreuer überarbeiten. Dann hat das Hauptprojekt-Repository schließlich genau die richtige Version aller Änderungen mit allen zugehörigen Änderungen in einem einzigen Commit. Git Interactive Rebase ist wirklich gut geeignet, um funktionierende Änderungen in einer Folge von Commits zu bereinigen, bei denen der Baum nicht kaputt bleibt, und mit Commit-Meldungen, die widerspiegeln, was Sie sagen möchten, nachdem Sie mit der Arbeit an der gesamten Sache fertig sind.
Peter Cordes

104

Möglicherweise suchen Sie nach einer Rebase-Erweiterung . (implementiert als Teil des SummerOfCode 2008 )

In diesen Fällen kann es nützlich sein, die lokalen Änderungen zu "trennen", das Repository mit dem Mainstream zu synchronisieren und dann die privaten Änderungen an die neuen Remote-Änderungen anzuhängen. Diese Operation wird als Rebase bezeichnet.

Anreise von :

Alt-Text

zu:

Alt-Text


Wie unten von steprobe kommentiert :

In dem Fall, in dem Sie die Änderungen nicht übernehmen und die beiden Zweige in Ihrem Repo haben, können Sie Folgendes tun ( mitkeepbranches ):

hg up newfeature-123 
hg rebase -d master --keepbranches

( --keepbranches: Erben Sie den ursprünglichen Filialnamen.)

Mojca erwähnt:

Ich benutze es gerne hg rebase --source {L1's-sha} --dest {R2's-sha}, aber ich wusste nicht, dass ich es --keepbranchesam Ende hinzufügen könnte .

Wie unten von Jonathan Blackburn dargestellt :

 hg rebase -d default --keepbranches

1
Ich habe mir die Rebase-Erweiterung angesehen, aber es ist mir immer noch nicht klar. Könnten Sie bitte die Schritte erklären, um das zu tun, was ich oben beschrieben habe?
Jpswain

6
Für den Fall, dass Sie die Änderungen nicht übernehmen und die beiden Zweige in Ihrem Repo haben, können Sie hg up newfeature-123hg rebase -d master --keepbranches
Folgendes

2
Ich glaube, nichts ist falsch an Rebase, es ist nur eine Frage der Wahl. Das Problem ist, dass Rebase missbraucht wird und ich mit @ Ry4an zusammen bin. Schreiben Sie den Verlauf nicht neu, damit Sie wissen, was wann passiert.
Jorge Vargas

@steprobe: Danke für den Hinweis zur Verwendung von --keepbranches. Ich benutze es gerne hg rebase --source {L1's-sha} --dest {R2's-sha}, aber ich wusste nicht, dass ich es --keepbranchesam Ende hinzufügen könnte . Dies wird in anderen Antworten mit niedrigerem Rang erwähnt, aber es wäre schön, es auch explizit auf diese Antwort zu schreiben.
Mojca

@ Mojca Kein Problem. Ich habe die Antwort entsprechend bearbeitet.
VonC

44

Angenommen, Sie haben eine moderne Hg-Installation, können Sie einfach hinzufügen:

[extensions]
rebase = 

zu ~ / .hgrc.

Dann können Sie die Befehle verwenden hg rebase, hg pull --rebaseoder hg help rebase.


2
Nur um dies in Bezug auf den Befehl hinzuzufügen, müssen Sie hg rebase -s o -d f
Simple-Solution

21

Ich glaube nicht, dass die obigen Antworten das Ziel des OP erreichen, nämlich seinen Aufgabenzweig beizubehalten, der sich nur gegen einen späteren Punkt im übergeordneten Zweig richtet.

Angenommen, ich beginne mit diesem Diagramm (das mit der Graphlog-Erweiterung erstellt wurde. Ernsthafte Geek-Liebe für Graphlog).

@  9a4c0eb66429 Feature 3 commit 2 tip feature3
|
| o  af630ccb4a80 default againagainagain  
| |
o |  98bdde5d2185 Feature 3 branch commit 1  feature3
|/
o  e9f850ac41da foo   

Wenn ich mich im Feature3-Zweig befinde und ihn aus dem erneuten Commit wiederherstellen möchte, verstehe ich, dass ich ausgeführt werden würde hg rebase -d default. Dies hat folgendes Ergebnis:

@  89dada24591e Feature 3 commit 2 tip 
|
o  77dcce88786d Feature 3 branch commit 1  
|
o  af630ccb4a80 default againagainagain  
|
o  e9f850ac41da foo  

Mission erfüllt? Das glaube ich nicht. Das Problem ist, dass der Feature3-Zweig gelöscht wurde , als die Commits für den Feature3-Zweig erneut auf Basis von erneut basiert wurden . Meine Commits wurden in den Standardzweig verschoben, was ich zunächst vermeiden wollte.

In Git würde das Ergebnis folgendermaßen aussehen:

@  9a4c0eb66429 Feature 3 commit 2 tip
|
o  98bdde5d2185 Feature 3 branch commit 1 **feature3**
|
o  af630ccb4a80 default againagainagain
|
o  e9f850ac41da foo

Beachten Sie, dass der Feature3-Zweig noch vorhanden ist, die beiden Commits sich noch im Feature3-Zweig befinden und standardmäßig nicht sichtbar sind. Ohne den Task-Zweig beizubehalten, sehe ich nicht, wie sich dies funktional von einer Zusammenführung unterscheidet.

UPDATE : Ich habe die --keepbranchesvon hg rebase unterstützte Flagge entdeckt und freue mich, Ihnen mitteilen zu können, dass alles in Ordnung ist. Mit hg rebase -d default --keepbranchesrepliziere ich genau das Git-Verhalten, nach dem ich mich sehnte. Ein paar Aliase später und ich bin neu, als ob es niemanden etwas angeht.


7
Habe gerade die Flagge --keepbranches beim Rebase entdeckt. Problem gelöst. Wäre dies mein Code, würde ich das zum Standard machen, aber das bin nur ich.
Jonathan Blackburn

1
Ich denke, es wäre nützlich, wenn Sie diese Information zu Ihrer obigen Antwort hinzufügen würden - ich habe eine Weile gebraucht, um sie zu finden.
HansMari

3

Da einige Leute zu dem Schluss gekommen sind, dass es gut ist, jede Iteration von allem beizubehalten, möchte ich darauf hinweisen, dass bei größeren Open-Source-Projekten das Akzeptieren von Änderungen voller Zusammenführungen und Entwicklungsiterationen zu einem chaotischen Verlauf der Hauptrevision führen würde Der Revisionsverlauf ist weniger nützlich, um zu sehen, wie die aktuelle Version dorthin gelangt ist.

Dies funktioniert gut, wenn eingereichte Änderungen von Personen überprüft werden, die sie nicht geschrieben haben, bevor sie akzeptiert werden. Änderungen, die in die Hauptzeile eingehen, werden daher im Allgemeinen debuggt und funktionieren. Wenn Sie dann zum Ursprung einer Linie zurückkehren, sehen Sie alle damit verbundenen Änderungen, nicht irgendwann in der Mitte der Entwicklung der Änderung, zu der sie gehört.

Auf der Seite mit den x265-Mitwirkenden wird erläutert, wie Sie eine Reihe von Änderungen, an denen Sie arbeiten, erneut festschreiben , um sie für die Übermittlung an das x265-Projekt vorzubereiten. (Einschließlich der Verwendung von TortoiseHG, um einige, aber nicht alle Änderungen in einer einzelnen Datei festzuschreiben, wie z. B. Git Guis Stage / Unstage Diff Hunk für Commit).

Der Prozess besteht darin, hg auf den Upstream-Tipp zu aktualisieren und dann alle Änderungen, die nicht festgeschrieben wurden, in das Arbeitsverzeichnis aufzunehmen. Speichern Sie alle Elemente, die nicht zu dem gehören, was Sie einreichen möchten, und teilen Sie den Rest in so viele separate Festschreibungen mit netten Festschreibungsnachrichten auf.

Ich denke, Sie würden Commit-Nachrichten aus früheren Iterationen eines Patchset, das Sie überarbeiten, kopieren / einfügen und dann bearbeiten. Oder vielleicht können Sie Ihre alten Commits (Cherry-Pick in Git-Sprache) pfropfen und sie dann einzeln ändern, um Ihre alten Commit-Nachrichten als Ausgangspunkt für die Bearbeitung zu erhalten.

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.