1. O que é o Git?

O Git é um sistema de controle de versão distribuído que permite aos desenvolvedores gerenciar e acompanhar alterações em arquivos de projetos. Ele armazena imagens do sistema de arquivos em miniatura, salvando apenas referências para arquivos não alterados, o que torna suas operações rápidas e eficientes.

2. Os Três Estados

Os arquivos no Git podem estar em três estados principais:

  • Modified: O arquivo foi modificado.
  • Staged: O arquivo está preparado para o próximo commit.
  • Committed: O arquivo foi salvo no banco de dados local.

3. Como o Git Garante a Integridade dos Arquivos?

Tudo no Git passa por uma soma de verificações (checksum) antes de ser armazenado e é referenciado por esse checksum. Isso torna impossível alterar o conteúdo de qualquer arquivo ou pasta sem que o Git saiba. O mecanismo de verificação é chamado de hash SHA-1, uma sequência de 40 caracteres hexadecimais (ex.: fa7064f204b8bb4ade5eed778ee1e278e54559d2), calculada com base no conteúdo de uma estrutura de arquivo ou diretório no Git.

Para gerar um hash SHA-1 no linux:

echo -e 'Hello, World!' | sha1sum
60fde9c2310b0d4cad4dab8d126b04387efba289

E caso seja removido algum caractere da string?

echo -e 'Hello, World' | sha1sum
4ab299c8ad6ed14f31923dd94f8b5f5cb89dfb54

Percebe-se que o valor mudou. Dessa forma, o Git garante a integridade dos arquivos.

4. Estrutura Básica de Pastas

Ao iniciar um projeto no Git, é criado o diretório de trabalho .git, que contém a seguinte estrutura:

git init
ls -F1 .git
Estrutura Descrição
HEAD Aponta para a branch atual.
config Contém opções de configuração específicas do projeto.
description Usado pelo GitWeb, pode ser ignorado.
hooks Scripts que podem ser executados em operações como commit e merge.
info Mantém o arquivo exclude global para padrões ignorados.
objects Guarda todo o conteúdo do banco de dados de objetos.
refs Guarda referências para objetos de commit.
index Guarda informações da staging area.

Os principais diretórios são: objects, refs e HEAD.

5. Plumbing vs. Porcelain

Existem duas categorias de comandos no Git:

  • Plumbing: Comandos de baixo nível, que oferecem acesso ao funcionamento interno do Git.
  • Porcelain: Comandos amigáveis usados no dia a dia.

Para os comandos porcelain, este guia expõe os comandos do dia a dia: Git Cheatsheet

6. Objetos

O Git utiliza uma estrutura de dados baseada em objetos para armazenar informações sobre o repositório. Existem três tipos principais de objetos:

  1. Blobs
  2. Trees
  3. Commits

6.1 Blobs

Um blob é um objeto que contém o conteúdo de um arquivo. Ele não armazena informações sobre o nome do arquivo ou o diretório em que está localizado.

  • Para criar um blob, utiliza-se o comando plumbing: hash-object:
echo 'Hello, World!' | git hash-object -w --stdin
8ab686eafeb1f44702738c8b0f24f2567c36da6d
  • -w: Informa ao hash-object para armazenar o objeto.
  • --stdin: Informa ao comando para ler o conteúdo do stdin. Se você não especificar, o parâmetro é o caminho para um arquivo.

O comando hash-object não apenas faz o hash do conteúdo, ele adiciona algumas informações importantes para o funcionamento interno, como: o tipo do objeto, o tamanho do conteúdo e o delimitador.

Para o exemplo Hello, World! fica dessa forma: blob 14\0Hello, World!.

Então, dessa obtêm-se o mesmo valor nesses dois casos:

echo 'Hello, World!' | git hash-object -w --stdin
8ab686eafeb1f44702738c8b0f24f2567c36da6d
echo -e 'blob 14\0Hello, World!' | sha1sum
8ab686eafeb1f44702738c8b0f24f2567c36da6d

Para visualizar como o Git armazenou os dados:

find .git/objects -type f
.git/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d

Para recuperar o conteúdo do arquivo:

git cat-file -p 
Hello, World!

6.2 Trees

Os objetos tree resolvem o problema de armazenar o nome do arquivo e também permitem armazenar de forma conjunta um grupo de arquivos.

Para agrupar os objetos blobs na área de staging em uma estrutura de árvore (tree):

  1. Criar um arquivo:
echo 'Hello, World!' > hello-world.txt
  1. Gerar um hash SHA-1 para esse arquivo:
git hash-object -w hello-world.txt
  1. Usar o comando plumbing update-index para adicionar o blob à área de staging e dar um nome a ele:
git update-index \
--add \
--cacheinfo 100644 \
 \
hello-world.txt
  • --add: Adiciona o blob à área de staging
  • --cacheinfo ,,: Registra um arquivo que ainda não está no diretório de trabalho (.git).

Alguns dos Modos UNIX Válidos no Git:

  • 100644: Arquivo normal (permissões de leitura e escrita para o proprietário, leitura para o grupo e outros).
  • 100755: Arquivo executável (permissões de leitura, escrita e execução para o proprietário, leitura e execução para o grupo e outros).
  • 120000: Link simbólico (um ponteiro para outro arquivo ou diretório).

E pode ser adicionado outros blobs à área de staging, após isso, já pode ser criado um objeto tree com o comando plumbing write-tree:

git write-tree
43b603fc7207c1f84ba7e65934181336a0e68b92

Para visualizar, basta utilizar o comando cat-file:

git cat-file -p 
100644 blob 8ab686eafeb1f44702738c8b0f24f2567c36da6d    hello-world.txt

Mas ainda falta algo: Onde está as informações do autor, a data da modificação e a mensagem?

6.3 Commits

Os objetos commit armazenam informações essenciais sobre as alterações feitas no repositório. Cada objeto commit contém:

  • Um ponteiro para um objeto tree, que representa o estado do diretório no momento do commit.
  • Informações sobre o autor do commit (nome e e-mail).
  • A data e hora em que o commit foi feito (timestamp).
  • Uma mensagem de commit que descreve as alterações realizadas.
  • Um ponteiro para o commit anterior (ou commits, no caso de merges), permitindo a construção de um histórico de alterações.

O comando plumbing commit-tree é o encarregado para criar um objeto commit:

git commit-tree 43b60 -m 'mensagem do commit'
21cc9199e22c5eb10731608f88d99a3bb147b2eb

Para visualizar o objeto commit, basta utilzar o comando plumbing cat-file:

git cat-file -p 
tree 43b603fc7207c1f84ba7e65934181336a0e68b92
author Gustavo Vasconcelos  1744048435 -0300
committer Gustavo Vasconcelos  1744048435 -0300

mensagem do commit

É possível também aninhar commits passando o hash do commit anterior para que eles tenham uma linha do tempo:

  1. Gerar hash:
'Para um segundo commit' | git hash-object -w --stdin
fa538a1ae921322d20bc39c86135ec528c261e1f
  1. Adicionar a área de staging:
git update-index \
--add \
--cacheinfo 100644 \
fa538a1ae921322d20bc39c86135ec528c261e1f \
hello-world.txt
  1. Criar o objeto tree:
git write-tree
a687f3f49545dba2ff9a424891d445b59c107893
  1. Criar o objeto commit referenciando o commit anterior:
git commit-tree a687f -m 'segundo commit' -p 21cc9
a3e6aedbac6c7e8c33db0cd6490658c8b3519dd3
  • -p: Usa para referenciar o commit anterior.

Pode-se utilizar o comando porcelain log para visualizar o histórico:

git log --stat 6aa7e
commit 6aa7e37d8ad583deaee0783462fb62a241c5b9c1
Author: Gustavo Vasconcelos 
Date:   Mon Apr 7 15:17:01 2025 -0300

    segundo commit

 hello-world.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

commit 21cc9199e22c5eb10731608f88d99a3bb147b2eb
Author: Gustavo Vasconcelos 
Date:   Mon Apr 7 14:53:55 2025 -0300

    mensagem do commit

 hello-world.txt | 1 +
 1 file changed, 1 insertion(+)

Basicamente este é o ciclo de vida de uma modificação até um commit no Git, tudo baseado em chave-valor e armazenado no banco de dados local, e agora dá pra entender como o git busca tão rapidamente alterações feitas em um projeto, independente do tamanho ou do tempo que o projeto existe.