As três árvores do Git
24 Jan 2020Quando trabalhamos com os comandos reset e checkout do Git, podemos utilizar um modelo mental em que o Git gerencia três árvores: HEAD, Index e Working Directory. Neste texto pretendo mostrar como estas árvores afetam nosso dia-a-dia com a ferramenta e como manipulá-las.
HEAD
A HEAD é um apontador para o branch atual, que por sua vez é um apontador para o último commit naquele branch.
Para exemplificar, imagine que você acabou de clonar um repositório e este tem os seguintes branches: master e branch1. Assim que você o clona, você está no branch master, portanto a sua HEAD aponta para o branch master.
C < master < HEAD
|
B D < branch1
| _/
|/
A
E no momento que executamos o comando git checkout branch1, estamos mudando o apontamento da HEAD para o branch branch1.
C < master
|
B D < branch1 < HEAD
| _/
|/
A
Index
O Index é seu próximo commit, ou seja, os arquivos adicionados na área de stagging com o comando git add e antes do comando git commit. O comando git status exibe os arquivos que irão para o próximo commit.
Working Directory
Também referenciado como working tree, o working directory representa os arquivo em seu diretório, na qual você os edita para adicioná-los ao seu index. Então ao mudar de branch com o comando git checkout, o Git irá mudar o apontamento da HEAD e irá modificar os arquivos em seu diretório.
Fluxo de trabalho
O fluxo de trabalho consiste em modificar um arquivo, que modifica seu working directory, adicioná-lo ao seu index (git add) e realizar o commit (git commit), que irá mudar o apontamento do seu branch atual, ou seja, o branch em que a HEAD está apontando.
Por fim, ao mudar de branch (mudar o apontamento da HEAD) com o comando git checkout, o Git irá atualizar seu working directory com os arquivos do branch.
Working Directory Index HEAD
| | |
| <---------- git checkout ---------- |
| | |
| --- git add --> | |
| | |
| | -- git commit --> |
Detalhando o processo
A partir do momento em que iniciamos um repositório no Git com git init, o branch master é criado automaticamente e não temos nenhum commit, assim a HEAD aponta para o branch master, que não aponta para um commit específico.
? < master
^
head
Então você começa seu trabalho e cria um novo arquivo index.html. Com isso você modificou seu working directory.
Working Directory Index HEAD
|
index.html
Ao executar o comando git add index.html, estamos colocando o arquivo index.html em nosso index.
Working Directory Index HEAD
|
index.html
Se adicionarmos outro arquivo chamado footer.html, nosso working directory é modificado novamente.
Working Directory Index HEAD
| |
footer.html index.html
Em seguida, para adicionar este arquivo em nosso próximo commit, devemos adicioná-lo em nosso index com o comando git add footer.html.
Working Directory Index HEAD
|
index.html
footer.html
Agora é hora de realizarmos nosso commit com o comando git commit -m "Mensagem".
Working Directory Index HEAD
|
96b7f8c
|
index.html
footer.html
reset
Durante o detalhamento do processo já vimos como modificar as três árvores, porém existem outros comandos que as modificam. O primeiro comando que veremos é o reset, que tem três modos.
reset --mixed
Imagine que você tem alguns commits e sua HEAD está apontando para o último commit.
5d50d76 < master < HEAD
|
|
f831ed8
$ git log --children --decorate
commit 51d7b8b43f6a7742f6a015dec50076d934f1e79f (HEAD -> master)
Author: João Vitor Retamero <joaovretamero@gmail.com>
Date: Sat Nov 2 17:54:34 2019 -0300
Arquivo 3
commit 5d50d7664780f91c91d480e1a6b185fdc71c7733 51d7b8b43f6a7742f6a015dec50076d934f1e79f
Author: João Vitor Retamero <joaovretamero@gmail.com>
Date: Sat Nov 2 17:54:10 2019 -0300
Arquivo 2
commit f831ed83ecc8e25cd4103fecdcb61ce2649cad93 5d50d7664780f91c91d480e1a6b185fdc71c7733
Author: João Vitor Retamero <joaovretamero@gmail.com>
Date: Sat Nov 2 17:53:55 2019 -0300
Arquivo 1
Usando o comando git reset --mixed 5d50d76, você mudará o apontamento do branch em que a HEAD aponta.
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
arquivo3.txt
nothing added to commit but untracked files present (use "git add" to track)
O comando
git resetsem argumentos é equivalente a executar o comandogit reset --mixed
51d7b8b
|
|
5d50d76 < master < HEAD
|
|
f831ed8
Um ponto interessante sobre modo --mixed é que além de modificar a HEAD, também modifica o Index, então o comando desfaz seu commit (conceitualmente) e desfaz o Index, ou seja, te deixa e um estado onde você acabou de fazer sua alteração no working directory.
reset --soft
Se executar o comando git reset --soft 5d50d76, o Git irá modificar a HEAD mas não o Index, ou seja, te deixa em um estado onde você modificou seu working directory e adicionou os arquivos no Index.
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: arquivo3.txt
reset --hard
Os dois modos anteriores modificam algumas coisas, mas não são perigosos por você ainda ter seu working directory preservado. O modo --hard irá alterar as três árvores do Git, então mudará sua HEAD, Index e working directory.
$ git reset --hard 5d50d76
HEAD is now at 5d50d76 Arquivo 2
$ git status
On branch master
nothing to commit, working tree clean
Executando git reset --hard 5d50d76 te deixará em um estado onde você acabou de realizar o commit que você especificou, perdendo todas as alterações após o commit.
$ git log --children --decorate
commit 5d50d7664780f91c91d480e1a6b185fdc71c7733 (HEAD -> master)
Author: João Vitor Retamero <joaovretamero@gmail.com>
Date: Sat Nov 2 17:54:10 2019 -0300
Arquivo 2
commit f831ed83ecc8e25cd4103fecdcb61ce2649cad93 5d50d7664780f91c91d480e1a6b185fdc71c7733
Author: João Vitor Retamero <joaovretamero@gmail.com>
Date: Sat Nov 2 17:53:55 2019 -0300
Arquivo 1
checkout
O comando checkout é muito similar ao reset --hard, ou seja, modifica as três árvores. A diferença é que o reset irá mover o branch que a HEAD aponta, já o checkout irá mover apenas a HEAD.
Então veja a situação abaixo:
51d7b8b < master < HEAD
|
|
5d50d76 9ef3499 < develop
| |
| ______/
|/
f831ed8
Usando o comando git checkout develop, irá mudar o apontamento da HEAD assim como as três árvores para que estejam sincronizadas com a nova situação.
51d7b8b < master
|
|
5d50d76 9ef3499 < develop < HEAD
| |
| ______/
|/
f831ed8
Conclusão
Neste texto vimos um conceito importante do Git e que usamos muito no dia-a-dia: as três árvores. Vimos também como as três árvores interagem entre si. Também vimos como modificá-las e as diferenças entre os comandos.
Espero que da próxima vez que usar o Git, você possa entender melhor o que está acontecendo com seu repositório.
Até mais.