Fundamentos de Programação/02 - Código que Presta/Semana 08 - Git Além do Básico4 min
Cherry-pick
- Quando cherry-pick é a ferramenta certa e quando é sintoma de branch mal organizada?
- Por que o commit resultante tem hash diferente do original?
- O que acontece se você depois mergear a branch de origem? (commit duplicado)
- Como pegar uma faixa de commits de uma vez?
Conceito
Cherry-pick aplica a mudança de um commit em outra branch. Não o commit — o diff.
Por que o hash resultante é diferente: o hash de um commit cobre o conteúdo da árvore, o pai, o autor, a data e a mensagem. O pai é outro, então o hash é outro. Você tem dois commits distintos com o mesmo diff — e o git só os relaciona se você pedir.
Quando é a ferramenta certa:
- hotfix que precisa ir para duas linhas — a correção nasce na branch de release e
precisa também da
main(ou vice-versa) - recuperar um commit de uma branch abandonada, sem trazer o resto
- antecipar um commit específico que já está pronto, sem esperar a branch toda
Quando é sintoma de branching errado: se você faz cherry-pick de rotina, o problema não é o cherry-pick — são branches de vida longa divergindo. Cada pick é um remendo manual de uma integração que não está acontecendo.
O problema do commit duplicado, e o sintoma real é pior que "duplicado":
Na prática
Cenário verificado: feat tem dois commits (add linha2, add linha3). Fiz cherry-pick
só do primeiro para master, e depois mergeei feat inteira.
$ git cherry-pick -x <add linha2>
$ cat f.txt
linha1 linha2
$ git merge feat
CONFLICT (content): Merge conflict in f.txt
Automatic merge failed; fix conflicts and then commit the result.
$ cat f.txt
linha1
linha2
<<<<<<< HEAD
=======
linha3
>>>>>>> feat
Repare no conflito: o lado HEAD está vazio. É um conflito que parece não fazer
sentido — nada seu foi alterado ali. A causa é que a base do merge não sabe que linha2 já
foi aplicada por outro caminho, então o git vê duas histórias divergentes num trecho onde o
conteúdo já coincide.
Esse conflito de aparência absurda é a marca do cherry-pick seguido de merge. Não é raro, e custa tempo justamente porque não parece ter causa.
git cherry detecta o pick por patch-id:
$ git cherry master hotfix
- b621beb9c1a8ef1947c1ecc20a9458420493c8ab
O prefixo - significa "já existe um equivalente upstream" (o cherry-pick foi
detectado). + significaria "não está lá". É como você audita o que falta migrar entre
branches.
E o -x grava a origem na mensagem, verificado:
fix: corrige bug
(cherry picked from commit b621beb9c1a8ef1947c1ecc20a9458420493c8ab)
Sem -x, a rastreabilidade se perde: você tem dois commits com o mesmo diff e nenhuma
ligação entre eles. Use sempre -x.
Respostas às perguntas-guia
1. Quando cherry-pick é a ferramenta certa e quando é sintoma de branching mal organizado?
Certo: hotfix para duas linhas de release, resgate de commit de branch abandonada, antecipação pontual. Sintoma: quando é rotina — aí são branches divergindo demais.
2. Por que o commit resultante tem hash diferente do original?
Porque o hash cobre o pai (além da árvore e dos metadados), e o pai é outro. Verificado:
b621beb → bbce7ae, mesmo diff.
3. O que acontece se você depois mergear a branch de origem?
O git pode resolver sozinho (conteúdo idêntico) ou produzir um conflito com um lado
vazio, como no cenário acima. O patch-id ajuda o git cherry a detectar, mas não
impede o conflito no merge.
4. Como pegar uma faixa de commits de uma vez?
git cherry-pick A..B # de A (exclusivo) até B
git cherry-pick A^..B # INCLUINDO A
git cherry-pick A B D # commits específicos, na ordem dada
O A..B exclui A — é a convenção de faixa do git, e a fonte de um erro comum. Use
A^..B quando quiser incluir o primeiro.
Armadilhas
- Sem
-xnão há rastro. Dois commits com o mesmo diff e nenhuma ligação. - Ordem importa numa faixa: picks fora de ordem conflitam entre si.
-n(--no-commit) aplica sem comitar — útil para juntar vários picks num commit só, e fácil de esquecer que ficou pendente.- Cherry-pick de merge commit exige
-m 1(qual pai seguir), e quase sempre não é o que você quer. - Conflito no meio de uma faixa te deixa no meio da operação:
--continue,--skipou--abort, exatamente como o rebase.
Comandos essenciais
git cherry-pick -x <sha> # aplica, registrando a origem na mensagem
git cherry-pick -x A^..B # faixa, incluindo A
git cherry-pick -n <sha> # aplica sem comitar
git cherry-pick --continue # segue após resolver conflito
git cherry-pick --abort # desiste
git cherry master feat # o que de feat NÃO está em master ('+' falta, '-' já existe)
git log --cherry-mark --left-right main...feat # compara ignorando equivalentes
Relacionado
- 1. Rebase vs Merge — as duas formas de integrar, e por que pick é a terceira
- 6. Resolução de Conflitos — o conflito de lado vazio que o pick produz
- 4. Reflog — para achar o sha de um commit em branch já apagada
Parte de Semana 08 - Git Além do Básico · 00 - MOC Fundamentos de Programação