Git para iniciantes: commits, branches e merge
Entenda o modelo mental do Git — commits, branches e merge — com os comandos que você vai usar no dia a dia, sem enrolação.
Você fez uma mudança que quebrou tudo, não lembra o que alterou, e o único backup é a versão de ontem que você enviou por e-mail para si mesmo. Isso tem nome: ausência de controle de versão. Git existe para que esse cenário nunca precise acontecer — mas a curva de entrada intimida o suficiente para que muita gente chegue nos primeiros meses de trabalho sem ter entendido o modelo mental por trás.
O que o Git realmente faz
Git não é um backup automático. É um sistema que registra intencionalmente o estado do seu projeto em momentos que você escolhe — e permite navegar entre esses estados, trabalhar em versões paralelas e combinar mudanças de múltiplas pessoas sem sobrescrever o trabalho umas das outras.
O modelo mental que faz tudo encaixar: imagine cada commit como uma foto tirada do seu projeto. Você decide quando tirar a foto, o que incluir nela e escreve uma legenda. O Git guarda cada foto e sabe como "voltar" para qualquer uma delas. Branches são linhas do tempo alternativas — você ramifica em um ponto, trabalha separado, e eventualmente funde as linhas de volta.
Como instalar e configurar
Se você ainda não tem Git instalado:
# macOS (com Homebrew)
brew install git
# Ubuntu/Debian
sudo apt install git
# Windows: baixar em git-scm.com
Depois de instalar, configure seu nome e e-mail — eles aparecem em todos os commits que você fizer:
git config --global user.name "Seu Nome"
git config --global user.email "seu@email.com"
Iniciando um repositório
Para começar a versionar um projeto existente:
cd meu-projeto
git init
Isso cria uma pasta .git oculta — é onde o Git guarda todo o histórico. Nunca apague essa pasta manualmente.
Se você está clonando um projeto existente (do GitHub, por exemplo):
git clone https://github.com/usuario/repositorio.git
O ciclo básico: working directory, staging, commit
Esse é o ponto onde a maioria das pessoas se confunde. O Git tem três estados para seus arquivos:
- Working directory — o que você editou mas ainda não disse ao Git para guardar
- Staging area (index) — o que você marcou para incluir no próximo commit
- Repository — o que foi efetivamente commitado e faz parte do histórico
O fluxo é sempre: editar → adicionar ao stage → commitar.
# Ver o estado atual
git status
# Adicionar arquivo específico ao stage
git add src/utils.js
# Adicionar tudo que mudou
git add .
# Criar o commit com uma mensagem
git commit -m "feat: adiciona validação de CPF"
A staging area existe porque commits devem ser atômicos — uma mudança coesa, com uma razão clara. Se você editou três arquivos mas só dois têm relação com o que estava resolvendo, você faz stage dos dois e commita. O terceiro fica para o próximo commit.
Como escrever uma boa mensagem de commit
Mensagem de commit ruim: "ajustes", "fix", "wip".
Mensagem de commit boa: "fix: corrige cálculo de desconto quando quantidade é zero".
O padrão mais adotado é o Conventional Commits: tipo: descrição no imperativo. Os tipos mais usados são feat (nova funcionalidade), fix (correção de bug), refactor (sem mudança de comportamento), docs, test, chore.
Não precisa seguir o padrão no começo, mas a regra de ouro é: a mensagem deve explicar o quê e por quê, não o como. O código já explica o como.
Branches: trabalhando em paralelo
Imagine que você quer adicionar uma nova feature mas não quer bagunçar o código que está funcionando. É para isso que servem as branches.
# Ver em qual branch você está
git branch
# Criar e mudar para uma nova branch
git checkout -b feature/login-com-google
# (Equivalente moderno, Git 2.23+)
git switch -c feature/login-com-google
A convenção mais comum é main (ou master) para o código estável, e branches descritivas para tudo que está em desenvolvimento: feature/nome, fix/nome, hotfix/nome.
Enquanto você trabalha na branch nova, main continua intocada. Você pode trocar de branch a qualquer momento (desde que tenha commitado as mudanças atuais):
git switch main
Merge: juntando as linhas do tempo
Quando a feature está pronta, você une as mudanças de volta para main:
git switch main
git merge feature/login-com-google
O Git vai tentar combinar os dois históricos automaticamente. Se as mudanças não se sobrepõem, ele consegue — isso se chama fast-forward ou merge commit dependendo do histórico.
Se as mesmas linhas foram editadas nas duas branches, você vai ter um conflito de merge. O Git pausa e marca os arquivos problemáticos assim:
<<<<<<< HEAD
return calcularDesconto(valor, 0.1);
=======
return calcularDesconto(valor, taxa);
>>>>>>> feature/login-com-google
Você resolve manualmente — decide qual versão fica (ou combina as duas), remove os marcadores, e:
git add arquivo-resolvido.js
git commit
Conflito não é erro do Git. É o Git dizendo "essas duas mudanças se contradizem — você decide".
Comandos que você vai usar todo dia
Além do ciclo básico, alguns comandos se tornam rotina rapidamente:
# Ver o histórico de commits
git log --oneline
# Ver o que mudou antes de commitar
git diff
# Ver o que está no stage
git diff --staged
# Desfazer mudanças não commitadas em um arquivo
git restore src/utils.js
# Criar um alias (atalho)
git config --global alias.st status
O git log --oneline é particularmente útil — cada commit aparece em uma linha com o hash curto e a mensagem. É fácil enxergar o histórico sem ruído.
Repositórios remotos: GitHub, GitLab, Bitbucket
Git é local por padrão. Para colaborar ou fazer backup na nuvem, você conecta a um repositório remoto:
# Adicionar o remote (geralmente chamado de "origin")
git remote add origin https://github.com/usuario/repo.git
# Enviar seus commits para o remote
git push origin main
# Puxar mudanças do remote
git pull origin main
O fluxo típico em time: você puxa as mudanças mais recentes do remote, trabalha na sua branch local, e quando termina, faz push e abre um pull request para alguém revisar antes de fazer merge em main.
Antes de abrir o PR, vale olhar o diff do que você está submetendo — é fácil deixar passar um console.log de debug ou uma mudança que não tem relação com o que você estava resolvendo. Para revisar fora do terminal, uso o Comparador de Texto quando preciso comparar versões de um arquivo sem contexto de Git — cola o antes e o depois e vê as diferenças lado a lado.
Perguntas frequentes
Posso desfazer um commit já feito?
Sim, mas depende se você já fez push ou não. Localmente, você tem algumas opções:
# Desfaz o commit mas mantém as mudanças no working directory
git reset --soft HEAD~1
# Desfaz o commit e remove as mudanças do stage
git reset --mixed HEAD~1
# Desfaz o commit e descarta as mudanças (cuidado — irreversível)
git reset --hard HEAD~1
Se o commit já foi para o remote e outras pessoas podem ter puxado, use git revert em vez de reset — ele cria um novo commit que desfaz o anterior, sem reescrever o histórico.
Qual a diferença entre merge e rebase?
Merge cria um commit de junção que preserva o histórico completo das duas branches. Rebase reescreve os commits da sua branch como se eles tivessem sido feitos a partir do topo da branch de destino — histórico mais linear, mas reescreve os hashes dos commits.
Para quem está começando: use merge. Quando você entender o modelo de histórico bem, avalie rebase — mas nunca faça rebase de commits que já estão no remote e que outras pessoas usaram.
O que é .gitignore?
Um arquivo na raiz do projeto que diz ao Git quais arquivos ignorar. Você não quer versionar node_modules/, .env, arquivos de build ou configurações locais de IDE. O formato é simples:
node_modules/
.env
dist/
*.log
.DS_Store
O site gitignore.io gera o arquivo para qualquer stack em segundos.
Qual a diferença entre git fetch e git pull?
git fetch baixa as mudanças do remote mas não aplica no seu working directory. git pull faz fetch e já faz merge automaticamente. Para ter mais controle, você pode fazer fetch e inspecionar as mudanças antes de aplicar — isso evita surpresas quando o remote mudou de forma inesperada.
O modelo mental que carrega tudo
Git confunde no começo porque a maioria das pessoas aprende os comandos antes de aprender o modelo. O que segura tudo é simples: commits são snapshots imutáveis, branches são ponteiros para commits, e merge é combinar duas linhas de histórico.
Com esse modelo na cabeça, comandos como reset, rebase e cherry-pick passam a fazer sentido intuitivamente — você não está manipulando arquivos, está manipulando ponteiros e histórico.
- 01 O que é DevOps além das ferramentas DevOps não é um pipeline nem um cargo. É responsabilidade compartilhada entre quem escreve código e quem coloca em produção — e por que a maioria dos times erra nisso.
- 02 Banco relacional vs NoSQL: como escolher Quando usar PostgreSQL e quando NoSQL faz sentido de verdade — tradeoffs reais de consistência, schema e escala, sem papo de marketing.