Branches e merge
Esta aula aborda o gerenciamento de branches no Git, incluindo criação, merge e resolução de conflitos. Também são apresentadas boas práticas para manter um fluxo de trabalho organizado em equipe.
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-loginVocê 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-loginSe 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:
- Identifique os arquivos conflitantes com
git status. - Abra o arquivo e edite removendo os marcadores, mantendo o código desejado.
- Adicione as alterações com
git add <arquivo>. - Finalize o merge com
git commit(ougit merge --continue).
Exemplo de arquivo conflitante:
# Conteúdo do arquivo index.html
<<<<<<< HEAD
<h1>Home</h1>
=======
<h1>Página Inicial</h1>
>>>>>>> feature-loginApó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
- Git Branching - Pro Git Book
- git-branch documentation
- git-merge documentation
- GitHub: About merge conflicts
- Atlassian Git Tutorials: Using Branches
- A successful Git branching model (Git Flow)
Exercícios
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-1No 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"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.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 comandogit log --graph --onelinemostra 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.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), ehotfix/*(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.