Git básico: commit, branch e merge

1. Visão geral

Git é a ferramenta de gerenciamento de versões que quase todo projeto usa, mas muitos iniciantes apenas memorizam alguns comandos sem entender a essência. Este artigo passa por três conceitos fundamentais: commit (salvar um snapshot), branch (uma ramificação de trabalho independente) e merge (mesclar ramificações). Após ler, você terá confiança para criar uma nova ramificação, fazer commit corretamente, lidar com conflitos ao fazer merge e saber quando usar merge em vez de rebase.

2. Commit – salvar um snapshot

2.1. O que é commit

Cada commit é um “snapshot” completo do estado de todos os arquivos em um ponto no tempo, junto com um ponteiro para o commit pai. Git não armazena diffs, mas snapshots — isso torna branch/merge rápido e eficiente.

# Inicializar repo e fazer o primeiro commit
git init order-service
cd order-service
echo "# Order Service" > README.md
git add README.md
git commit -m "docs: adicionar README para Order Service"

Explicação:

  • git init cria um novo repositório, gerando o diretório oculto .git.
  • git add README.md coloca o arquivo na staging area (área de preparação).
  • git commit -m "..." cria um snapshot com uma mensagem descrevendo a mudança.

Verificar o histórico:

git log --oneline
a1b2c3d docs: adicionar README para Order Service

2.2. Escrever boas mensagens de commit

Um commit deve responder a pergunta “o que essa mudança faz e por quê”. A convenção comum (conventional commits):

feat: adicionar API para criar pedido
fix: corrigir cálculo incorreto do total ao aplicar cupom
refactor: extrair OrderValidator de OrderService

Nota: Evite mensagens de commit vagas como "update" ou "fix bug". Meses depois, ao olhar git log, você não saberá o que esse commit corrigiu.

2.3. Commit de partes com git add -p

Quando um arquivo tem várias mudanças não relacionadas, não faça commit de tudo de uma vez.

git add -p src/main/java/com/nonladev/OrderService.java

Git perguntará qual “hunk” (seção de mudança) você deseja preparar, permitindo separar em commits menores, fáceis de revisar e reverter se necessário.

3. Branch – trabalhar independentemente em várias ramificações

3.1. Criar e alternar entre branches

Uma branch é apenas um ponteiro leve para um commit específico. Criar uma branch não copia todo o código, apenas cria um novo ponteiro.

# Criar uma nova branch e alternar para ela
git branch feature/add-payment
git checkout feature/add-payment

# Ou combinar os dois comandos acima em um (Git ≥ 2.23)
git switch -c feature/add-payment

Explicação:

  • git branch <nome> cria uma branch, mas você continua na branch atual.
  • git checkout <nome> ou git switch <nome> move HEAD para essa branch.
  • -c em git switch é um atalho para criar e alternar ao mesmo tempo.

3.2. Listar branches

git branch -v
* feature/add-payment  d4e5f6a adicionar esqueleto do PaymentService
  main                 a1b2c3d docs: adicionar README para Order Service

O asterisco * indica a branch atual (onde HEAD está).

3.3. Nomear branches com convenção

Nomear branches claramente ajuda todo o time a saber o que a ramificação faz:

feature/add-payment       # adicionar nova funcionalidade
fix/order-total-wrong     # corrigir erro
refactor/extract-validator
hotfix/prod-null-pointer

3.4. Deletar branch após conclusão

# Deletar branch local que foi mesclada
git branch -d feature/add-payment

# Deletar branch não mesclada (forçar, cuidado com perda de commits)
git branch -D feature/add-payment

-d (minúscula) é mais seguro porque Git avisará se a branch ainda não foi mesclada.

4. Merge – mesclar ramificações

4.1. Merge de avanço rápido

Se main não tem nenhum novo commit desde que você criou a branch, Git apenas move o ponteiro main para o último commit da branch feature — chamado fast-forward.

git switch main
git merge feature/add-payment
Updating a1b2c3d..d4e5f6a
Fast-forward
 PaymentService.java | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

Nenhum novo commit de merge é criado, o histórico permanece linear.

4.2. Merge commit (merge de 3 vias)

Se ambas as branches têm commits próprios desde o ponto de divergência, Git cria um merge commit com dois pais, mesclando o conteúdo de ambas.

git switch main
git commit -am "fix: corrigir validação de quantidade de item"   # main tem novo commit
git switch feature/add-payment
git switch main
git merge feature/add-payment
Merge made by the 'recursive' strategy.
 PaymentService.java | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

Git cria automaticamente um commit de merge com a mensagem padrão Merge branch 'feature/add-payment'.

4.3. Resolver conflitos ao fazer merge

Conflitos ocorrem quando duas branches modificam a mesma região de código. Por exemplo, a classe OrderService:

// Em main
public class OrderService {
    public BigDecimal calculateTotal(Order order) {
        return order.getSubtotal().multiply(BigDecimal.valueOf(1.1)); // adicionar imposto de 10%
    }
}
// Em feature/add-payment
public class OrderService {
    public BigDecimal calculateTotal(Order order) {
        return order.getSubtotal().subtract(order.getDiscount()); // subtrair desconto
    }
}

Ao fazer merge, Git não consegue decidir qual linha manter, e o arquivo mostrará marcadores:

public class OrderService {
    public BigDecimal calculateTotal(Order order) {
<<<<<<< HEAD
        return order.getSubtotal().multiply(BigDecimal.valueOf(1.1));
=======
        return order.getSubtotal().subtract(order.getDiscount());
>>>>>>> feature/add-payment
    }
}

Você precisa editar manualmente para combinar a lógica corretamente (por exemplo: subtrair o desconto primeiro, depois adicionar o imposto), deletar os marcadores <<<<<<<, =======, >>>>>>>, depois:

git add OrderService.java
git commit

Nota: Ao fazer commit após resolver um conflito, você não precisa de -m, Git abrirá um editor com uma mensagem padrão deixando claro que é um merge commit — manter essa mensagem é suficiente, a menos que queira adicionar uma observação.

4.4. Merge vs Rebase

Critério merge rebase
Histórico Mantém, tem commit de merge Reescreve histórico, branch linear
Segurança Não modifica commits antigos Modifica commits antigos (muda hash)
Usar quando Branch compartilhada pública (main, develop) Branch pessoal, quer histórico limpo antes de criar PR
# Rebase da branch feature sobre main mais recente
git switch feature/add-payment
git rebase main

Nota: Não faça rebase em uma branch que já foi pushed e outras pessoas estão puxando — você vai desalinhar o histórico deles, causando conflitos em massa quando puxarem novamente.

5. Testar um cenário de merge com JUnit

Embora Git não seja código Java, você pode escrever testes para confirmar que a lógica de negócio após fazer merge de duas branches não quebrou — é por isso que CI sempre executa testes no merge commit. Exemplo de OrderService após resolver o conflito na seção 4.3:

public class OrderService {

    public BigDecimal calculateTotal(Order order) {
        BigDecimal afterDiscount = order.getSubtotal().subtract(order.getDiscount());
        return afterDiscount.multiply(BigDecimal.valueOf(1.1));
    }
}
class OrderServiceTest {

    private final OrderService orderService = new OrderService();

    @Test
    void shouldApplyDiscountThenTax() {
        Order order = new Order(
                BigDecimal.valueOf(100),  // subtotal
                BigDecimal.valueOf(10)    // desconto
        );

        BigDecimal total = orderService.calculateTotal(order);

        assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(99.0));
    }
}

Explicação: subtotal 100 menos desconto 10 restam 90, multiplicado por imposto de 10% resulta em 99. Este teste é exatamente a “rede de segurança” que garante que mesclar duas lógicas (imposto e desconto) não se cancelem.

✓ shouldApplyDiscountThenTax()

6. Fluxo de trabalho recomendado para equipe pequena

  1. Sempre crie uma nova branch a partir de main para cada tarefa: git switch -c feature/xxx.
  2. Commits pequenos, frequentes, mensagens claras.
  3. Antes de criar um Pull Request, atualize a branch com o main mais recente:
    git switch feature/add-payment
    git fetch origin
    git merge origin/main
  4. Resolva conflitos (se houver) localmente, execute os testes novamente.
  5. Faça push e crie um PR, deixe CI executar os testes no merge commit antes de mesclar em main.

Nota: Não faça commit diretamente em main em uma equipe com várias pessoas — sempre passe por branch + PR para ter revisão e verificação de CI primeiro.

7. Erros comuns

  • Esquecer git add antes do commit: Git reporta nothing to commit, working tree clean mesmo que você tenha modificado o arquivo — porque o arquivo ainda não foi preparado (staged).
  • Mesclar a branch errada: sempre use git status e git branch para verificar onde você está antes de fazer merge.
  • Marcadores de conflito deixados no código: esquecer de deletar <<<<<<< fará o código não compilar — sempre compile/teste imediatamente após resolver um conflito.
  • Force push em branch compartilhada: git push --force após rebase de uma branch compartilhada deletará o histórico de seus colegas. Use apenas --force-with-lease e apenas em branches pessoais.

8. Conclusão

Commit, branch e merge são as três operações que você usa diariamente com Git. Dominar commit deixa o histórico do código claro, branch permite trabalho paralelo sem se afetar, e entender merge (junto com como resolver conflitos) constrói confiança ao integrar código de várias pessoas. Pratique commits pequenos, nomeie branches com convenção e sempre execute testes antes de mesclar em main.

gitversion-controlbranchmergedevops