segunda-feira, 9 de março de 2015

O que é uma branch no Git?

Olá, pessoal!

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.

1. O master é uma branch como qualquer outra dentro do Git.
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.

Nenhum comentário :

Postar um comentário