Git e fluxo de trabalho
O modelo de commits e branches, e as estratégias de integração que as equipes usam.
Os comandos desta página foram executados com o Git 2.43, em repositórios de teste.
O fluxo do dia a dia
# Atualizar a main e começar uma branch curta
git switch main
git pull --rebase
git switch -c corrige-frete-interior
# Trabalhar em commits pequenos, escolhendo o que entra em cada um
git add -p # revisa e escolhe trecho por trecho
git commit -m "Corrige frete para o interior quando o CEP tem traço"
# Trazer as mudanças da main antes de abrir o PR
git fetch origin
git rebase origin/main # commits ainda não compartilhados: rebase é seguro
# Enviar
git push -u origin corrige-frete-interior
# depois de um rebase numa branch já enviada:
git push --force-with-leasegit add -p é a forma mais simples de manter commits coesos: o Git mostra cada trecho alterado e pergunta se ele entra no commit. Com código gerado por IA, que costuma mexer em vários assuntos de uma vez, ele ajuda a separar a mudança em partes que fazem sentido sozinhas.
Resolver um conflito
Um conflito acontece quando duas mudanças alteram as mesmas linhas. O Git marca o trecho no arquivo:
<<<<<<< HEAD
const FRETE_INTERIOR = 25;
=======
const FRETE_INTERIOR = 30;
>>>>>>> corrige-frete-interior| Passo | Comando |
|---|---|
| Ver quais arquivos têm conflito | git status |
| Editar o arquivo e decidir o conteúdo final | Editor; remover os marcadores |
| Marcar como resolvido | git add arquivo |
| Continuar o rebase ou o merge | git rebase --continue ou git commit |
| Desistir e voltar ao estado anterior | git rebase --abort ou git merge --abort |
Resolver um conflito não é escolher um dos lados: é entender as duas intenções e escrever o resultado que atende ambas. Depois de resolver, rode os testes antes de continuar.
Encontrar o commit que introduziu um defeito
git bisect faz uma busca binária no histórico: você informa um commit bom e um ruim, e o Git testa o commit do meio, depois o meio da metade restante, e assim por diante. Com git bisect run, um script faz o teste sozinho.
No teste, um repositório com 20 commits tinha um defeito introduzido no commit 14, na função de soma:
# teste_soma.py: sai com erro se o defeito estiver presente
from calc import soma
assert soma(2, 3) == 5git bisect start HEAD HEAD~19 # ruim: agora; bom: 19 commits atrás
git bisect run python3 -B teste_soma.py
# ...
# Commit 14 is the first bad commit
git bisect reset # volta para onde estavaO Git encontrou o commit 14 rodando o teste cinco vezes, e não vinte. Com mil commits, seriam cerca de dez.
| Linguagem | Comando de teste seguro para o bisect |
|---|---|
| Node | npm ci && npx vitest run caminho/do/teste (dependências podem mudar entre commits) |
| Python | python3 -B -m pytest caminho/do/teste |
| Go | go test ./pacote/ -run NomeDoTeste -count=1 (-count=1 ignora o cache de testes) |
| Java | mvn -q clean test -Dtest=NomeDoTeste |
| C# | dotnet test --no-incremental --filter NomeDoTeste |
Ligar cada deploy ao commit
Quando algo quebra em produção, a primeira pergunta é "qual versão está rodando?". Expor o commit numa rota responde na hora e permite usar git log e git bisect a partir dali.
O commit entra na imagem no momento do build:
ARG GIT_COMMIT=desconhecido
ENV GIT_COMMIT=$GIT_COMMITdocker build --build-arg GIT_COMMIT=$(git rev-parse --short HEAD) -t loja/api:$(git rev-parse --short HEAD) .E a aplicação o expõe:
import http from "node:http";
const versao = JSON.stringify({ commit: process.env.GIT_COMMIT ?? "desconhecido" });
http
.createServer((req, res) => {
if (req.url === "/versao") {
res.setHeader("Content-Type", "application/json");
return res.end(versao);
}
// ... rotas da aplicação
})
.listen(3000);import os
from fastapi import FastAPI
app = FastAPI()
COMMIT = os.environ.get("GIT_COMMIT", "desconhecido")
@app.get("/versao")
def versao():
return {"commit": COMMIT}package main
import (
"encoding/json"
"net/http"
)
// Preenchida na compilação:
// go build -ldflags "-X main.commit=$(git rev-parse --short HEAD)"
var commit = "desconhecido"
func main() {
http.HandleFunc("GET /versao", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"commit": commit})
})
http.ListenAndServe(":3000", nil)
}import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
public class Servidor {
public static void main(String[] args) throws Exception {
var commit = System.getenv().getOrDefault("GIT_COMMIT", "desconhecido");
var versao = ("{\"commit\":\"" + commit + "\"}").getBytes(StandardCharsets.UTF_8);
var servidor = HttpServer.create(new InetSocketAddress(3000), 0);
servidor.createContext("/versao", ex -> {
ex.getResponseHeaders().set("Content-Type", "application/json");
ex.sendResponseHeaders(200, versao.length);
try (var saida = ex.getResponseBody()) {
saida.write(versao);
}
});
servidor.start();
}
}var app = WebApplication.Create(args);
var commit = Environment.GetEnvironmentVariable("GIT_COMMIT") ?? "desconhecido";
app.MapGet("/versao", () => new { commit });
app.Run();O que muda de uma linguagem para outra
| Onde o commit entra | Alternativa comum | |
|---|---|---|
| Node | Variável de ambiente definida no build da imagem | Gravar num arquivo versao.json durante o build |
| Python | Variável de ambiente | Idem |
| Go | Gravado no próprio binário com -ldflags -X | O Go 1.18+ grava a revisão do Git sozinho, lida com debug.ReadBuildInfo(), quando compilado dentro do repositório |
| Java | Variável de ambiente | O plugin git-commit-id do Maven, exposto pelo Actuator do Spring Boot em /actuator/info |
| C# | Variável de ambiente | A propriedade SourceRevisionId do MSBuild, que entra na versão informativa do assembly |
No Go, o commit fica no binário e não depende da configuração do container: conferimos que o mesmo programa imprime "desconhecido" sem a opção e o hash informado com -ldflags "-X main.commit=3f9c2e1".
Com o commit em cada resposta de /versao, nos logs e nas métricas, investigar um incidente começa por git log <commit-anterior>..<commit-atual>, e não por uma adivinhação.
O mínimo do .gitignore de um backend
# Segredos e configuração local
.env
.env.*
!.env.example
*.pem
*.key
# Dependências e artefatos de build
node_modules/
__pycache__/
.venv/
bin/
obj/
target/
dist/
# Editor e sistema
.idea/
.vscode/
.DS_StoreO !.env.example mantém no repositório um arquivo de exemplo, com os nomes das variáveis e valores fictícios, que documenta a configuração sem expor nada. E um .gitignore não remove arquivos que já foram commitados: para esses, git rm --cached arquivo e, se forem segredos, troque-os.
Lista de revisão
Branches e PRs
- As branches duram poucos dias e são integradas com frequência?
- Os PRs são pequenos e focados num assunto?
- A forma de juntar (squash, rebase ou merge) é sempre a mesma?
- A
mainé protegida contra push forçado, com revisão e testes obrigatórios?
Commits
- Cada commit trata de um assunto e passa nos testes?
- As mensagens explicam o motivo da mudança?
- Commits já compartilhados são desfeitos com
revert, e não reescritos?
Segredos e rastreabilidade
.enve arquivos de credenciais estão no.gitignore?- Há verificação automática de segredos antes do commit e na integração contínua?
- Existe um procedimento para trocar segredos que tenham sido commitados?
- Cada deploy expõe o commit de origem?
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Praticar merge, rebase e conflitos
Quero praticar as operações do Git num repositório de teste, sem medo de estragar nada. Vamos em etapas, com os comandos para eu rodar: 1. Criar um repositório e uma branch feature com dois commits, e um commit novo na main. 2. Juntar com merge e ver o grafo com git log --graph. 3. Recriar o cenário e juntar com rebase, e ver o grafo. 4. Recriar e juntar com squash. 5. Provocar um conflito, resolvê-lo e continuar. 6. Fazer um reset --hard, perder um commit e recuperá-lo pelo reflog. 7. Reverter um commit com git revert. Em cada etapa, peça que eu preveja o grafo antes de rodar o comando.
Encontrar um defeito com git bisect
Quero aprender a usar o git bisect para encontrar o commit que introduziu um defeito. Pergunte qual linguagem e ferramenta de testes eu uso. Explique: 1. Como a busca binária funciona e quantos passos ela leva para 100 e para 1.000 commits. 2. O bisect manual, com git bisect good e bad. 3. O git bisect run com um script de teste que sai com erro quando o defeito aparece. 4. Como escrever esse script na minha linguagem sem ser enganado por caches de compilação, de dependências ou de testes. 5. O que fazer com commits que não compilam (git bisect skip). Depois me ajude a montar um repositório de teste com o defeito em um commit no meio do histórico e a encontrá-lo.
Revisar a configuração do repositório
Quero revisar a configuração do meu repositório Git e do servidor (GitHub, GitLab ou outro). Pergunte qual servidor eu uso e me peça para colar o .gitignore e descrever as regras da branch principal. Verifique: 1. O .gitignore exclui segredos, dependências e artefatos de build da minha linguagem? 2. A branch principal está protegida contra push forçado e exclusão? 3. PRs exigem revisão e testes passando antes de integrar? 4. A forma de juntar PRs está definida? 5. Há verificação automática de segredos? 6. Cada deploy está ligado a um commit, com a versão exposta pela aplicação? Para cada item faltando, mostre como configurar.