git log, diff, show e blame: como ler o histórico do projeto no Git
O git log mostra a lista de commits e filtra por autor, data, arquivo e texto; o git diff compara a pasta de trabalho, o índice e quaisquer commits; o git show exibe um commit inteiro; o git blame mostra qual commit alterou por último cada linha de um arquivo.
Quatro comandos resolvem quase todas as perguntas sobre o histórico de um projeto. O git log responde "o que aconteceu", o git diff "o que exatamente mudou", o git show "o que há dentro de um commit específico" e o git blame "quem escreveu esta linha e quando". A seguir, técnicas práticas para cada um, com as flags que realmente se usam no desenvolvimento do dia a dia.
git log: a lista de commits
Sem argumentos, o git log exibe os commits da branch atual do mais novo para o mais antigo: hash SHA-1 completo, autor, data e mensagem. A saída abre num paginador (normalmente o less): espaço — próxima página, /texto — busca, q — sair.
commit 7be41d0c9a2e4f1b8d6c3e5a7f9b0c2d4e6f8a1b
Author: Anna Petrova <anna@example.com>
Date: Mon Oct 5 14:22:10 2026 +0300
feat: cache das respostas da API
Não é preciso copiar o hash inteiro: o Git entende qualquer prefixo único a partir de 4 caracteres; na prática, 7 bastam.
Saída compacta: --oneline
git log --oneline -10
# 7be41d0 (HEAD -> main, origin/main) feat: cache das respostas da API
# 1c02e8f fix: timeout das requisições
# 9a4b3c2 refactor: cliente movido para um módulo
A flag -10 (ou -n 10) limita o número de commits. Entre parênteses ficam os ponteiros: onde estão o HEAD, as branches locais e remotas e as tags.
Grafo de branches: git log --graph
Quem procura "git graph" normalmente quer exatamente isto: visualizar ramificações e merges direto no terminal.
git log --oneline --graph --all
# * 3e1f0aa (feature/auth) feat: login por token
# | * 7be41d0 (HEAD -> main) feat: cache das respostas da API
# |/
# * 1c02e8f fix: timeout das requisições
# * 5d2c7b1 Merge branch 'feature/search'
# |\
# | * 8f3a9e0 feat: busca
# |/
# * 9a4b3c2 refactor: cliente movido para um módulo
--all mostra todas as branches, e não só a atual. Vale criar um alias: git config --global alias.lg "log --oneline --graph --all --decorate", e depois basta git lg.
Formato próprio: --pretty=format
git log --pretty=format:"%h %ad %an %s" --date=short
# 7be41d0 2026-10-05 Anna Petrova feat: cache das respostas da API
Principais placeholders: %h — hash curto, %H — completo, %an — autor, %ae — e-mail dele, %ad — data do autor, %ar — data relativa ("2 days ago"), %s — título da mensagem.
Como filtrar o histórico no git log
Por autor e data
git log --author="Anna"
git log --since="2 weeks ago" --until="2026-10-01"
git log --since=yesterday --oneline
--author aceita expressão regular e busca no nome e no e-mail. O Git entende datas tanto em formato livre quanto em ISO.
Pelo texto da mensagem
git log --grep="fix" -i --oneline
-i torna a busca insensível a maiúsculas e minúsculas. Se o seu time coloca o número da tarefa nas mensagens, assim você encontra rápido todos os commits de uma tarefa.
Por arquivo ou pasta
git log --oneline -- src/api/client.py
git log --follow -p -- src/api/client.py
O traço duplo separa os caminhos das branches. --follow continua o histórico do arquivo através de renomeações (funciona para um arquivo só), -p mostra o patch de cada commit.
Pelo código alterado: a busca "picareta"
git log -S "retry_count" --oneline # commits em que mudou o número de ocorrências da string
git log -G "timeout\s*=" --oneline # commits cujo diff tem linhas que casam com a regex
-S encontra o commit em que uma função ou variável foi adicionada ou removida — indispensável quando você precisa descobrir para onde foi um trecho de código.
Intervalos de commits
git log main..feature # o que está em feature, mas não em main
git log origin/main..HEAD # meus commits locais ainda não enviados
git log --no-merges # sem commits de merge
git log --stat # quais arquivos e quantas linhas mudaram
git diff: o que exatamente mudou
O principal a entender sobre o git diff é quais dois estados ele compara. A confusão aqui é a origem da pergunta "por que o diff está vazio, se eu mudei tudo?".
| Comando | O que compara |
|---|---|
git diff | pasta de trabalho vs. índice (alterações não preparadas) |
git diff --staged (--cached) | índice vs. último commit (o que vai entrar no commit) |
git diff HEAD | pasta de trabalho vs. último commit (tudo junto) |
git diff A B | dois commits, branches ou tags |
git diff A...B | alterações em B desde que ele se separou de A |
Se você fez git add, o git diff comum fica vazio — as alterações já estão no índice; use --staged. Arquivos novos, nunca adicionados, o diff simplesmente não mostra.
Como ler a saída do diff
diff --git a/config.py b/config.py
index 3b18e51..a1c4f02 100644
--- a/config.py
+++ b/config.py
@@ -10,7 +10,7 @@ DEFAULTS = {
"host": "localhost",
- "timeout": 5,
+ "timeout": 30,
"retries": 3,
Linhas com - foram removidas, com + adicionadas, as demais são contexto. O cabeçalho @@ -10,7 +10,7 @@ significa: o trecho começa na linha 10 e ocupa 7 linhas nas versões antiga e nova.
Flags úteis do git diff
git diff --stat main feature # resumo por arquivo
git diff --name-only HEAD~3 # só os nomes dos arquivos alterados
git diff --word-diff # alterações por palavra, prático para textos
git diff -w # ignorar alterações de espaços
git diff main feature -- src/ # limitar a uma pasta
Antes de cada commit, vale olhar o git diff --staged: é a última chance de notar um print de depuração ou um arquivo adicionado sem querer.
git show: um commit inteiro
O git show exibe os metadados do commit e o diff dele em relação ao pai. Sem argumentos, mostra o último commit.
git show 7be41d0
git show HEAD~2 --stat # sem patch, só o resumo
git show v1.2.0 # tag: anotação e commit
git show main:src/config.py # o arquivo como estava no commit
A última forma, <revisão>:<caminho>, é especialmente útil: dá para ver a versão antiga de um arquivo sem mudar para ela. Redirecione a saída para um arquivo e você recupera conteúdo apagado sem checkout.
git blame: quem alterou esta linha
git blame arquivo mostra, para cada linha, o último commit que a alterou, o autor e a data. O nome soa acusatório, mas na prática é uma ferramenta para achar contexto: encontrar o commit, ler a mensagem e entender por que foi feito assim.
git blame -L 40,60 src/api/client.py
# 1c02e8f3 (Anna Petrova 2026-09-28 11:04:51 +0300 41) timeout = settings.timeout
# 9a4b3c2e (Ivan Smirnov 2026-09-14 18:30:02 +0300 42) session = build_session()
-L 40,60— só um intervalo de linhas; também dá para usar-L :nome_da_funcao.-w— ignorar alterações de espaços, para não cair em commits de formatação.-C— rastrear linhas movidas de outros arquivos.--ignore-rev <hash>ou o arquivo.git-blame-ignore-revs— pular commits em massa, como os de formatação automática.
Roteiro típico de investigação: o git blame encontra o commit, o git show mostra ele inteiro e o git log do arquivo dá as alterações vizinhas.
Navegando pelo passado: HEAD~ e HEAD^
Não é só por hash que se referencia um commit. HEAD~1 é o pai do commit atual, HEAD~3 o bisavô. HEAD^2 é o segundo pai de um commit de merge. Nomes de branches, tags e branches remotas também funcionam: origin/main~2. Para ver o projeto inteiro num commit antigo, use git switch --detach <hash>; para voltar, git switch -.
Histórico bom e histórico ruim
Todos esses comandos só são tão úteis quanto o histórico é significativo. Mensagens como "fix", "wip", "mais ajustes" tornam o git log --grep e o git blame inúteis. Um bom commit é uma única alteração lógica com um título que explica o quê e por quê: fix: timeout da API aumentado para 30 s por causa do gateway lento. Daqui a seis meses, é exatamente esse commit que o blame vai encontrar, economizando uma hora sua.
Prática: investigando um bug pelo histórico
Suponha que, depois do último release, as requisições à API começaram a falhar por timeout. Veja como a combinação de comandos leva à causa em poucos minutos.
- Vemos o que entrou no release:
git log v1.4.0..v1.5.0 --oneline. A lista é curta, mas pelos títulos o culpado não é óbvio. - Filtramos pelo arquivo:
git log v1.4.0..v1.5.0 --oneline -- src/api/. Restam dois commits. - Buscamos no código:
git log -S "timeout" --oneline -- src/api/encontra o commit em que o valor do timeout mudou. - Vemos o commit inteiro:
git show 1c02e8f. O diff mostra que o timeout foi reduzido de 30 para 5 segundos, e a mensagem explica: "acelerando a resposta da UI". - Conferimos quem mais mexeu nessa linha:
git blame -L :build_session src/api/client.py.
Daí em diante, a decisão é de engenharia: restaurar o valor, mover o timeout para as configurações ou desfazer o commit com git revert. Já se os commits forem centenas e não der para achar o culpado no olho, entra em cena o git bisect — uma busca binária no histórico que, em poucos passos, aponta o commit que quebrou o comportamento.
Configurações que facilitam a vida
git config --global log.date iso # formato das datas no log
git config --global diff.colorMoved zebra # destacar linhas movidas no diff
git config --global core.pager "less -FRX" # não abrir o paginador para saídas curtas
Se a saída vai ser processada por um script, desative as cores e o paginador: git --no-pager log --format="%H %s" --no-color. Para processamento automático, use o hash completo %H: com o tempo, prefixos curtos podem ficar ambíguos num repositório grande.
Resumo
O git log com os filtros --oneline, --graph, --author, -S e caminhos responde à pergunta "o que aconteceu". O git diff compara a pasta de trabalho, o índice e os commits — o importante é lembrar o que ele compara por padrão. O git show revela um commit ou uma versão antiga de um arquivo, e o git blame leva de uma linha de código à decisão que a originou. A leitura do histórico e a navegação pelos commits são praticadas no curso de Git.
Perguntas frequentes
Como sair do git log?
Pressione q — a saída está aberta no paginador less; para imprimir sem paginador, use git --no-pager log ou a flag -n com um número pequeno.
Como ver o histórico de um arquivo apagado?
Execute git log --oneline -- caminho/do/arquivo (com o traço duplo), e para ver o conteúdo antes da exclusão, git show <hash>^:caminho/do/arquivo.
Como comparar a branch local com a remota?
Primeiro git fetch, depois git diff origin/main ou git log origin/main..HEAD, para ver os seus commits ainda não enviados.
Existe uma visualização gráfica do histórico?
Sim, o Git vem com o gitk, e o grafo do histórico também está embutido no VS Code, nas IDEs JetBrains e nas interfaces web do GitHub e do GitLab.
Por que o git diff não mostra nada?
Provavelmente as alterações já foram adicionadas ao índice com git add — use git diff --staged; arquivos novos não rastreados o git diff não mostra de jeito nenhum.
Este tema é abordado em profundidade, com prática, no curso “Git do zero ao desenvolvedor confiante”.
Ir para o curso