Git avançado
Esta aula aprofunda tópicos essenciais do Git para fluxos de trabalho profissionais: rebase vs merge, cherry-pick, tags e hooks. Você aprenderá quando usar cada ferramenta, como aplicá-las corretamente e boas práticas para manter um histórico limpo e eficiente.
Nesta aula, vamos explorar funcionalidades avançadas do Git que são fundamentais para equipes que buscam controle de versão eficiente e colaboração suave. Dominar rebase, cherry-pick, tags e hooks permite gerenciar o histórico de commits de forma precisa, automatizar tarefas e manter a qualidade do código. Vamos começar com um dos debates mais comuns: rebase versus merge.
Rebase vs Merge
Tanto rebase quanto merge são usados para integrar mudanças de uma branch em outra, mas cada um tem implicações diferentes no histórico. O merge cria um commit de merge que une duas linhas de desenvolvimento, preservando o contexto de quando as branches divergiram. Já o rebase reescreve o histórico ao aplicar os commits de uma branch sobre a ponta de outra, resultando em uma linha linear.
A escolha entre eles depende da política da equipe e do objetivo. Merge é mais seguro para branches compartilhadas, pois não reescreve o histórico. Rebase é útil para manter um histórico limpo antes de integrar uma feature branch, mas nunca deve ser usado em branches públicas que outros já usam.
# Exemplo de merge
$ git checkout main
$ git merge feature-branch
# Exemplo de rebase
$ git checkout feature-branch
$ git rebase main
Cherry-pick
Cherry-pick permite aplicar commits específicos de uma branch para outra, sem trazer todo o histórico. É útil para portar correções urgentes ou selecionar mudanças isoladas. O comando copia o diff do commit e cria um novo commit na branch atual.
Use com cuidado, pois pode gerar conflitos e duplicar commits. Prefira merge ou rebase quando possível, mas cherry-pick é uma ferramenta valiosa para cenários de hotfix.
# Aplicar um commit específico na branch atual
$ git cherry-pick <hash-do-commit>
# Exemplo real
$ git checkout release-1.0
$ git cherry-pick a1b2c3d
Tags
Tags são referências fixas a um commit específico, geralmente usadas para marcar versões de release (ex: v1.0.0, v2.1.0). Elas podem ser leves (apenas um ponteiro) ou anotadas (com metadados como autor, data e mensagem). Tags anotadas são recomendadas para releases, pois armazenam informação adicional.
Tags são imutáveis e não se movem com novos commits. Para compartilhá-las, é necessário fazer push explícito com git push origin --tags.
# Criar tag anotada
$ git tag -a v1.0.0 -m "Versão 1.0.0"
# Listar tags
$ git tag
# Enviar tags para o remoto
$ git push origin --tags
Hooks (Visão Geral)
Hooks são scripts que o Git executa automaticamente em pontos específicos do ciclo de vida (commit, push, merge, etc.). Eles ficam no diretório .git/hooks e podem ser escritos em qualquer linguagem (bash, Python, etc.). Hooks do lado do cliente (como pre-commit) podem validar código ou executar testes antes de um commit. Hooks do lado do servidor (como pre-receive) podem impor políticas de aceitação.
Para ativar um hook, remova a extensão .sample do arquivo correspondente e torne-o executável. Exemplo comum: pre-commit para rodar linter e testes unitários.
# Exemplo de hook pre-commit (salvo em .git/hooks/pre-commit)
#!/bin/sh
# Verifica se há arquivos com erros de sintaxe
if ! npm run lint; then
echo "Erro no lint. Commit cancelado."
exit 1
fi
Boas Práticas e Observações Finais
- Preferir merge para branches compartilhadas e rebase para branches locais ou de feature.
- Usar cherry-pick com moderação e documentar o motivo.
- Sempre criar tags anotadas para versões oficiais.
- Não versionar hooks no repositório; compartilhe scripts de instalação separadamente.
Referências
- Git - Rebasing (Documentação Oficial)
- Git - cherry-pick (Documentação Oficial)
- Git - Tagging (Documentação Oficial)
- Git - Hooks (Documentação Oficial)
- Atlassian - Merging vs. Rebasing
Exercícios
-
Crie um repositório Git local, faça dois commits na branch main, crie uma branch feature com dois commits adicionais. Faça o rebase da feature sobre main. Explique o que acontece com o histórico.
✓ Resposta: O rebase pega os commits da branch feature e os aplica sobre o commit mais recente da main, resultando em um histórico linear. Os commits originais da feature são reescritos com novos hashes. Após o rebase, a branch feature aponta para os novos commits. -
Usando o mesmo repositório do exercício anterior, faça um cherry-pick do primeiro commit da branch feature para a branch main. Verifique o log.
✓ Resposta: O comandogit cherry-pick <hash-primeiro-commit-feature>aplica as mudanças do commit na branch main, criando um novo commit com hash diferente. O log mostrará um commit extra em main com as mesmas alterações. -
Crie uma tag anotada chamada v1.0.0 no commit atual e envie-a para um repositório remoto (simulado). Liste as tags.
✓ Resposta:$ git tag -a v1.0.0 -m "Versão 1.0.0" $ git push origin v1.0.0 $ git tag v1.0.0 -
Crie um hook pre-commit que impeça commits com mensagens vazias. Teste criando um commit com e sem mensagem.
✓ Resposta: Crie o arquivo.git/hooks/pre-commitcom:
Torne-o executável:#!/bin/sh if [ -z "$GIT_EDITOR" ] && [ -z "$(git log -1 --format=%s)" ]; then echo "Mensagem de commit não pode ser vazia" exit 1 fichmod +x .git/hooks/pre-commit. Ao tentar commitar sem mensagem, o hook cancela. -
Explique a diferença entre merge e rebase e quando usar cada um.
✓ Resposta: Merge preserva o histórico completo com um commit de merge, mostrando quando as branches divergiram e foram unidas. Rebase reescreve o histórico, aplicando commits linearmente, resultando em um histórico mais limpo. Use merge em branches públicas (ex: main) para evitar reescrita. Use rebase em branches de feature locais antes de integrar, para manter histórico linear.