Chapters ▾ 2nd Edition

3.5 Ramificação (Branching) no Git - Branches Remotos (Remote Branches)

Branches Remotos (Remote Branches)

Referências remotas são referências (ponteiros) nos seus repositórios remotos, incluindo branches, tags e assim por diante. Você pode obter uma lista completa de referências remotas explicitamente com git ls-remote <remote>, ou git remote show <remote> para branches remotos, bem como mais informações. No entanto, uma maneira mais comum é aproveitar os branches de rastreamento remoto (remote-tracking branches).

Branches de rastreamento remoto são referências ao estado dos branches remotos. Eles são referências locais que você não pode mover; o Git os move para você sempre que você faz qualquer comunicação de rede, para garantir que eles representem com precisão o estado do repositório remoto. Pense neles como favoritos, para lembrá-lo de onde estavam os branches nos seus repositórios remotos da última vez que você se conectou a eles.

Nomes de branches de rastreamento remoto assumem a forma <remote>/<branch>. Por exemplo, se você quisesse ver como o branch master no seu remoto origin estava da última vez que você se comunicou com ele, você verificaria o branch origin/master. Se você estivesse trabalhando em uma issue com um parceiro e ele fizesse o push de um branch iss53, você poderia ter o seu próprio branch local iss53, mas o branch no servidor seria representado pelo branch de rastreamento remoto origin/iss53.

Isso pode ser um pouco confuso, então vamos ver um exemplo. Digamos que você tenha um servidor Git na sua rede em git.ourcompany.com. Se você clonar a partir dele, o comando clone do Git o nomeia automaticamente como origin para você, baixa todos os seus dados, cria um ponteiro para onde o branch master dele está e o nomeia como origin/master localmente. O Git também fornece a você o seu próprio branch master local começando no mesmo lugar que o branch master do origin, para que você tenha algo a partir do qual trabalhar.

Note
“origin” não é especial

Assim como o nome do branch “master” não tem nenhum significado especial no Git, o “origin” também não. Enquanto “master” é o nome padrão para um branch inicial quando você executa o git init, o que é a única razão pela qual ele é amplamente utilizado, “origin” é o nome padrão para um remoto quando você executa o git clone. Se, em vez disso, você executar git clone -o booyah, você terá booyah/master como o seu branch remoto padrão.

Repositórios locais e do servidor após clonar
Figure 30. Repositórios locais e do servidor após clonar

Se você fizer algum trabalho no seu branch local master e, nesse meio-tempo, outra pessoa fizer push para git.ourcompany.com e atualizar o branch master de lá, os seus históricos avançarão de forma diferente. Além disso, enquanto você não se comunicar com o seu servidor origin, o seu ponteiro origin/master não se moverá.

O trabalho local e o remoto podem divergir
Figure 31. O trabalho local e o remoto podem divergir

Para sincronizar o seu trabalho com um determinado remoto, você executa o comando git fetch <remote> (no nosso caso, git fetch origin). Esse comando verifica qual servidor é o “origin” (neste caso, é git.ourcompany.com), busca os dados que você ainda não tem e atualiza o seu banco de dados local, movendo o seu ponteiro origin/master para a sua posição nova e mais atualizada.

`git fetch` atualiza os seus branches de rastreamento remoto
Figure 32. git fetch atualiza os seus branches de rastreamento remoto

Para demonstrar como ter vários servidores remotos e como se parecem os branches remotos para esses projetos remotos, vamos supor que você tenha outro servidor Git interno que é usado apenas para desenvolvimento por uma das suas equipes de sprint. Este servidor está em git.team1.ourcompany.com. Você pode adicioná-lo como uma nova referência remota ao projeto em que você está trabalhando atualmente executando o comando git remote add, conforme abordamos no Fundamentos do Git. Dê a este remoto o nome teamone, que será o seu nome abreviado (shortname) para toda a URL.

Adicionando outro servidor como remoto
Figure 33. Adicionando outro servidor como remoto

Agora, você pode executar o git fetch teamone para buscar tudo o que o servidor remoto teamone tem e que você ainda não tem. Como esse servidor tem um subconjunto dos dados que o seu servidor origin tem no momento, o Git não busca dados, mas define um branch de rastreamento remoto chamado teamone/master para apontar para o commit que teamone tem como sendo o seu branch master.

Branch de rastreamento remoto para `teamone/master`
Figure 34. Branch de rastreamento remoto para teamone/master

Enviando (Pushing)

Quando você quiser compartilhar um branch com o mundo, você precisa fazer o push dele para um remoto no qual você tenha acesso de gravação. Os seus branches locais não são sincronizados automaticamente com os remotos nos quais você grava — você tem que fazer o push explicitamente dos branches que deseja compartilhar. Dessa forma, você pode usar branches privados para trabalhos que não deseja compartilhar e fazer o push apenas dos branches de tópicos nos quais deseja colaborar.

Se você tiver um branch chamado serverfix no qual deseja trabalhar com outras pessoas, você pode fazer o push dele da mesma forma que fez o push do seu primeiro branch. Execute git push <remote> <branch>:

$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
 * [new branch]      serverfix -> serverfix

Isso é um pouco de atalho. O Git expande automaticamente o nome do branch serverfix para refs/heads/serverfix:refs/heads/serverfix, o que significa: “Pegue meu branch local serverfix e faça o push para atualizar o branch serverfix do remoto.” Abordaremos a parte refs/heads/ em detalhes em Git Internals (Por Dentro do Git), mas geralmente você pode omiti-la. Você também pode fazer git push origin serverfix:serverfix, que faz a mesma coisa — ele diz: “Pegue meu serverfix e faça dele o serverfix do remoto.” Você pode usar esse formato para fazer o push de um branch local em um branch remoto com nome diferente. Se você não quisesse que ele fosse chamado de serverfix no remoto, você poderia executar git push origin serverfix:awesomebranch para fazer o push do seu branch local serverfix para o branch awesomebranch no projeto remoto.

Note
Não digite sua senha toda vez

Se você estiver usando uma URL HTTPS para fazer o push, o servidor Git solicitará o seu nome de usuário e senha para autenticação. Por padrão, ele solicitará essas informações no terminal para que o servidor possa saber se você tem permissão para fazer o push.

Se você não quiser digitá-la todas as vezes que fizer um push, você pode configurar um “cache de credenciais” (credential cache). A maneira mais simples é mantê-la na memória por alguns minutos, o que você pode configurar facilmente executando git config --global credential.helper cache.

Para obter mais informações sobre as várias opções de cache de credenciais disponíveis, consulte Armazenamento de Credenciais.

Na próxima vez que um de seus colaboradores fizer um fetch do servidor, ele obterá uma referência de onde a versão do serverfix do servidor está no branch remoto origin/serverfix:

$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
 * [new branch]      serverfix    -> origin/serverfix

É importante observar que, ao fazer um fetch que traz novos branches de rastreamento remoto, você não obtém automaticamente cópias locais editáveis deles. Em outras palavras, neste caso, você não tem um novo branch serverfix — você tem apenas um ponteiro origin/serverfix que não pode modificar.

Para mesclar (merge) esse trabalho no seu branch de trabalho atual, você pode executar git merge origin/serverfix. Se você quiser o seu próprio branch serverfix no qual possa trabalhar, você pode baseá-lo no seu branch de rastreamento remoto:

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

Isso fornece a você um branch local no qual você pode trabalhar e que começa onde origin/serverfix está.

Branches de Rastreamento (Tracking Branches)

Fazer o checkout de um branch local a partir de um branch de rastreamento remoto cria automaticamente o que é chamado de “branch de rastreamento” (tracking branch) (e o branch que ele rastreia é chamado de “branch upstream”). Branches de rastreamento são branches locais que têm uma relação direta com um branch remoto. Se você estiver em um branch de rastreamento e digitar git pull, o Git saberá automaticamente de qual servidor buscar (fetch) e qual branch mesclar (merge in).

Quando você clona um repositório, ele geralmente cria automaticamente um branch master que rastreia origin/master. No entanto, você pode configurar outros branches de rastreamento, se desejar — aqueles que rastreiam branches em outros remotos, ou não rastreiam o branch master. O caso simples é o exemplo que você acabou de ver, executando git checkout -b <branch> <remote>/<branch>. Esta é uma operação comum o suficiente para que o Git forneça o atalho --track:

$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

Na verdade, isso é tão comum que há até um atalho para esse atalho. Se o nome do branch que você está tentando fazer checkout (a) não existir e (b) corresponder exatamente a um nome em apenas um remoto, o Git criará um branch de rastreamento para você:

$ git checkout serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

Para configurar um branch local com um nome diferente do branch remoto, você pode usar facilmente a primeira versão com um nome de branch local diferente:

$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'

Agora, o seu branch local sf fará o pull automaticamente de origin/serverfix.

Se você já tem um branch local e deseja defini-lo para um branch remoto do qual você acabou de fazer o pull (pulled down), ou deseja alterar o branch upstream que você está rastreando, você pode usar a opção -u ou --set-upstream-to para git branch para defini-lo explicitamente a qualquer momento.

$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Note
Atalho Upstream

Quando você tem um branch de rastreamento configurado, você pode referenciar o seu branch upstream com o atalho @{upstream} ou @{u}. Portanto, se você estiver no branch master e ele estiver rastreando origin/master, você pode dizer algo como git merge @{u} em vez de git merge origin/master se desejar.

Se você quiser ver quais branches de rastreamento você configurou, você pode usar a opção -vv do git branch. Isso listará os seus branches locais com mais informações, incluindo o que cada branch está rastreando e se o seu branch local está à frente (ahead), atrás (behind) ou ambos.

$ git branch -vv
  iss53     7e424c3 [origin/iss53: ahead 2] Add forgotten brackets
  master    1ae2a45 [origin/master] Deploy index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] This should do it
  testing   5ea463a Try something new

Então, aqui podemos ver que o nosso branch iss53 está rastreando origin/iss53 e está “à frente” (ahead) por dois, o que significa que temos dois commits localmente que não foram enviados (pushed) para o servidor. Também podemos ver que o nosso branch master está rastreando origin/master e está atualizado. A seguir, podemos ver que o nosso branch serverfix está rastreando o branch server-fix-good no nosso servidor teamone e está à frente por três e atrás (behind) por um, o que significa que há um commit no servidor que ainda não mesclamos (merged in) e três commits localmente que não fizemos o push. Finalmente, podemos ver que o nosso branch testing não está rastreando nenhum branch remoto.

É importante observar que esses números são apenas desde a última vez que você fez um fetch de cada servidor. Esse comando não chega aos servidores, ele está dizendo o que tem em cache desses servidores localmente. Se você deseja números totalmente atualizados sobre o que está à frente e atrás, você precisará fazer um fetch de todos os seus remotos logo antes de executar isso. Você pode fazer isso assim:

$ git fetch --all; git branch -vv

Obtendo (Pulling)

Embora o comando git fetch busque todas as alterações no servidor que você ainda não tem, ele não modificará o seu diretório de trabalho de forma alguma. Ele simplesmente obterá os dados para você e permitirá que você os mescle (merge) você mesmo. No entanto, há um comando chamado git pull que é essencialmente um git fetch imediatamente seguido por um git merge na maioria dos casos. Se você tiver um branch de rastreamento configurado conforme demonstrado na seção anterior, definindo-o explicitamente ou fazendo com que seja criado para você pelos comandos clone ou checkout, o git pull procurará qual servidor e branch o seu branch atual está rastreando, fará o fetch desse servidor e tentará mesclar (merge in) esse branch remoto.

Excluindo Branches Remotos

Suponha que você tenha terminado com um branch remoto — digamos que você e seus colaboradores tenham terminado um recurso (feature) e o tenham mesclado (merged) no branch master do seu remoto (ou em qualquer branch em que a sua linha de código estável esteja). Você pode excluir um branch remoto usando a opção --delete para git push. Se você deseja excluir o seu branch serverfix do servidor, execute o seguinte:

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

Basicamente, tudo o que isso faz é remover o ponteiro do servidor. O servidor Git geralmente manterá os dados lá por um tempo até que uma coleta de lixo (garbage collection) seja executada, então, se ele for excluído acidentalmente, geralmente é fácil de recuperar.