Vraag Verplaats de meest recente commit (s) naar een nieuwe branch met Git


Ik wil graag de laatste verschillende commits verplaatsen die ik heb vastgelegd om een ​​nieuwe branch te beheersen en de master terug te nemen voordat die commits zijn gemaakt. Helaas is mijn Git-fu nog niet sterk genoeg, geen hulp?

D.w.z. Hoe kan ik hiervan afwijken

master A - B - C - D - E

hieraan?

newbranch     C - D - E
             /
master A - B 

3753
2017-10-27 03:07


oorsprong


antwoorden:


Verhuizen naar een nieuwe vestiging

WAARSCHUWING: Deze methode werkt omdat u met de eerste opdracht een nieuwe branch maakt: git branch newbranch. Als je commits naar een wilt verplaatsen bestaande tak je moet je wijzigingen samenvoegen in de bestaande branch voordat je ze uitvoert git reset --hard HEAD~3 (zien Verhuizen naar een bestaande vestiging hieronder). Als u uw wijzigingen niet eerst samenvoegt, gaan ze verloren.

Tenzij er andere omstandigheden zijn betrokken, kan dit eenvoudig worden gedaan door vertakking en terugrol.

# Note: Any changes not committed will be lost.
git branch newbranch      # Create a new branch, saving the desired commits
git reset --hard HEAD~3   # Move master back by 3 commits (GONE from master)
git checkout newbranch    # Go to the new branch that still has the desired commits

Maar zorg ervoor dat je vastlegt om terug te gaan. Als alternatief kunt u in plaats van HEAD~3, geef eenvoudig de hash van de commit (of de referentie zoals origin / master) u wilt "terugkeren naar" op de meester (/ huidige) tak, bijvoorbeeld:

git reset --hard a1b2c3d4

* 1 Dat doet u enkel en alleen zijn "verliest" commits van de master branch, maar maak je geen zorgen, je zult die commits in newbranch hebben!

WAARSCHUWING: Met Git versie 2.0 en later, als je later bent git rebase de nieuwe tak op het origineel (master) branch, heeft u mogelijk een expliciete nodig --no-fork-point optie tijdens de rebase om te voorkomen dat de overgedragen commits verloren gaan. Het hebben van branch.autosetuprebase always set maakt dit waarschijnlijker. Zien John Mellor's antwoord voor details.

Verhuizen naar een bestaande vestiging

Als je je commits naar een wilt verplaatsen bestaande tak, het zal er als volgt uitzien:

git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch

4778
2017-07-22 22:37



Voor degenen die zich afvragen waarom het werkt (zoals ik eerst was):

U wilt teruggaan naar C en D en E naar de nieuwe vertakking verplaatsen. Hier is hoe het er eerst uitziet:

A-B-C-D-E (HEAD)
        ↑
      master

Na git branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

Na git reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

Omdat een tak slechts een aanwijzer is, meester wees naar de laatste commit. Toen je het maakte nieuwe tak, je hebt simpelweg een nieuwe aanwijzer gemaakt voor de laatste commit. Dan gebruiken git reset jij hebt het meester pointer back twee commits. Maar omdat je niet bewoog nieuwe tak, het wijst nog steeds naar de commit die het oorspronkelijk deed.


846
2018-02-07 16:58



In het algemeen...

De methode die wordt getoond door sykora is in dit geval de beste optie. Maar soms is dit niet de gemakkelijkste en het is geen algemene methode. Voor een algemene methode gebruik git cherry-pick:

Om te bereiken wat OP wil, is het een tweestaps proces:

Stap 1 - Notitie die vastlegt van de master die je wilt op a newbranch

uitvoeren

git checkout master
git log

Let op de hashes van (laten we zeggen 3) commits waarop je wilt newbranch. Hier zal ik gebruiken:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3 

Notitie: U kunt de eerste zeven tekens of gebruiken   de hele commit-hash

Stap 2 - Zet ze op de newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

OF (op Git 1.7.2+, gebruikscategorieën)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

git cherry-pick past die drie commits toe op newbranch.


342
2018-03-26 08:13



Nog een andere manier om dit te doen, met slechts 2 commando's. Houdt ook uw huidige werkende boom intact.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

Oude versie - voordat ik erover hoorde git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

Het kunnen push naar . is een leuke truc om te weten.


251
2018-04-06 22:38



De meeste eerdere antwoorden zijn gevaarlijk verkeerd!

Doe dit niet:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

Zoals de volgende keer dat je rent git rebase (of git pull --rebase) die 3 commits zouden zwijgend worden weggegooid newbranch! (zie uitleg hieronder)

Doe dit in plaats daarvan:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • Eerst worden de 3 meest recente commits verwijderd (--keep is als --hard, maar veiliger, als mislukken in plaats van ongecommitteerde wijzigingen weg te gooien).
  • Dan gaat het weg newbranch.
  • Vervolgens pakt hij die 3 commits terug op newbranch. Omdat er niet langer naar wordt verwezen door een filiaal, doet het dat met behulp van git's reflog: HEAD@{2} is de commit die HEAD gebruikt om te verwijzen naar 2 bewerkingen geleden, d.w.z. voordat we 1. hebben uitgecheckt newbranch en 2. gebruikt git reset om de 3 commits te verwijderen.

Waarschuwing: de reflog is standaard ingeschakeld, maar als je deze handmatig hebt uitgeschakeld (bijvoorbeeld door een "blote" git-repository te gebruiken), kun je de 3 commits niet meer terugkrijgen na het uitvoeren git reset --keep HEAD~3.

Een alternatief dat niet afhankelijk is van de reflog is:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(als u wilt, kunt u schrijven @{-1} - de eerder uitgecheckte vestiging - in plaats van oldbranch).


Technische uitleg

Waarom zou git rebase gooit u de 3 commits na het eerste voorbeeld weg? Het is omdat git rebase zonder argumenten maakt het --fork-point standaardoptie, die de lokale reflog gebruikt om te proberen robuust te zijn tegen gedwongen upstream-branch.

Stel dat je de oorsprong / master hebt vertakt toen deze commits M1, M2, M3 bevatte en vervolgens zelf drie commits maakte:

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

maar dan herschrijft iemand de geschiedenis door de oorsprong / meester dwangmatig te verwijderen om M2 te verwijderen:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

Gebruik je lokale reflog, git rebase kan zien dat je bent gevorkt uit een eerdere incarnatie van de oorsprong / master branch, en dus dat de M2 ​​en M3 commits niet echt deel uitmaken van je topic branch. Vandaar dat redelijkerwijs aangenomen wordt dat sinds M2 uit de stroomopwaartse tak verwijderd is, je het niet langer in je topic branch wilt, ook niet als de topic branch gerebased is:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

Dit gedrag is logisch en is over het algemeen het juiste om te doen bij het rebasen.

Dus de reden dat de volgende opdrachten mislukken:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

is omdat ze de reflog in de verkeerde staat achterlaten. Git ziet het newbranch als van de stroomopwaartse tak gevorkt bij een herziening die de 3 commits omvat, dan is de reset --hard herschrijft de geschiedenis van de upstream om de commits te verwijderen, en dus de volgende keer dat je wegrent git rebase het legt ze weg als elke andere verbintenis die stroomopwaarts is verwijderd.

Maar in dit specifieke geval willen we dat deze 3 commits worden beschouwd als onderdeel van de topic branch. Om dat te bereiken, moeten we de stroomopwaartse splitsing afsnijden bij de eerdere herziening die de 3 commits niet omvat. Dat is wat mijn voorgestelde oplossingen doen, vandaar dat ze allebei de reflog in de juiste staat achterlaten.

Zie de definitie van voor meer informatie --fork-point in de git rebase en git merge-basedocs.


209
2017-10-19 14:12



Dit "beweegt" ze niet in de technische zin, maar heeft hetzelfde effect:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)

27
2018-01-21 16:10



Om dit te doen zonder de geschiedenis te herschrijven (dat wil zeggen als je de commits al hebt gepusht):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Beide takken kunnen dan zonder kracht worden ingedrukt!


19
2017-09-20 10:17



Had alleen deze situatie:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

Ik heb uitgevoerd:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

Ik verwachtte dat ik zou zeggen dat ik de HOOFD zou zijn, maar bega L is het nu ...

Om zeker te zijn om op de juiste plek in de geschiedenis te landen, is het gemakkelijker om met de hash van de commit te werken

git branch newbranch 
git reset --hard #########
git checkout newbranch

12
2018-05-01 07:50