Comando git: controlo de versões e ramos no Linux

Cartão de terminal com o comando git commit -m em destaque e a frase «do init ao merge, sem perder história», sobre fundo azul HJFR

Quem já teve de descobrir, num servidor de produção, «quem mudou isto, e quando?» percebe depressa porque é que o git se tornou o padrão do desenvolvimento de software. O git regista a história completa de um projeto — cada alteração, quem a fez e quando — e permite voltar atrás, abrir linhas de trabalho paralelas e colaborar sem pisar o trabalho dos outros.

Neste guia percorremos o fluxo do dia a dia no terminal: configuração, as três áreas, commits e histórico, ramos e fusões, remotos e como desfazer erros com segurança. E um aviso: o histórico guarda tudo — incluindo os segredos que lá nunca deviam ter entrado.

Veja o guia em vídeo

Prefere ver antes de experimentar? No canal HJFR-IT no YouTube mostramos o git opção a opção, com o resultado de cada comando no ecrã — narrado em português e com legendas.

git — Guia Completo: controlo de versões a partir do terminal — canal HJFR-IT no YouTube. Se o áudio não arrancar em português, abra as definições do vídeo (ícone da roda dentada) → Faixa de áudio → Português.

O que faz o git

O git é um sistema de controlo de versões distribuído: cada cópia de um repositório contém a história completa, guardada numa pasta escondida chamada .git. A forma geral é sempre a mesma — git, um subcomando e as respetivas opções:

git <subcomando> [opções] [argumentos]
git help <subcomando>    # o manual de cada subcomando

Configuração e começar um repositório

Antes do primeiro commit, o git precisa de saber quem é: cada commit fica assinado com um nome e um email. Com --global, a configuração é gravada uma única vez em ~/.gitconfig e serve para todos os repositórios.

git config --global user.name "Aluno HJFR"
git config --global user.email aluno@example.com
  • git init — transforma a pasta atual num repositório novo e vazio, criando a pasta .git onde vai viver a história.
  • git clone URL — a forma mais comum: copia um repositório existente, de uma plataforma de alojamento de código ou de um servidor interno, com toda a história e já ligado à origem remota, o origin.

As três áreas: trabalho, staging e repositório

Para entender o git é essencial perceber as suas três áreas. O diretório de trabalho é onde edita os ficheiros; a staging (ou índice) é a zona intermédia onde prepara o próximo commit; o repositório é a história permanente.

git status, a bússola

O git status diz onde está cada coisa: o que mudou e não foi preparado, o que já está na staging e o que o git não segue (untracked). Use-o constantemente.

git add: preparar o próximo commit

  • git add ficheiro — passa um ficheiro concreto para a staging.
  • git add -A — prepara tudo de uma vez: novos, modificados e apagados. Prático, mas apanha também o que não devia.
  • git add -p — o modo patch mostra cada bloco de alterações (hunk) e pergunta se entra. Assim divide um trabalho confuso em commits coerentes.

O que nunca deve ser seguido — segredos, artefactos de compilação — lista-se no .gitignore, um padrão por linha (por exemplo .env ou build/). Atenção: só atua sobre ficheiros ainda não seguidos; um ficheiro que já esteja no índice tem de sair com git rm --cached.

Commits e histórico

O commit é o coração do git: grava na história o que está na staging, criando um instantâneo com um identificador único (o hash). A mensagem deve descrever o porquê da alteração, não só o quê.

  • git commit -m "mensagem" — grava a staging com uma mensagem.
  • git commit -am "mensagem" — prepara e grava de uma vez os ficheiros já seguidos, saltando o git add. Não apanha ficheiros novos.
  • git commit --amend — corrige o último commit (a mensagem ou algo esquecido). Cria um commit novo, com outro hash: só antes de o enviar para o remoto.

Para ver a história, git log --oneline --graph dá uma linha por commit e o desenho dos ramos; HEAD -> main é o ponteiro que indica onde está. O git log -p mostra também as alterações de cada commit.

$ git log --oneline --graph
* 4d40940 (HEAD -> main) Muda a porta para 8080
* 0e453d0 Desliga o modo debug
* 3d3acf5 Primeira versão do site

Ramos e merge

Um ramo é uma linha de trabalho independente: experimenta-se uma funcionalidade sem tocar no código principal e junta-se quando estiver pronta. Criar um ramo é instantâneo — é só um novo ponteiro.

  • git branch — lista os ramos e marca com * o atual.
  • git switch -c nome — cria um ramo e muda para ele; a forma clássica equivalente é git checkout -b nome.
  • git switch main e git merge nome — volta ao ramo principal e traz para ele o trabalho do outro ramo.

Quando há conflito

Se as alterações não colidirem, a fusão é automática (e, se o ramo principal não avançou, é um simples fast-forward). Se os dois ramos mudaram as mesmas linhas, o git assinala um conflito e marca os dois lados no ficheiro:

<<<<<<< HEAD
<footer>Contactos</footer>
=======
<a href="/login">Entrar</a>
>>>>>>> feature/login

Resolve-se à mão, retirando as marcas, e completa-se a fusão com git add e git commit.

Repositórios remotos

Um remoto é uma versão do repositório noutro servidor, tipicamente chamado origin, e permite colaborar e ter cópias fora da sua máquina. O git remote -v lista os remotos e os endereços. Para sincronizar há três comandos:

  • git fetch — descarrega as alterações do remoto sem as juntar ao seu trabalho: só atualiza o origin/main, para ver o que mudou antes de integrar.
  • git pull — um fetch seguido de um merge: traz e integra logo no ramo atual.
  • git push — envia os seus commits. Na primeira vez, o -u liga o ramo local ao remoto (o upstream); daí em diante, push e pull sabem para onde ir.
git remote add origin git@git.hjfr-info.pt:aluno/lab.git
git push -u origin main
git fetch && git log --oneline main..origin/main
git pull

Desfazer alterações com segurança

  • git restore ficheiro — descarta as alterações não gravadas, voltando à última versão. O trabalho não guardado perde-se.
  • git restore --staged ficheiro — o oposto do add: tira da staging, mas mantém as alterações.
  • git reset --soft HEAD~1 — desfaz o último commit e mantém tudo preparado.
  • git reset --mixed HEAD~1 — o modo padrão: desfaz e tira da staging, mas mantém as alterações nos ficheiros.
  • git reset --hard HEAD~1 — o perigoso: desfaz e apaga tudo, voltando ao estado do commit.

O reset reescreve a história: em commits já enviados, use o git revert, que cria um commit novo a anular o antigo. E o git stash é uma gaveta temporária: guarda o trabalho em curso para trocar de tarefa a meio; git stash pop recupera-o.

Inspecionar: diff, blame, show e tags

  • git diff — as alterações ainda fora da staging; com --staged, o que já está preparado, ou seja, exatamente o que vai no próximo commit.
  • git blame ficheiro — o commit, o autor e a data de cada linha.
  • git show commit — a mensagem e as alterações de um commit.
  • git tag -a v1.0 -m "Versão 1.0" — marca uma versão; envia-se com git push --follow-tags.

Ficam de fora o rebase, para reorganizar a história, e o cherry-pick, para trazer um commit isolado.

git em cibersegurança

O histórico não esquece. Um .env apanhado por um git add -A descuidado fica no commit, e apagá-lo depois não o tira da história: um git log -p | grep -E "KEY|PASS" encontra-o em segundos. Se o commit não saiu da máquina, um --amend resolve; se já foi enviado, o segredo está comprometido — troque-o e só depois limpe a história, por exemplo com git filter-repo.

Prevenir é mais barato do que limpar. Um .gitignore desde o primeiro dia, a revisão com git diff --staged e um hook de pre-commit que procure credenciais evitam a maioria dos incidentes. E nunca deixe a pasta .git na raiz pública de um site: um /.git/ acessível por HTTP permite reconstruir o código e o histórico.

Rastreabilidade e assinaturas. Em resposta a incidentes, git log, git blame e git show dizem o que mudou, quando e por quem — mas o autor de um commit é texto definido com git config, fácil de falsificar. Assinar commits e tags (git commit -S, git tag -s, com chave GPG ou SSH) e verificá-los com git log --show-signature garante a autoria.

Experimente no simulador interativo

O simulador recria um pequeno projeto em ~/lab, com um .env que guarda uma chave de API fictícia. À esquerda vê as três áreas, com os ficheiros a mudar de área a cada comando, e o grafo de commits com ramos, HEAD, origin/main e tags; à direita, o terminal. Demonstrar tudo percorre 40 passos, do git init à tag v1.0, com um segredo esquecido e um conflito pelo meio; Anterior e Seguinte navegam passo a passo e Repor estado volta ao início. Experimente também clicar em qualquer comando.

Erros comuns e boas práticas

  • Usar git add -A sem .gitignore: prepara tudo, incluindo credenciais. Crie o .gitignore antes do primeiro commit.
  • Fazer commit sem rever: git status e git diff --staged antes de gravar.
  • Reescrever história partilhada: reset ou --amend em commits enviados baralham a equipa. Use git revert.
  • Usar reset --hard por reflexo: apaga as alterações locais sem retorno. Para arrumar o trabalho por uns minutos, prefira o git stash.
  • Confundir fetch com pull: o primeiro só descarrega, o segundo também integra. Na dúvida, fetch primeiro.
  • Mensagens vagas: «alterações» não diz nada daqui a seis meses. Explique o porquê.

Conclusão

Config e init, status e add, commit e log, ramos e merge, fetch, pull e push, e restore, reset, revert e stash: é o fluxo completo do dia a dia. A regra que fica é simples — reveja o que entra no commit, porque o histórico não esquece.

Os remotos do tipo git@servidor:projeto.git autenticam-se com chaves SSH — veja como as gerir no artigo sobre ssh e scp. E para procurar padrões no código e no histórico, o grep é o companheiro natural do git log -p.

Workshop — Linha de Comandos Linux

Quer passar da leitura à prática, com exercícios reais e acompanhamento? No workshop Linha de Comandos Linux da HJFR trabalha estes e outros comandos da série — da administração do dia a dia à resposta a incidentes.

Este artigo faz parte da série «Comandos Linux, um a um» do blog HJFR — fique atento aos próximos comandos.