Fluxo de Trabalho no Git: do commit ao rebase, sem trauma

Se você já trabalhou em qualquer projeto de software em equipe, sabe que o Git deixou de ser “só um detalhe técnico” para se tornar parte do seu raciocínio diário. Entender como e quando usar cada comando é o que separa quem só “sobrevive” ao Git de quem realmente flui com ele.

Neste artigo eu quero ir além do básico (init, add, commit, push) e mostrar o fluxo de trabalho que uso no dia a dia, incluindo feature branches, rebase e as correções de erro mais comuns que aparecem no trabalho real.

O que você vai ver

Configuração inicial do Git

Antes de qualquer coisa, configure seu nome e e-mail - eles ficam gravados em cada commit que você fizer:

BASH
git config --global user.name "Seu Nome"
git config --global user.email "[email protected]"
Clique para expandir e ver mais

Alguns ajustes que eu recomendo desde o primeiro dia:

BASH
# define "main" como nome padrão para novas branches
git config --global init.defaultBranch main

# editor padrão para mensagens de commit e rebase interativo
git config --global core.editor "nvim"

# evita fast-forward silencioso em pulls (evita histórico confuso)
git config --global pull.rebase true
Clique para expandir e ver mais

Para iniciar um repositório novo:

BASH
git init
Clique para expandir e ver mais

Ou, para trabalhar em um projeto existente:

BASH
git clone [email protected]:usuario/repositorio.git
Clique para expandir e ver mais

Os três estados do Git

O Git organiza o seu trabalho local em três áreas:

  1. Working Directory - os arquivos como estão no seu disco agora.
  2. Staging Area (Index) - uma área intermediária com o que vai entrar no próximo commit.
  3. Repository (HEAD) - o histórico de commits já confirmados.

Visualmente, o fluxo entre essas áreas funciona assim:

TEXT
Working Directory        Staging Area (Index)         Repository (HEAD)
------------------        ---------------------         ------------------
 arquivo.py (editado)  --git add-->  arquivo.py   --git commit-->   o---o---o
                                     (pronto p/                              ^
                                      commit)                              HEAD

        <---------------------- git reset ----------------------------------
        <----------------------------- git restore --------------------------
Clique para expandir e ver mais
BASH
# ver o estado atual dos arquivos
git status

# mover um arquivo específico para o staging
git add caminho/do/arquivo.py

# mover tudo que foi modificado
git add .

# criar um commit com o que está no staging
git commit -m "feat: adiciona validação de CPF no formulário"
Clique para expandir e ver mais

Uma boa prática aqui é escrever mensagens de commit no padrão Conventional Commits (feat:, fix:, refactor:, docs:, chore:…). Isso facilita gerar changelog automático e entender o histórico rapidamente.

Feature Branch: isolando o trabalho

A branch main (ou master, em projetos mais antigos) deve representar sempre um código estável, pronto para ir para produção. Por isso, não trabalhamos direto nela.

O fluxo de feature branch consiste em criar uma branch isolada para cada funcionalidade, correção ou experimento:

BASH
# cria e já muda para a nova branch
git checkout -b feature/login-com-google

# forma mais moderna, equivalente ao checkout -b
git switch -c feature/login-com-google
Clique para expandir e ver mais

Algumas convenções de nomenclatura comuns:

Trabalhando assim, você ganha algumas vantagens importantes:

Visualmente, o histórico fica assim enquanto a feature está em desenvolvimento:

TEXT
main       o---o---o---------------------o---o---o
                    \
feature/login        o---o---o---o
                      (commits isolados,
                       não afetam a main)
Clique para expandir e ver mais

Enquanto você trabalha na sua feature, a main continua recebendo outros merges. Por isso é importante manter sua branch atualizada - e é aqui que entram merge e rebase.

Merge vs Rebase

Os dois comandos resolvem o mesmo problema - trazer mudanças de uma branch para outra - mas de formas diferentes, e isso importa bastante no histórico do projeto.

git merge

O merge cria um novo commit de mesclagem (merge commit) que junta os dois históricos:

BASH
git checkout main
git pull origin main

git checkout feature/login-com-google
git merge main
Clique para expandir e ver mais

Vantagens: preserva o histórico exatamente como ele aconteceu, é mais seguro para branches compartilhadas e não reescreve commits já enviados.

Desvantagem: o histórico pode ficar “poluído” com vários commits de merge, especialmente em projetos com muitas branches de curta duração.

TEXT
Antes do merge:

main       o---o---o
                    \
feature              o---o---o

Depois do merge (novo commit de mesclagem "M"):

main       o---o---o-------------M
                    \            /
feature              o---o---o--/
Clique para expandir e ver mais

git rebase

O rebase reaplica os commits da sua branch em cima do último estado da branch base, como se você tivesse começado seu trabalho a partir daquele ponto mais recente:

BASH
git checkout feature/login-com-google
git fetch origin
git rebase origin/main
Clique para expandir e ver mais

O resultado é um histórico linear, sem commits de merge extras - como se toda a feature tivesse sido desenvolvida do zero em cima da main mais atual.

TEXT
Antes do rebase:

main       o---o---o---o        <- main avançou com novos commits
                    \
feature              o---o---o  <- seus commits antigos

Depois do "git rebase origin/main":

main       o---o---o---o
                        \
feature                  o'---o'---o'   <- seus commits reaplicados
                                          (hashes novos!)
Clique para expandir e ver mais

⚠️ Regra de ouro do rebase: nunca faça rebase de uma branch que já foi compartilhada com outras pessoas (ex: main, develop) e que já foi pushada. Rebase reescreve o histórico (novos hashes de commit), e isso quebra o trabalho de quem já baixou os commits antigos. Use rebase livremente na sua branch de feature, antes de abrir o PR ou enquanto só você trabalha nela.

Rebase interativo: limpando o histórico

Antes de abrir um Pull Request, é comum “limpar” a sequência de commits com o rebase interativo:

BASH
git rebase -i HEAD~5
Clique para expandir e ver mais

Isso abre um editor onde você pode, para cada um dos últimos 5 commits:

É muito útil para transformar vários commits do tipo “wip”, “ajuste”, “corrige typo” em um histórico limpo e compreensível antes de enviar para revisão.

Quando usar cada um?

SituaçãoRecomendação
Atualizar sua branch de feature local com a mainrebase
Trazer uma feature finalizada para a main via Pull Requestmerge (geralmente feito pela interface do GitHub/GitLab)
Branch já compartilhada com o timemerge (nunca rebase)
Limpar commits antes de abrir o PRrebase -i

Resolvendo conflitos

Conflitos acontecem quando o Git não consegue decidir sozinho como combinar duas mudanças na mesma linha de código. Tanto merge quanto rebase podem gerar conflitos - a diferença é como você resolve.

Exemplo prático

Imagine que, na main, alguém alterou a função de desconto:

PYTHON
# main
def calcular_desconto(valor):
    return valor * 0.9  # 10% de desconto
Clique para expandir e ver mais

E, na sua branch feature/desconto-especial, você alterou a mesma linha:

PYTHON
# feature/desconto-especial
def calcular_desconto(valor):
    return valor * 0.85  # 15% de desconto
Clique para expandir e ver mais

Ao rodar git merge main (ou git rebase origin/main), o Git não sabe qual das duas versões manter e marca o arquivo como conflitante:

BASH
$ git merge main
Auto-merging app.py
CONFLICT (content): Merge conflict in app.py
Automatic merge failed; fix conflicts and then commit the result.
Clique para expandir e ver mais

Abrindo app.py, você verá os marcadores de conflito:

PYTHON
def calcular_desconto(valor):
<<<<<<< HEAD
    return valor * 0.9  # 10% de desconto
=======
    return valor * 0.85  # 15% de desconto
>>>>>>> feature/desconto-especial
Clique para expandir e ver mais

Você resolve editando manualmente para o resultado desejado (aqui, decidindo manter os 15%) e removendo os marcadores:

PYTHON
def calcular_desconto(valor):
    return valor * 0.85  # 15% de desconto (decisão do time de vendas)
Clique para expandir e ver mais

E então finaliza o processo:

BASH
# durante um merge com conflito
git status                    # mostra os arquivos em conflito
git add app.py                # marca o conflito como resolvido
git commit                    # finaliza o merge

# durante um rebase com conflito
git status
git add app.py
git rebase --continue          # continua o rebase, NÃO use "git commit"

# para abortar e voltar ao estado anterior ao conflito
git merge --abort
git rebase --abort
Clique para expandir e ver mais

Resolvendo rápido, mantendo um dos lados

Quando você sabe de antemão que quer manter inteiramente uma das versões (sem misturar), pode pular a edição manual:

BASH
# mantém a versão da sua branch atual
git checkout --ours app.py

# mantém a versão que está sendo integrada
git checkout --theirs app.py

git add app.py
git commit   # ou git rebase --continue
Clique para expandir e ver mais

⚠️ Cuidado com o rebase: durante um rebase, o significado de --ours e --theirs é invertido em relação ao merge, porque o Git está reaplicando seus commits sobre a outra branch. Na dúvida, sempre confira o conteúdo do arquivo antes de commitar - não confie apenas no nome da flag.

Dica: rode git rebase --abort sem medo se as coisas saírem do controle - você volta exatamente ao estado antes de começar o rebase.

Git Worktree: complemento (não substituto) da feature branch

Um erro comum é achar que git worktree é “outra forma de criar feature branch”. Na verdade, são coisas diferentes que resolvem problemas diferentes:

Ou seja: você continua usando feature branches normalmente - o worktree só muda onde e como você acessa cada uma delas no seu disco.

O problema que o worktree resolve

Sem worktree, se você está no meio de uma feature (com mudanças não commitadas) e precisa atender um hotfix urgente, o fluxo típico é:

BASH
git stash                      # guarda o trabalho em andamento
git switch main
git switch -c hotfix/corrige-login
# ... resolve o hotfix, commita, envia ...
git switch feature/nova-funcionalidade
git stash pop                  # recupera o trabalho
Clique para expandir e ver mais

Funciona, mas é um vai-e-vem arriscado - principalmente se você tem arquivos gerados, servidores rodando ou testes em andamento que dependem do estado atual dos arquivos.

Com git worktree, você cria uma pasta adicional apontando para outra branch, sem mexer no seu diretório de trabalho atual:

BASH
# cria um novo worktree em ../projeto-hotfix, na branch hotfix/corrige-login
git worktree add ../projeto-hotfix -b hotfix/corrige-login main

# lista os worktrees ativos
git worktree list

# remove o worktree quando não precisar mais dele
git worktree remove ../projeto-hotfix
Clique para expandir e ver mais

Agora você tem duas pastas, cada uma com seu próprio working directory, checkouts independentes, mas compartilhando o mesmo histórico de commits:

TEXT
~/projetos/meu-app              (branch: feature/nova-funcionalidade)
~/projetos/meu-app-hotfix       (branch: hotfix/corrige-login)

                 mesmo repositório .git por trás dos dois
Clique para expandir e ver mais

Feature branch vs Worktree

AspectoFeature BranchGit Worktree
O que éUma linha isolada de commits dentro do históricoUm diretório de trabalho extra, ligado ao mesmo repositório
Troca de contextogit switch/checkout troca os arquivos na mesma pastaBasta abrir outra pasta/terminal - nada muda na pasta atual
Precisa de stash?Sim, se houver mudanças não commitadasNão - cada worktree mantém seu próprio estado
Uso típicoIsolar o desenvolvimento de uma funcionalidadeTrabalhar em duas branches ao mesmo tempo (ex: hotfix urgente + feature em andamento), rodar testes em paralelo, ou revisar um PR sem sair da sua branch atual
Substitui o outro?NãoNão - os dois se complementam

Na prática: você continua criando uma feature branch para cada funcionalidade; o worktree é só uma ferramenta a mais para quando você precisa estar em duas branches fisicamente ao mesmo tempo, sem o custo de clonar o repositório inteiro de novo.

Corrigindo erros comuns

Aqui estão os “socorros” que mais uso no dia a dia.

Esqueci de incluir um arquivo no último commit

BASH
git add arquivo-esquecido.py
git commit --amend --no-edit
Clique para expandir e ver mais

Quero mudar a mensagem do último commit

BASH
git commit --amend -m "fix: corrige mensagem anterior"
Clique para expandir e ver mais

⚠️ --amend reescreve o commit (novo hash). Só use em commits que ainda não foram enviados (push) ou em branches só suas.

Fiz commit na branch errada

BASH
# desfaz o commit mas mantém as mudanças no working directory
git reset --soft HEAD~1

# muda para a branch correta
git switch nome-da-branch-certa

# refaz o commit lá
git commit -m "feat: minha mudança na branch certa"
Clique para expandir e ver mais

Preciso desfazer o último commit

BASH
# mantém as alterações no staging
git reset --soft HEAD~1

# mantém as alterações no working directory (fora do staging)
git reset --mixed HEAD~1

# descarta as alterações completamente (cuidado, é destrutivo!)
git reset --hard HEAD~1
Clique para expandir e ver mais

Já enviei o commit errado e outras pessoas já puxaram esse histórico

Nesse caso, não use reset (que reescreve histórico). Use revert, que cria um novo commit desfazendo as mudanças, sem apagar o histórico:

BASH
git revert <hash-do-commit>
Clique para expandir e ver mais

Quero descartar mudanças não commitadas em um arquivo

BASH
# forma moderna
git restore arquivo.py

# forma antiga, ainda muito usada
git checkout -- arquivo.py
Clique para expandir e ver mais

Preciso trocar de branch, mas tenho mudanças no meio do caminho

BASH
git stash                     # guarda as mudanças temporariamente
git switch outra-branch
# ... faz o que precisa ...
git switch branch-original
git stash pop                 # traz as mudanças de volta
Clique para expandir e ver mais

Enviei um push, mas preciso corrigir o histórico da minha própria feature branch

BASH
git push --force-with-lease origin feature/login-com-google
Clique para expandir e ver mais

--force-with-lease é mais seguro que --force puro: ele falha se alguém mais enviou commits para a branch remota depois da sua última atualização, evitando que você sobrescreva o trabalho de outra pessoa sem perceber.

Atualizando o repositório remoto

BASH
# baixa as referências remotas sem alterar sua branch local
git fetch origin

# baixa e já integra (merge ou rebase, conforme configuração)
git pull origin main

# envia sua branch para o remoto
git push origin feature/login-com-google
Clique para expandir e ver mais

Um fluxo completo, do início ao fim

  1. Atualize a main local: git checkout main && git pull
  2. Crie a feature branch: git switch -c feature/nova-funcionalidade
  3. Trabalhe em commits pequenos e com mensagens claras
  4. Antes de abrir o PR, atualize com a main: git fetch && git rebase origin/main
  5. Se necessário, limpe o histórico: git rebase -i HEAD~N
  6. Envie: git push -u origin feature/nova-funcionalidade
  7. Abra o Pull Request e peça revisão
  8. Após aprovado, faça o merge (geralmente squash and merge ou merge commit, dependendo do padrão do time)
  9. Delete a branch local e remota depois do merge

Conclusão

Dominar o fluxo básico do Git (init, add, commit, push) é o primeiro passo, mas o que realmente muda sua produtividade em equipe é entender feature branches, saber escolher entre merge e rebase, e ter confiança para corrigir seus próprios erros sem entrar em pânico.

Com esses comandos na manga, você deixa de ter medo do Git e passa a usá-lo como o que ele é: uma ferramenta poderosa para trabalhar em equipe com segurança.

Ficou com alguma dúvida sobre algum desses comandos? Deixe nos comentários - vou adorar ajudar!

Referências

Iniciar busca

Digite palavras-chave para buscar

↑↓
ESC
⌘K Atalho