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
Referências para Aprofundamento
Documentação oficial
- Git Documentation — git-scm.com — Documentação oficial completa do Git, incluindo o livro Pro Git disponível gratuitamente em português.
- Pro Git Book — Português — O livro mais completo sobre Git, escrito pelos criadores. Gratuito, online e com tradução em português.
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.