web-dev-qa-db-de.com

git Befehl, um einen Zweig wie einen anderen zu machen

Ich versuche, einen Zweig mit Änderungen zu übernehmen und ihn zurückzubringen, damit er identisch mit dem Upstream ist, von dem er abweicht. Die Änderungen sind sowohl lokal als auch auf github übertragen worden, so dass git reset und git rebase nicht wirklich realisierbar sind, da sie den Verlauf ändern. Dies ist eine schlechte Sache mit einem bereits gepushten Zweig.

Ich habe auch git merge mit verschiedenen Strategien ausprobiert, aber keine davon macht die lokalen Änderungen rückgängig, dh wenn ich eine Datei hinzugefügt habe, könnte eine Zusammenführung andere Dateien wieder in Einklang bringen, aber ich habe immer noch diese Datei, die der Upstream nicht verwendet. t haben.

Ich könnte einfach einen neuen Zweig außerhalb des Upstreams erstellen, aber ich würde wirklich gerne eine Fusion haben, die in Bezug auf die Revisionshistorie alle Änderungen anwendet, um meinen Zweig zu übernehmen und ihn wieder mit dem Upstream identisch zu machen, so dass ich diese Änderung sicher verschieben kann ohne raobige Geschichte. Gibt es einen solchen Befehl oder eine Reihe von Befehlen?

67
Arne Claassen

Sie könnten Ihren Upstream-Zweig mit einem benutzerdefinierten Merge-Treiber "keepTheirs" mit Ihrem dev-Zweig zusammenführen:
Siehe " " git merge -s theirs "wird benötigt - aber ich weiß, dass es nicht existiert ".
In Ihrem Fall wäre nur ein .gitattributes erforderlich, und ein keepTheirs-Skript wie:

mv -f $3 $2
exit 0

git merge --strategy=theirs Simulation # 1

Zeigt als Zusammenführung mit Upstream als erstes übergeordnetes Element.

Jefromi Erwähnt (in den Kommentaren) den merge -s ours, indem Sie Ihre Arbeit im Upstream (oder in einem temporären Zweig ab dem Upstream) zusammenführen und dann den Zweig schnell zum Ergebnis dieser Zusammenführung vorspulen:

git checkout -b tmp Origin/upstream
git merge -s ours downstream         # ignoring all changes from downstream
git checkout downstream
git merge tmp                        # fast-forward to tmp HEAD
git branch -D tmp                    # deleting tmp

Dies hat den Vorteil, dass der Upstream-Vorfahre als erstes übergeordnetes Element aufgezeichnet wird, so dass merge "Aufheben dieses veralteten Zweigzweigs" bedeutet, anstatt "diesen Zweigzweig zu zerstören und durch Upstream zu ersetzen".

(Edit 2011):

Dieser Workflow wurde in diesem Blogbeitrag vom OP gemeldet :

Warum will ich das nochmal?

Solange mein Repo nichts mit der öffentlichen Version zu tun hatte, war dies alles in Ordnung, aber da ich jetzt die Möglichkeit hätte, mit anderen Teammitgliedern und externen Mitarbeitern zusammenarbeiten zu können, möchte ich sicherstellen, dass meine öffentlichen Zweigstellen vorhanden sind Zuverlässig für andere, um abzweigen und abzubrechen, dh keine Neuerungen und Zurücksetzen von Dingen, die ich auf das Remote-Backup verschoben habe, da es jetzt auf GitHub und public ist.

So bleibt mir die Frage, wie ich vorgehen soll.
In 99% der Fälle wird meine Kopie in den Upstream-Master übertragen, daher möchte ich meistens meinen Master und Push-Upstream verwenden.
Aber ab und zu wird das, was ich in wip habe, durch das, was in den Upstream geht, für ungültig erklärt, und ich werde einen Teil meiner wip aufgeben.
An diesem Punkt möchte ich meinen Master wieder mit dem Upstream synchronisieren, aber keine Commit-Punkte auf meinem öffentlich gesteuerten Master zerstören. Das heißt Ich möchte eine Zusammenführung mit Upstream, die mit dem Changeset endet, das meine Kopie mit Upstream identisch macht.
Und genau das sollte git merge --strategy=theirs tun.


git merge --strategy=theirs Simulation # 2

Zeigt als Zusammenführung, bei uns als erstem Elternteil.

(vorgeschlagen von jcwenger )

git checkout -b tmp upstream
git merge -s ours thebranch         # ignoring all changes from downstream
git checkout downstream
git merge --squash tmp               # apply changes from tmp but not as merge.
git rev-parse upstream > .git/MERGE_HEAD #record upstream 2nd merge head
git commit -m "rebaselined thebranch from upstream" # make the commit.
git branch -D tmp                    # deleting tmp

git merge --strategy=theirs Simulation # 3

Dieser Blogeintrag erwähnt :

git merge -s ours ref-to-be-merged
git diff --binary ref-to-be-merged | git apply -R --index
git commit -F .git/COMMIT_EDITMSG --amend

manchmal möchten Sie dies tun, und nicht, weil Sie "Mist" in Ihrer Historie haben, sondern vielleicht, weil Sie die Baseline für die Entwicklung in einem öffentlichen Repository ändern möchten, wo eine Umbasierung vermieden werden sollte.


git merge --strategy=theirs Simulation # 4

(derselbe Blogeintrag)

Wenn Sie die lokalen Upstream-Zweige schnell weiterleiten möchten, besteht ein möglicher Kompromiss darin, zu verstehen, dass für Sid/Unstable der Upstream-Zweig von Zeit zu Zeit zurückgesetzt werden kann (basierend auf den Ereignissen, die letztendlich ausgehen Ihrer Kontrolle auf der Upstream-Projektseite).
Dies ist keine große Sache und die Arbeit mit dieser Annahme bedeutet, dass es einfach ist, den lokalen Upstream-Zweig in einem Zustand zu halten, in dem nur Aktualisierungen zum schnellen Vorlauf erforderlich sind.

git branch -m upstream-unstable upstream-unstable-save
git branch upstream-unstable upstream-remote/master
git merge -s ours upstream-unstable
git diff --binary ref-to-be-merged | git apply -R --index --exclude="debian/*"
git commit -F .git/COMMIT_EDITMSG --amend

git merge --strategy=theirs Simulation # 5

(vorgeschlagen von Barak A. Pearlmutter ):

git checkout MINE
git merge --no-commit -s ours HERS
git rm -rf .
git checkout HERS -- .
git checkout MINE -- debian # or whatever, as appropriate
git gui # edit commit message & click commit button

git merge --strategy=theirs Simulation # 6

(vorgeschlagen vom selben Michael Gebetsroither ):

Michael Gebetsroither mischte sich ein und behauptete, ich würde "betrügen";) und gab eine andere Lösung mit untergeordneten Sanitärbefehlen:

(Es wäre nicht git, wenn es mit git only-Befehlen nicht möglich wäre. Alles in git mit diff/patch/apply ist keine echte Lösung;).

# get the contents of another branch
git read-tree -u --reset <ID>
# selectivly merge subdirectories
# e.g superseed upstream source with that from another branch
git merge -s ours --no-commit other_upstream
git read-tree --reset -u other_upstream     # or use --prefix=foo/
git checkout HEAD -- debian/
git checkout HEAD -- .gitignore
git commit -m 'superseed upstream source' -a
89
VonC

Das geht jetzt ganz leicht:

$ git fetch Origin
$ git merge Origin/master -s recursive -Xtheirs

Dadurch wird Ihr lokales Repo mit dem Origin synchronisiert und der Verlauf wird beibehalten.

13
user1828582

Es klingt für mich so, als müsstest du nur noch tun:

$ git reset --hard Origin/master

Wenn Push-Upstream nicht geändert wird und Sie möchten, dass der Upstream-Zweig einfach Ihr aktueller Zweig ist, geschieht dies. Es ist nicht schädlich, dies lokal zu tun, aber Sie verlieren alle lokalen Änderungen **, die nicht zum Master gebracht wurden.

** Tatsächlich sind die Änderungen immer noch vorhanden, wenn Sie sie lokal festgelegt haben, da die Zusagen immer noch in Ihrem git reflog sind, normalerweise für mindestens 30 Tage.

13
wuputah

Eine weitere Simulation für git merge -s theirs ref-to-be-merged:

git merge --no-ff -s ours ref-to-be-merged         # enforce a merge commit; content is still wrong
git reset --hard HEAD^2; git reset --soft [email protected]{1} # fix the content
git commit --amend

Eine Alternative zum Double Reset wäre das Reverse Patch:

git diff --binary ref-to-be-merged | git apply -R --index
5
michas

Es gibt auch einen Weg mit wenig Hilfe des Klempnerbefehls - IMHO der einfachste. Angenommen, Sie möchten "ihre" für 2 Fälle nachahmen:

head1=$(git show --pretty=format:"%H" -s foo)
head2=$(git show --pretty=format:"%H" -s bar)
tree=$(git show --pretty=format:"%T" -s bar)
newhead=$(git commit-tree $tree -p $head1 -p $head2 <<<"merge commit message")
git reset --hard $newhead

Dies führt eine beliebige Anzahl von Heads (2 im obigen Beispiel) zusammen, wobei der Tree eines dieser Heads verwendet wird (Balken im obigen Beispiel, der 'ihres' Tree enthält), wobei alle diff/file-Probleme ignoriert werden (Commit-Tree ist ein Low-Level-Befehl interessiert sich nicht für diese). Beachten Sie, dass der Kopf nur 1 sein kann (also gleichbedeutend mit Kirschpflücken mit "ihren").

Beachten Sie, dass der übergeordnete Kopf zuerst etwas beeinflusst (siehe z. B. den Befehl --first-parent des Befehls git-log). Denken Sie daran.

Anstelle von git-show kann alles andere verwendet werden, das Tree- und Commit-Hashes ausgeben kann - was auch immer zum Parsen verwendet wird (cat-file, rev-list, ...). Sie können alles mit git commit --amend verfolgen, um die Commit-Nachricht interaktiv zu verschönern.

3
Michal Soltys

Verwenden Sie git reset BACKWARDS!

Mit git reset können Sie einen Zweig wie jedes andere Commit aussehen lassen, aber Sie müssen es rundherum tun.

Um einen Zweig beim Commit <old> wie ein Commit <new> aussehen zu lassen, können Sie dies tun

git reset --hard <new>

um <new> den Inhalt des Arbeitsbaums zu machen.

Dann mach 

git reset --mixed <old> 

um den Zweig wieder in das ursprüngliche Commit zu ändern, den Arbeitsbaum jedoch im<new>state zu belassen.

Anschließend können Sie die Änderungen hinzufügen und festschreiben, damit Ihr Zweig genau mit dem Inhalt des <new>-Commits übereinstimmt.

Es ist nicht intuitiv, dass Sie zum Wechseln vom <old>-Status zum <new> einen git resetvon<new>zu<old> machen müssen. Mit der Option --mixed bleibt der Arbeitsbaum jedoch bei <new> und der Zweigzeiger ist auf <old> gesetzt, so dass der Zweig, wenn die Änderungen festgeschrieben werden, so aussieht, wie wir wollen.

Warnung

Verpassen Sie nicht Ihre Commits, z. Vergessen Sie, was <old> ist, wenn Sie git reset --hard <new> ausführen.

1
user3070485

Schwere Hände, aber zur Hölle, was kann schief gehen?

  • Schauen Sie sich den Zweig X an, der wie das Y aussehen soll
  • cp -r .git/tmp
  • Schauen Sie sich den Zweig Y an
  • rm -rf .git && cp -r /tmp/.git.
  • Commit & Push einen Unterschied
  • ERLEDIGT.
1
sscarduzio

wechseln Sie in den entfernten Upstream-Zweig, und führen Sie einen git merge aus, wobei die Zusammenführungsstrategie auf ours gesetzt ist.

git checkout Origin/master
git merge dev --strategy=ours
git commit ...
git Push

Die gesamte Historie ist immer noch vorhanden, aber Sie haben ein zusätzliches Zusammenführungs-Commit. Das Wichtigste hier ist, von der Version auszugehen, in der Sie sein wollen, und ours mit dem Zweig zu verbinden, in dem sich github tatsächlich befindet.

1
kelloti

Ich bin diesen Rollen gefolgt:

Holen Sie den Ursprung ab, setzen Sie ihn vom Zweig zurück, rekursiv von dort aus und zwingen Sie ihn dann zum Verzweigen

AUF EIGENE GEFAHR

git fetch Origin
git reset --hard Origin/<branch>
git merge Origin/<branch> -s recursive -Xtheirs
git Push -f <remote> <branch>
0
esso