Vamos falar hoje de como o Git organiza internamente as branches criadas no repositório. Embora soe como um assunto complicado, veremos que é muito simples e que irá esclarecer várias dúvidas sobre o próprio Git.
O Git realiza a organização dos commits por meio de uma estrutura de grafos, conforme foi dito e explicado em um texto anterior aqui no blog. Entendendo este conceito, podemos agora falar de como o Git usa esta estrutura em grafo para saber onde começa (e termina) uma branch e em qual branch o usuário está dentro de um repositório.
As branches: o que elas realmente são
Para o Git saber onde estão as branches, em qual branch o usuário está e até para criar tags dentro do repositório, o Git usa uma única coisa: ponteiros.
Uma branch no Git é um ponteiro para um commit.
Sim, é tão simples quanto isto. Agora, como ele consegue "se virar" apenas com um único ponteiro?
Imaginemos que criamos um repositório novo e fizemos o nosso primeiro commit! Como ficaria a árvore de grafos do Git? Vejamos:
M01
Bem, isto não nos diz muita coisa ainda. Como este repositório é novo e todo repositório novo tem obrigatoriamente uma branch master1, o Git precisa saber que o M01 faz parte da branch master. Assim, o Git cria um ponteiro para o commit M01:
M01
|
|
|MASTER|
O |MASTER| é o nome do ponteiro criado pelo Git para indicar qual commit faz parte da branch master.
Digamos que o usuário faz um novo commit na branch master:
M01<---M02
O que o Git irá fazer com relação ao ponteiro |MASTER|? Vai duplicá-lo no M02?
Não, o ponteiro de uma branch é sempre único! Ocorrerá o seguinte:
M01<---M02
|
|
|MASTER|
O que ocorreu? O Git moveu o ponteiro |MASTER| para o commit M02. Assim, o Git determina que tudo para trás do M02 pertence a branch master. Se fizermos mais 2 commits, a estrutura de grafos do repositório Git ficaria assim:
M01<---M02<---M03<---M04
|
|
|MASTER|
Simples assim :).
Digamos que, agora, queremos criar uma nova branch chamada de hotfix para este repositório. Esta branch será criada a partir do último commit da branch master.
Ao criar uma nova branch chamada hotfix, teremos um novo ponteiro chamado |HOTFIX|. Veja:
|HOTFIX|
|
|
M01<---M02<---M03<---M04
|
|
|MASTER|
Temos agora 2 branches (master e hotfix) apontando para o mesmo commit M04.
Digamos que quero fazer um commit na branch hotfix. Logicamente, este commit só deve pertencer a branch hotfix e não pertencer a branch master.
Fazendo um commit M05 na branch hotfix, vamos ter o seguinte grafo:
|HOTFIX|
|
|
.---M05
/
M01<---M02<---M03<---M04<--´
|
|
|MASTER|
Como é possível observar, o ponteiro do master permaneceu apontando para o mesmo commit que antes, o M04. Do M04 para trás temos a branch master. O ponteiro do hotfix moveu-se para o novo commit M05. Do M05 para trás temos a branch hotfix.
Agora, vamos fazer um commit M06 na master! Como ficará o grafo? Vejamos:
|HOTFIX|
|
|
.---M05
/
M01<---M02<---M03<---M04<--´-----M06
|
|
|MASTER|
Como ocorreu com o hotfix, o ponteiro do master moveu-se para o novo commit, o M06. Tanto o M05 como o M04 tem como commit "pai" o M04. Agora do M06 para trás (observe o grafo!) temos a branch master, na qual não incluirá o commit M05, que pertence neste momento unicamente a branch hotfix.
Onde estou? Conheça o ponteiro HEAD
No exemplo anterior, mencionamos que o usuário poderia estar na branch master ou hotfix quando realizava os seus commits. Mas, como o Git sabe em qual branch o usuário está?
Agora temos na jogada um outro ponteiro, o HEAD. O HEAD nos diz onde você está dentro da estrutura de grafos de commits do Git, e ele pode apontar para uma branch, tag2 ou um commit específico.
Quando faço o comando para mudar de branch:
git checkout master
Estou alterando o ponteiro HEAD para apontar para a branch master. Temos no grafo então:
|HOTFIX|
|
|
.---M05
/
M01<---M02<---M03<---M04<--´-----M06
|
|
|MASTER|
|
|
<HEAD>
Assim como se eu fizer um:
git checkout hotfix
Estou alterando o ponteiro HEAD para a hotfix.
<HEAD>
|
|
|HOTFIX|
|
|
.---M05
/
M01<---M02<---M03<---M04<--´-----M06
|
|
|MASTER|
Você também pode fazer um checkout para qualquer commit específico. Se quisermos fazer um checkout para o M03:
|HOTFIX|
|
<HEAD> |
| .---M05
| /
M01<---M02<---M03<---M04<--´-----M06
|
|
|MASTER|
Acredito que com este texto tenha ficado mais claro como o Git organiza-se para controlar as branches do repositório e de como ele controla em qual branch o usuário está no momento.
Nos próximos textos pretendo falar um pouco de alguns comandos interessantes (para dizer o mínimo), como o stash, squash, tag, etc.
2. A tag é usada para marcar algum commit relevante dentro do repositório (como a entrega de uma versão, por exemplo). Tal como uma branch, a tag também é um ponteiro, tanto que uma tag não pode ter o mesmo nome de uma branch já existente no repositório.↩