Quando temos um projecto de software e queremos fazer um ou mais projectos semelhantes, com apenas algumas alterações em alguns ficheiros, ou seja, fazer um fork do projecto. É frequente termos a necessidade de copiar código entre os projectos, sempre que corrigimos um bug ou implementamos uma nova funcionalidade num dos projectos.

Git fork


Quando criei o blogue empython.com, aproveitei o código que já tinha deste blogue. E sempre que corrigia um bug ou implementava uma nova funcionalidade num dos projectos, lá ia eu copiar as alterações que tinha feito para o outro projecto, recorrendo muitas vezes a uma aplicação para comparar as diferenças entre ficheiros. O problema é que isto é um processo muito repetitivo, entediante e sujeito a falhas. Para pequenos projectos pessoais a coisa ainda vai, mas para grandes projectos torna-se difícil gerir este processo. Decidi então procurar por uma alternativa, e os candidatos óbvios que surgiram para solucionar o problema foram os softwares de controle de versões.

Como estou a usar o git para o controle de versões, lá fui eu procurar como poderia usar o git ajudar-me a executar esta tarefa.

Git


Existe a possibilidade de fazer merge, mas o merge é mais indicado para usar dentro do mesmo projecto. É usado quando criamos um novo branch (ramo) do projecto para desenvolver uma nova funcionalidade ou corrigir um bug e no fim fazemos o merge para adicionar as alterações criadas no branch ao nosso projecto. Não me parece que o merge seja o mais indicado para usar em projectos diferentes, ainda que estes sejam muito semelhantes.

O Cherry Pick

O Cherry Pick, permite aplicar as alterações introduzidas num determinado commit.

Vamos supor que temos dois projectos semelhantes, o proj1 e o proj2.
Quando fizermos alterações no código, por exemplo no proj1, e desde que os ficheiros alterados sejam iguais em ambos os projectos, podemos usar o cherry-pick do git para adicionar ao proj2 as alterações incluídas no último commit do proj1.

Cherry Pick


No Proj2:
Primeiro temos que adicionar o remote do proj1 ao repositório do proj2. Este passo só é feito uma vez, não sendo necessário repetir em futuras alterações.
$git remote add proj1 /path/proj1


No Proj1:
Fazemos commit das alterações.
$git add .
$git commit -a -m "mensagem do commit"


No Proj2:
Fazemos o fetch do repositório do proj1.
$git fetch proj1

Adicionamos o último commit no branch master do proj1 no branch actual do proj2.
$git cherry-pick proj1/master


Podemos também escolher qual o commit cujas alterações pretendemos introduzir.

No Proj2:
Ver os commits do proj1
$git log proj1

Obter a assinatura sha do commit pretendido e utilizá-la no cherry-pick.
$git cherry-pick proj1/master <assinatura_sha_do_commit_pretendido>


Nota: Neste exemplo adicionamos o remote de um repositório local, mas poderíamos ter adicionado o remote de um repositório remoto (do github.com por exemplo), nesse caso teríamos primeiro de fazer o push das alterações para o repositório remoto e só depois fazer o fetch do proj1 para o repositório local do proj2.

E pronto, as alterações ao código do proj1 foram incluídas no proj2, e como extra o commit também é incluído no repositório do proj2. Uma verdadeira cereja no topo do bolo :)


Além do cherry pick o git também permite aplicar patches de alterações introduzidas noutros projectos ou branchs. Mais sobre os patches na documentação do git.
A aplicação de patches é mais flexível, mas o cherry-pick é mais fácil de usar, menos trabalhoso e o mais indicado nas situações em que se altera o código de um projecto e imediatamente a seguir se procede à alteração nos restantes projectos.

Espero que esta informação vos seja útil na gestão de código entre projectos que partilhem uma mesma base de código.


Quem é que já precisou de fazer algo parecido? Que estratégias costumam usar?