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
60fde9c2310b0d4cad4dab8d126b04387efba289E caso seja removido algum caractere da string?
echo -e 'Hello, World' | sha1sum
4ab299c8ad6ed14f31923dd94f8b5f5cb89dfb54Percebe-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:
- Blobs
- Trees
- 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 aohash-objectpara 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
8ab686eafeb1f44702738c8b0f24f2567c36da6dPara visualizar como o Git armazenou os dados:
find .git/objects -type f
.git/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6dPara 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):
- Criar um arquivo:
echo 'Hello, World!' > hello-world.txt- Gerar um hash SHA-1 para esse arquivo:
git hash-object -w hello-world.txt- Usar o comando plumbing
update-indexpara 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
43b603fc7207c1f84ba7e65934181336a0e68b92Para visualizar, basta utilizar o comando cat-file:
git cat-file -p
100644 blob 8ab686eafeb1f44702738c8b0f24f2567c36da6d hello-world.txtMas 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'
21cc9199e22c5eb10731608f88d99a3bb147b2ebPara 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:
- Gerar hash:
'Para um segundo commit' | git hash-object -w --stdin
fa538a1ae921322d20bc39c86135ec528c261e1f- Adicionar a área de staging:
git update-index \
--add \
--cacheinfo 100644 \
fa538a1ae921322d20bc39c86135ec528c261e1f \
hello-world.txt- Criar o objeto tree:
git write-tree
a687f3f49545dba2ff9a424891d445b59c107893- 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.