Bifurcações e upstreams do Git: instruções e uma dica legal

Nicola Paolucci
Nicola Paolucci
Voltar para a lista

A bifurcação de projetos para fazer suas próprias alterações permite integrar com facilidade as próprias contribuições. Mas se você não estiver enviando essas alterações de volta para o upstream, o que significa enviar de volta para o repositório pai, você corre o risco de perdê-las, o que pode causar linhas divergentes no repositório. Para garantir que todos os colaboradores estejam desenhando do mesmo lugar, você vai precisar saber alguns princípios de como o git forking interage com o git upstream. Neste blog, vou apresentar o básico, as pegadinhas e até deixar uma dica legal para você ficar à frente da curva.

Git upstream: fique atualizado e contribua

Vamos começar detalhando uma configuração comum e o fluxo de trabalho mais básico para interagir com repositórios upstream.

Em uma configuração padrão, você em geral tem uma origem e um remoto upstream — o último é o guardião do projeto ou a fonte da verdade para a qual você quer contribuir.

Primeiro, verifique se você já configurou um remoto para o repositório upstream e, com sorte, uma origem também:

git remote -v

origin  git@bitbucket.org:my-user/some-project.git (fetch)
origin  git@bitbucket.org:my-user/some-project.git (push)

Se você não tiver um upstream, vai poder adicionar com facilidade com o comando remote:

git remote add upstream git@bitbucket.org:some-gatekeeper-maintainer/some-project.git

Verifique se o controle remoto foi adicionado com precisão:

git remote -v

origin    git@bitbucket.org:my-user/some-project.git (fetch)
origin    git@bitbucket.org:my-user/some-project.git (push)
upstream  git@bitbucket.org:some-gatekeeper-maintainer/some-project.git (fetch)
upstream  git@bitbucket.org:some-gatekeeper-maintainer/some-project.git (push)

Agora você pode coletar as alterações mais recentes do repositório upstream com fetch. Repita sempre que quiser receber atualizações:

(Se o projeto tiver marcações que não passaram por merge para o mestre, você também deve fazer git fetch upstream --tags)

git fetch upstream

No geral, você quer manter a ramificação master local como um espelho próximo do upstream master e executar qualquer trabalho em ramificações de recursos, pois elas podem se tornar pull requests em seguida.

Nesse ponto, não importa se você usa merge ou rebase, pois o resultado tende a ser o mesmo. Vamos usar o merge:

git checkout main
git merge upstream/main

Quando você quiser compartilhar algum trabalho com os mantenedores upstream que você ramifica fora do principal, crie uma ramificação de recurso. Quando estiver satisfeito, envie-o para o repositório remoto.

Você também pode usar o rebase e, em seguida, o merge para garantir que o upstream tenha um conjunto limpo de confirmações (de preferência um) para avaliar:

git checkout -b feature-x

#some work and some commits happen
#some time passes

git fetch upstream
git rebase upstream/main

Publicar com git fork

Após as etapas acima, publique o trabalho na bifurcação remota com um simples push:

git push origin feature-x

Um pequeno problema surge se você tiver que atualizar a ramificação remota feature-x depois da publicação, por causa de alguns comentários dos mantenedores upstream. Você tem algumas opções:

git push -f origin feature-x

Em particular, prefiro manter o histórico o mais limpo possível e optar pela opção três, mas equipes diferentes têm fluxos de trabalho diferentes. Nota: Você deve fazer isso somente quando estiver trabalhando com sua própria bifurcação. Reescrever o histórico de repositórios e branches compartilhados é algo que você NUNCA deve fazer.

Dica do dia: números à frente/atrás no prompt

Depois de um fetch, git status mostra quantos commits você está à frente ou atrás da ramificação remota sincronizada. Não seria bom se você pudesse ver essas informações no fiel prompt de comando? Eu também pensei assim, então comecei a mexer meus pauzinhos bash e cozinhei.

Veja como ele vai ficar no prompt depois da configuração:

nick-macbook-air:~/dev/projects/stash[1|94]$

E isso é o que você vai precisar adicionar ao .bashrc ou equivalente — apenas uma única função:

function ahead_behind {
    curr_branch=$(git rev-parse --abbrev-ref HEAD);
    curr_remote=$(git config branch.$curr_branch.remote);
    curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);
    git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';
}

Prompt de amostra:

export PS1="\h:\w[\$(ahead_behind)]$"

Funcionamento interno

Para quem gosta de informações e explicações, é assim que funciona:

Obtemos o nome simbólico para o HEAD atual, ou seja, a ramificação atual:

curr_branch=$(git rev-parse --abbrev-ref HEAD);

Obtemos o remoto para o qual a ramificação atual está apontando:

curr_remote=$(git config branch.$curr_branch.remote);

Obtemos a ramificação ao qual esse remoto deve passar por merge (com um truque do Unix para descartar tudo e incluindo a última barra [ / ]):

curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);

Agora a gente tem o que precisa para coletar o número de contagens das confirmações que temos à frente ou atrás:

git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';

Usamos o antigo Unix tr para converter o TAB em um separador |.

Primeiros passos com o git upstream

Esse é um passo a passo básico no git upstream — como configurar um git upstream, criar uma nova ramificação, coletar alterações, publicar com git fork e uma dica sobre quantas ramificações à frente/atrás você está da ramificação remota.

O Bitbucket Server inclui sincronização de bifurcação que em resumo, alivia o desenvolvedor de todo o fardo de se manter atualizado com as bifurcações e o Bitbucket Cloud tem uma sincronização fácil de 1 etapa, confira!

Siga as minhas publicações em @durdn e a incrível equipe do @Bitbucket para arrasar no DVCS.

Pronto(a) para aprender Git?

Tente este tutorial interativo.

Comece agora mesmo