trilha

Fundamentos de Programação/02 - Código que Presta/Semana 08 - Git Além do Básico4 min

Cherry-pick

Perguntas-guia
  • 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: b621bebbbce7ae, 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 -x nã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, --skip ou --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


Parte de Semana 08 - Git Além do Básico · 00 - MOC Fundamentos de Programação

Buscar

Busca por título, seção e texto das notas