Como aplicar um patch Git a um arquivo com um nome e caminho diferente?

100

Eu tenho dois repositórios. Em um, faço alterações no arquivo ./hello.test. Eu confirmo as mudanças e crio um patch a partir desse commit com git format-patch -1 HEAD. Agora, eu tenho um segundo repositório que contém um arquivo que tem o mesmo conteúdo como hello.test mas é colocado em um diretório diferente com um nome diferente: ./blue/red/hi.test. Como faço para aplicar o patch mencionado acima ao hi.testarquivo? Eu tentei, git am --directory='blue/red' < patch_filemas claro que reclama que os arquivos não têm o mesmo nome (o que achei que o Git não se importava?). Eu sei que provavelmente poderia editar o diff para aplicar a esse arquivo específico, mas estou procurando uma solução de comando.

mart1n
fonte
Relacionado a: stackoverflow.com/q/3367254/1959808
Ioannis Filippidis

Respostas:

97

Você pode criar o patch usando git diffe aplicá-lo usando o patchutilitário, que permite especificar o arquivo ao qual deseja aplicar o diff.

Por exemplo:

cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file
Georgebrock
fonte
7
Ah, legal, não pensei nisso. No entanto, existe alguma maneira de fazer isso com os comandos Git para que os dados de commit (data e hora, autor do commit, mensagem de commit) sejam mantidos?
Mart1n
2
É possível que você possa fazer algo com amou apply, mas não consigo encontrar. Se você estiver duplicando muito as mudanças, pode haver uma solução melhor usando submódulos, ou qualquer outra linguagem de sua escolha para compartilhar código (por exemplo, em Ruby você pode extrair o código duplicado como uma gema).
georgebrock
1
Na verdade, isso está relacionado à documentação (os arquivos de origem são XMLs). Submódulos não são realmente uma opção, pois eu teria que fazer um caso forte para eles em nossa infraestrutura existente.
Mart1n
52

Existe uma solução simples que não envolve edição manual de patch nem script externo.

No primeiro repositório (isso também pode exportar um intervalo de commit, use -1se você quiser selecionar apenas um commit):

git format-patch --relative <committish> --stdout > ~/patch

No segundo repositório:

git am --directory blue/red/ ~/patch

Em vez de usar --relativein git format-patch, outra solução é usar a -p<n>opção in git ampara retirar os ndiretórios do caminho dos patches, conforme mencionado em uma resposta a uma pergunta semelhante .

Também é possível executar git format-patch --relative <committish>sem o --stdout, e irá gerar um conjunto de.patch arquivos. Esses arquivos podem ser alimentados diretamente git amcom git am --directory blue/red/ path/to/*.patch.

Magiraud
fonte
9
Isso ainda depende do fato de que os nomes dos arquivos são os mesmos, certo?
mart1n
3
Deve-se observar que a --directoryopção parece exigir que você especifique o caminho completo do diretório em relação à raiz do repo; algo como --directory=./while chdir'd em um subdiretório no repo não funcionará.
Reid
1
Usando --3wayajuda com does not exist in index:git am --3way --directory (relative-path) (patch)
Brent Bradburn
Use a -ktecla em ambos os comandos para não retirar a primeira linha da mensagem de confirmação.
ruvim
Usar --3waynão apenas ajuda com erros "não existe no índice" (como apontado por @nobar), mas também permite que você trate de forma limpa os conflitos de mesclagem. Em vez de deixar os arquivos em conflito intactos, é adicionado um bloco de conflito que pode ser resolvido.
Daniel Wolf,
11

Respondendo minha própria pergunta com um script que faz exatamente isso: https://github.com/mprpic/apply-patch-to-file

Em vez de modificar o arquivo de patch manualmente, ele solicita ao usuário o arquivo de destino, modifica o patch e o aplica ao repositório em que você está.

mart1n
fonte
5

Com base na resposta de @georgebrock, aqui está uma solução que usei:

Primeiro, crie os arquivos de patch como de costume (por exemplo git format-patch commitA..commitB).

Em seguida, certifique-se de que seu repositório de destino está limpo (não deve haver arquivos alterados ou não rastreados) e aplique os patches como este:

cd second-repo
git am ~/00*.patch

Para cada arquivo de patch, você receberá um erro como "erro: XYZ não existe no índice". Agora você pode aplicar este arquivo de patch manualmente:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

Você deve seguir estas três etapas para cada arquivo de patch.

Isso preservará a mensagem de confirmação original, etc., sem exigir nenhum git format-patchcomando especial ou editar os arquivos de patch.

Oliver
fonte
1
Boa resposta, acho que esta é a melhor base para qualquer tipo de manipulação de patch "não padrão". Eu faço isso em 3 etapas. (1) Comprometa-se com o texto - git format-patch -1 commitA --stdout > thing.diff; (2) Edite o arquivo de patch até que ele faça o que eu preciso; (3) Texto para confirmação git am --3way thing.diff que tem a vantagem de que você pode aceitar as partes do patch que se aplicam de forma limpa e usar gito processo de resolução de conflito padrão para as partes que não se aplicam .
Somos Todos Monica
2

Eu entendo que os dois arquivos são exatamente os mesmos na sua situação, portanto, o patch provavelmente terá sucesso.

No entanto, caso você queira aplicar um patch a um arquivo semelhante, mas não exatamente o mesmo, ou deseja fazer um patch interativo, você usará a fusão de três vias.

Digamos que você modificou Arquivo A, vamos denotar A~1como a versão anterior, e você deseja aplicar a diferença entre A~1a Apara Arquivo B.

Abra uma ferramenta de mesclagem de três vias, por exemplo Beyond Compare, o caminho do painel esquerdo é A, o painel do meio é o ancestral comum, então o caminho é A~1, o caminho do painel direito é B. Em seguida, o painel inferior mostra o resultado da aplicação da diferença entre A~1a Apara Arquivo B.

A figura a seguir ilustra a ideia.

insira a descrição da imagem aqui

Gqqnbig
fonte
0

Para sua informação: Recentemente, tive problemas ao tentar baixar um patch do Github e aplicá-lo a um arquivo local (o que foi uma "substituição" em um novo local).

git amnão aplicaria o patch porque o arquivo "não estava no índice" ou "sujo". Mas, descobri que o patchcomando simples pode aplicar o patch. Ele solicitou o nome do arquivo a ser corrigido.

Terminei o trabalho, de qualquer maneira ...

Mike Robinson
fonte