Intermediário 12 min de leitura 01/09/2026

Git na prática: do primeiro commit ao workflow profissional

Do git init ao workflow de times — iniciando projetos, padrões de commits e branches, resolução de situações comuns e sincronização com repositórios remotos.

Git é a ferramenta mais usada no desenvolvimento de software — e também a mais mal usada. A maioria aprende o básico (add, commit, push) e para por aí, usando o histórico como lixeira em vez de documentação. Este tutorial cobre desde o início de um projeto até o workflow usado em times profissionais.

1. Configuração inicial (faça uma vez)

Antes de usar o Git em qualquer projeto, configure seu nome e email. Esses dados aparecem em cada commit que você fizer.

git config --global user.name "Seu Nome"
git config --global user.email "seu@email.com"

# Editor padrão para mensagens de commit
git config --global core.editor "code --wait"  # VS Code
# git config --global core.editor "nano"        # nano

# Verificar tudo que foi configurado
git config --list

2. Iniciando um projeto

Cenário A: projeto novo, do zero

# Cria a pasta e entra nela
mkdir meu-projeto && cd meu-projeto

# Inicia o repositório Git
git init

# Cria o .gitignore antes do primeiro commit
echo "venv/
__pycache__/
*.pyc
.env
db.sqlite3
staticfiles/
media/
.DS_Store" > .gitignore

# Primeiro commit
git add .
git commit -m "chore: setup inicial do projeto"

Cenário B: clonar um projeto existente do GitHub

# Clona o repositório
git clone https://github.com/usuario/repositorio.git

# Clona em uma pasta com nome diferente
git clone https://github.com/usuario/repositorio.git meu-nome

# Já entra na pasta certa
cd repositorio

Cenário C: projeto local já existente, subir para o GitHub

# Inicia o Git no projeto que já existe
git init
git add .
git commit -m "chore: primeiro commit"

# Conecta ao repositório remoto (crie ele no GitHub primeiro, sem README)
git remote add origin https://github.com/usuario/repositorio.git

# Sobe o código
git push -u origin main
O -u define a branch remota como padrão para essa local. Depois disso, basta git push sem argumentos.

3. O ciclo diário: add, commit, push

# Ver o que mudou
git status

# Ver as diferenças linha a linha
git diff

# Adicionar arquivo específico
git add arquivo.py

# Adicionar tudo
git add .

# Commitar com mensagem
git commit -m "feat: adiciona página de perfil do usuário"

# Ver histórico
git log --oneline

# Subir para o GitHub
git push

4. Padrão de mensagens de commit (Conventional Commits)

Mensagens como "ajuste", "correção" ou "wip" não dizem nada. O padrão Conventional Commits define uma estrutura que qualquer pessoa do time (ou você mesmo em 6 meses) consegue entender.

O formato é:

tipo(escopo opcional): descrição curta no imperativo

corpo opcional — explica o "por quê", não o "o quê"

rodapé opcional — referência a issues, breaking changes

Tipos principais

TipoQuando usarExemplo
featNova funcionalidadefeat: adiciona login com Google
fixCorreção de bugfix: corrige upload de avatar no Vercel
docsDocumentaçãodocs: atualiza README com setup do Cloudinary
styleFormatação, sem lógicastyle: aplica padding mobile no container
refactorRefatoração sem novo comportamentorefactor: extrai lógica de email para service
testTestestest: adiciona testes para view de tutoriais
choreTarefas de manutenção, deps, configchore: adiciona cloudinary ao requirements.txt
perfMelhoria de performanceperf: adiciona select_related nas queries de tutorial

Exemplos reais bem escritos

# Simples e direto
git commit -m "feat: adiciona filtro por tag na listagem de tutoriais"

# Com escopo
git commit -m "fix(auth): corrige redirect após login via Google"

# Com corpo explicando o porquê
git commit -m "fix: troca DEFAULT_FILE_STORAGE por STORAGES dict

Django 6.0 ignorou silenciosamente a configuração legada,
causando erro 500 no upload de avatares na Vercel.
O novo formato STORAGES é obrigatório a partir do Django 4.2."

# Referenciando uma issue do GitHub
git commit -m "feat: implementa newsletter semanal

Closes #42"

5. Branches: trabalhando em paralelo

Nunca desenvolva diretamente na branch main. Crie uma branch para cada funcionalidade ou correção — assim você pode trabalhar em múltiplas coisas sem misturar código incompleto.

# Ver branches existentes
git branch

# Criar e já mudar para a nova branch
git checkout -b nome-da-branch
# ou, na sintaxe moderna:
git switch -c nome-da-branch

# Mudar de branch
git switch main

# Deletar branch local (após merge)
git branch -d feat/pagina-perfil

# Deletar branch remota
git push origin --delete feat/pagina-perfil

6. Padrão de nomenclatura de branches

O padrão mais adotado usa um prefixo que indica o tipo, seguido de uma descrição curta em kebab-case:

PrefixoUsoExemplo
feat/Nova funcionalidadefeat/pagina-perfil-usuario
fix/Correção de bugfix/upload-avatar-vercel
hotfix/Correção urgente em produçãohotfix/erro-500-login
refactor/Refatoraçãorefactor/settings-cloudinary
docs/Documentaçãodocs/atualiza-readme-deploy
chore/Manutenção, dependênciaschore/atualiza-django-6
test/Adição de testestest/cobertura-views-tutoriais
Use apenas letras minúsculas, números e hífens. Nunca espaços, underscores ou caracteres especiais no nome da branch.

7. Integrando branches: merge e rebase

Merge — une os históricos

# Estando na main, traz o conteúdo da feature branch
git switch main
git merge feat/pagina-perfil

# Sobe o resultado
git push

Rebase — reescreve o histórico de forma linear

# Na sua feature branch, atualiza com o que está na main
git switch feat/pagina-perfil
git rebase main

# Resolve conflitos se houver, depois:
git rebase --continue

# Volta para main e faz o merge (agora linear, sem commits de merge)
git switch main
git merge feat/pagina-perfil
Use merge para branches compartilhadas (preserva o histórico real). Use rebase para atualizar sua branch local com o que chegou na main antes de abrir um Pull Request.

8. Resolvendo situações comuns

Desfazer o último commit (mas manter as mudanças)

git reset --soft HEAD~1
# Mudanças voltam para staged — você pode editar e commitar de novo

Corrigir a mensagem do último commit

git commit --amend -m "feat: mensagem corrigida"
# Atenção: não use amend em commits já enviados ao GitHub

Desfazer mudanças num arquivo antes do commit

# Descarta as mudanças e volta ao último commit
git checkout -- arquivo.py
# ou:
git restore arquivo.py

Guardar mudanças temporariamente sem commitar (stash)

# Guarda tudo que está modificado
git stash

# Recupera as mudanças guardadas
git stash pop

# Lista todos os stashes
git stash list

# Descarta o stash sem aplicar
git stash drop

Trazer um commit específico de outra branch (cherry-pick)

# Copia o commit abc1234 para a branch atual
git cherry-pick abc1234

Ver quem modificou cada linha de um arquivo

git blame arquivo.py

Encontrar qual commit introduziu um bug (bisect)

git bisect start
git bisect bad           # o commit atual tem o bug
git bisect good v1.0     # essa tag/commit não tinha o bug
# Git vai navegando pelos commits — você testa e responde:
git bisect good          # ou
git bisect bad
# Quando terminar:
git bisect reset

9. Mantendo o repositório sincronizado

# Baixa mudanças do remoto SEM aplicar
git fetch origin

# Baixa E aplica (fetch + merge)
git pull

# Pull com rebase (histórico mais limpo)
git pull --rebase

# Ver o que mudou no remoto antes de aplicar
git fetch origin
git log HEAD..origin/main --oneline

10. Tags: marcando versões

# Cria uma tag anotada na versão atual
git tag -a v1.0.0 -m "Versão 1.0.0 — MVP lançado"

# Lista todas as tags
git tag

# Sobe as tags para o GitHub
git push origin --tags

# Taguear um commit antigo
git tag -a v0.9.0 abc1234 -m "Versão 0.9.0"

11. .gitignore: o que nunca deve subir

Para projetos Django/Python, o .gitignore mínimo recomendado:

# Ambiente virtual
venv/
.venv/
env/

# Python
__pycache__/
*.pyc
*.pyo
*.pyd
*.egg-info/
dist/
build/

# Django
db.sqlite3
*.log
media/
staticfiles/

# Variáveis de ambiente — NUNCA commitar
.env
.env.local
.env.production

# Sistema operacional
.DS_Store         # macOS
Thumbs.db         # Windows

# IDEs
.vscode/
.idea/
*.swp
Se você commitou o .env por acidente, não basta adicionar ao .gitignore. Você precisa removê-lo do histórico e considerar as credenciais comprometidas — troque todas as senhas e chaves imediatamente.

Resumo: o workflow do dia a dia

# 1. Atualiza a main
git switch main
git pull

# 2. Cria branch para a tarefa
git switch -c feat/nome-da-funcionalidade

# 3. Desenvolve, commitando com frequência
git add .
git commit -m "feat: descrição clara do que foi feito"

# 4. Antes de abrir PR, atualiza com o que chegou na main
git rebase main

# 5. Sobe a branch
git push -u origin feat/nome-da-funcionalidade

# 6. Abre Pull Request no GitHub
# 7. Após aprovação e merge, limpa localmente
git switch main
git pull
git branch -d feat/nome-da-funcionalidade