Como mesclar ou selecionar seletivamente alterações de outro ramo no Git?

1450

Estou usando o git em um novo projeto que possui dois ramos de desenvolvimento paralelos - mas atualmente experimentais:

  • master: importação da base de código existente mais alguns mods dos quais geralmente tenho certeza
  • exp1: ramo experimental nº 1
  • exp2: ramo experimental nº 2

exp1e exp2representam duas abordagens arquitetônicas muito diferentes. Até me aprofundar, não tenho como saber qual deles (se é que existe) irá funcionar. À medida que progrido em uma ramificação, às vezes tenho edições que seriam úteis na outra ramificação e gostaria de mesclar apenas essas.

Qual é a melhor maneira de mesclar mudanças seletivas de um ramo de desenvolvimento para outro, deixando para trás todo o resto?

Abordagens que considerei:

  1. git merge --no-commit seguido pela desagregação manual de um grande número de edições que eu não quero tornar comum entre as ramificações.

  2. Cópia manual de arquivos comuns em um diretório temporário, seguida por git checkoutpara mover para a outra ramificação e, em seguida, mais cópia manual do diretório temporário para a árvore de trabalho.

  3. Uma variação acima. Abandone as expramificações por enquanto e use dois repositórios locais adicionais para experimentação. Isso torna a cópia manual de arquivos muito mais simples.

Todas essas três abordagens parecem tediosas e propensas a erros. Espero que exista uma abordagem melhor; algo semelhante a um parâmetro do caminho do filtro que tornaria git-mergemais seletivo.

David Joyner
fonte
5
Se as alterações em suas ramificações experimentais são bem organizadas em confirmações separadas, é melhor pensar em termos de mesclar confirmações seletivas em vez de arquivos seletivos. A maioria das respostas abaixo assume que este é o caso.
Akaihola
2
Uma combinação de git merge -s ours --no-commitseguidos por alguns não git read-treeseria uma boa solução para isso? Veja stackoverflow.com/questions/1214906/…
VonC
34
Uma pergunta mais recente tem um de uma linha, bem escrito resposta: stackoverflow.com/questions/10784523/...
Brahn
Faça check-
Ashutosh Chamoli

Respostas:

475

Você usa o comando cherry-pick para obter confirmações individuais de uma ramificação.

Se as alterações desejadas não estiverem em confirmações individuais, use o método mostrado aqui para dividir a confirmação em confirmações individuais . Grosso modo, você costuma usar git rebase -io commit original para editar, git reset HEAD^reverter seletivamente as alterações e depois git commitconfirmar esse bit como um novo commit no histórico.

Há outro método interessante aqui na Red Hat Magazine, onde eles usam git add --patchou possivelmentegit add --interactive que permite adicionar apenas partes de um pedaço, se você deseja dividir diferentes alterações em um arquivo individual (pesquise nessa página por "divisão").

Depois de dividir as alterações, agora você pode escolher apenas as que deseja.

1800 INFORMAÇÃO
fonte
14
Pelo meu entendimento, isso é desnecessariamente mais complicado do que a resposta mais votada.
Alexander Ave
54
Esta é tecnicamente a resposta correta, a resposta correta parece "complicada". --- A resposta mais votada é apenas uma resposta rápida e suja "faz o truque", que para a maioria das pessoas é tudo o que elas são sobre: ​​(:
Jacob
3
@akaihola: HEAD ^ está correto. Veja man git-rev-parse: Um sufixo ^ para um parâmetro de revisão significa o primeiro pai desse objeto de confirmação. A notação prefixo ^ é usada para excluir confirmações alcançáveis ​​de uma confirmação.
Tyler Rick
13
Eu só queria compartilhar uma outra abordagem que parece o mais limpo e menos complicado de todos eles: jasonrudolph.com/blog/2009/02/25/... total simplicidade e incrível
superuseroi
14
Confuso com o debate sobre qual abordagem é 'correta'? Considere a diferença entre arquivos e confirmações (consulte Comentários na parte inferior) . O OP deseja mesclar ARQUIVOS e não menciona COMMITS. A resposta mais votada é específica dos arquivos; a resposta aceita usa cherry-pick, que é específica para confirmações. A seleção de cereja pode ser a chave para mesclar confirmações seletivamente, mas pode ser muito doloroso para mover arquivos de uma ramificação para outra. Embora commits sejam o coração da força do git, não esqueça que os arquivos ainda têm um papel!
Kay V
970

Eu tive exatamente o mesmo problema, como mencionado por você acima. Mas eu achei isso mais claro ao explicar a resposta.

Resumo:

  • Faça o checkout do (s) caminho (s) da ramificação que você deseja mesclar,

    $ git checkout source_branch -- <paths>...
    

    Dica: Ele também funciona sem --gostar do visto no post vinculado.

  • ou mesclar seletivamente pedaços

    $ git checkout -p source_branch -- <paths>...
    

    Alternativamente, o uso redefinir e, em seguida, adicionar a opção -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Finalmente confirmar

    $ git commit -m "'Merge' these changes"
    
Chris Steinbach
fonte
9
O artigo vinculado de Bart J é a melhor abordagem. Claro, simples, um comando. É o que eu estou prestes a usar. :)
Pistos
256
Esta não é uma fusão real. Você está escolhendo as alterações por arquivo, em vez de por confirmação, e perderá todas as informações de confirmação existentes (autor, mensagem). Concedido, isso é bom se você deseja mesclar todas as alterações em alguns arquivos e não há problema em refazer todas as confirmações. Mas se os arquivos contiverem alterações para mesclar e outros para descartar, um dos métodos fornecidos em outras respostas o servirá melhor.
Akaihola
10
@mykhal e outros: isso prepara os arquivos no índice automaticamente, portanto, se você fez check-out foo.c, git reset HEAD foo.cdesestabiliza esse arquivo e pode diferenciá-lo. Descobri isso depois de tentar e voltar aqui para procurar uma resposta para isso #
michiakig
12
para ver as alterações que você também pode usar:git diff --cached
OderWat
9
De acordo com esta resposta git checkout -p <revision> -- <path> será o mesmo que emitir os três primeiros comandos que você descreveu :)
7hi4g0
338

Para mesclar seletivamente arquivos de um ramo em outro ramo, execute

git merge --no-ff --no-commit branchX

Onde branchX é a ramificação da qual você deseja mesclar na ramificação atual.

o --no-commit opção preparará os arquivos que foram mesclados pelo Git sem realmente enviá-los. Isso lhe dará a oportunidade de modificar os arquivos mesclados da maneira que desejar e depois enviá-los você mesmo.

Dependendo de como você deseja mesclar arquivos, existem quatro casos:

1) Você deseja uma verdadeira mesclagem.

Nesse caso, você aceita os arquivos mesclados da maneira que o Git os mescla automaticamente e os confirma.

2) Existem alguns arquivos que você não deseja mesclar.

Por exemplo, você deseja manter a versão na ramificação atual e ignorar a versão na ramificação da qual você está mesclando.

Para selecionar a versão na ramificação atual, execute:

git checkout HEAD file1

Isso recuperará a versão da file1ramificação atual e substituirá ofile1 automerged pelo Git.

3) Se você deseja a versão no branchX (e não uma mesclagem verdadeira).

Corre:

git checkout branchX file1

Isso recuperará a versão de file1in branchXe substituiráfile1 a automática pelo Git.

4) O último caso é se você deseja selecionar apenas mesclagens específicas em file1 .

Nesse caso, você pode editar o modificado file1diretamente, atualizá-lo para o que quiser que a versão dofile1 se torne e depois confirmar.

Se o Git não puder mesclar um arquivo automaticamente, ele o reportará como "não imerso " e produzirá uma cópia na qual você precisará resolver os conflitos manualmente.



Para explicar mais com um exemplo, digamos que você deseja mesclar branchXno ramo atual:

git merge --no-ff --no-commit branchX

Você então executa o git status comando para visualizar o status dos arquivos modificados.

Por exemplo:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Onde file1, file2efile3 são os arquivos git têm sucesso fundiu-auto.

O que isto significa é que as mudanças no masterebranchX para todos esses três arquivos foram combinadas sem conflitos.

Você pode inspecionar como a mesclagem foi realizada executando o git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

Se você achar que uma mesclagem é indesejável, poderá

  1. edite o arquivo diretamente
  2. Salve 
  3. git commit

Se você não deseja mesclar file1e deseja manter a versão na ramificação atual

Corre

git checkout HEAD file1

Se você não deseja mesclar file2e deseja apenas a versão embranchX

Corre

git checkout branchX file2

Se você deseja file3mesclar automaticamente, não faça nada.

O Git já o fundiu neste momento.


file4acima, há uma falha na mesclagem do Git. Isso significa que há alterações nos dois ramos que ocorrem na mesma linha. É aqui que você precisará resolver os conflitos manualmente. Você pode descartar a mesclagem feita editando o arquivo diretamente ou executando o comando checkout da versão na ramificação que deseja file4se tornar.


Finalmente, não esqueça git commit.

Alvinabad
fonte
10
Cuidado, porém: Se o git merge --no-commit branchXé apenas um fast-forward, o ponteiro será atualizado, eo --no-commit é, assim, ignorado
TPI
16
@cfi Que tal adicionar --no-ffpara impedir esse comportamento?
Eduardo Costa
5
Definitivamente, sugiro atualizar esta resposta com a opção "--no-ff" de Eduardo. Eu li a coisa toda (que, de outra forma, era ótima) apenas para que minha mesclagem fosse acelerada.
Funktr0n
7
Esta solução oferece os melhores resultados e flexibilidade.
Thiago Macedo
20
Diferentemente da resposta com mais votos, essa solução preservou meu histórico de mesclagem, o que é importante para mim quando eu tecer confirmações parciais entre filiais. Não tentei todas as outras soluções sugeridas; talvez algumas delas também o façam.
ws_e_c421
107

Eu não gosto das abordagens acima. O uso de pick-cherry é ótimo para escolher uma única alteração, mas é doloroso se você deseja incluir todas as alterações, exceto algumas ruins. Aqui está a minha abordagem.

Não há --interactiveargumento que você possa passar para o git merge.

Aqui está a alternativa:

Você tem algumas alterações no 'recurso' da ramificação e deseja trazer algumas, mas não todas, para o 'mestre' de uma maneira não desleixada (ou seja, você não quer escolher e comprometer cada uma delas)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

Então, apenas envolva isso em um script de shell, mude o master para $ to e o recurso para $ from e você estará pronto:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp
nosatalian
fonte
Fixei-se a formatação - este é um método muito bom, se você quiser fazer uma seleção de commits
1800 INFORMATION
Estou usando essa técnica agora e parece ter funcionado muito bem.
Dllanfm
4
Você pode querer mudar git rebase -i $topara git rebase -i $to || $SHELL, para que o usuário possa ligar git --skipetc, conforme necessário, se a rebase falhar. Também vale a pena encadear as linhas, em &&vez de novas linhas.
sircolinton
2
Infelizmente, parece que o link na resposta está morto.
Thomasw
O link não está apenas morto, mas também possui um aviso de má reputação do WOT. Portanto, eu o removi.
Jean-François Corbett
93

Há outro caminho a seguir:

git checkout -p

É uma mistura entre git checkoute git add -ppode ser exatamente o que você está procurando:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.
Chronial
fonte
10
Esse é, de longe, o método mais fácil e simples, desde que você tenha apenas um número gerenciável de alterações para incorporar. Espero que mais pessoas percebam essa resposta e a votem positivamente. Exemplo: git checkout --patch exp1 file_to_merge
Tyler Rick
1
Resposta semelhante postada nesta pergunta: stackoverflow.com/a/11593308/47185
Tyler Rick
Ah, eu não sabia que o checkout tinha um patch! Eu fiz checkout / reset / add -p.
Daniel C. Sobral,
2
Verdadeiramente o método mais simples. git checkout -p featurebranch nome do arquivo. E o melhor é que, quando o comando é executado, ele fornece ay / n / e /? / ... etc. opção para decidir como mesclar o arquivo. Eu tentei com e e eu poderia até editar o patch antes de aplicar ... Como é legal. Um verdadeiro liner para mesclar arquivos seletivos de outras ramificações.
infoclogou 02/03
55

Embora algumas dessas respostas sejam muito boas, sinto que nenhuma delas respondeu à restrição original do OP: selecionar arquivos específicos de ramificações específicas. Essa solução faz isso, mas pode ser tediosa se houver muitos arquivos.

Vamos dizer que você tem as master, exp1e exp2ramos. Você deseja mesclar um arquivo de cada uma das ramificações experimentais no mestre. Eu faria algo assim:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# save these files as a stash
git stash
# merge stash with master
git merge stash

Isso fornecerá diferenças de arquivo para cada um dos arquivos que você deseja. Nada mais. Nada menos. É útil que você tenha alterações radicalmente diferentes de arquivo entre versões - no meu caso, alterando um aplicativo do Rails 2 para o Rails 3.

EDIT : isso mescla arquivos, mas faz uma mesclagem inteligente. Não consegui descobrir como usar esse método para obter informações de diferenças no arquivo (talvez ainda seja para diferenças extremas. Coisas pequenas e irritantes, como espaço em branco, são mescladas novamente, a menos que você use a -s recursive -X ignore-all-spaceopção)

Eric Hu
fonte
4
Observe também: você pode fazer vários arquivos de um determinado ramo, todos em linha, por exemplo,git checkout exp1 path/to/file_a path/to/file_x
EMiller
2
Isso é lindo. Eu fiz git checkout feature <path>/*para obter grupos de arquivos.
Isherwood
Isso funciona bem, mas foram adicionados dois objetos de confirmação adicionais. Não é um grande negócio, mas um confuso pouco
MightyPork
@MightyPork você está certo. Infelizmente, desde que escrevi isso há muito tempo, não sei mais por que as etapas "git stash" e "git merge stash" estão lá, em vez de um "git commit".
Eric Hu
2
Oh, está claro, eu acho. Dessa forma, ele mescla um arquivo, não necessariamente substituindo as alterações anteriores na ramificação de destino.
MightyPork
48

A resposta do 1800 INFORMAÇÃO está completamente correta. Como um git noob, no entanto, "usar git cherry-pick" não foi suficiente para eu descobrir isso sem um pouco mais de pesquisa na internet, então pensei em publicar um guia mais detalhado caso alguém mais esteja em um barco semelhante.

Meu caso de uso estava querendo puxar seletivamente alterações do ramo de github de outra pessoa para o meu. Se você já possui uma filial local com as alterações, precisará executar apenas as etapas 2 e 5-7.

  1. Crie (se não for criado) um ramo local com as alterações que deseja trazer.

    $ git branch mybranch <base branch>

  2. Mude para ele.

    $ git checkout mybranch

  3. Puxe para baixo as alterações desejadas da conta da outra pessoa. Caso ainda não o tenha, adicione-os como um controle remoto.

    $ git remote add repos-w-changes <git url>

  4. Puxe tudo do ramo deles.

    $ git pull repos-w-changes branch-i-want

  5. Veja os logs de confirmação para ver quais alterações você deseja:

    $ git log

  6. Volte para a ramificação na qual deseja extrair as alterações.

    $ git checkout originalbranch

  7. Cherry escolhe seus commits, um por um, com os hashes.

    $ git cherry-pick -x hash-of-commit

Dica: http://www.sourcemage.org/Git_Guide

Cory
fonte
3
Dica: primeiro use o git cherrycomando (consulte o manual primeiro) para identificar confirmações que ainda não foram mescladas.
Akaihola
Isso funciona .. 1. criou um novo ramo 2. criou alguns arquivos / fez algumas alterações 3. confirmar 4. verificar o ramo mestre 5. Execute o hash de confirmação do git cherry-pick -x e resolva conflitos de mesclagem, você é bom ir.
RamPrasadBismil
Seu link não está mais funcionando. Você pode atualizá-lo, por favor?
Creep3007
42

Aqui está como você pode substituir o Myclass.javaarquivo na masterramificação por Myclass.javana feature1ramificação. Funcionará mesmo se Myclass.javanão existir master.

git checkout master
git checkout feature1 Myclass.java

Observe que isso substituirá - não mesclará - e ignorará as alterações locais na ramificação principal.

maestr0
fonte
6
Isso não irá mesclar. Ele substituirá as alterações no mestre pelas alterações do ramo feature1.
Skunkwaffle 6/09/2013
3
Perfeitamente, eu estava olhando para este tipo de merge onde theirssobrescrever ours=> +1 Felicidades;)
olibre
1
Algumas vezes, tudo o que você quer fazer é substituir o arquivo inteiro, então era isso que eu queria, mas você precisa ter certeza de que deseja perder todas as alterações feitas nesse arquivo.
MagicLAMP
1
Solução mais limpa, considerando que o OP queria especificamente substituir o arquivo inteiro pelo equivalente em outro ramo:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Brent Faust
29

A maneira simples, para realmente mesclar arquivos específicos de duas ramificações, não apenas substituir arquivos específicos por arquivos de outra ramificação.

Etapa 1: diferencie os galhos

git diff branch_b > my_patch_file.patch

Cria um arquivo de correção da diferença entre o ramo atual e o ramo_b

Etapa 2: Aplicar o patch nos arquivos que correspondem a um padrão

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

notas úteis sobre as opções

Você pode usar * como curinga no padrão de inclusão.

As barras não precisam ser escapadas.

Além disso, você pode usar --exclude e aplicá-lo a tudo, exceto aos arquivos correspondentes ao padrão, ou reverter o patch com -R

A opção -p1 é uma reserva do comando * unix patch e o fato de o conteúdo do arquivo de correção preceder cada nome de arquivo com a/oub/ (ou mais dependendo de como o arquivo de correção foi gerado) que você precisa remover para que ele possa descobrir o arquivo real para o caminho para o arquivo ao qual o patch precisa ser aplicado.

Confira a página de manual do git-apply para obter mais opções.

Etapa três: não há etapa três

Obviamente, você gostaria de confirmar suas alterações, mas quem pode dizer que você não tem outros ajustes relacionados antes de fazer seu commit.

masukomi
fonte
1
Isso foi muito útil quando o current_branch teve muitas alterações "adicionais" que precisavam ser preservadas. Obteve o diff de apenas as alterações trazidas por branch_b como: git diff HEAD ... branch_b (sim - três períodos fazem o truque de mágica).
Saad Malik
@masukomi, Na etapa 2, você não deve adicionar o arquivo de correção criado na etapa 1 como argumento?
Spiralis
Para mim, todas as alterações são rejeitadas. Alguma idéia do porquê?
LinusGeffarth 31/01
pensamento inicial @LinusGeffarth é que talvez você tenha os galhos para trás ao fazer o patch? acompanhará fora do SO para ver se conseguimos descobrir.
masukomi
24

Veja como você pode fazer com que o histórico siga apenas alguns arquivos de outra ramificação com um mínimo de confusão, mesmo que uma mesclagem mais "simples" traga muito mais alterações que você não deseja.

Primeiro, você executará a etapa incomum de declarar antecipadamente que o que você está prestes a confirmar é uma mesclagem, sem o git fazer nada com os arquivos no diretório de trabalho:

git merge --no-ff --no-commit -s ours branchname1

. . . onde "branchname" é o que você alega estar fundindo. Se você cometer imediatamente, isso não fará alterações, mas ainda mostrará ascendência do outro ramo. Você pode adicionar mais branches / tags / etc. para a linha de comando, se necessário. Neste ponto, porém, não há alterações a serem confirmadas, portanto, obtenha os arquivos das outras revisões a seguir.

git checkout branchname1 -- file1 file2 etc

Se você estava mesclando de mais de um outro ramo, repita conforme necessário.

git checkout branchname2 -- file3 file4 etc

Agora, os arquivos da outra ramificação estão no índice, prontos para serem confirmados, com histórico.

git commit

e você terá muito o que explicar nessa mensagem de confirmação.

Observe, no entanto, caso não esteja claro, que isso é uma bagunça. Não está no espírito do que é um "ramo", e escolher a cereja é uma maneira mais honesta de fazer o que você faria aqui. Se você quisesse fazer outra "mesclagem" para outros arquivos no mesmo ramo que você não trouxe da última vez, ele o interromperá com uma mensagem "já atualizada". É um sintoma de não ramificar quando deveríamos ter, no ramo "de" deve haver mais de um ramo diferente.

jejese
fonte
3
Seu primeiro comando ( git merge --no-ff --no-commit -s outs branchname1) é exatamente o que eu estava procurando! Obrigado!
RobM
1
Com várias ramificações, o histórico necessário, precisa mesclar arquivos únicos e precisar alterar o conteúdo do arquivo antes de enviar por push, essa parece ser uma alternativa decente. Por exemplo, dev => master, mas você deseja alterar a definição de host ou similar antes de pressionar para master.
timss
15

Sei que estou um pouco atrasado, mas esse é o meu fluxo de trabalho para mesclar arquivos seletivos.

#make a new branch ( this will be temporary)
git checkout -b newbranch
# grab the changes 
git merge --no-commit  featurebranch
# unstage those changes
git reset HEAD
(you can now see the files from the merge are unstaged)
# now you can chose which files are to be merged.
git add -p
# remember to "git add" any new files you wish to keep
git commit
Felix
fonte
Eu usei uma ligeira variação nisso. Em vez de fundir, escolhi cereja. Faz o trabalho. A única desvantagem dessa abordagem é que você perde a referência ao hash de confirmação original.
Matt Florence
15

A maneira mais fácil é definir seu repo para o ramo com o qual você deseja mesclar e executar,

git checkout [branch with file] [path to file you would like to merge]

Se você correr

git status

você verá o arquivo já preparado ...

Então corra

git commit -m "Merge changes on '[branch]' to [file]"

Simples.

dsieczko
fonte
3
Esta é quase a melhor resposta que encontrei. consulte jasonrudolph.com/blog/2009/02/25/… Tão claro, conciso e funciona!
Superuseroi
1
que irá substituir completamente o conteúdo de arquivos do ramo de origem, em vez de fusão
Amare
Eu estava prestes a responder assim, pensei em inventar coisas novas que ainda não foram respondidas! Mas esta é a maneira mais simples de fazer isso. Isso deve estar no topo!
Irfandy Jip
15

Eu encontrei este post para conter a resposta mais simples. Simplesmente faça:

$ #git checkout <branch from which you want files> <file paths>

Exemplo:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

Veja a postagem para mais informações.

Stunner
fonte
3
Isso realmente não se mescla, substitui o arquivo na ramificação atual.
Igor Ralic
1
@igrali Esse é um comentário útil, mas comparado à dificuldade das maneiras "apropriadas" de fazer isso, essa é uma boa solução alternativa. É preciso ter muito cuidado.
Owensmartin
12

É estranho que o git ainda não tenha uma ferramenta tão conveniente "pronta para uso". Eu o uso muito quando atualizo algum ramo da versão antiga (que ainda possui muitos usuários de software) apenas com algumas correções do ramo da versão atual. Nesse caso, geralmente é necessário obter rapidamente apenas algumas linhas de código do arquivo no tronco, ignorando muitas outras alterações (que não devem entrar na versão antiga) ... E, é claro, mesclagem interativa de três vias é necessário nesse caso, git checkout --patch <branch> <file path>não é utilizável para essa finalidade de mesclagem seletiva.

Você pode fazer isso facilmente:

Basta adicionar esta linha à [alias]seção em seu arquivo global .gitconfigou local .git/config:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Isso implica que você use o Beyond Compare. Apenas mude para o software de sua escolha, se necessário. Ou você pode alterá-lo para mesclagem automática de três vias se não precisar da mesclagem seletiva interativa:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

Então use assim:

git mergetool-file <source branch> <file path>

Isso lhe dará a verdadeira oportunidade de mesclagem seletiva em árvore de qualquer arquivo em outra ramificação.

JimStar
fonte
10

Não é exatamente o que você estava procurando, mas foi útil para mim:

git checkout -p <branch> -- <paths> ...

É uma mistura de algumas respostas.

Felipe
fonte
2
Isso é realmente útil e pode ser adicionado à melhor resposta para mim, a resposta do @ alvinabad. Ao fazer: git checkout HEAD file1para manter a versão atual e desmembrar o arquivo file1, pode-se usar a -popção para selecionar parte do arquivo a ser mesclado. obrigado pelo truque!
Simon C.
Esta é a minha resposta favorita. Simples, direto ao ponto e funciona
Jesse Reza Khorasanee
8

Eu faria um

git diff commit1..commit2 filepattern | git-apply --index && git commit

Dessa forma, você pode limitar o intervalo de confirmações para um padrão de arquivo de uma ramificação.

Roubado em: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html

lumpidu
fonte
Em algumas situações, isso pode ser muito útil. No entanto, se as alterações ocorrerem em um ramo diferente, basta fazer o checkout na ponta desse ramo, como na resposta de Bart J acima.
cdunn2001
Quem é Bart Js?
Preto
8

Eu tive exatamente o mesmo problema, como mencionado por você acima. Mas achei esse blog git mais claro ao explicar a resposta.

Comando no link acima:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
Susheel Javadi
fonte
você testou isso? Estou certo de que os arquivos serão substituídos a partir <branch_you_want_to_merge_from> Ao invés de ser fundida
Amare
7

Gosto da resposta 'git-Interactive-Merge', acima, mas há uma mais fácil. Deixe o git fazer isso por você usando uma combinação de rebase interativa e para:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

Portanto, o caso é que você queira C1 e C2 da ramificação 'feature' (ponto de ramificação 'A'), mas nada disso por enquanto.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Que, como acima, leva você ao editor interativo, onde você seleciona as linhas de 'seleção' para C1 e C2 (como acima). Salve e saia e, em seguida, ele prosseguirá com a rebase e fornecerá a ramificação 'temp' e também HEAD no master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Em seguida, você pode simplesmente atualizar o mestre para HEAD e excluir o ramo temporário e pronto:

# git branch -f master HEAD
# git branch -d temp
Wade
fonte
7

Que tal git reset --soft branch? Estou surpreso que ninguém tenha mencionado ainda.

Para mim, é a maneira mais fácil de selecionar seletivamente as alterações de outro ramo, pois esse comando coloca na minha árvore de trabalho todas as diferenças e eu posso escolher ou reverter facilmente qual delas preciso. Dessa forma, eu tenho controle total sobre os arquivos confirmados.

GarryOne
fonte
6

Sei que essa pergunta é antiga e há muitas outras respostas, mas escrevi meu próprio script chamado 'pmerge' para mesclar parcialmente diretórios. É um trabalho em andamento e ainda estou aprendendo os scripts git e bash.

Este comando usa git merge --no-commit e depois aplica as alterações que não correspondem ao caminho fornecido.

Uso: git pmerge branch path
Exemplo:git merge develop src/

Eu não testei extensivamente. O diretório de trabalho deve estar livre de alterações não confirmadas e arquivos não rastreados.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS
Andy
fonte
4

Você pode usar read-treepara ler ou mesclar determinada árvore remota no índice atual, por exemplo:

git remote add foo [email protected]/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

Para executar a mesclagem, use em -mvez disso.

Veja também: Como mesclar um subdiretório no git?

kenorb
fonte
3

Uma abordagem simples para mesclar / confirmar seletivamente por arquivo:

git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes

Dave C
fonte
3

Se você não tiver muitos arquivos alterados, isso deixará você sem confirmações adicionais.

1. Ramificação duplicada temporariamente
$ git checkout -b temp_branch

2. Redefina para a última confirmação desejada
$ git reset --hard HEAD~n , onde nestá o número de confirmações necessárias para voltar

3. Faça o checkout de cada arquivo da ramificação original
$ git checkout origin/original_branch filename.ext

Agora você pode confirmar e forçar push (para substituir o controle remoto), se necessário.

JBaczuk
fonte
3

Se você só precisar mesclar um diretório específico e deixar tudo intacto e ainda preservar o histórico, poderá tentar isso ... crie um novo target-branchfora do diretóriomaster antes de experimentar.

As etapas abaixo assumem que você tem duas ramificações target-branche source-branch, e o diretório dir-to-mergeque você deseja mesclar está no source-branch. Suponha também que você tenha outros diretórios, como dir-to-retainno destino, que não deseja alterar e reter o histórico. Além disso, assume que há conflitos de mesclagem no dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.
code4kix
fonte
2

Quando apenas alguns arquivos foram alterados entre as confirmações atuais das duas ramificações, mesclo manualmente as alterações passando pelos arquivos diferentes.

git difftoll <branch-1>..<branch-2>

raratiru
fonte