Nesta aula, vamos explorar o conceito de branches no Git, que são linhas paralelas de desenvolvimento. Branches permitem que você trabalhe em funcionalidades, correções ou experimentos sem afetar o código principal. O merge é a operação que combina essas linhas de volta. Entender como criar branches, realizar merges e resolver conflitos é essencial para qualquer profissional de DevOps.

Além disso, veremos boas práticas para nomear branches, manter commits atômicos e usar estratégias como Git Flow ou GitHub Flow. Ao final, você terá domínio para gerenciar branches de forma eficiente em projetos reais.

Criando branches

Branches são ponteiros para commits específicos. Ao criar um branch, você cria um novo ponteiro que pode se mover independentemente. O comando git branch <nome> cria um novo branch a partir do commit atual. Para mudar para ele, use git checkout <nome> ou git switch <nome> (Git 2.23+).

Exemplo: criar um branch chamado feature-login e mudar para ele:

git branch feature-login
git checkout feature-login
# ou em um comando:
git checkout -b feature-login

Você pode listar branches com git branch (o atual aparece com *). Para ver branches remotos, use git branch -r. Lembre-se de que branches são leves: apenas um arquivo de 41 bytes no diretório .git/refs/heads.

merge

O merge combina o histórico de dois branches. O comando git merge <branch> traz as alterações do branch especificado para o branch atual. Existem dois tipos principais: merge fast-forward (quando não há divergência) e merge com commit de merge (quando há divergência).

Exemplo: estando no branch main, mesclar o branch feature-login:

git checkout main
git merge feature-login

Se não houver conflitos, o Git cria um commit de merge automaticamente (ou faz fast-forward se possível). É recomendável usar a flag --no-ff para forçar um commit de merge, preservando o histórico da funcionalidade. Após o merge, você pode deletar o branch com git branch -d feature-login.

Conflitos

Conflitos ocorrem quando o Git não consegue mesclar automaticamente alterações concorrentes na mesma parte de um arquivo. O Git marca o arquivo com marcadores especiais (<<<<<<<, =======, >>>>>>>) e você precisa editar manualmente para resolver.

Passos para resolver um conflito:

  1. Identifique os arquivos conflitantes com git status.
  2. Abra o arquivo e edite removendo os marcadores, mantendo o código desejado.
  3. Adicione as alterações com git add <arquivo>.
  4. Finalize o merge com git commit (ou git merge --continue).

Exemplo de arquivo conflitante:

# Conteúdo do arquivo index.html
<<<<<<< HEAD
<h1>Home</h1>
=======
<h1>Página Inicial</h1>
>>>>>>> feature-login

Após editar, remova os marcadores e deixe <h1>Página Inicial</h1>. Depois adicione e commit.

Boas práticas

Branches devem ser curtos e ter nomes descritivos (ex: feature/issue-123, bugfix/login-error). Mantenha branches atualizados com a base frequentemente (rebase ou merge) para evitar grandes conflitos. Faça commits pequenos e atômicos, e evite branches de longa duração.

Estratégias de branching como Git Flow e GitHub Flow ajudam a organizar o fluxo. Git Flow usa branches develop e main, enquanto GitHub Flow trabalha com branches de feature e merge direto para main. Escolha a que se adapta ao seu time. Lembre-se de deletar branches após o merge para manter o repositório limpo.

Referências

Exercícios

  1. Crie um repositório Git local, inicialize-o e crie um branch chamado feature-1. Faça um commit no branch main e outro no branch feature-1. Em seguida, faça o merge de feature-1 em main.

    ✓ Resposta:
    git init
    echo "main" > arquivo.txt
    git add .
    git commit -m "commit em main"
    git checkout -b feature-1
    echo "feature-1" >> arquivo.txt
    git add .
    git commit -m "commit em feature-1"
    git checkout main
    git merge feature-1
  2. No mesmo repositório, crie um conflito propositalmente: no branch main, modifique a primeira linha de um arquivo. No branch feature-2, modifique a mesma linha de forma diferente. Tente fazer o merge e resolva o conflito manualmente.

    ✓ Resposta:
    git checkout main
    echo "linha main" > conflito.txt
    git add .
    git commit -m "main"
    git checkout -b feature-2
    echo "linha feature-2" > conflito.txt
    git add .
    git commit -m "feature-2"
    git checkout main
    git merge feature-2  # conflito!
    # Editar conflito.txt para manter uma versão
    git add conflito.txt
    git commit -m "merge resolvido"
  3. Explique a diferença entre merge fast-forward e merge com commit de merge. Dê um exemplo de quando cada um ocorre.

    ✓ Resposta: O merge fast-forward ocorre quando o branch de destino não teve novos commits desde a criação do branch de origem; o Git simplesmente avança o ponteiro. O merge com commit de merge ocorre quando houve commits divergentes, criando um novo commit que une os históricos. Exemplo: fast-forward se você cria um branch, faz commits e volta ao main sem novos commits; commit de merge se você faz commits no main enquanto o branch também evolui.
  4. Usando o comando git log --graph, visualize o histórico de merges do repositório do exercício 1. O que você observa?

    ✓ Resposta: O comando git log --graph --oneline mostra um gráfico linear se foi fast-forward, ou um gráfico com bifurcação e merge se houve commit de merge. No exercício 1, como feature-1 foi criado a partir de main e não houve novos commits em main, o merge será fast-forward, resultando em histórico linear.
  5. Pesquise sobre a estratégia Git Flow. Liste os branches principais e suas funções.

    ✓ Resposta: Git Flow utiliza os branches: main (produção), develop (integração), feature/* (funcionalidades), release/* (preparação de release), e hotfix/* (correções urgentes). Cada um tem um propósito específico para organizar o ciclo de desenvolvimento.

Bônus: Sempre que possível, use git merge --no-ff para preservar o histórico de branches. Isso facilita a visualização de quando uma funcionalidade foi integrada.