Como escolher uma variedade de confirmações e mesclar em outra ramificação?

640

Eu tenho o seguinte layout de repositório:

  • filial mestre (produção)
  • integração
  • trabalhando

O que eu quero alcançar é escolher uma variedade de confirmações do ramo de trabalho e mesclá-lo no ramo de integração. Eu sou muito novo no git e não consigo descobrir como fazer exatamente isso (a escolha inicial do commit varia em uma operação e não na mesclagem) sem atrapalhar o repositório. Alguma indicação ou opinião sobre isso? Obrigado!

crazybyte
fonte
1
draconianoverlord.com/2013/09/07/no-cherry-picking.html (não é o meu blog)
Christophe Roussy

Respostas:

808

Quando se trata de uma variedade de confirmações, a escolha da cereja é não foi prático.

Conforme mencionado abaixo por Keith Kim , o Git 1.7.2+ introduziu a capacidade de escolher uma variedade de confirmações (mas você ainda precisa estar ciente das conseqüências da escolha da cereja para mesclagem futura )

O git cherry-pick "aprendeu a escolher uma variedade de confirmações
(por exemplo," cherry-pick A..B"e" cherry-pick --stdin"), o mesmo fez" git revert"; no entanto , elas não suportam o melhor controle de seqüenciamento" rebase [-i].

damian comenta e nos adverte:

No cherry-pick A..Bformulário " ", Adeve ter mais deB .
Se eles estiverem na ordem errada, o comando falhará silenciosamente .

Se você quer escolher a gama Batravés de D(inclusive) que seria B^..D.
Consulte " Git create branch a partir do intervalo de confirmações anteriores? " Como uma ilustração.

Como Jubobs menciona nos comentários :

Isso pressupõe que Bnão é um commit raiz; você receberá um unknown revisionerro " " caso contrário.

Nota: a partir do Git 2.9.x / 2.10 (terceiro trimestre de 2016), você pode escolher um intervalo de confirmação diretamente em um ramo órfão (cabeça vazia): consulte " Como tornar um ramo existente um órfão no git ".


Resposta original (janeiro de 2010)

A rebase --ontoseria melhor, onde você reproduz o intervalo determinado de confirmação no topo do seu ramo de integração, como Charles Bailey descreveu aqui .
(também, procure "Aqui está como você transplantaria uma ramificação de tópico com base em uma ramificação para outra" na página do manual git rebase , para ver um exemplo prático de git rebase --onto)

Se sua ramificação atual for integração:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

Isso repetirá tudo entre:

  • após o pai first_SHA-1_of_working_branch_range(daí o ~1): o primeiro commit que você deseja reproduzir
  • até " integration" (que aponta para o último commit que você deseja reproduzir, a partir da workingramificação)

para " tmp" (que aponta para onde integrationestava apontando antes)

Se houver algum conflito quando uma dessas confirmações for repetida:

  • resolva-o e execute " git rebase --continue".
  • ou pule este patch e execute " git rebase --skip"
  • ou cancele tudo com um " git rebase --abort" (e recoloque o integrationramo no tmpramo)

Depois disso rebase --onto, integrationvoltaremos ao último commit da ramificação de integração (isto é " tmp" branch + todas as confirmações reproduzidas)

Com a seleção de cereja ou rebase --onto, não se esqueça, isso tem consequências nas mesclagens subsequentes, conforme descrito aqui .


Uma cherry-picksolução " " pura é discutida aqui e envolveria algo como:

Se você deseja usar uma abordagem de patch, "git format-patch | git am" e "git cherry" são suas opções.
Atualmente, git cherry-pickaceita apenas um único cometer, mas se você quer escolher o intervalo Batravés de Dque seria B^..Dem git linguagem, de modo

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

Mas de qualquer maneira, quando você precisar "repetir" uma série de confirmações, a palavra "repetir" deve empurrá-lo para usar o rebaserecurso " " do Git.

VonC
fonte
1
Se você tem confirmações que têm pais que exigem a -mopção, como você lida com essas confirmações? Ou existe uma maneira de filtrar esses commits?
agosto
O @aug -mdeve lidar com eles para você, selecionando a linha principal referenciada pelo -mparâmetro que você escolheu para essa escolha de cereja.
VonC 14/07
O problema é que, se você seleciona uma variedade de confirmações, ele seleciona as confirmações corretamente, mas quando atinge uma confirmação normal, ele falha e diz que a confirmação não é uma mesclagem. Eu acho que minha pergunta está melhor formulada: como fazer com que ela passe a -mopção somente quando atinge um commit pai quando o intervalo de commit é escolhido? No momento, se eu passar -mcomo git cherry-pick a87afaaf..asfa789 -m 1se aplica a todos os commits dentro do intervalo.
agosto
@ August Strange, eu não reproduzi o problema. Qual é a sua versão do git e qual é a mensagem de erro exata que você vê?
VonC 14/07/16
Ah, estou executando o git versão 2.6.4 (Apple Git-63). O erro que eu vejo seria algo como error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.eu realmente percebi que você poderia apenas git cherry-pick --continuee seria muito bem (mas não incluiria o pai cometer)
agosto
136

A partir do git v1.7.2, o cherry pick pode aceitar uma variedade de confirmações:

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].

Keith Kim
fonte
103
Note-se que cherry-pick A..Bnão vai se comprometer A (você precisaria A~1..Bpara isso), e se existem quaisquer conflitos git não continuará automaticamente como rebase faz (pelo menos a partir de 1.7.3.1)
Gabe Moothart
3
Também é bom observar que git cherry-pick A..B Cnão funciona como você esperaria, ingenuamente. Ele não seleciona tudo no intervalo A..Be confirma C! Para fazer isso, você precisa se dividir em duas linhas, primeiro git cherry-pick A..Be depois git cherry-pick C. Portanto, sempre que houver um intervalo, você precisará executá-lo separadamente.
MicroVirus
42

Suponha que você tenha 2 ramos,

"branchA": inclui confirmações que você deseja copiar (de "commitA" para "commitB"

"branchB": o ramo que você deseja que as confirmações sejam transferidas de "branchA"

1)

 git checkout <branchA>

2) obtenha os IDs de "commitA" e "commitB"

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) Caso você tenha um conflito, resolva-o e digite

git cherry-pick --continue

para continuar o processo de escolha da cereja.

KostasA
fonte
Trabalhou como charme! Muito obrigado!
snukone
28

Tem certeza de que não deseja mesclar os galhos? Se o ramo de trabalho tiver algumas confirmações recentes que você não deseja, basta criar um novo ramo com um HEAD no ponto desejado.

Agora, se você realmente deseja escolher uma variedade de confirmações, por qualquer motivo, uma maneira elegante de fazer isso é apenas puxar um conjunto de patches e aplicá-lo ao seu novo ramo de integração:

git format-patch A..B
git checkout integration
git am *.patch

Isso é essencialmente o que o git-rebase está fazendo de qualquer maneira, mas sem a necessidade de jogar. Você pode adicionar --3waypara git-amse você precisa mesclar. Verifique se não há outros arquivos * .patch no diretório em que você faz isso, se você seguir as instruções literalmente ...

djs
fonte
1
Observe que, assim como em outros intervalos de revisão, ele precisa ser A^incluído A.
Gino Mempin 12/11/19
9

Enrolei o código do VonC em um script bash short, git-multi-cherry-pick, para facilitar a execução:

#!/bin/bash

if [ -z $1 ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage:  $0 start^..end";
    echo "";
    exit 1;
fi

git rev-list --reverse --topo-order $1 | while read rev 
do 
  git cherry-pick $rev || break 
done 

Atualmente, estou usando isso ao reconstruir o histórico de um projeto que tinha código e personalizações de terceiros misturados no mesmo tronco svn. Agora, estou dividindo o código principal de terceiros, os módulos de terceiros e as personalizações em suas próprias ramificações git para entender melhor as personalizações daqui para frente. git-cherry-pické útil nessa situação, pois tenho duas árvores no mesmo repositório, mas sem um ancestral compartilhado.

Adam Franco
fonte
3

Todas as opções acima solicitarão que você resolva conflitos de mesclagem. Se você estiver mesclando alterações confirmadas para uma equipe, é difícil resolver os conflitos de mesclagem dos desenvolvedores e prosseguir. No entanto, "git merge" fará a mesclagem de uma só vez, mas você não pode passar por uma série de revisões como argumento. nós precisamos usar os comandos "git diff" e "git apply" para fazer a mesclagem de rotações. Eu observei que "git apply" falhará se o arquivo de correção tiver diff para muitos arquivos, portanto, precisamos criar um patch por arquivo e depois aplicar. Observe que o script não poderá excluir os arquivos excluídos na ramificação de origem. Este é um caso raro, você pode excluir manualmente esses arquivos do ramo de destino. O status de saída de "git apply" não é zero se não for possível aplicar o patch,

Abaixo está o script.

enter code here



  #!/bin/bash

    # This script will merge the diff between two git revisions to checked out branch
    # Make sure to cd to git source area and checkout the target branch
    # Make sure that checked out branch is clean run "git reset --hard HEAD"


    START=$1
    END=$2

    echo Start version: $START
    echo End version: $END

    mkdir -p ~/temp
    echo > /tmp/status
    #get files
    git --no-pager  diff  --name-only ${START}..${END} > ~/temp/files
    echo > ~/temp/error.log
    # merge every file
    for file in `cat  ~/temp/files`
    do
      git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
      if [ $? -ne 0 ]
      then
#      Diff usually fail if the file got deleted 
        echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
        echo Skipping the merge: git diff command failed for $file
        echo "STATUS: FAILED $file" >>  /tmp/status
        echo "STATUS: FAILED $file"
    # skip the merge for this file and continue the merge for others
        rm -f ~/temp/git-diff
        continue
      fi

      git apply  --ignore-space-change --ignore-whitespace  --3way --allow-binary-replacement ~/temp/git-diff

      if [ $? -ne 0 ]
       then
#  apply failed, but it will fall back to 3-way merge, you can ignore this failure
         echo "git apply command filed for $file"
       fi
       echo
       STATUS=`git status -s $file`


       if [ ! "$STATUS" ]
       then
#   status is null if the merged diffs are already present in the target file
         echo "STATUS:NOT_MERGED $file"
         echo "STATUS: NOT_MERGED $file$"  >>  /tmp/status
       else
#     3 way merge is successful
         echo STATUS: $STATUS
         echo "STATUS: $STATUS"  >>  /tmp/status
       fi
    done

    echo GIT merge failed for below listed files

    cat ~/temp/error.log

    echo "Git merge status per file is available in /tmp/status"
Yoganand Bijapur
fonte
2

Eu testei isso alguns dias atrás, depois de ler a explicação muito clara de Vonc.

Meus passos

Começar

  • Ramo dev : ABCDEFGHIJ
  • Ramo target : ABCD
  • Eu não quero EnemH

Etapas para copiar recursos sem as etapas E e H na ramificação dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff Donde eu coloco na dropfrente EeH no editor rebase
  • resolver conflitos, continuar e commit

Etapas para copiar o ramo dev_feature_wo_E_Hno destino.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • resolver conflitos, continuar e commit

Algumas observações

  • Eu fiz isso por causa de muito cherry-picknos dias anteriores
  • git cherry-pick é poderoso e simples, mas

    • cria duplicatas confirma
    • e quando eu quiser merge, tenho que resolver conflitos de confirmações iniciais e duplicatas, portanto, para um ou dois cherry-pick, não há problema em "escolher", mas, para mais, é muito detalhado e o ramo se tornará muito complexo
  • Na minha opinião, os passos que dei são mais claros do que git rebase --onto
phili_b
fonte
1
Bom post. Votado. Isso me lembra stackoverflow.com/a/38418941/6309 . Recapitulei as desvantagens da colheita de cerejas em 2012: stackoverflow.com/a/13524494/6309 .
VonC
1

Outra opção pode ser mesclar a estratégia nossa com o commit antes do intervalo e, em seguida, um 'normal' mesclar com o último commit desse intervalo (ou ramificar quando ele for o último). Portanto, suponha que apenas 2345 e 3456 confirmam o mestre a serem mesclados no ramo de recurso:

mestre:
1234
2345
3456
4567

na ramificação do recurso:

git mesclar -s nosso 4567
git merge 2345
Koos Vriezen
fonte