Git na Prática: Commits, Branches e Merges sem Medo

Git na Prática: Commits, Branches e Merges sem Medo

Git no fluxo real de trabalho: os três estados de um arquivo, o ciclo status/add/commit, leitura do histórico, branches e merge, resolução de conflitos e a diferença entre reset e revert em histórico já compartilhado.
DevOps

• • 10 min de leitura

Antes do Git, equipes de desenvolvimento compartilhavam código por e-mail, pen drives ou pastas em servidores de rede. Cada mudança era um risco. Desfazer algo era manual e impreciso. Trabalhar em paralelo era um pesadelo de conflitos.

O Git resolveu tudo isso de forma elegante: ele rastreia cada mudança em cada arquivo, permite que múltiplas pessoas trabalhem simultaneamente no mesmo projeto e oferece um histórico completo e auditável de tudo que aconteceu. Em DevOps, o Git não é apenas uma ferramenta de desenvolvimento — é a base sobre a qual pipelines, deploys e automações são construídos. Nenhum pipeline de CI/CD existe sem um repositório Git na origem.

Este artigo cobre o que se usa no dia a dia: commits, branches e merges. Sem teoria desnecessária, direto ao fluxo real de trabalho.

Configuração Inicial

Antes de qualquer coisa, é necessário informar ao Git quem está fazendo as alterações. Essa informação aparece em cada commit:

git config --global user.name "Ricardo Matos"
git config --global user.email "ricardo@exemplo.com"

# Define o VSCode como editor padrão (opcional)
git config --global core.editor "code --wait"

# Define 'main' como nome padrão da branch principal
git config --global init.defaultBranch main

Para verificar todas as configurações:

git config --list

Iniciando um Repositório

Dentro do diretório do projeto:

mkdir meu-projeto
cd meu-projeto
git init

O comando git init cria um diretório oculto .git que armazena todo o histórico e configurações do repositório. Esse diretório não deve ser modificado manualmente.

Para clonar um repositório existente do GitHub:

git clone https://github.com/usuario/repositorio.git

# Clona para uma pasta com nome diferente
git clone https://github.com/usuario/repositorio.git meu-nome-local

O Ciclo Fundamental: Status, Add, Commit

O fluxo básico do Git envolve três estados para cada arquivo: não rastreado (novo arquivo que o Git ainda não conhece), staged (preparado para o próximo commit) e committed (salvo no histórico).

# Verifica o estado atual do repositório
git status

Cria-se um arquivo e observa-se o que o Git reporta:

echo "# Meu Projeto DevOps" > README.md
git status
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README.md

Adiciona-se ao stage e faz-se o commit:

# Adiciona um arquivo específico
git add README.md

# Adiciona todos os arquivos modificados
git add .

# Confirma o que está no stage
git status

# Faz o commit com uma mensagem descritiva
git commit -m "feat: adiciona README inicial do projeto"

A mensagem do commit deve descrever o que foi feito e por quê — não o como. Mensagens vagas como "atualização" ou "fix" são problemáticas em projetos com múltiplos colaboradores.

Visualizando o Histórico

# Histórico completo
git log

# Formato compacto — um commit por linha
git log --oneline

# Com gráfico de branches
git log --oneline --graph --all

# Últimos 5 commits
git log --oneline -5

Para ver o que mudou em um commit específico:

git show abc1234

Para ver as diferenças entre o estado atual e o último commit:

git diff

Trabalhando com Branches

Uma branch é uma linha independente de desenvolvimento. O uso de branches permite trabalhar em uma nova funcionalidade sem afetar o código principal, testar mudanças com segurança e colaborar em paralelo com outros desenvolvedores.

# Lista todas as branches
git branch

# Cria uma nova branch
git branch feature/configurar-nginx

# Muda para a branch criada
git checkout feature/configurar-nginx

# Atalho: cria e já muda para a branch
git checkout -b feature/configurar-nginx

# Forma moderna (Git 2.23+)
git switch -c feature/configurar-nginx

Trabalhando na nova branch:

# Cria um arquivo de configuração fictício
mkdir config
echo "server { listen 80; }" > config/nginx.conf

git add config/nginx.conf
git commit -m "feat: adiciona configuração base do Nginx"

Neste momento, a branch main não tem esse arquivo. As mudanças existem apenas na branch feature/configurar-nginx.

Merge: Integrando o Trabalho

Quando o trabalho em uma branch está pronto, integra-se de volta à branch principal:

# Volta para a branch principal
git checkout main

# Integra as mudanças da feature branch
git merge feature/configurar-nginx

O Git tentará fazer o merge automaticamente. Se não houver conflitos, o resultado é um fast-forward (quando a branch principal não teve nenhum commit novo desde que a feature foi criada) ou um merge commit (quando ambas as branches avançaram).

Após o merge, a branch de feature pode ser removida:

git branch -d feature/configurar-nginx

Resolvendo Conflitos

Conflitos acontecem quando dois commits modificam a mesma linha do mesmo arquivo de formas diferentes. O Git não decide qual versão é a correta — ele marca o conflito e aguarda resolução manual.

O arquivo conflitante terá a seguinte aparência:

<<<<<<< HEAD
server { listen 80; server_name localhost; }
=======
server { listen 8080; }
>>>>>>> feature/porta-alternativa

A seção entre <<<<<<< HEAD e ======= é o conteúdo da branch atual. Entre ======= e >>>>>>> é o conteúdo da branch sendo integrada. Edita-se o arquivo manualmente, remove-se os marcadores e deixa-se o resultado desejado:

server { listen 80; server_name localhost; }

Depois:

git add config/nginx.conf
git commit -m "merge: resolve conflito de configuração de porta"

Desfazendo Coisas

Uma das maiores vantagens do Git é a capacidade de desfazer. As situações mais comuns:

# Desfaz mudanças não commitadas em um arquivo
git checkout -- arquivo.txt

# Remove um arquivo do stage (sem perder as mudanças)
git restore --staged arquivo.txt

# Desfaz o último commit, mantendo as mudanças no stage
git reset --soft HEAD~1

# Desfaz o último commit e descarta as mudanças — irreversível
git reset --hard HEAD~1

# Cria um novo commit que inverte um commit anterior (seguro para histórico compartilhado)
git revert abc1234

A diferença entre reset --hard e revert é fundamental: o reset reescreve o histórico — perigoso quando o código já foi enviado para um repositório remoto. O revert cria um novo commit de desfazimento, preservando o histórico intacto.

Conectando ao GitHub

# Adiciona o repositório remoto
git remote add origin https://github.com/usuario/meu-projeto.git

# Verifica os remotos configurados
git remote -v

# Envia a branch main para o repositório remoto
git push -u origin main

# Nos envios seguintes, basta
git push

# Baixa e integra as mudanças do remoto
git pull

Fontes e leituras recomendadas

Documentação oficial

Referência rápida

  • Git Cheat Sheet — GitHub Education — PDF com os comandos mais usados do Git. Útil nos primeiros meses até que os comandos se tornem automáticos.
  • Oh Shit, Git! — Guia direto em português para as situações em que algo deu errado e é preciso desfazer. Prático e bem-humorado.

Prática interativa

  • Learn Git Branching — Simulador visual e interativo de branches e merges no navegador. É o melhor recurso disponível para desenvolver intuição sobre o modelo de branches do Git.
  • GitHub Skills — Cursos práticos do próprio GitHub ensinando Git e GitHub com repositórios reais.

Exercícios

Exercício 1

Quais são os três estados de um arquivo no Git, e qual comando move o arquivo de um estado para o outro? Por que o estado intermediário existe?

Ver resposta

✓ Resposta: Não rastreado (o Git ainda não conhece o arquivo), staged (preparado para o próximo commit) e committed (gravado no histórico). O git add leva do primeiro ao segundo; o git commit leva do segundo ao terceiro. O estado intermediário existe para que você escolha o que entra em cada commit: é ele que permite separar, numa mesma sessão de trabalho, a correção de um bug da reorganização de indentação que você fez no caminho. Sem o stage, todo commit seria "tudo que eu mexi desde a última vez".

Exercício 2

Qual a diferença entre um merge fast-forward e um merge commit? O que determina qual dos dois o Git vai fazer?

Ver resposta

✓ Resposta: O que determina é se a branch principal avançou desde que a feature nasceu. Se não avançou, o Git faz fast-forward: apenas move o ponteiro da main até o último commit da feature, sem criar commit algum, e o histórico continua linear. Se as duas avançaram em paralelo, não existe ponteiro a simplesmente adiantar — o Git cria um merge commit, que tem dois pais e une as duas linhas. O primeiro produz histórico mais limpo; o segundo registra de forma explícita que houve trabalho paralelo e quando ele foi integrado.

Exercício 3

No arquivo em conflito abaixo, o que representa cada seção? E o que não pode, de jeito nenhum, sobrar no arquivo final?

<<<<<<< HEAD
server { listen 80; server_name localhost; }
=======
server { listen 8080; }
>>>>>>> feature/porta-alternativa
Ver resposta

✓ Resposta: Entre <<<<<<< HEAD e ======= está o conteúdo da branch em que você está. Entre ======= e >>>>>>> feature/porta-alternativa está o conteúdo da branch que está sendo integrada. O Git não escolhe entre as duas: ele marca a divergência e devolve a decisão para você. O que não pode sobrar são os próprios marcadores — <<<<<<<, ======= e >>>>>>>. Esquecê-los é um erro clássico e silencioso no Git, que aceita o commit sem reclamar; a falha só aparece quando o Nginx recusa o arquivo de configuração ou o código quebra em execução.

Exercício 4

Por que a diferença entre git reset --hard e git revert é decisiva quando o commit já foi enviado para o repositório remoto?

Ver resposta

✓ Resposta: reset --hard HEAD~1 remove o commit da história e descarta as mudanças — ele reescreve o passado. Se aquele commit já estava no remoto, seu histórico passa a divergir do de todo mundo, e a única forma de "alinhar" é um force-push, que apaga trabalho de quem já havia baixado. revert não apaga nada: cria um novo commit que desfaz o efeito do anterior, deixando registrado que a mudança existiu e foi revertida. A regra prática é essa: reset para o que ainda é só seu, revert para o que já é de todos.

Exercício 5

Os três comandos abaixo desfazem coisas diferentes. Explique cada um — e diga qual deles é destrutivo.

git reset --soft HEAD~1
git restore --staged arquivo.txt
git checkout -- arquivo.txt
Ver resposta

✓ Resposta: reset --soft HEAD~1 desfaz o commit mas mantém as mudanças no stage — serve para corrigir a mensagem ou acrescentar algo que ficou de fora. restore --staged arquivo.txt tira o arquivo do stage sem tocar no conteúdo: ele volta a ser "modificado, não preparado". checkout -- arquivo.txt descarta as edições não commitadas e devolve o arquivo ao estado do último commit — este é o destrutivo. O que você digitou e não levou nem ao stage nem ao commit nunca esteve sob controle do Git, e não há de onde recuperá-lo.

Exercício 6

Por que mensagens de commit como "atualização" ou "fix" são problemáticas? O que uma boa mensagem precisa responder — e por que isso pesa ainda mais em DevOps?

Ver resposta

✓ Resposta: Porque o histórico é lido no futuro, por alguém que não estava lá — frequentemente você mesmo, meses depois. A mensagem precisa responder o que mudou e por quê; o como já está no diff, e repeti-lo é desperdício. O custo aparece no pior momento: algo quebra em produção, alguém roda git log --oneline procurando o commit responsável e encontra cinquenta linhas de "fix" e "ajustes" — o histórico deixou de ser ferramenta de investigação. Em DevOps o peso é maior porque o commit é a origem do pipeline: a mensagem costuma alimentar changelog, versionamento e nota de release.

Comentários

Mais em DevOps

Instalação Manual do MySQL no Debian, Arch, Fedora e openSUSE
Instalação Manual do MySQL no Debian, Arch, Fedora e openSUSE

Instalação do MySQL pelo tarball genérico da Oracle em Debian, Arch, Fedora e…

Deploy Automático para Servidores com GitHub Actions
Deploy Automático para Servidores com GitHub Actions

Os três modelos de deploy — push, pull e por agente — e quando cada um se…

Navegando pelo Sistema de Arquivos com Confiança
Navegando pelo Sistema de Arquivos com Confiança

A árvore de diretórios do Linux e o que cada ramo guarda: /etc, /var, /home…