Como mesclar um commit específico no Git


1035

Eu peguei uma ramificação de um repositório no GitHub e cometi algo específico para mim. Agora eu achei que o repositório original tinha um bom recurso que estava em HEAD.

Quero mesclá-lo apenas sem confirmações anteriores. O que devo fazer? Eu sei como mesclar todos os commits:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push

Se você está tentando fazer isso em relação ao github, este artigo o orientará. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Respostas:


1176

' git cherry-pick' deve ser sua resposta aqui.

Aplique a alteração introduzida por uma confirmação existente.

Não se esqueça de ler a resposta de bdonlan sobre as conseqüências da escolha da cereja neste post:
"Puxe todos os commits de um ramo, envie os commit especificados para outro" , onde:

A-----B------C
 \
  \
   D

torna-se:

A-----B------C
 \
  \
   D-----C'

O problema com esse commit é que o git considera os commit para incluir todo o histórico antes deles

Onde C 'tem um SHA-1ID diferente .
Da mesma forma, escolher um commit de um ramo para outro envolve basicamente gerar um patch e aplicá-lo, perdendo assim o histórico.

Essa alteração dos IDs de confirmação interrompe a funcionalidade de mesclagem do git entre outras coisas (embora, se usada com moderação, haja heurísticas que serão publicadas sobre isso).
Mais importante ainda, ele ignora dependências funcionais - se C realmente usou uma função definida em B, você nunca saberá .


1
@ openid000: "galhos mais finos": que é exatamente o que bdonlan sugeriu em sua resposta.
VonC

8
Nota: "git rebase" também altera o SHA-1. Consulte também "git rebase vs. git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) e" git workflow "( stackoverflow.com/questions/457927/… ) para casos em que" git rebase "é legítimo.
VonC 22/05/2009

1
Entre "ramificações refinadas", "seleção de cereja" e "rebase", você terá todas as possibilidades para gerenciar código em ramificações com o git.
VonC

@VonC "galhos finos" sim. Eu tinha duas ramificações e fiz atualizações para um módulo de visualização específico em uma ramificação. Este módulo foi independente da todos os outros códigos, de modo cereja-pick era maldita conveniente aplicar essas alterações para outro ramo bem
Cheeku

1
Mas observe que, quando você mescla, as confirmações C 'e C prevalecerão no histórico de confirmação.
Rahul Shah

739

Você pode usar o git cherry-pick para aplicar um único commit à sua ramificação atual.

Exemplo: git cherry-pick d42c389f


68
+1 na sua postagem anterior sobre a escolha da cereja ( stackoverflow.com/questions/880957/… ). Tomei a liberdade de copiar um extrato dele em minha própria resposta acima.
VonC

4
Provavelmente git cherry-pick d42cou git cherry-pick d42c3 vai funcionar. Git é inteligente. ;)
guneysus

2
fatal: revisão ruim
Ievgen Naida

2
Acabei de executar este comando e parece ter funcionado, mas quando faço o status git, ele diz "nada a confirmar, trabalhando com a árvore limpa". Quando atualizo a página Confirma na minha página da Web bitbucket, ela não aparece. Mas aparece quando executo o git log. E eu vejo o código modificado. Alguém pode explicar se eu tenho que executar outras etapas?
Ray

1
Infelizmente, isso não responde à pergunta: na verdade, não cria uma mesclagem. Não há ancestralidade apontando d42c389f. Talvez o OP não tenha se preocupado em criar uma mesclagem em si, mas a diferença importa algumas vezes.
LarsH

29

Vamos tentar dar um exemplo e entender:

Eu tenho um ramo, digamos mestre , apontando para X <commit-id>, e eu tenho um novo ramo apontando para Y <sha1>.

Onde Y <commit-id> = <master> ramificação confirma - poucas confirmações

Agora digamos que para o ramo Y, preciso fechar o espaço entre as confirmações entre o ramo mestre e o novo ramo. Abaixo está o procedimento que podemos seguir:

Passo 1:

git checkout -b local origin/new

onde local é o nome da filial. Qualquer nome pode ser dado.

Passo 2:

  git merge origin/master --no-ff --stat -v --log=300

Mesclar as confirmações da ramificação mestre para a nova ramificação e também criar uma confirmação de mesclagem da mensagem de log com descrições de uma linha de no máximo <n> confirmações reais que estão sendo mescladas.

Para mais informações e parâmetros sobre a fusão do Git, consulte:

git merge --help

Além disso, se você precisar mesclar um commit específico, poderá usar:

git cherry-pick <commit-id>

você mudou a definição de Yna sua frase 3d? "Eu tenho um novo ramo apontando para Y" vs "Agora diga para o ramo Y", parece que Y costumava ser um commit e depois se tornou um branch
Purefan

como posso fazer isso a partir de um certo ponto de um ramo que eu quero fundir comprometer
Kulbhushan Singh

3

No meu caso de uso, tínhamos uma necessidade semelhante de CD de CI. Utilizamos o fluxo git com ramos de desenvolvimento e master. Os desenvolvedores são livres para mesclar as alterações diretamente para desenvolver ou por meio de uma solicitação pull de uma ramificação de recursos. No entanto, para dominar, mesclamos apenas o commit estável do ramo de desenvolvimento de maneira automatizada via Jenkins.

Nesse caso, escolher a cereja não é uma boa opção. Entretanto, criamos uma ramificação local a partir do commit-id e, em seguida, mesclamos essa ramificação local para dominar e executar a verificação limpa do mvn (usamos o maven). Se for bem-sucedido, libere o artefato da versão de produção para o nexus usando o plug-in maven release com a opção localCheckout = true e pushChanges = false. Finalmente, quando tudo der certo, empurre as alterações e a tag para a origem.

Um trecho de código de exemplo:

Supondo que você esteja no mestre se feito manualmente. No entanto, no jenkins, quando você faz o checkout do repo, você estará na ramificação padrão (mestre, se configurado).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Isso lhe dará um controle total com uma fusão sem medo ou um inferno de conflito.

Sinta-se livre para aconselhar caso haja alguma opção melhor.


3

As respostas principais descrevem como aplicar as alterações de uma confirmação específica na ramificação atual. Se é isso que você quer dizer com "como mesclar", basta usar a opção de seleção de cereja, conforme sugerido.

Mas se você realmente deseja uma mesclagem , ou seja, deseja um novo commit com dois pais - o commit existente na ramificação atual e o commit do qual você deseja aplicar as alterações -, então uma seleção de cereja não fará isso.

Ter um histórico de mesclagem verdadeiro pode ser desejável, por exemplo, se seu processo de construção tirar proveito da ancestralidade git para definir automaticamente as strings de versão com base na tag mais recente (using git describe).

Em vez de escolher a cereja, você pode fazer um real git merge --no-commite, em seguida, ajustar manualmente o índice para remover as alterações que você não deseja.

Suponha que você esteja no branch Ae deseje mesclar o commit na ponta do branch B:

git checkout A
git merge --no-commit B

Agora você está configurado para criar um commit com dois pais, a dica atual confirma Ae B. No entanto, você pode ter mais alterações aplicadas do que deseja, incluindo alterações de confirmações anteriores na ramificação B. Você precisa desfazer essas alterações indesejadas e confirmar.

(Pode haver uma maneira fácil de definir o estado do diretório de trabalho e o índice antes da mesclagem, para que você tenha uma lista limpa sobre a qual escolher em primeira mão o commit que você queria em primeiro lugar. Eu não sei como alcançar esse slate limpo. git checkout HEADE git reset HEADambos removerão o estado de mesclagem, derrotando o objetivo deste método.)

Então, desfaça manualmente as alterações indesejadas. Por exemplo, você poderia

git revert --no-commit 012ea56

para cada confirmação indesejada 012ea56.

Quando terminar de ajustar as coisas, crie seu commit:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Agora você tem apenas a alteração que deseja, e a árvore de ascendência mostra que você se uniu tecnicamente de B.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.