Introdução
A proficiência em Git vai além de commits básicos. Engenheiros sénior precisam de gerir estratégias de branching complexas, resolver conflitos de forma eficiente e manter um histórico de projeto limpo. Este guia aborda workflows e técnicas práticas de Git para equipas de desenvolvimento profissionais.
Gestão de Branches
Workflow de Feature Branch
# Comece a partir de main atualizada
git checkout main
git pull origin main
# Criar feature branch
git checkout -b feature/user-authentication
# Trabalhar, fazer commit, fazer push
git add .
git commit -m "Add login form component"
git push -u origin feature/user-authentication
# Manter a branch atualizada com main
git fetch origin
git rebase origin/main
# Ou faça merge, se preferir
git merge origin/main
# Após revisão, fazer merge para main
git checkout main
git merge --no-ff feature/user-authentication
git push origin main
# Limpeza
git branch -d feature/user-authentication
git push origin --delete feature/user-authentication
Convenções de Nomenclatura de Branches
feature/add-user-auth # Novas funcionalidades
bugfix/fix-login-error # Correções de bugs
hotfix/security-patch # Correções urgentes em produção
release/v1.2.0 # Preparação de release
docs/update-readme # Documentação
refactor/cleanup-models # Refatoração de código
Boas Práticas de Commits
Commits Atómicos
Cada commit deve ser uma única alteração lógica:
# Mau: um commit com várias alterações
git commit -m "Add login, fix header, update styles"
# Bom: commits separados
git commit -m "Add login form validation"
git commit -m "Fix header alignment on mobile"
git commit -m "Update button styles for consistency"
Formato da Mensagem de Commit
<type>(<scope>): <subject>
<body>
<footer>
Exemplo:
feat(auth): add JWT token refresh mechanism
Implement automatic token refresh when access token expires.
The refresh happens 5 minutes before expiration to prevent
interruption of user sessions.
Closes #123
Tipos:
feat: Nova funcionalidadefix: Correção de bugdocs: Documentaçãostyle: Formatação (sem alteração de código)refactor: Reestruturação de códigotest: Adição de testeschore: Tarefas de manutenção
Staging Interativo
# Fazer stage de hunks específicos
git add -p
# y: fazer stage deste hunk
# n: ignorar este hunk
# s: dividir em hunks mais pequenos
# e: editar o hunk manualmente
# Fazer stage de ficheiros específicos de forma interativa
git add -i
Estratégias de Merge
Merge vs Rebase
# Merge: preserva o histórico, cria um merge commit
git checkout main
git merge feature/branch
# Rebase: histórico linear, reescreve commits
git checkout feature/branch
git rebase main
git checkout main
git merge feature/branch # Fast-forward
Quando Usar Cada Um
| Cenário | Estratégia |
|---|
| Branches públicas | Merge |
| Branches pessoais de funcionalidade | Rebase |
| Branches de funcionalidade partilhadas | Merge |
| Atualizar a partir de main | Rebase (normalmente) |
Resolução de Conflitos
# Durante o merge
git merge feature/branch
# CONFLICT in file.txt
git status # Ver ficheiros em conflito
# Editar ficheiros para resolver conflitos
# Remover marcadores de conflito: <<<<<<<, =======, >>>>>>>
# Marcar como resolvido
git add file.txt
git commit
# Durante o rebase
git rebase main
# CONFLICT in file.txt
# Resolver conflitos
git add file.txt
git rebase --continue
# Ou abortar, se necessário
git rebase --abort
Manter a Versão Local ou Remota
# Manter a nossa versão (branch atual)
git checkout --ours file.txt
# Manter a versão deles (branch recebida)
git checkout --theirs file.txt
# Para ficheiros binários
git checkout --ours -- path/to/binary.png
git add path/to/binary.png
Reescrever o Histórico
Amend ao Último Commit
# Corrigir mensagem de commit
git commit --amend -m "New message"
# Adicionar ficheiros esquecidos
git add forgotten-file.txt
git commit --amend --no-edit
Rebase Interativo
# Editar os últimos 5 commits
git rebase -i HEAD~5
# No editor:
# pick abc1234 Primeiro commit
# reword def5678 Segundo commit # Alterar mensagem
# squash ghi9012 Terceiro commit # Combinar com o anterior
# fixup jkl3456 Quarto commit # Combinar, descartar mensagem
# drop mno7890 Quinto commit # Remover por completo
# edit pqr1234 Sexto commit # Parar para editar
# Durante a paragem para edição
# Fazer alterações
git add .
git commit --amend
git rebase --continue
Squash de Commits Antes do Merge
# Fazer squash de todos os commits da feature branch num só
git checkout feature/branch
git rebase -i main
# Marcar todos, exceto o primeiro, como «squash»
# Ou squash merge
git checkout main
git merge --squash feature/branch
git commit -m "Add complete feature"
Stashing de Alterações
Stash Básico
# Fazer stash das alterações atuais
git stash
# Fazer stash com mensagem
git stash save "WIP: working on login form"
# Incluir ficheiros não rastreados
git stash -u
# Incluir ficheiros não rastreados e ignorados
git stash -a
# Listar stashes
git stash list
# Aplicar e remover
git stash pop
# Aplicar sem remover
git stash apply
# Aplicar stash específico
git stash apply stash@{2}
# Eliminar stash
git stash drop stash@{0}
# Limpar todos os stashes
git stash clear
Workflow de Stash
# A trabalhar numa funcionalidade, entra um bug urgente
git stash save "feature: in progress"
# Corrigir bug em main
git checkout main
git checkout -b hotfix/urgent
# ... corrigir bug ...
git commit -m "fix: urgent bug"
git checkout main
git merge hotfix/urgent
# Voltar à funcionalidade
git checkout feature/branch
git stash pop
Comparar e Rever
Comandos Diff
# Diretório de trabalho vs. staging
git diff
# Staging vs. último commit
git diff --staged
git diff --cached
# Entre commits
git diff abc123..def456
# Entre branches
git diff main..feature/branch
# Ficheiro específico
git diff HEAD~3 -- path/to/file.txt
# Apenas resumo
git diff --stat
Comparar Branches
# Commits em feature que não estão em main
git log main..feature/branch
# Commits em qualquer uma, mas não em ambas
git log main...feature/branch
# Que branch contém um commit
git branch --contains abc123
Recuperar de Erros
Anular Alterações
# Descartar alterações no diretório de trabalho
git checkout -- file.txt
git restore file.txt # Git 2.23+
# Retirar ficheiro do staging
git reset HEAD file.txt
git restore --staged file.txt # Git 2.23+
# Anular o último commit (manter alterações)
git reset --soft HEAD~1
# Anular o último commit (descartar alterações)
git reset --hard HEAD~1
# Reverter commit (cria novo commit)
git revert abc123
Recuperar Branches Eliminadas
# Encontrar o commit
git reflog
# Recriar branch
git checkout -b recovered-branch abc123
Recuperar Commits Perdidos
# Encontrar no reflog
git reflog
# Cherry-pick ou reset para recuperar
git cherry-pick abc123
# ou
git reset --hard abc123
Git Hooks
Hook pre-commit
# .git/hooks/pre-commit
#!/bin/sh
# Executar linter
npm run lint
if [ $? -ne 0 ]; then
echo "Lint failed. Commit aborted."
exit 1
fi
# Executar testes
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Commit aborted."
exit 1
fi
Hook commit-msg
# .git/hooks/commit-msg
#!/bin/sh
# Impor conventional commits
commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}'
if ! grep -qE "$commit_regex" "$1"; then
echo "Invalid commit message format."
echo "Example: feat(auth): add login functionality"
exit 1
fi
Trabalhar com Repositórios Remotos
Configuração de Chave SSH
# Gerar chave SSH
ssh-keygen -t ed25519 -C "[email protected]"
# Iniciar o agente SSH
eval "$(ssh-agent -s)"
# Adicionar chave ao agente
ssh-add ~/.ssh/id_ed25519
# Copiar chave pública
cat ~/.ssh/id_ed25519.pub
# Adicionar nas definições do GitHub/GitLab
Múltiplas Chaves SSH
# ~/.ssh/config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github_key
Host gitlab.company.com
HostName gitlab.company.com
User git
IdentityFile ~/.ssh/gitlab_key
Remover Ficheiros do Repo mas Manter Localmente
# Remover do repo, manter ficheiro local
git rm --cached sensitive-file.txt
echo "sensitive-file.txt" >> .gitignore
git commit -m "Remove sensitive file from tracking"
# Remover ficheiros ignorados do repo
git rm -r --cached .
git add .
git commit -m "Remove files listed in .gitignore"
Compactar Repositório
# Remover objetos não referenciados
git gc --prune=now --aggressive
# Verificar o tamanho do repositório
git count-objects -vH
Corrigir Problemas de Encoding Durante o Merge
# Para ficheiros com encodings mistos
git config --global merge.renormalize true
# Ou corrigir manualmente um ficheiro específico
git checkout --ours problematic-file.txt
iconv -f WINDOWS-1252 -t UTF-8 problematic-file.txt > temp.txt
mv temp.txt problematic-file.txt
git add problematic-file.txt
git commit
Configuração
Aliases Úteis
# Adicionar a ~/.gitconfig
[alias]
st = status -sb
co = checkout
br = branch
ci = commit
lg = log --oneline --graph --decorate -20
lga = log --oneline --graph --decorate --all
last = log -1 HEAD --stat
unstage = reset HEAD --
amend = commit --amend --no-edit
undo = reset --soft HEAD~1
wip = !git add -A && git commit -m 'WIP'
cleanup = !git branch --merged | grep -v main | xargs git branch -d
# Mostrar o nome da branch atual
current = rev-parse --abbrev-ref HEAD
# Mostrar ficheiros alterados no último commit
changed = diff-tree --no-commit-id --name-only -r HEAD
# Listar branches ordenadas pelo último commit
recent = for-each-ref --sort=-committerdate refs/heads --format='%(committerdate:short) %(refname:short)'
Configuração Global
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global pull.rebase true
git config --global core.editor "vim"
git config --global merge.tool vimdiff
# Gerir finais de linha
git config --global core.autocrlf input # Linux/Mac
git config --global core.autocrlf true # Windows
# Melhor algoritmo de diff
git config --global diff.algorithm histogram
# Mostrar diretórios não rastreados
git config --global status.showUntrackedFiles all
Workflows Avançados
Rebase Interativo para um Histórico Limpo
Limpe o seu histórico antes de fazer merge para main:
# Editar os últimos 5 commits
git rebase -i HEAD~5
# Ações disponíveis:
# - Squash: combinar commits «WIP» em «Feature X»
# - Reword: corrigir gralha nas mensagens de commit
# - Drop: remover commits acidentais
# - Edit: parar para modificar um commit
Dica Sénior: Trate o seu histórico de git como um produto. Um histórico limpo (linear, commits atómicos) permite reverter funcionalidades específicas sem reverter toda a release.
Encontrar Bugs com Bisect e Pesquisa Binária
Quando é que o bug apareceu?
- Pesquisa manual: n passos de log
git bisect: Pesquisa binária (O(log n))
git bisect start
git bisect bad HEAD
git bisect good v1.0
# Automatize os testes!
git bisect run npm test
O Git fará checkout do commit do meio, executará o teste e reduzirá o intervalo automaticamente até encontrar o commit exato que introduziu o bug.
Submodules vs. Monorepo
Submodules: Apontadores para commits específicos de outros repos.
- Dor: Esqueceu-se de atualizar o apontador? O build falha.
- Perspetiva Sénior: Evite submodules a menos que seja estritamente necessário. Prefira monorepos (Nx, Turborepo) ou package managers (npm private registry).
# Se tiver mesmo de usar submodules
git submodule update --init --recursive
git submodule foreach git pull origin main
Conclusão
Dominar workflows de Git melhora a colaboração da equipa e a qualidade do código. Use feature branches para isolamento, commits significativos para o histórico e rebase para merges limpos. Conheça as suas opções de recuperação — o reflog é o seu amigo quando algo corre mal. Lembre-se: um histórico de git limpo é documentação — faça com que conte uma história clara de como o seu código evoluiu.