O staging area: entendendo o índice do Git

O que é o staging area (índice)?

O staging area, também conhecido como índice ou cache, é uma área intermediária no Git que fica entre o diretório de trabalho (working directory) e o repositório local. Ele funciona como uma "mesa de preparação" onde você organiza e revisa as mudanças antes de confirmá-las no histórico do projeto.

O Git trabalha com três árvores principais:

  1. Working Directory — seus arquivos reais no disco, onde você edita o código
  2. Staging Area (Índice) — uma área de preparação armazenada no arquivo .git/index
  3. Repositório Local — o banco de dados de objetos do Git (commits, árvores, blobs)

O arquivo .git/index é o coração do staging area. Ele contém metadados sobre cada arquivo rastreado, incluindo o nome, modo de permissão, timestamp e, mais importante, o hash SHA-1 do blob que representa o conteúdo do arquivo naquele momento.

# Estrutura simplificada do .git/index
# Cada entrada contém:
# - Metadados (ctime, mtime, dev, ino, mode, uid, gid, size)
# - SHA-1 do blob
# - Nome do arquivo

Por que o staging area existe?

O staging area resolve um problema fundamental no controle de versão: como separar mudanças logicamente independentes que você fez simultaneamente.

Imagine que você está trabalhando em um arquivo e fez duas alterações não relacionadas: uma correção de bug e uma melhoria de desempenho. Sem o staging, você seria forçado a commitá-las juntas. Com o staging, você pode:

  • Selecionar partes específicas de arquivos para commit
  • Criar commits lógicos e atômicos sem misturar alterações não relacionadas
  • Revisar e validar mudanças antes de confirmá-las no histórico
# Exemplo: duas alterações no mesmo arquivo
# Arquivo: app.js

# Linha 10: Correção de bug
# Antes:  const result = calculate(x, y) // bug: ordem errada
# Depois: const result = calculate(y, x)

# Linha 25: Melhoria de desempenho
# Antes:  for(let i=0; i<data.length; i++) { ... }
# Depois: data.forEach(item => { ... })

Operações básicas com o staging area

git add — Movendo arquivos para o staging

# Adicionar um arquivo específico
$ git add index.html

# Adicionar todos os arquivos modificados e não rastreados
$ git add .

# Adicionar múltiplos arquivos específicos
$ git add src/main.js src/utils.js

git status — Visualizando o estado atual

$ git status

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   index.html
        new file:   src/utils.js

Changes not staged for commit:
        modified:   style.css

Untracked files:
        README.md

git restore --staged — Removendo do staging

# Remover um arquivo do staging (mantendo as alterações no working directory)
$ git restore --staged index.html

# Remover todos os arquivos do staging
$ git restore --staged .

Inspecionando o staging area

git diff --cached — Diferenças entre staging e último commit

$ git diff --cached

diff --git a/index.html b/index.html
index e69de29..d95f3ad 100644
--- a/index.html
+++ b/index.html
@@ -0,0 +1,3 @@
+<html>
+  <body>Hello World</body>
+</html>

git diff — Diferenças entre working directory e staging

$ git diff

diff --git a/style.css b/style.css
index 4b825dc..a3f5a6b 100644
--- a/style.css
+++ b/style.css
@@ -1 +1,2 @@
 body { margin: 0; }
+.highlight { color: red; }

git ls-files --stage — Listando o conteúdo detalhado do índice

$ git ls-files --stage

100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       index.html
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0       src/main.js
100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0       src/utils.js

Gerenciamento avançado do staging

git add -p — Adicionando partes específicas

O modo patch permite selecionar partes específicas (hunks) de um arquivo modificado:

$ git add -p style.css

diff --git a/style.css b/style.css
index 4b825dc..a3f5a6b 100644
--- a/style.css
+++ b/style.css
@@ -1 +1,3 @@
 body { margin: 0; }
+.header { background: blue; }
+.footer { color: gray; }
Stage this hunk [y,n,q,a,d,/,s,e,?]? y

@@ -3,0 +5,2 @@
+.highlight { color: red; }
+.warning { color: orange; }
Stage this hunk [y,n,q,a,d,/,s,e,?]? n

git add -i — Modo interativo

$ git add -i

*** Commands ***
  1: status   2: update   3: revert   4: add untracked
  5: patch    6: diff     7: quit     8: help
What now> 1

           staged     unstaged path
  1:    unchanged        +4/-0 index.html
  2:    unchanged        +2/-1 src/main.js

.gitignore e o staging

Arquivos listados no .gitignore não aparecem como untracked e não podem ser adicionados ao staging acidentalmente:

# .gitignore
node_modules/
*.log
.env
dist/

Staging e o fluxo de trabalho diário

Ciclo típico vs. git commit -a

# Ciclo explícito (recomendado)
$ git add src/main.js
$ git add tests/main.test.js
$ git commit -m "Adiciona validação de entrada"

# Ciclo abreviado (menos controle)
$ git commit -a -m "Adiciona validação de entrada"

Staging parcial para commits atômicos

# Situação: você editou três arquivos para duas tarefas diferentes

# Tarefa 1: Correção de bug no login
$ git add src/login.js
$ git commit -m "Corrige validação de email no login"

# Tarefa 2: Nova funcionalidade de relatório
$ git add src/report.js src/utils.js
$ git commit -m "Adiciona geração de relatório mensal"

Erros comuns e como corrigir

# Erro: adicionou arquivo grande acidentalmente
$ git add bigfile.zip
$ git restore --staged bigfile.zip
$ echo "*.zip" >> .gitignore

# Erro: adicionou arquivo com senhas
$ git add .env
$ git restore --staged .env
$ echo ".env" >> .gitignore

O staging area em operações de merge e reset

Staging durante conflitos de merge

Quando ocorre um conflito de merge, o índice armazena até três versões do arquivo:

$ git ls-files --stage -u

100644 2a7d6... 1  conflicted.txt  # versão base (ancestral comum)
100644 8c3f2... 2  conflicted.txt  # versão do branch atual (HEAD)
100644 4e9b1... 3  conflicted.txt  # versão do branch sendo mesclado

git reset e o staging

# --soft: move HEAD apenas, staging e working directory intactos
$ git reset --soft HEAD~1

# --mixed (padrão): move HEAD e staging, working directory intacto
$ git reset HEAD~1

# --hard: move HEAD, staging e working directory
$ git reset --hard HEAD~1

git checkout e git switch

# Trocar de branch atualiza o staging com os arquivos do novo branch
$ git switch feature-branch

# Se houver mudanças não commitadas no staging, o Git pode impedir a troca
$ git switch main
error: Your local changes to the following files would be overwritten...

Boas práticas e dicas finais

  1. Commits atômicos: Use o staging para separar mudanças lógicas. Cada commit deve representar uma única mudança coesa.

  2. Revise antes de commitar: Sempre execute git status e git diff --cached antes de fazer commit.

  3. Evite git commit -a em projetos complexos: O staging explícito com git add dá controle total sobre o que entra em cada commit.

  4. Use git add -p para commits cirúrgicos: Quando você fez várias alterações no mesmo arquivo, o modo patch permite selecionar apenas as partes relevantes.

  5. Mantenha o staging limpo: Se você mudar de ideia sobre uma alteração, use git restore --staged para removê-la do staging antes do commit.

# Fluxo recomendado para commits limpos
$ git status                    # Verificar o que foi modificado
$ git diff                      # Revisar mudanças não staged
$ git add -p src/feature.js     # Adicionar partes específicas
$ git diff --cached             # Revisar o que será commitado
$ git commit -m "Mensagem descritiva"

O staging area é uma das ferramentas mais poderosas do Git para manter um histórico limpo e compreensível. Dominá-lo transforma seu fluxo de trabalho de "commits bagunçados" para "commits que contam uma história clara do seu projeto".

Referências