{
 "grupo": "git",
 "descricao": "Git e versionamento",
 "itens": [
  {
   "id": "git-desfazer-commit",
   "nome": "Desfazer o último commit sem perder o trabalho",
   "receita": "git reset --soft HEAD~1 (mantém staged); --mixed tira do stage; nunca --hard sem git stash antes",
   "prova": "git status mostra as mudanças ainda lá"
  },
  {
   "id": "git-corrigir-mensagem",
   "nome": "Corrigir a mensagem do último commit",
   "receita": "git commit --amend -m \"nova\" (só se ainda não foi enviado)",
   "prova": "git log -1 mostra a nova mensagem"
  },
  {
   "id": "git-guardar-trabalho",
   "nome": "Guardar trabalho em andamento",
   "receita": "git stash push -u -m \"nome\"; voltar com git stash pop",
   "prova": "git status limpo e stash list com o item"
  },
  {
   "id": "git-nova-branch",
   "nome": "Criar branch a partir da main atualizada",
   "receita": "git switch main && git pull --rebase && git switch -c tipo/assunto",
   "prova": "git branch --show-current"
  },
  {
   "id": "git-conflito",
   "nome": "Resolver conflito de merge",
   "receita": "git status lista; abra cada arquivo, ache <<<<<<<, decida a intenção dos dois lados, git add, continue",
   "prova": "grep -r '<<<<<<<' vazio e teste verde"
  },
  {
   "id": "git-rebase-limpo",
   "nome": "Atualizar branch com rebase",
   "receita": "git fetch && git rebase origin/main; em conflito, resolva e git rebase --continue",
   "prova": "git log --oneline --graph curto e linear"
  },
  {
   "id": "git-achar-bug",
   "nome": "Achar o commit que quebrou (bisect)",
   "receita": "git bisect start; bisect bad; bisect good <sha>; rode o teste a cada passo; git bisect reset",
   "prova": "SHA culpado e o diff dele"
  },
  {
   "id": "git-quem-mudou",
   "nome": "Ver quem mudou uma linha e por quê",
   "receita": "git blame -L a,b arquivo; git log -S\"texto\" -- arquivo",
   "prova": "SHA e mensagem do commit"
  },
  {
   "id": "git-trazer-commit",
   "nome": "Levar um commit para outra branch",
   "receita": "git cherry-pick <sha>; conflito: resolva e --continue",
   "prova": "git log mostra o commit novo"
  },
  {
   "id": "git-limpar-branches",
   "nome": "Apagar branches já mescladas",
   "receita": "git branch --merged main ",
   "prova": " grep -v main "
  },
  {
   "id": "git-voltar-arquivo",
   "nome": "Restaurar um arquivo de outro commit",
   "receita": "git restore --source=<sha> -- caminho",
   "prova": "git diff mostra só o arquivo esperado"
  },
  {
   "id": "git-remover-segredo",
   "nome": "Segredo foi commitado",
   "receita": "REVOGUE a chave agora; depois git filter-repo ou BFG; force-push só com o aval do dono",
   "prova": "rg pela chave em todo o histórico vazio"
  },
  {
   "id": "git-gitignore",
   "nome": "Parar de rastrear arquivo",
   "receita": "adicione ao .gitignore e git rm --cached caminho",
   "prova": "git status não lista mais o arquivo"
  },
  {
   "id": "git-diff-util",
   "nome": "Ler o diff antes de commitar",
   "receita": "git diff --stat, depois git diff --staged; confira se não há .env, log ou debug esquecido",
   "prova": "resumo do diff lido e sem sobras"
  },
  {
   "id": "git-tag-release",
   "nome": "Marcar versão",
   "receita": "git tag -a v1.2.0 -m \"nota\"; git push origin v1.2.0 (só com pedido)",
   "prova": "git tag -l mostra"
  },
  {
   "id": "git-worktree",
   "nome": "Trabalhar em duas branches ao mesmo tempo",
   "receita": "git worktree add ../pasta branch",
   "prova": "git worktree list"
  },
  {
   "id": "git-descrever-pr",
   "nome": "Escrever descrição de PR",
   "receita": "título curto; o que mudou; por que; como testar; risco; screenshot se UI",
   "prova": "um revisor entende sem abrir o chat"
  },
  {
   "id": "git-historico-arquivo",
   "nome": "Ver a história de um arquivo",
   "receita": "git log --follow -p -- caminho",
   "prova": "commits listados com o diff"
  },
  {
   "id": "git-submodulo",
   "nome": "Atualizar submódulo",
   "receita": "git submodule update --init --recursive",
   "prova": "git submodule status sem '-'"
  },
  {
   "id": "git-reflog",
   "nome": "Recuperar commit perdido",
   "receita": "git reflog; git switch -c recuperada <sha>",
   "prova": "branch nova com o commit"
  },
  {
   "id": "git-desfazer-commit-ja-enviado",
   "nome": "Desfazer commit já enviado",
   "receita": "git revert SHA cria um commit que anula o anterior; nunca reescreva história compartilhada",
   "prova": "git log mostra o revert e o código voltou ao estado anterior"
  },
  {
   "id": "git-descartar-mudancas-nao-commitadas-de-um-arquivo",
   "nome": "Descartar mudanças não commitadas de um arquivo",
   "receita": "git restore caminho (ou git checkout -- caminho em versões antigas); confira com git diff antes",
   "prova": "git status não lista mais o arquivo"
  },
  {
   "id": "git-tirar-um-arquivo-do-stage",
   "nome": "Tirar um arquivo do stage",
   "receita": "git restore --staged caminho mantém a alteração no disco",
   "prova": "git status mostra o arquivo como não preparado"
  },
  {
   "id": "git-commitar-so-parte-de-um-arquivo",
   "nome": "Commitar só parte de um arquivo",
   "receita": "git add -p e escolha os trechos com y/n/s",
   "prova": "git diff --staged mostra apenas os trechos escolhidos"
  },
  {
   "id": "git-dividir-um-commit-grande-em-dois",
   "nome": "Dividir um commit grande em dois",
   "receita": "git reset HEAD~1, depois git add -p e dois commits separados",
   "prova": "git log mostra dois commits menores"
  },
  {
   "id": "git-juntar-commits-antes-do-pr",
   "nome": "Juntar commits antes do PR",
   "receita": "git rebase -i HEAD~N e troque pick por squash ou fixup nos que sobram",
   "prova": "o PR mostra a quantidade de commits desejada"
  },
  {
   "id": "git-autoajustar-commit-anterior",
   "nome": "Autoajustar commit anterior",
   "receita": "git commit --fixup SHA e depois git rebase -i --autosquash",
   "prova": "o ajuste entrou no commit original"
  },
  {
   "id": "git-reordenar-commits",
   "nome": "Reordenar commits",
   "receita": "git rebase -i e mude a ordem das linhas; resolva os conflitos se aparecerem",
   "prova": "git log mostra a nova ordem"
  },
  {
   "id": "git-adicionar-arquivo-esquecido-ao-ultimo-commit",
   "nome": "Adicionar arquivo esquecido ao último commit",
   "receita": "git add arquivo && git commit --amend --no-edit (antes do push)",
   "prova": "git show --stat inclui o arquivo"
  },
  {
   "id": "git-mudar-o-autor-do-ultimo-commit",
   "nome": "Mudar o autor do último commit",
   "receita": "git commit --amend --author=\"Nome <email>\" --no-edit",
   "prova": "git log -1 mostra o autor novo"
  },
  {
   "id": "git-trocar-o-e-mail-configurado",
   "nome": "Trocar o e-mail configurado",
   "receita": "git config user.email \"x@y\" por repositório, --global para todos",
   "prova": "git config user.email devolve o valor"
  },
  {
   "id": "git-forcar-push-com-seguranca",
   "nome": "Forçar push com segurança",
   "receita": "git push --force-with-lease nunca --force puro, e só em branch sua",
   "prova": "o push passa e recusa se alguém enviou algo novo"
  },
  {
   "id": "git-atualizar-sem-criar-commit-de-merge",
   "nome": "Atualizar sem criar commit de merge",
   "receita": "git pull --rebase (ou configure pull.rebase true)",
   "prova": "git log --graph mostra história linear"
  },
  {
   "id": "git-ver-o-que-vai-ser-enviado",
   "nome": "Ver o que vai ser enviado",
   "receita": "git log @{u}..HEAD --oneline e git diff @{u}",
   "prova": "a lista bate com o que se espera enviar"
  },
  {
   "id": "git-ver-o-que-chegou-do-remoto",
   "nome": "Ver o que chegou do remoto",
   "receita": "git fetch e git log HEAD..@{u} --oneline",
   "prova": "lista dos commits novos antes de integrar"
  },
  {
   "id": "git-comparar-duas-branches",
   "nome": "Comparar duas branches",
   "receita": "git diff main...minha-branch para mudanças desde o ponto de partida",
   "prova": "o diff mostra só o que a branch trouxe"
  },
  {
   "id": "git-listar-arquivos-mudados",
   "nome": "Listar arquivos mudados",
   "receita": "git diff --name-status main...HEAD",
   "prova": "lista com A/M/D por arquivo"
  },
  {
   "id": "git-estatistica-de-mudancas",
   "nome": "Estatística de mudanças",
   "receita": "git diff --stat e git log --shortstat",
   "prova": "linhas adicionadas e removidas por arquivo"
  },
  {
   "id": "git-buscar-quando-uma-string-entrou",
   "nome": "Buscar quando uma string entrou",
   "receita": "git log -S\"texto\" --oneline (ou -G para regex)",
   "prova": "o commit que introduziu ou removeu o texto"
  },
  {
   "id": "git-historico-de-uma-funcao",
   "nome": "Histórico de uma função",
   "receita": "git log -L :nome:arquivo",
   "prova": "as versões da função em ordem cronológica"
  },
  {
   "id": "git-culpa-ignorando-espacos-e-movimentos",
   "nome": "Culpa ignorando espaços e movimentos",
   "receita": "git blame -w -C -C arquivo",
   "prova": "o autor real da lógica, não do reformatador"
  },
  {
   "id": "git-ignorar-um-commit-de-formatacao-no-blame",
   "nome": "Ignorar um commit de formatação no blame",
   "receita": "liste o SHA em .git-blame-ignore-revs e git config blame.ignoreRevsFile",
   "prova": "git blame passa a apontar o autor anterior"
  },
  {
   "id": "git-achar-arquivo-apagado",
   "nome": "Achar arquivo apagado",
   "receita": "git log --diff-filter=D --name-only -- caminho",
   "prova": "o commit que apagou o arquivo"
  },
  {
   "id": "git-recuperar-arquivo-apagado",
   "nome": "Recuperar arquivo apagado",
   "receita": "git checkout SHA^ -- caminho (o commit anterior ao que apagou)",
   "prova": "o arquivo reapareceu no disco"
  },
  {
   "id": "git-voltar-uma-branch-para-o-estado-do-remoto",
   "nome": "Voltar uma branch para o estado do remoto",
   "receita": "git fetch && git reset --hard origin/branch (só se descartar o local)",
   "prova": "git status limpo e igual ao remoto"
  },
  {
   "id": "git-guardar-o-estado-antes-de-algo-arriscado",
   "nome": "Guardar o estado antes de algo arriscado",
   "receita": "git branch backup/antes-do-risco ou git stash",
   "prova": "a branch backup existe com o SHA anterior"
  },
  {
   "id": "git-stash-com-nome-e-so-parte",
   "nome": "Stash com nome e só parte",
   "receita": "git stash push -p -m \"nome\" caminho",
   "prova": "git stash list mostra a entrada nomeada"
  },
  {
   "id": "git-aplicar-stash-sem-apagar",
   "nome": "Aplicar stash sem apagar",
   "receita": "git stash apply stash@{1}; apague depois com git stash drop",
   "prova": "a mudança volta e a entrada continua na lista"
  },
  {
   "id": "git-stash-em-outra-branch",
   "nome": "Stash em outra branch",
   "receita": "git stash branch nova-branch stash@{0}",
   "prova": "a branch nova contém a mudança guardada"
  },
  {
   "id": "git-mudar-de-branch-com-mudancas",
   "nome": "Mudar de branch com mudanças",
   "receita": "git switch outra (leva as mudanças se não conflitar) ou guarde antes",
   "prova": "você está na branch nova com o trabalho preservado"
  },
  {
   "id": "git-criar-branch-a-partir-de-um-commit",
   "nome": "Criar branch a partir de um commit",
   "receita": "git switch -c nome SHA",
   "prova": "git branch mostra a branch no SHA pedido"
  },
  {
   "id": "git-renomear-branch",
   "nome": "Renomear branch",
   "receita": "git branch -m antigo novo e git push -u origin novo, apague a antiga no remoto",
   "prova": "a branch nova existe no remoto"
  },
  {
   "id": "git-apagar-branch-remota",
   "nome": "Apagar branch remota",
   "receita": "git push origin --delete nome",
   "prova": "git branch -r não lista mais"
  },
  {
   "id": "git-limpar-referencias-mortas",
   "nome": "Limpar referências mortas",
   "receita": "git fetch --prune",
   "prova": "branches apagadas no remoto somem do local"
  },
  {
   "id": "git-listar-branches-por-data",
   "nome": "Listar branches por data",
   "receita": "git for-each-ref --sort=-committerdate refs/heads --format=\"%(refname:short) %(committerdate:relative)\"",
   "prova": "as mais recentes aparecem primeiro"
  },
  {
   "id": "git-cherry-pick-de-varios-commits",
   "nome": "Cherry-pick de vários commits",
   "receita": "git cherry-pick A^..B",
   "prova": "todos os commits aparecem na branch atual"
  },
  {
   "id": "git-cherry-pick-com-conflito",
   "nome": "Cherry-pick com conflito",
   "receita": "resolva, git add e git cherry-pick --continue (ou --abort)",
   "prova": "git status sem cherry-pick em andamento"
  },
  {
   "id": "git-abortar-merge-ou-rebase",
   "nome": "Abortar merge ou rebase",
   "receita": "git merge --abort ou git rebase --abort devolve ao estado de antes",
   "prova": "git status mostra o estado anterior ao comando"
  },
  {
   "id": "git-resolver-conflito-aceitando-um-lado",
   "nome": "Resolver conflito aceitando um lado",
   "receita": "git checkout --ours/--theirs caminho; lembre que no rebase os lados invertem",
   "prova": "o arquivo não tem marcadores e o build passa"
  },
  {
   "id": "git-ver-conflitos-de-um-merge",
   "nome": "Ver conflitos de um merge",
   "receita": "git diff --name-only --diff-filter=U",
   "prova": "lista de arquivos ainda em conflito"
  },
  {
   "id": "git-reutilizar-resolucao-de-conflito",
   "nome": "Reutilizar resolução de conflito",
   "receita": "git config rerere.enabled true",
   "prova": "o mesmo conflito se resolve sozinho na segunda vez"
  },
  {
   "id": "git-merge-com-squash",
   "nome": "Merge com squash",
   "receita": "git merge --squash branch e commit único",
   "prova": "um commit representa a branch"
  },
  {
   "id": "git-merge-sem-avancar-rapido",
   "nome": "Merge sem avançar rápido",
   "receita": "git merge --no-ff branch preserva o agrupamento",
   "prova": "git log --graph mostra o ramo"
  },
  {
   "id": "git-mesclar-so-quando-for-fast-forward",
   "nome": "Mesclar só quando for fast-forward",
   "receita": "git merge --ff-only; senão rebase antes",
   "prova": "nenhum commit de merge criado"
  },
  {
   "id": "git-enviar-branch-nova",
   "nome": "Enviar branch nova",
   "receita": "git push -u origin HEAD",
   "prova": "git status mostra a branch acompanhando o remoto"
  },
  {
   "id": "git-trocar-o-remoto",
   "nome": "Trocar o remoto",
   "receita": "git remote set-url origin URL",
   "prova": "git remote -v mostra a URL nova"
  },
  {
   "id": "git-fork-e-atualizar-do-original",
   "nome": "Fork e atualizar do original",
   "receita": "git remote add upstream URL; git fetch upstream; git rebase upstream/main",
   "prova": "o fork contém os commits do original"
  },
  {
   "id": "git-clone-raso-para-ci",
   "nome": "Clone raso para CI",
   "receita": "git clone --depth 1 URL (ou fetch-depth: 1)",
   "prova": "o clone é rápido e o log tem 1 commit"
  },
  {
   "id": "git-clone-parcial-de-repo-grande",
   "nome": "Clone parcial de repo grande",
   "receita": "git clone --filter=blob:none e sparse-checkout para as pastas necessárias",
   "prova": "só as pastas escolhidas aparecem no disco"
  },
  {
   "id": "git-sparse-checkout",
   "nome": "Sparse checkout",
   "receita": "git sparse-checkout set pasta1 pasta2",
   "prova": "o diretório de trabalho tem só essas pastas"
  },
  {
   "id": "git-worktree-para-revisar-pr",
   "nome": "Worktree para revisar PR",
   "receita": "git worktree add ../revisao branch",
   "prova": "a pasta nova tem a branch sem mexer na sua"
  },
  {
   "id": "git-remover-worktree",
   "nome": "Remover worktree",
   "receita": "git worktree remove ../revisao",
   "prova": "git worktree list não mostra mais"
  },
  {
   "id": "git-ignorar-arquivo-so-na-sua-maquina",
   "nome": "Ignorar arquivo só na sua máquina",
   "receita": "adicione em .git/info/exclude",
   "prova": "o arquivo não aparece no git status e não vai ao repositório"
  },
  {
   "id": "git-gitignore-que-nao-funciona",
   "nome": ".gitignore que não funciona",
   "receita": "o arquivo já está rastreado: git rm --cached caminho e commit",
   "prova": "git status mostra o arquivo ignorado"
  },
  {
   "id": "git-arquivos-grandes",
   "nome": "Arquivos grandes",
   "receita": "use Git LFS (git lfs track \"*.psd\") e nunca commite binário acima de 50 MB",
   "prova": "o ponteiro LFS aparece no commit"
  },
  {
   "id": "git-tirar-arquivo-grande-do-historico",
   "nome": "Tirar arquivo grande do histórico",
   "receita": "git filter-repo --path caminho --invert-paths, depois force push coordenado",
   "prova": "o tamanho do repositório cai (git count-objects -vH)"
  },
  {
   "id": "git-remover-segredo-do-historico",
   "nome": "Remover segredo do histórico",
   "receita": "revogue o segredo primeiro, depois git filter-repo --replace-text; avise a equipe",
   "prova": "git log -S\"segredo\" não encontra nada"
  },
  {
   "id": "git-hook-de-pre-commit",
   "nome": "Hook de pre-commit",
   "receita": "core.hooksPath em .githooks, script que roda lint e testes rápidos e bloqueia em falha",
   "prova": "um commit com erro de lint é barrado"
  },
  {
   "id": "git-pular-o-hook-com-consciencia",
   "nome": "Pular o hook com consciência",
   "receita": "git commit --no-verify só em exceção, e registre o motivo",
   "prova": "commit criado e motivo anotado na mensagem"
  },
  {
   "id": "git-commits-convencionais",
   "nome": "Commits convencionais",
   "receita": "tipo(escopo): resumo no imperativo, corpo com o porquê",
   "prova": "git log --oneline lê como changelog"
  },
  {
   "id": "git-mensagem-de-commit-boa",
   "nome": "Mensagem de commit boa",
   "receita": "linha de assunto até 72 caracteres, linha vazia, corpo com o porquê e o que mudou",
   "prova": "o commit se entende sem abrir o diff"
  },
  {
   "id": "git-assinar-commits",
   "nome": "Assinar commits",
   "receita": "gpg ou ssh signing: git config gpg.format ssh, user.signingkey e commit.gpgsign true",
   "prova": "git log --show-signature mostra \"Good signature\""
  },
  {
   "id": "git-tags-anotadas",
   "nome": "Tags anotadas",
   "receita": "git tag -a v1.2.0 -m \"notas\" e git push origin v1.2.0",
   "prova": "git show v1.2.0 mostra a anotação"
  },
  {
   "id": "git-apagar-ou-mover-uma-tag",
   "nome": "Apagar ou mover uma tag",
   "receita": "git tag -d v1 e git push origin :refs/tags/v1",
   "prova": "a tag sumiu do remoto"
  },
  {
   "id": "git-gerar-changelog",
   "nome": "Gerar changelog",
   "receita": "git log v1..v2 --pretty=\"- %s (%h)\" --no-merges",
   "prova": "lista de mudanças entre as versões"
  },
  {
   "id": "git-contar-contribuicoes",
   "nome": "Contar contribuições",
   "receita": "git shortlog -sn --no-merges",
   "prova": "ranking de commits por autor"
  },
  {
   "id": "git-ver-arquivos-mais-mudados",
   "nome": "Ver arquivos mais mudados",
   "receita": "git log --name-only --pretty=format: | sort | uniq -c | sort -rn | head",
   "prova": "os arquivos com mais mudanças"
  },
  {
   "id": "git-reflog-para-salvar",
   "nome": "Reflog para salvar",
   "receita": "git reflog mostra onde o HEAD esteve; git reset --hard HEAD@{N} volta",
   "prova": "o commit perdido reaparece no log"
  },
  {
   "id": "git-verificar-integridade",
   "nome": "Verificar integridade",
   "receita": "git fsck --lost-found",
   "prova": "lista de objetos soltos ou nenhuma falha"
  },
  {
   "id": "git-limpar-o-repositorio",
   "nome": "Limpar o repositório",
   "receita": "git gc --prune=now --aggressive em repositório local, depois confira o tamanho",
   "prova": "git count-objects -vH com tamanho menor"
  },
  {
   "id": "git-pasta-nunca-rastreada",
   "nome": "Pasta nunca rastreada",
   "receita": "git clean -nd mostra o que seria removido; git clean -fd só depois de conferir",
   "prova": "o diretório fica só com arquivos rastreados"
  },
  {
   "id": "git-submodulo-ao-clonar",
   "nome": "Submódulo ao clonar",
   "receita": "git clone --recurse-submodules ou git submodule update --init --recursive",
   "prova": "as pastas dos submódulos têm conteúdo"
  },
  {
   "id": "git-trocar-submodulo-por-dependencia",
   "nome": "Trocar submódulo por dependência",
   "receita": "remova com git submodule deinit e git rm; adicione o pacote",
   "prova": "o build funciona sem o submódulo"
  },
  {
   "id": "git-subarvore-em-vez-de-submodulo",
   "nome": "Subárvore em vez de submódulo",
   "receita": "git subtree add --prefix=pasta URL branch --squash",
   "prova": "a pasta contém o código sem apontar para outro repositório"
  },
  {
   "id": "git-patch-por-e-mail",
   "nome": "Patch por e-mail",
   "receita": "git format-patch -1 SHA e git am arquivo.patch",
   "prova": "o commit chegou com autor e mensagem"
  },
  {
   "id": "git-aplicar-um-diff",
   "nome": "Aplicar um diff",
   "receita": "git apply --check arquivo.diff e depois git apply",
   "prova": "git status mostra as mudanças aplicadas"
  },
  {
   "id": "git-diff-de-arquivo-binario-ou-imagem",
   "nome": "Diff de arquivo binário ou imagem",
   "receita": "git diff --stat; use gitattributes com textconv ou abra o arquivo",
   "prova": "o diff indica o que mudou no binário"
  },
  {
   "id": "git-fim-de-linha-consistente",
   "nome": "Fim de linha consistente",
   "receita": ".gitattributes com \"* text=auto\" e *.sh eol=lf; git add --renormalize .",
   "prova": "o diff deixa de mostrar linhas só com fim de linha"
  },
  {
   "id": "git-proteger-a-branch-principal",
   "nome": "Proteger a branch principal",
   "receita": "regras do host: PR obrigatório, checagens verdes e sem push direto",
   "prova": "push direto na main é recusado"
  },
  {
   "id": "git-revisar-pr-localmente",
   "nome": "Revisar PR localmente",
   "receita": "gh pr checkout N e rode os testes; ou git fetch origin pull/N/head:pr-N",
   "prova": "a branch do PR roda localmente"
  },
  {
   "id": "git-criar-pr-pela-linha-de-comando",
   "nome": "Criar PR pela linha de comando",
   "receita": "gh pr create --fill --draft e depois marque como pronto",
   "prova": "o link do PR aparece na saída"
  },
  {
   "id": "git-responder-ao-feedback-de-pr",
   "nome": "Responder ao feedback de PR",
   "receita": "commits pequenos de ajuste e resposta em cada comentário, resolvendo as threads",
   "prova": "as threads resolvidas e a CI verde"
  },
  {
   "id": "git-atualizar-pr-apos-a-main-mudar",
   "nome": "Atualizar PR após a main mudar",
   "receita": "git fetch && git rebase origin/main e git push --force-with-lease",
   "prova": "o PR sem conflito e com a CI verde"
  },
  {
   "id": "git-cortar-pr-grande",
   "nome": "Cortar PR grande",
   "receita": "divida por camada ou comportamento em branches empilhadas, cada uma com teste",
   "prova": "cada PR pequeno passa sozinho"
  },
  {
   "id": "git-desfazer-merge-ja-enviado",
   "nome": "Desfazer merge já enviado",
   "receita": "git revert -m 1 SHA_DO_MERGE",
   "prova": "a main volta ao estado anterior ao merge"
  },
  {
   "id": "git-descobrir-branch-que-contem-o-commit",
   "nome": "Descobrir branch que contém o commit",
   "receita": "git branch -a --contains SHA",
   "prova": "lista das branches com o commit"
  },
  {
   "id": "git-comparar-dois-commits-em-um-arquivo",
   "nome": "Comparar dois commits em um arquivo",
   "receita": "git diff SHA1 SHA2 -- caminho",
   "prova": "o diff só daquele arquivo"
  },
  {
   "id": "git-ultima-versao-de-arquivo-na-main",
   "nome": "Última versão de arquivo na main",
   "receita": "git show origin/main:caminho",
   "prova": "o conteúdo é impresso sem trocar de branch"
  },
  {
   "id": "git-mudar-a-data-de-um-commit",
   "nome": "Mudar a data de um commit",
   "receita": "git commit --amend --date=\"...\" ou rebase com exec",
   "prova": "git log mostra a data nova"
  },
  {
   "id": "git-fluxo-de-release",
   "nome": "Fluxo de release",
   "receita": "branch release, tag, notas e merge de volta na main",
   "prova": "a tag existe e a main contém a correção"
  },
  {
   "id": "git-hotfix-em-producao",
   "nome": "Hotfix em produção",
   "receita": "branch a partir da tag, correção mínima, tag nova e cherry-pick para a main",
   "prova": "a correção está em produção e na main"
  },
  {
   "id": "git-manter-fork-sincronizado-automaticamente",
   "nome": "Manter fork sincronizado automaticamente",
   "receita": "agende fetch do upstream e abra PR automático",
   "prova": "o fork sem atraso para o original"
  },
  {
   "id": "git-pushar-tags-e-commits-juntos",
   "nome": "Pushar tags e commits juntos",
   "receita": "git push --follow-tags",
   "prova": "a tag anotada chega com o commit"
  }
 ]
}
