Todos os artigos
45 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Tutoriais

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.

COVER · Tutoriais

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:

  1. Working directory — o que você editou mas ainda não disse ao Git para guardar
  2. Staging area (index) — o que você marcou para incluir no próximo commit
  3. 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.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil