Sincronizando um repositório replicado do git

fork-1_abertura

Quando é usada a opção de fork¹ para replicar² um repositório git, o resultado final será um novo repositório contendo a mesma história do original (os commits passados) porém independente dele a partir deste ponto (os commits futuros). Algo diferente do que acontece quando é utilizado o git clone para copiar um repositório remoto pois ele permanece ligado ao original e sendo possível mantê-lo sincronizado — git pull — como também enviar novos commitsgit push — para ele.

TL;DR! Pode saltar o resto da explicação se preferir…

A interação entre os repositórios de origem e o replicado acontece a partir da ferramenta utilizada para o gerenciamento dos repositórios (ver nota 1) através do pull request. Que basicamente é um instrumento que permite aplicar commits vindos “de fora” ao repositório e mediante intervenção humana para fazer a validação. O mesmo também acontece com o git push no envio dos commits do repositório clonado para o remoto com a diferença de que como você precisa ter permissões para fazê-lo eles são automaticamente aplicados³.

Até aí tudo bem se considerarmos dois casos bem específicos: (i) repositório remoto com contribuições que virão sempre de membros válidos trabalhando a partir de repositórios clonados ou; (ii) repositório replicado que terá um desenvolvimento distinto daquele no qual ele se originou.

Mas existe um terceiro caso, aquele em que você faz a réplica de um repositório ao qual não é membro mas ao qual não dá para como combinar com os membros dele para que aguardem seus pull requests. Não é estritamente necessário mas é recomendável para não trabalhar em versões (muito) antigas dos arquivos.

E este é fim desta longa introdução! 🙂

(¹) A opção está disponível nas interfaces de gerenciamento de repositórios, como BitBucket, GitHub GitLab etc não na ferramenta (git) em si.

(²) A tradução seria “bifurcado” mas creio que “replicado” é um termo bem melhor.

(³) Em alguns casos, o administrador do repositório pode proibir a atualização em ramos específicos, como o master e neste caso também é necessário utilizar um pull request para aplicar suas alterações.

Replicando e clonando um repositório

Pegando como exemplo o Bootstrap e seu repositório git que está no GitHub.

fork-1_twbs

Selecione a opção «fork», aguarde até que o processo ser concluído, e você terá também um repositório chamado “bootstrap”.

fork-1_plainspooky

Que agora pode ser clonado localmente (já que não tenho como enviar meus commits diretamente ao repositório original).

$ git clone git@github.com:plainspooky/bootstrap.git
Cloning into 'bootstrap'...
...

Repare que o repositório clonado é da minha réplica e não o original.

Adicionando outro repositório remoto

Durante o processo de clonagem o git nomeou meu repositório remoto de “origin”:

$ git remote --verbose
origin git@github.com:plainspooky/bootstrap.git (fetch)
origin git@github.com:plainspooky/bootstrap.git (push)

Ele será utilizado por padrão durante a execução dos comandos git fetch e também do git push. Mas é possível acrescentar outros repositórios remotos como, por exemplo, o repositório original do Bootstrap!

$ git remote add upstream https://github.com/twbs/bootstrap.git

Usei o nome de “upstream” mas pode-se utilizar qualquer outro nome.

$ git remote --verbose
origin	git@github.com:plainspooky/bootstrap.git (fetch)
origin	git@github.com:plainspooky/bootstrap.git (push)
upstream	https://github.com/twbs/bootstrap.git (fetch)
upstream	https://github.com/twbs/bootstrap.git (push)

Visto que não há necessidade de autenticação, preferi usar HTTPS ao invés de SSH neste repositório, mas não faz diferença.

Atualizando ambos os repositórios

Para sincronizar seus repositório local, primeiro atualize o “upstream”.

$ git checkout master
$ git fetch upstream
remote: Counting objects: 54, done.
remote: Total 54 (delta 40), reused 40 (delta 40), pack-reused 14
Unpacking objects: 100% (54/54), done.
From https://github.com/twbs/bootstrap
   a3e45d8ce..7b9c8e8eb  v4-dev     -> upstream/v4-dev
$ git rebase upstream/master
Current branch master is up to date.

O git fetch apenas baixará os commits (objetos) do repositório mas não mexerá no conteúdo local, o git rebase se encarregará da tarefa de reorganizar os commits para deixar seu repositório tal qual o “upstream”. O único cuidado aqui é usar o git rebase sempre dentro do mesmo ramo que se deseja sincronizar.

Como como não havia diferenças entre os ramos “master” em “origin” e “upstream” nada foi alterado. Mas aproveitando que o fetch trouxe um novo ramo, vamos atualizá-lo também, primeiro indo para ele.

$ git checkout v4-dev
Switched to branch 'v4-dev'
Your branch is up to date with 'origin/v4-dev'.

Depois atualizando a lista de commits.

$ git rebase upstream/v4-dev
First, rewinding head to replay your work on top of it...
Fast-forwarded v4-dev to upstream/v4-dev.

Daí subir as mudanças para o repositório remoto.

$ git push 
Counting objects: 60, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (57/57), done.
Writing objects: 100% (60/60), 6.78 KiB | 6.78 MiB/s, done.
Total 60 (delta 42), reused 5 (delta 2)
remote: Resolving deltas: 100% (42/42), completed with 23 local
objects.
To github.com:plainspooky/bootstrap.git
a3e45d8ce..7b9c8e8eb v4-dev -> v4-dev

E pronto, repositórios atualizados.

commit 7b9c8e8eb34618f294ee6585aaa866205402bb49 (HEAD -> v4-dev,
upstream/v4-dev, origin/v4-dev, origin/HEAD)
Author: Herst <Herst@users.noreply.github.com>
Date: Mon Sep 3 17:55:04 2018 +0200
...

Detalhe para o fato de que o ID do commit que referencia o ramo “v4-dev” é o mesmo tanto em “origin” quanto em “upstream”, ou seja, o histórico dos commits dos repositórios estão iguais novamente.

Concluindo…

E para enviar seus commits ao repositório original, basta criar um novo pull request a partir da interface de gerenciamento e aguardar pela resposta e uma vez tornando-se um membro do repositório (e com permissão para push) esta “dupla sincronização” deixa de ser necessária.

Bem, isto não precisava ter ficado assim tão longo, bastava apenas listar as linhas dos comandos mas preferi acrescentar um pouco de teoria envolvida pois é sempre bom ter alguma ideia do que está sendo feito.

Até!

Anúncios