Nesta aula, você aprenderá a trabalhar com branches no Git, um dos conceitos mais poderosos para controle de versão. Branches permitem desenvolver funcionalidades isoladamente, sem interferir no código principal, e depois integrá-las via merge. Vamos explorar desde a criação de branches até a resolução de conflitos, com exemplos práticos e boas práticas.

Dominar branches é essencial para fluxos de trabalho colaborativos como Git Flow e GitHub Flow. Ao final, você será capaz de gerenciar múltiplas linhas de desenvolvimento com confiança.

Criando branches

Uma branch no Git é um ponteiro móvel para um commit específico. Por padrão, o repositório começa com a branch main (ou master). Para criar uma nova branch, use o comando git branch seguido do nome desejado. Isso cria um novo ponteiro apontando para o commit atual.

Para começar a trabalhar na nova branch, você precisa mudar para ela com git checkout ou, de forma mais moderna, git switch. O comando git checkout -b combina criação e troca em um único passo.

# Criar uma branch chamada 'feature-login'
git branch feature-login

# Mudar para a branch criada
git checkout feature-login

# Ou, de forma mais concisa:
git checkout -b feature-login

# Usando o comando moderno (Git 2.23+):
git switch -c feature-login

Para listar todas as branches do repositório, use git branch. A branch atual será destacada com um asterisco.

git branch
# * feature-login
#   main

merge

O merge é o processo de integrar alterações de uma branch em outra. Geralmente, você faz merge de uma branch de funcionalidade de volta para a branch principal (main). O Git cria um commit de merge que combina os históricos.

Para realizar um merge, primeiro mude para a branch de destino (ex: main) e depois execute git merge com o nome da branch de origem.

# Mudar para a branch main
git checkout main

# Fazer merge da branch feature-login
git merge feature-login

Se não houver conflitos, o Git cria automaticamente um commit de merge (ou realiza um fast-forward se possível). Fast-forward acontece quando a branch de destino não teve novos commits desde a criação da branch de origem; nesse caso, o Git apenas move o ponteiro para frente.

# Exemplo de merge com fast-forward
# Suponha que main não teve commits desde que feature-login foi criada
git merge feature-login
# Atualizando main para o commit mais recente de feature-login

Conflitos

Conflitos ocorrem quando duas branches modificam a mesma parte do mesmo arquivo de maneiras diferentes. O Git não consegue decidir automaticamente qual versão manter, então você precisa resolver manualmente.

Quando um conflito acontece, o Git marca o arquivo com marcadores especiais:

# Exemplo de arquivo com conflito
<<<<<<< HEAD
Versão da branch atual (main)
=======
Versão da branch que está sendo mergeada (feature-login)
>>>>>>> feature-login

Para resolver, edite o arquivo removendo os marcadores e deixando o código desejado. Depois, adicione o arquivo à área de staging e faça um commit.

# Após editar manualmente o arquivo para resolver o conflito
git add arquivo-conflitante.txt
git commit -m "Resolve conflito entre main e feature-login"

Você pode usar ferramentas visuais como git mergetool para facilitar a resolução.

# Iniciar ferramenta de merge configurada
git mergetool

Boas práticas

1. Mantenha branches curtas e focadas: crie uma branch para cada funcionalidade ou correção, e faça merge rapidamente. Evite branches de longa duração que divergem muito da base.

2. Nomeie branches de forma consistente: use prefixos como feature/, bugfix/, hotfix/. Exemplo: feature/login.

3. Mantenha sua branch atualizada com a branch base: faça git merge main periodicamente na sua branch de funcionalidade para reduzir conflitos futuros.

4. Use git pull --rebase para evitar commits de merge desnecessários ao sincronizar com o repositório remoto.

5. Resolva conflitos com cuidado: sempre teste o código após resolver conflitos. Use git diff para verificar as alterações.

6. Após o merge, delete a branch de funcionalidade (local e remota) para manter o repositório limpo.

# Deletar branch local
git branch -d feature-login

# Deletar branch remota
git push origin --delete feature-login

Referências

Exercícios

  1. Crie um repositório Git local e uma branch chamada feature/exercicio1. Depois, mude para ela e crie um arquivo teste.txt com o conteúdo "Primeiro exercício". Faça um commit.

    ✓ Resposta:
    git init
    git checkout -b feature/exercicio1
    echo "Primeiro exercício" > teste.txt
    git add teste.txt
    git commit -m "Adiciona teste.txt"
  2. Na branch main, crie um arquivo main.txt com "Arquivo principal" e faça commit. Depois, faça merge da branch feature/exercicio1 para main. Resolva qualquer conflito (neste caso não deve haver).

    ✓ Resposta:
    git checkout main
    echo "Arquivo principal" > main.txt
    git add main.txt
    git commit -m "Adiciona main.txt"
    git merge feature/exercicio1
  3. Crie um conflito proposital: na branch main, altere a primeira linha de teste.txt para "Linha alterada na main". Faça commit. Depois, na branch feature/exercicio1, altere a mesma linha para "Linha alterada na feature" e faça commit. Volte para main e tente fazer merge. Resolva o conflito mantendo a versão da branch feature.

    ✓ Resposta:
    # Na main
    git checkout main
    echo "Linha alterada na main" > teste.txt
    git add teste.txt
    git commit -m "Altera teste.txt na main"
    
    # Na feature
    git checkout feature/exercicio1
    echo "Linha alterada na feature" > teste.txt
    git add teste.txt
    git commit -m "Altera teste.txt na feature"
    
    # Tentar merge
    git checkout main
    git merge feature/exercicio1  # Conflito!
    
    # Resolver: editar teste.txt para conter apenas "Linha alterada na feature"
    git add teste.txt
    git commit -m "Resolve conflito mantendo versão da feature"
  4. Após resolver o conflito, delete a branch feature/exercicio1 localmente. Verifique se ela foi removida com git branch.

    ✓ Resposta:
    git branch -d feature/exercicio1
    git branch  # Deve mostrar apenas main
  5. Explique em poucas palavras a diferença entre um merge com fast-forward e um merge com commit de merge. Dê um exemplo de quando cada um ocorre.

    ✓ Resposta:Fast-forward ocorre quando a branch de destino não teve commits desde a criação da branch de origem; o Git apenas move o ponteiro. Já o merge com commit de merge é necessário quando ambas as branches tiveram commits divergentes, criando um novo commit que une os históricos. Exemplo: fast-forward ao merge de uma branch criada há pouco tempo sem alterações na main; commit de merge quando a main recebeu commits enquanto a feature estava sendo desenvolvida.