Como o Git Funciona Por Baixo do Capô: Objetos, Árvores e Blobs
Usamos git add, git commit e git push todos os dias. Mas você já olhou dentro da pasta .git?
Entender os componentes internos do Git desmistifica a "mágica" e torna você um usuário muito mais confiante (e melhor em resolver conflitos de merge!).
Em seu núcleo, o Git é um sistema de arquivos endereçável por conteúdo. Idealmente, é um armazenamento chave-valor onde:
- Chave: Hash SHA-1 do conteúdo.
- Valor: O próprio conteúdo.
Vamos explorar os três objetos principais que o Git usa para armazenar seu histórico.
1. O Blob (Binary Large Object)
Quando você faz git add em um arquivo, o Git pega o conteúdo desse arquivo e o armazena como um Blob.
- Não armazena o nome do arquivo, apenas o conteúdo.
- Se dois arquivos tiverem exatamente o mesmo conteúdo, eles compartilham o mesmo Blob (desduplicação!).
Você pode ver isso com git cat-file:
$ echo "hello" | git hash-object -w --stdin ce013625030ba8dba906f756967f9e9ca394464a
Esse hash é a chave.
2. A Árvore (Tree)
Então, onde os nomes de arquivo são armazenados? Em um objeto Tree.
Uma Árvore é como um diretório. Contém uma lista de:
- Permissões de arquivo (ex:
100644) - Tipo de objeto (
bloboutree) - Hash SHA-1
- Nome do arquivo
Pense em uma Árvore como um instantâneo de uma estrutura de pastas.
100644 blob ce0136... hello.txt
100644 blob 9a8b7c... README.md
040000 tree 3b1c2d... src
3. O Commit
Finalmente, temos o objeto Commit.
Um commit é simplesmente um wrapper que aponta para uma Tree específica (o instantâneo do projeto naquele momento) e adiciona metadados:
- Autor e Committer
- Data
- Mensagem do Commit
- Hash do Commit Pai
Como cada commit aponta para seu pai, eles formam uma lista encadeada (ou melhor, um Grafo Acíclico Dirigido - DAG).
tree 3b1c2d...
parent 8a9b0c...
author John Doe <[email protected]>
committer John Doe <[email protected]>
Initial commit
Visualizando a Relação
graph TD subgraph Commit C[Objeto Commit] -->|Aponta para| T[Objeto Tree] C -->|Aponta para| P[Commit Pai] end subgraph Tree T -->|Contém| B1[Blob: hello.txt] T -->|Contém| B2[Blob: README.md] T -->|Contém| T2[Tree: src] end subgraph Blob B1 -->|Conteúdo| Content1["hello"] B2 -->|Conteúdo| Content2["# Readme"] end
Juntando Tudo
Quando você executa git commit:
- O Git cria Blobs para os arquivos alterados.
- O Git cria uma Tree representando a estrutura do projeto.
- O Git cria um objeto Commit apontando para essa Árvore e para o commit anterior.
- O ponteiro da branch (ex:
main) move-se para este novo hash de commit.
Por Que Isso Importa
- Git é imutável: Uma vez que um objeto é criado, ele nunca muda. "Modificar" o histórico na verdade cria novos objetos.
- Branching barato: Uma branch é apenas um pequeno arquivo de 40 bytes contendo um hash de commit. Criar uma branch não custa nada.
- Integridade: O hash SHA-1 garante que se um único bit de dados mudar, o ID muda. É impossível corromper o histórico sem detecção.
Na próxima vez que você estiver preso no estado "detached HEAD", lembre-se: você está apenas apontando para um objeto Commit que não está rotulado com um nome de branch!
Explore ferramentas relacionadas
Experimente estas ferramentas gratuitas do Pockit