Merge no git em repositórios não relacionados
(esse post foi criado pra compartilhar com o time no Facebook da F.biz, já que o face não é muito bom pra textos longos)
Tenho um projeto que existe num repositório (um bootstrap) e que tava precisando atualizar em outro repositório não relacionado (sem fork, nem nada, só a lista de arquivos base).
Usei uma técnica — quaaaaase 100% — que consiste em usar o bom e velho patch mesmo e a saída do git diff:
No repositório base eu gero o diff necessário, ou seja, se o projeto que eu quero atualizar está na versão 0.1.0, eu faço:
$ git diff --binary --full-index 0.1.0..0.2.2 > 0.1.0-to-0.2.2.patch;
No repositório do projeto eu rodo o comando patch:
$ patch -p1 < ~/projeto/0.1.0-to-0.2.2.patch;
Se houver conflito, ele cria os arquivos .rej (com o diff do que não foi aplicado) e .orig com o arquivo original.
O problema que peguei foi ele não ter levado arquivos novos E que fossem vazios.
Outra coisa é quando temos 2 projetos que compartilham a estrutura e você precisa só aplicar o patch sem usar o git mesmo. Nesse caso, uso o comando diff mesmo:
$ diff -uBNa ~/projeto/ . > nome-do-patch.patch
Onde -u é pra que ele gere um unified diff, -B pra ignorar linhas vazias, -N pra adicionar arquivos que não existem no destino e -a considerando arquivos binários como de texto (pra que ele leve o arquivo binário do atual pro novo), que eu posso aplicar num outro projeto:
$ patch < nome-do-patch.patch
Note que esse diff é só dos arquivos diretamente filhos daquele diretório, dá pra fazer recursivo com o -r, mas tem que tomar cuidado pra não colocar no diff coisas que não fazem sentido, como a pasta .git, por exemplo.
Note que neste caso ele não leva os arquivos binários. Pra isso acredito que seja necessário algum outro programa.
No meio do caminho, descobri uma ferramentinha bem legal chamada patchutils. Ele tem vários comandos legais, como o filtro de diffs que eu usei agora pouco pra evitar conflitos em arquivos que já foram alterados no projeto:
$ filterdiff -x '*/includes/*' -x '*/assets/*' < ~/projeto/0.1.0-to-0.2.2.patch > custom.patch
Neste caso ele remove todos os caminhos que baterem */includes/* e */assets/, lembrando que o * no pattern do filterdiff inclui também as /.
Além disso tem várias outras ferramentas, como merge de patches, pra quando tenho um patch do 0.1.1 pro 0.1.2, depois do 0.1.2 pro 0.1.3, por exemplo. Ao invés de ficar aplicando vários patches, eu faço o “merge” e aplico tudo de uma vez.
Pra instalar:
$ brew install patchutils