git cherry-pick diz que “… 38c74d é uma fusão, mas nenhuma opção -m foi dada”

519

Fiz algumas alterações no meu ramo principal e quero trazê-las para montante. quando escolho as seguintes confirmações, no entanto, fico preso no fd9f578 onde o git diz:

$ git cherry-pick fd9f578
fatal: Commit fd9f57850f6b94b7906e5bbe51a0d75bf638c74d is a merge but no -m option was given.

O que o git está tentando me dizer e é a escolha certa para usar aqui? O ramo mestre inclui alterações nos arquivos que foram modificados no ramo upstream, portanto, tenho certeza de que haverá alguns conflitos de mesclagem, mas esses não são ruins demais para corrigir. Eu sei quais mudanças são necessárias onde.

Esses são os commits que eu quero trazer para cima.

e7d4cff added some comments...
23e6d2a moved static strings...
44cc65a incorporated test ...
40b83d5 whoops delete whitspace...
24f8a50 implemented global.c...
43651c3 cleaned up ...
068b2fe cleaned up version.c ...
fd9f578 Merge branch 'master' of ssh://extgit/git/sessions_common
4172caa cleaned up comments in sessions.c ...
wufoo
fonte

Respostas:

607

A maneira como uma seleção de cereja funciona é pegando o diff que um conjunto de alterações representa (a diferença entre a árvore de trabalho nesse ponto e a árvore de trabalho de seu pai) e aplicando-a à sua ramificação atual.

Portanto, se um commit tem dois ou mais pais, ele também representa dois ou mais diffs - qual deles deve ser aplicado?

Você está tentando escolher a cereja fd9f578, que foi uma fusão com dois pais. Então, você precisa dizer ao comando cherry-pick qual o contra o qual o diff deve ser calculado, usando a -mopção Por exemplo, git cherry-pick -m 1 fd9f578para usar o pai 1 como base.

Não posso afirmar com certeza a sua situação específica, mas geralmente é aconselhável usar em git mergevez de git cherry-pick. Quando você escolhe uma confirmação de mesclagem, ele recolhe todas as alterações feitas no pai que você não especificou -mnessa confirmação . Você perde toda a história deles e junta todas as suas diferenças. Sua chamada.

Borealid
fonte
3
@ wufoo Você provavelmente também deve aprender sobre git rebase- é como uma mesclagem, mas, em vez de integrar dois galhos, transplanta um para sentar em cima do outro.
Borealid
92
como você sabe o número dos pais?
Anentropic
66
@Anentropic 1 é o "primeiro pai", 2 é o "segundo pai" e assim por diante. A ordem é aquela em que estão listados no commit (conforme visualizado por git showe similares).
Borealid
2
@lkraav Você também pode ter feito um git reset --hard HEAD@{1}para recuperar seu commit ausente. git resetnão se restringe a retroceder na história. git checkout -b mybranch HEAD@{1}também funcionaria.
Borealid
4
AVISO: git mergepode ter consequências não intencionais. Esse comando adicionará todas as outras confirmações (mais antigas) existentes na ramificação pai. Geralmente, as pessoas escolhem escolher cereja, porque não querem que os outros comprometam. Verifique se está implementando apenas as alterações desejadas!
Kay V
52

-m significa o número pai.

No documento git:

Normalmente, você não pode escolher uma mesclagem de forma cerejeira porque não sabe qual lado da mesclagem deve ser considerado a linha principal. Esta opção especifica o número pai (começando em 1) da linha principal e permite que a seleção de cereja repita a alteração em relação ao pai especificado.

Por exemplo, se sua árvore de confirmação for como abaixo:

- A - D - E - F -   master
   \     /
    B - C           branch one

então git cherry-pick Eproduzirá o problema que você enfrentou.

git cherry-pick E -m 1significa usar D-E, enquanto git cherry-pick E -m 2significa usar B-C-E.

gavincook
fonte
32

A resposta do @ Borealid está correta, mas suponha que você não se preocupe em preservar o histórico exato de mesclagem de um ramo e apenas queira escolher uma versão linearizada dele. Aqui está uma maneira fácil e segura de fazer isso:

Estado inicial: você está no ramo Xe deseja escolher os commits Y..Z.

  1. git checkout -b tempZ Z
  2. git rebase Y
  3. git checkout -b newX X
  4. git cherry-pick Y..tempZ
  5. (opcional) git branch -D tempZ

O que isso faz é criar uma ramificação tempZbaseada em Z, mas com o histórico a partir de Ylinearizado, e depois selecioná-lo em uma cópia de Xchamada newX. (É mais seguro fazer isso em uma nova ramificação, em vez de sofrer mutação X.) É claro que pode haver conflitos na etapa 4, que você precisará resolver da maneira usual ( cherry-pickfunciona muito parecido rebasecom esse aspecto). Finalmente, ele exclui a tempZramificação temporária .

Se a etapa 2 exibir a mensagem "O tempZ do ramo atual está atualizado", ele Y..Zjá era linear, então ignore essa mensagem e continue com as etapas 3 em diante.

Em seguida, revise newXe veja se isso fez o que você queria.

(Nota: isso não é o mesmo que um simples git rebase Xquando está no ramo Z, porque não depende de forma alguma do relacionamento entre Xe Y; pode haver confirmações entre o ancestral comum e o Yque você não desejava.)

Daira Hopwood
fonte
1
git rebase YdizCurrent branch tempZ is up to date
Basilevs 10/10
Eu acho que isso significa que Y..Zjá era linear. Então você pode ignorar essa mensagem e prosseguir com os passos 3 e 4.
Daira Hopwood
1
Idéia interessante, eu tinha que desenhá-la em papel para apreciar plenamente o que estava acontecendo = D
Chris
2
Brilhante. O git cherry-pick para todo um intervalo reclamou que a opção -m estava faltando ou que foi fornecida. Sua solução foi dourada. (Uma sugestão: exclusão ramo tempZ depois)
Otheus
1
isso é incrível! Eu tenho lutado com a escolha da cereja e só isso fazia sentido. Eu tinha um pouco de dificuldade com as letras escolhidas (alguns ramos e commits são letras maiúsculas e alguns ramos são minúsculas)
pcarvalho
19

Simplificar. Escolha a cereja dos commits. Não escolha a fusão.

Aqui está uma reescrita da resposta aceita que idealmente esclarece as vantagens / riscos de possíveis abordagens:

Você está tentando escolher fd9f578, que foi uma fusão com dois pais.

Em vez de escolher uma mesclagem de cereja, a coisa mais simples é escolher a confirmação (ões) que você realmente deseja de cada ramificação na mesclagem.

Como você já mesclou, é provável que todos os commits desejados estejam na sua lista. Escolha-os diretamente e você não precisa mexer no commit da mesclagem.

explicação

A maneira como uma seleção de cereja funciona é pegando o diff que um conjunto de alterações representa (a diferença entre a árvore de trabalho nesse ponto e a árvore de trabalho de seu pai) e aplicando o conjunto de alterações à sua ramificação atual.

Se um commit tiver dois ou mais pais, como é o caso de uma mesclagem, esse commit também representará dois ou mais diffs. O erro ocorre devido à incerteza sobre a qual diff deve ser aplicada.

alternativas

Se você determinar que precisa incluir a mesclagem versus a seleção de cereja dos commits relacionados, você tem duas opções:

  1. (Mais complicado e obscuro; também descarta o histórico), você pode indicar qual pai ou mãe deve aplicar.

    • Use a -mopção para fazer isso. Por exemplo, git cherry-pick -m 1 fd9f578usará o primeiro pai listado na mesclagem como base.

    • Considere também que, quando você escolhe uma confirmação de mesclagem, ele recolhe todas as alterações feitas no pai que você não especificou -mnessa confirmação . Você perde toda a história deles e junta todas as suas diferenças. Sua chamada.

  2. (Mais simples e mais familiar; preserva a história) que você pode usar em git mergevez de git cherry-pick.

    • Como é habitual git merge, ele tentará aplicar todas as confirmações existentes na ramificação que você está mesclando e listá-las individualmente em seu log do git.
Kay V
fonte
2

A simplificação do método @Daira Hopwood é boa para escolher um único commit. Não precisa de ramificações temporárias.

No caso do autor:

  • Z é confirmado como desejado (fd9f578)
  • Y é confirmado antes dele
  • X ramo de trabalho atual

então faça:

git checkout Z   # move HEAD to wanted commit
git reset Y      # have Z as changes in working tree
git stash        # save Z in stash
git checkout X   # return to working branch
git stash pop    # apply Z to current branch
git commit -a    # do commit
efêmero
fonte
2
Obviamente, isso perde os metadados associados ao commit original. Eu acho que é uma questão de opinião se é mais simples. Às vezes, uso-o quando quero perder os metadados e manter apenas as alterações gerais do código. Observe que ele funciona mesmo que Y não seja um pai imediato de Z (nesse caso, as alterações serão esmagadas).
Daira Hopwood