Como escolher várias confirmações de cereja

875

Eu tenho dois galhos. Commit aé a cabeça de um, enquanto o outro tem b, c, d, ee fno topo a. Eu quero mover c, d, ee fa primeira filial sem comprometer b. Usando cereja escolher é fácil: check-out primeiro ramo cherry-pick um por um cpara fe rebase segundo ramo em primeiro lugar. Mas há alguma maneira de cherry-pick tudo c- fem um comando?

Aqui está uma descrição visual do cenário (obrigado JJD ):

insira a descrição da imagem aqui

tig
fonte
3
a rebase que você mencionou não é realmente relevante para a questão, é? (Eu entendo que você pode querer bbasear-se fmais tarde, mas que não tem nada a ver com o cherry-picking.)
Superole

Respostas:

1282

O Git 1.7.2 introduziu a capacidade de escolher uma variedade de confirmações. Nas notas de versão :

git cherry-pickaprendeu a escolher uma variedade de confirmações (por exemplo, cherry-pick A..Be cherry-pick --stdin), o mesmo fez git revert; eles não oferecem suporte ao controle de seqüenciamento mais agradável rebase [-i].

Para escolher todos os commits de commit Apara commit B(onde Aé mais antigo B), execute:

git cherry-pick A^..B

Se você deseja ignorar o próprio A, execute:

git cherry-pick A..B

(O crédito vai para damian, JB Rainsberger e sschaef nos comentários)

Eric Darchis
fonte
249
No formulário "escolha cereja A..B", A deve ter mais de B. Se estiverem na ordem errada, o comando falhará silenciosamente.
Damian
294
Além disso, este não cherry-pick A, mas sim tudo após um até e incluindo B.
JB Rainsberger
455
Para incluir um tipo justogit cherry-pick A^..B
kiritsuku 11/11/2013
17
Se você possui o git 1.7.1 ou anterior e não pode atualizá-lo, é possível selecioná-los rapidamente em ordem, executando git cherry-pick f~3-os git cherry-pick f~2etc. etc. até git cherry-pick f(pressionar a seta para cima obtém o comando anterior para que eu possa alterar rapidamente o número e executar deve ser semelhante na maioria dos consoles).
David Mason
19
Pode ser bom saber que essa sintaxe também funciona com nomes de ramificações. git cherry-pick master..somebranchselecionará todos os commits em somebranch desde o master (supondo que já tenha sido reestruturado no master) e os aplicará à sua ramificação atual.
Tor Klingberg
103

A maneira mais simples de fazer isso é com a ontoopção de rebase. Suponha-se que o ramo que termina em curso a aé chamado mybranch e este é o ramo que você deseja mover c- fem.

# 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
CB Bailey
fonte
1
Obrigado! Você também pode adicionar git checkout secondbranch && git rebase mybranchpara a resposta completa
tig
1
Essa resposta me ajudou muito a entender qual commit é qual nesse cenário. E: você também pode usar rebaseo modo interativo. Obrigado, @Charles!
21413 Oliver
1
A beleza dessa abordagem é que você pode usar --interactivepara remover alguns commits da sequência ou reordená-los antes da "escolha de cerejeira". +1
Michael Merickel
esse é um comando genial, um pouco complicado de entender, mas faz maravilhas.
Valerio
É selvagem que você tenha que enviar o commit que você não deseja rebase como um dos argumentos ( bneste exemplo), mas sim, isso funcionou para mim.
asontu 14/05
85

Ou o pedido único:

git rebase --onto a b f
wolfc
fonte
5
Se apenas por sua brevidade, esta é a melhor resposta.
Nate Chandler
9
Upvoted mas vai deixá-lo no estado CABEÇA destacado se f é um commit (ao contrário de um ramo) - você deve editar para adicionar que se deve fazer o checkout um ramo como na resposta abaixo
Mr_and_Mrs_D
68

Você pode usar uma combinação serial git rebasee git branchaplicar um grupo de confirmações em outra ramificação. Como já postado por wolfc, o primeiro comando realmente copia os commits. No entanto, a alteração não estará visível até você adicionar um nome de filial à confirmação mais importante do grupo.

Por favor, abra a imagem em uma nova aba ...

Fluxo de trabalho

Para resumir os comandos no formato de texto:

  1. Abrir gitk como um processo independente usando o comando: gitk --all &.
  2. Corra git rebase --onto a b f.
  3. Imprensa F5em gitk . Nada muda. Mas não HEADestá marcado.
  4. Corre git branch selection
  5. Imprensa F5em gitk . A nova ramificação com suas confirmações é exibida.

Isso deve esclarecer as coisas:

  • Confirmar aé o novo destino raiz do grupo.
  • Confirmar bé o commit antes do primeiro commit do grupo (exclusivo).
  • Confirmar fé o último commit do grupo (inclusive).

Depois, você pode usar git checkout feature && git reset --hard bpara excluir as confirmações caté fdo featureramo.

Além desta resposta, escrevi uma postagem no blog que descreve os comandos em outro cenário que deve ajudar a usá-lo geralmente.

JJD
fonte
2
Se o mybranch (o a..f confirmar) não for mais necessário, isso pode ser simplificado para: git rebase --onto a b mybranche btw - qual programa essas imagens bacanas do Git?
Mr_and_Mrs_D
2
@Mr_and_Mrs_D Obrigado pelo seu comentário. Acho que usei o cacoo.com para desenhar as imagens.
JJD
48

Para aplicar os comentários de JB Rainsberger e sschaef para responder especificamente à pergunta ... Para usar um intervalo de seleção de cereja neste exemplo:

git checkout a
git cherry-pick b..f

ou

git checkout a
git cherry-pick c^..f
Andy
fonte
3
Eu uso git 2.7.0.windows.1e notei que, quando tento escolher uma variedade de confirmações, tudo está bem, mas o git não informa em nenhum lugar o que você precisa fazer git cherry-pick --continue | --abort | --quitantes de tentar confirmar / escolher novamente. Portanto, se você escolher uma variedade de confirmações, precisará executar git cherry-pick --continuetoda vez que estiver pronto (resolvendo conflitos ou algo assim) com uma confirmação do intervalo especificado.
Kuskmen
Fiz exatamente igual, mas fatal: não é possível localizar 'a..b'
Amit Karnik
Eu não sei onde estou errado, mas quando eu faço o git cherry-pick c ^ .. f 'do meu lado, isso inclui o commit f, mas não o commit c. Mas como eu leio em todos os lugares, é suposto definir cef como inclusivo. Ou eu estou errado?
Samuel
@ Samuel sim, está correto. O ^depois do c realmente significa "o commit antes de c", que é b nesse caso. É por isso que c^..fé sinônimo de b..f. Tente fazer git log c^..fe você verá commits c a f, exatamente como se você fez #git log b..f
Andy Andy
41

Se você tiver revisões seletivas para mesclar, digamos A, C, F, J de A, B, C, D, E, F, G, H, I, J confirma, basta usar o comando abaixo:

git cherry-pick ACFJ

Shrikant Wandhare
fonte
1
agradável e simples
spinup
21
git rev-list --reverse b..f | xargs -n 1 git cherry-pick
Dustin
fonte
2
Funciona perfeitamente se não houver conflitos; caso contrário, "re-criar" poderá ser mais fácil, pois você não precisará descobrir onde parou e reaplicar o restante dos patches.
Ruslan Kabalin
6
Por favor adicionar comentários explicando o que isso está fazendo
Mr_and_Mrs_D
7
como ninguém explicou ... o git rev-list imprime todas as revisões do ramo b a f (invertido) para que, quando cada linha (o hash de confirmação) for passada em ordem, ele escolherá cada uma delas no git HEAD atual. iegit cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
coderatchet
8

Para escolher uma identificação de confirmação até a ponta do ramo, você pode usar:

git cherry-pick commit_id^..branch_name

ut9081
fonte
Isso já faz parte da resposta stackoverflow.com/a/31640427/96823
tig
1
Esta resposta é realmente diferente e foi útil para mim. Ele especifica o nome da ramificação, não o SHA de confirmação final.
Subtletree
7

Outra variante que vale a pena mencionar é que, se você deseja que os últimos ncommits de um branch, a ~sintaxe pode ser útil:

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

Nesse caso, o comando acima selecionaria os últimos 4 commit de um branch chamado some-branch(embora você também possa usar um hash de commit no lugar do nome do branch)

Nick F
fonte
1
Resposta MUITO útil, obrigado! :)
Jacek Dziurdzikowski
3

Na verdade, a maneira mais simples de fazer isso seria:

  1. registre a base de mesclagem entre os dois ramos: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. avançar rapidamente ou refazer a ramificação mais antiga na ramificação mais nova
  3. rebase a ramificação resultante em si mesma, iniciando na base de mesclagem da etapa 1 e remova manualmente as confirmações que não são desejadas:

    git rebase ${SAVED_MERGE_BASE} -i
    

    Como alternativa, se houver apenas algumas novas confirmações, pule a etapa 1 e simplesmente use

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

    na primeira etapa, usando o suficiente ^para passar da base de mesclagem.

Você verá algo parecido com isto no rebase interativo:

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

Em seguida, remova as linhas b (e quaisquer outras que você desejar)

ealfonso
fonte
1
git format-patch --full-index --binary --stdout range... | git am -3
Roger Wang
fonte
42
Por favor adicionar comentários explicando o que isso está fazendo
Mr_and_Mrs_D
0

Aqui está um script que permitirá que você escolha cereja em várias confirmações consecutivas, simplesmente dizendo ao script quais ramificações de origem e de destino para as cerejas e o número de confirmações:

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

Para escolher cereja do seu ramo para o mestre (usa o ramo atual como fonte):

./gcpl.sh -m

Para escolher os 5 commits mais recentes da ramificação 6.19.x para dominar:

./gcpl.sh -c 5 -s 6.19.x -t master
nickboldt
fonte
Por que esse script ser necessário quando você pode fazer isso com Git diretamente ...?
code_dredd
Menos digitação, e meu script seleciona automaticamente os commits para você, para que você não precise escolher cada um pelo seu SHA. Enfim, YMMV.
Nickboldt 11/05/19