Domanda Sposta i commit più recenti in un nuovo ramo con Git


Mi piacerebbe spostare gli ultimi commit che ho commesso per diventare master in una nuova filiale e riprendere il master prima che venissero presi questi commit. Sfortunatamente, il mio Git-fu non è ancora abbastanza forte, nessun aiuto?

Cioè Come posso andare da questo

master A - B - C - D - E

a questa?

newbranch     C - D - E
             /
master A - B 

3752
2017-10-27 03:07


origine


risposte:


Trasferirsi in una nuova filiale

AVVERTIMENTO: Questo metodo funziona perché stai creando un nuovo ramo con il primo comando: git branch newbranch. Se vuoi spostarti si impegna a filiale esistente è necessario unire le modifiche nel ramo esistente prima dell'esecuzione git reset --hard HEAD~3 (vedere Passaggio a un ramo esistente sotto). Se non si uniscono prima le modifiche, saranno perse.

A meno che non ci siano altre circostanze coinvolte, questo può essere fatto facilmente per ramificazioni e arretramenti.

# 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

Ma assicurati di quanti impegni torni indietro. In alternativa, puoi invece di HEAD~3, semplicemente fornire l'hash del commit (o il riferimento come origin / master) si desidera "ripristinare" su maestro (/ corrente) ramo, ad esempio:

git reset --hard a1b2c3d4

* 1 Lo farai solo essere "perdente" commette dal ramo principale, ma non ti preoccupare, avrai quei commit in newbranch!

AVVERTIMENTO: Con Git versione 2.0 e successive, se più tardi git rebase il nuovo ramo sull'originale (master) ramo, potrebbe essere necessario un esplicito --no-fork-point opzione durante il rebase per evitare di perdere i commit effettuati. avere branch.autosetuprebase always set rende questo più probabile. Vedere La risposta di John Mellor per dettagli.

Passaggio a un ramo esistente

Se vuoi spostare il tuo commit su un filiale esistente, assomiglierà a questo:

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



Per chi si chiede perché funzioni (come all'inizio):

Vuoi tornare a C e spostare D ed E nel nuovo ramo. Ecco come appare all'inizio:

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

Dopo git branch newBranch:

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

Dopo git reset --hard HEAD~2:

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

Dal momento che un ramo è solo un puntatore, maestro ha indicato l'ultimo commit. Quando hai fatto newBranch, hai semplicemente fatto un nuovo puntatore all'ultima commit. Quindi usando git reset hai spostato il maestro pointer back two commits. Ma visto che non ti sei mosso newBranch, indica ancora il commit originariamente fatto.


846
2018-02-07 16:58



In generale...

Il metodo esposto da sykora è l'opzione migliore in questo caso. Ma a volte non è il più semplice e non è un metodo generale. Per un uso generale del metodo prendi la ciliegina:

Per ottenere ciò che l'OP vuole, è un processo in 2 fasi:

Passo 1 - Nota che commette dal master che vuoi su a newbranch

Eseguire

git checkout master
git log

Notare gli hash di (diciamo 3) che si desidera vengano attivati newbranch. Qui userò:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3 

Nota: È possibile utilizzare i primi sette caratteri o   l'intero hash di commit

Passaggio 2: posizionali sul newbranch

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

O (su Git 1.7.2+, usa gli intervalli)

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

prendi la ciliegina applica quei tre commit a newbranch.


341
2018-03-26 08:13



Un altro modo per farlo, usando solo 2 comandi. Mantiene anche intatto il tuo albero di lavoro corrente.

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

Vecchia versione - prima che imparassi git branch -f

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

Essere capace di push a . è un bel trucco da sapere.


251
2018-04-06 22:38



La maggior parte delle risposte precedenti sono pericolosamente sbagliate!

Non farlo:

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

Come la prossima volta che corri git rebase (o git pull --rebase) quei 3 commit verrebbero scartati in silenzio newbranch! (vedi spiegazione sotto)

Invece fai questo:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • Prima scarta i 3 commit più recenti (--keep è come --hard, ma più sicuro, in quanto fallisce piuttosto che buttare via modifiche non eseguite).
  • Quindi si stacca newbranch.
  • Quindi seleziona in modo sequenziale quei 3 commit newbranch. Dal momento che non sono più referenziati da un ramo, lo fa usando git's reflog: HEAD@{2} è il commit che HEAD usato per riferirsi a 2 operazioni fa, vale a dire prima di 1. check-out newbranch e 2. usato git reset scartare i 3 commit.

Attenzione: il reflog è abilitato di default, ma se lo hai disabilitato manualmente (ad esempio usando un repository git "nudo"), non sarai in grado di riavere i 3 commit dopo aver eseguito git reset --keep HEAD~3.

Un'alternativa che non si basa sul reflog è:

# 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

(se preferisci puoi scrivere @{-1} - la filiale precedentemente controllata - invece di oldbranch).


Spiegazione tecnica

Perché sarebbe git rebase scartare il 3 commit dopo il primo esempio? È perchè git rebase senza argomenti abilita il --fork-point opzione per impostazione predefinita, che utilizza il reflog locale per cercare di essere efficace contro il ramo upstream che viene forzato.

Supponiamo di aver smistato origine / master quando conteneva commit M1, M2, M3, quindi fatto tre commit da solo:

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

ma poi qualcuno riscrive la storia forzando l'origine / master per rimuovere M2:

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

Usando il tuo reflog locale, git rebase puoi vedere che hai biforcato da una precedente incarnazione del ramo origine / master, e quindi che i commit M2 e M3 non fanno realmente parte del tuo ramo dell'argomento. Di conseguenza, si presuppone ragionevolmente che dal momento che M2 sia stato rimosso dal ramo upstream, non lo si voglia più nel ramo dell'argomento neanche una volta che il ramo dell'argomento viene ridefinito:

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

Questo comportamento ha un senso ed è generalmente la cosa giusta da fare quando si ridefinisce.

Quindi la ragione per cui i seguenti comandi falliscono:

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

è perché lasciano il reflog nello stato sbagliato. Git vede newbranch come dopo aver biforcato il ramo upstream ad una revisione che include i 3 commit, quindi il reset --hard riscrive la cronologia dell'upstream per rimuovere i commit e così alla prossima esecuzione git rebase li scarta come qualsiasi altro commit che è stato rimosso dall'upstream.

Ma in questo caso particolare vogliamo che quei 3 commit siano considerati come parte del branch dell'argomento. Per raggiungere questo obiettivo, dobbiamo rimandare l'upstream alla revisione precedente che non include i 3 commit. Questo è quello che fanno le mie soluzioni suggerite, quindi entrambi lasciano il reflog nello stato corretto.

Per maggiori dettagli, vedere la definizione di --fork-point nel git rebase e git merge-baseDocumenti.


208
2017-10-19 14:12



Questo non li "sposta" in senso tecnico ma ha lo stesso effetto:

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



Per fare ciò senza riscrivere la cronologia (ad esempio se hai già inserito il commit):

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

Entrambi i rami possono quindi essere spinti senza forza!


19
2017-09-20 10:17



Aveva solo questa situazione:

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

Mi sono esibito:

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

Mi aspettavo che commettessi sarebbe il TESTO, ma commetto L è ora ...

Per essere sicuri di atterrare nel posto giusto nella storia è più facile lavorare con l'hash del commit

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

12
2018-05-01 07:50



Soluzione molto più semplice

Se:

  1. Il tuo scopo principale è di tornare indietro master, e
  2. Vuoi mantenere i cambiamenti ma non ti interessa particolarmente l'individuo si impegna, e
  3. Non hai ancora spinto,

Quindi il seguente è molto più semplice:

git reset HEAD~3
git stash
git checkout otherbranch
git stash pop

Cosa fa questo:

  1. Annulla gli ultimi tre commit (e i loro messaggi), ma lascia intatti tutti i file di lavoro
  2. Elimina tutte le modifiche al file di lavoro, rendendo il master albero di lavoro uguale allo stato HEAD ~ 3.
  3. Passa a un ramo esistente otherbranch
  4. Estrae i file modificati dalla scorta e nella directory di lavoro.

Ora puoi usare git add e git commit come faresti normalmente. Tutte le modifiche verranno eseguite in otherbranch.

Questo non mantiene i commit sbagliati e non commette messaggi. Dovrai aggiungere un nuovo messaggio di commit a questo nuovo commit, ricreando i tuoi messaggi di commit precedenti. Tuttavia, il PO ha affermato che l'obiettivo era "riportare il master prima che questi vengano commessi" senza perdere le modifiche, e questa soluzione lo fa.

Lo faccio almeno una volta alla settimana quando accetto accidentalmente nuovi commit master invece di develop. Di solito ho solo un commit per il rollback, nel qual caso git reset HEAD^ funziona per il rollback di un solo commit.


6
2018-05-25 14:14



Puoi farlo solo 3 semplici passaggi che ho usato.

1) crea un nuovo ramo in cui vuoi impegnarti con l'aggiornamento recente.

git branch <branch name> 

2) Trova ID commit recente per il commit sul nuovo ramo.

git log

3) Copiare quell'ID di commit nota che l'elenco di commit più recente ha luogo in cima. così puoi trovare il tuo impegno. lo trovi anche via messaggio.

git cherry-pick d34bcef232f6c... 

puoi anche fornire qualche identificativo di rang of commit.

git cherry-pick d34bcef...86d2aec

Ora il tuo lavoro è finito. Se hai scelto l'ID corretto e il ramo corretto, avrai successo. Quindi, prima fai attenzione. altrimenti può verificarsi un altro problema.

Ora puoi spingere il tuo codice

git push


0
2018-06-01 05:40