Come selezionare più commit


875

Ho due rami. Commit aè la testa di uno, mentre l'altro ha b, c, d, ee fin cima a. Voglio spostare c, d, ee fal primo ramo, senza impegnarsi b. Utilizzando ciliegia scegliere è facile: cassa primo ramo ciliegio scegliere uno per uno cda fe rebase secondo ramo sul primo. Ma esiste un modo per cherry-pick tutto c- fin un unico comando?

Ecco una descrizione visiva dello scenario (grazie JJD ):

inserisci qui la descrizione dell'immagine


3
il rebase di cui parli non è realmente rilevante per la domanda, vero? (I ottenere che si potrebbe voler bessere basata su fpiù tardi, ma che non ha nulla a che fare con il cherry-picking.)
Superole

Risposte:


1282

Git 1.7.2 ha introdotto la possibilità di scegliere una serie di commit. Dalle note di rilascio :

git cherry-pickimparato a scegliere una serie di commit (es. cherry-pick A..Be cherry-pick --stdin), così ha fatto git revert; questi, tuttavia, non supportano il migliore controllo del sequenziamento rebase [-i].

Per selezionare tutti i commit da commit Aa commit B(dove Aè più vecchio di B), esegui:

git cherry-pick A^..B

Se si desidera ignorare A stesso, eseguire:

git cherry-pick A..B

(Il merito va a Damian, JB Rainsberger e sschaef nei commenti)


249
Nel modulo "cherry-pick A..B", A dovrebbe essere più vecchio di B. Se sono nell'ordine sbagliato il comando fallirà silenziosamente.
Damian,

294
Inoltre, questo non sceglierà A, ma piuttosto tutto dopo A fino a B. compresa
J

455
Per includere A basta digitaregit cherry-pick A^..B
kiritsuku l'

17
Se hai git 1.7.1 o precedenti e non riesci ad aggiornare, puoi selezionarli abbastanza rapidamente in ordine eseguendo git cherry-pick f~3quindi git cherry-pick f~2ecc. Fino a git cherry-pick f(premendo la freccia su si ottiene il comando precedente in modo da poter cambiare rapidamente il numero ed eseguire dovrebbe essere simile nella maggior parte delle console).
David Mason,

19
Potrebbe essere utile sapere che questa sintassi funziona anche con i nomi dei rami. git cherry-pick master..somebranchsceglierà tutti i commit su somebranch dal master (supponendo che sia già stato reimpostato sul master) e li applicherà al tuo ramo corrente.
Tor Klingberg,

103

Il modo più semplice per farlo è con l' ontoopzione di rebase. Supponiamo che il ramo che attuali finiture a aè chiamato mybranch e questo è il ramo che si desidera spostare c- fsu.

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b

1
Grazie! Potrebbe anche aggiungere git checkout secondbranch && git rebase mybranchper risposta completa
tig

1
Questa risposta mi ha aiutato molto a capire qual è il commit in questo scenario. E: puoi anche usare rebasela modalità interattiva. Grazie, @Charles!
Oliver,

1
Il bello di questo approccio è che puoi usare --interactiveper rimuovere alcuni commit dalla sequenza o riordinarli prima del "cherry pick". +1
Michael Merickel,

questo è un comando geniale, un po 'complicato da avvolgere la testa, ma fa miracoli.
Valerio,

Selvaggio che devi impegnarti a non ribattere come uno degli argomenti ( bin questo esempio) ma sì, questo ha funzionato per me.
asontu

85

O il one-liner richiesto:

git rebase --onto a b f

5
Se non altro per la sua brevità, questa è la risposta migliore.
Nate Chandler,

9
Eseguito l'upgrade ma ti lascerà nello stato HEAD distaccato se f è un commit (al contrario di un ramo) - dovresti modificare per aggiungere che si dovrebbe effettuare il checkout di un ramo come nella risposta di seguito
Mr_and_Mrs_D

68

È possibile utilizzare una combinazione seriale di git rebasee git branchper applicare un gruppo di commit su un altro ramo. Come già pubblicato da wolfc, il primo comando copia effettivamente i commit. Tuttavia, la modifica non è visibile fino a quando non si aggiunge un nome di ramo al commit più alto del gruppo.

Si prega di aprire l'immagine in una nuova scheda ...

Flusso di lavoro

Per riassumere i comandi in forma di testo:

  1. Aprire gitk come un processo indipendente utilizzando il comando: gitk --all &.
  2. Corri git rebase --onto a b f.
  3. Press F5in gitk . Niente cambia. Ma no HEADè segnato.
  4. Correre git branch selection
  5. Press F5in gitk . Viene visualizzata la nuova filiale con i relativi commit.

Questo dovrebbe chiarire le cose:

  • Commit aè la nuova destinazione principale del gruppo.
  • Commit bè il commit prima del primo commit del gruppo (esclusivo).
  • Commit fè l'ultimo commit del gruppo (incluso).

Successivamente, è possibile utilizzare git checkout feature && git reset --hard bper eliminare i commit cfino fal featureramo.

Oltre a questa risposta, ho scritto un post sul blog che descrive i comandi in un altro scenario che dovrebbe aiutare a usarlo in generale.


2
Se il mybranch (il commit a..f) non è più necessario, questo può essere semplificato in: git rebase --onto a b mybranche tra l'altro - quale programma fa quelle belle foto di git?
Mr_and_Mrs_D,

2
@Mr_and_Mrs_D Grazie per il tuo commento. Penso di aver usato cacoo.com per disegnare le foto.
JJD

48

Per applicare i commenti di JB Rainsberger e sschaef per rispondere in modo specifico alla domanda ... Per utilizzare un intervallo di selezione ciliegia su questo esempio:

git checkout a
git cherry-pick b..f

o

git checkout a
git cherry-pick c^..f

3
Uso git 2.7.0.windows.1e ho notato che quando provo a selezionare una serie di commit tutto è ok, ma git non ti dice da nessuna parte che devi fare git cherry-pick --continue | --abort | --quitprima di provare a eseguire nuovamente il commit / cherry-pick. Quindi, se scegli una serie di commit, dovrai eseguire git cherry-pick --continueogni volta che sei pronto (risolvere conflitti o simili) con un commit dall'intervallo dato.
kuskmen,

Ho fatto esattamente lo stesso, ma fatale: non riesco a trovare "a..b"
Amit Karnik,

Non so dove sbaglio ma quando faccio 'git cherry-pick c ^ .. f' dalla mia parte, questo include il commit f ma non il commit c. Ma mentre leggo dappertutto, si suppone che ce ne sia inclusivo. O mi sbaglio?
Samuel,

@ Samuel sì, è corretto. Il ^dopo la c in realtà significa "il commit prima di c" che in questo caso è b. Questo è il motivo per cui c^..fè sinonimo b..f. Prova a fare git log c^..fe dovresti vedere i commit da c a f, esattamente come se lo avessi fattogit log b..f
Andy,

41

Se hai delle revisioni selettive da unire, dì A, C, F, J da A, B, C, D, E, F, G, H, I, J, semplicemente, usa il comando seguente:

git cherry-pick ACFJ


1
bello e semplice
spinup

21
git rev-list --reverse b..f | xargs -n 1 git cherry-pick

2
Funziona perfettamente se non ci sono conflitti, altrimenti "rifare su" potrebbe essere più facile in quanto non dovrai capire dove si è fermato e riapplicare il resto delle patch.
Ruslan Kabalin,

6
Si prega di aggiungere commenti che spiegano cosa sta facendo
Mr_and_Mrs_D

7
dal momento che nessuno ha spiegato ... git rev-list stampa tutte le revisioni dal ramo bf (invertito) in modo che quando ogni riga (hash di commit) viene passata in ordine, raccoglierà ognuna sull'attuale git HEAD. cioègit cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
coderatchet,

8

Per scegliere da un ID di commit fino alla punta del ramo, puoi usare:

git cherry-pick commit_id^..branch_name


Questo fa già parte della risposta stackoverflow.com/a/31640427/96823
tig

1
Questa risposta è in realtà diversa ed è stata utile per me. Specifica il nome del ramo, non il SHA di commit finale.
Subtletree

7

Un'altra variante degna di nota è che se si desidera l'ultimo ncommit da un ramo, la ~sintassi può essere utile:

git cherry-pick some-branch~4..some-branch

In questo caso, il comando precedente selezionerebbe gli ultimi 4 commit da un ramo chiamato some-branch(anche se potresti anche usare un hash di commit al posto del nome di un ramo)


1
Risposta MOLTO utile, grazie! :)
Jacek Dziurdzikowski l'

3

In realtà, il modo più semplice per farlo potrebbe essere:

  1. registra la base di unione tra i due rami: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. avanzamento rapido o rifacimento del ramo più vecchio sul ramo più recente
  3. rifare il ramo risultante su se stesso, iniziando dalla base di unione dal passaggio 1 e rimuovere manualmente i commit che non sono desiderati:

    git rebase ${SAVED_MERGE_BASE} -i
    

    In alternativa, se ci sono solo alcuni nuovi commit, salta il passaggio 1 e usa semplicemente

    git rebase HEAD^^^^^^^ -i
    

    nel primo passaggio, usando abbastanza ^per passare oltre la base di unione.

Vedrai qualcosa di simile nel rebase interattivo:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

Quindi rimuovere le righe b (e tutte le altre desiderate)


1
git format-patch --full-index --binary --stdout range... | git am -3

42
Si prega di aggiungere commenti che spiegano cosa sta facendo
Mr_and_Mrs_D

0

Ecco uno script che ti consentirà di selezionare più commit di fila semplicemente dicendo allo script quale sorgente e target branch per i cherry pick e il numero di commit:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

Per selezionare la ciliegia dal ramo al master (utilizza il ramo corrente come sorgente):

./gcpl.sh -m

Per selezionare gli ultimi 5 commit dalla filiale 6.19.x da padroneggiare:

./gcpl.sh -c 5 -s 6.19.x -t master

Perché questo script sarebbe necessario quando puoi farlo direttamente con Git? ...
code_dredd

Meno battitura a macchina, e la mia sceneggiatura seleziona automaticamente le commit per te, quindi non devi sceglierne ognuna dal suo SHA. Comunque, YMMV.
nickboldt,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.