Todo mundo já passou por isso. Uma pasta com projeto_final.zip, projeto_final_v2.zip, projeto_final_v2_revisado.zip e, finalmente, projeto_final_v2_AGORA_VAI.zip. Ninguém lembra qual é a versão boa, o que mudou de uma para a outra, nem quem apagou aquela função que funcionava ontem. Agora multiplique isso por cinco pessoas mexendo no mesmo código, ao mesmo tempo, e você tem o caos.
O Git resolve exatamente esse problema. O GitHub pega essa solução e a transforma em uma plataforma de colaboração usada por praticamente toda a indústria de software. Este guia é longo de propósito: ele vai do "o que é um commit" até rebase interativo, bisect, GitHub Actions e recuperação de desastres, com cada comando explicado passo a passo, a saída que você deve esperar e as armadilhas que pegam até gente experiente.
Os exemplos foram testados no Git 2.52. Qualquer versão a partir da 2.40 serve para praticamente tudo aqui; quando um recurso exige uma versão mais nova, isso é dito explicitamente.
O problema que o controle de versão resolve
Um sistema de controle de versão (VCS, version control system) registra a história de um conjunto de arquivos ao longo do tempo. Para cada mudança, ele guarda o que mudou, quem mudou, quando e, se você escrever uma boa mensagem, por quê. Com isso você ganha superpoderes bem concretos:
- Voltar no tempo: recuperar qualquer versão anterior de qualquer arquivo, ou do projeto inteiro.
- Trabalhar em paralelo: várias pessoas (ou várias ideias suas) evoluindo o código ao mesmo tempo, sem uma pisar na outra.
- Juntar trabalho: combinar mudanças de pessoas diferentes de forma quase sempre automática.
- Investigar: descobrir em qual mudança um bug apareceu e o contexto em que ela foi feita.
- Experimentar sem medo: se a ideia não der certo, basta descartá-la.
Existem duas grandes famílias de VCS:
O Git é distribuído. Isso explica por que quase tudo nele é instantâneo: log, diff, commit e troca de branch acontecem no seu disco, sem ida e volta pela rede.
Uma breve história: Linus Torvalds, o BitKeeper e 2005
Entre 2002 e 2005, o kernel do Linux era versionado com o BitKeeper, uma ferramenta proprietária que a comunidade podia usar de graça. Em 2005, a relação entre os desenvolvedores do kernel e a empresa por trás do BitKeeper azedou e a licença gratuita foi revogada. Linus Torvalds, criador do Linux, decidiu escrever a própria ferramenta, com metas bem claras:
- ser rápida, mesmo com o histórico gigantesco do kernel;
- ter um design simples;
- dar suporte forte a desenvolvimento não linear, com milhares de branches em paralelo;
- ser totalmente distribuída;
- garantir a integridade dos dados, de forma que nada seja corrompido sem que se perceba.
O primeiro commit do Git foi feito em 7 de abril de 2005, pelo próprio Linus, com a mensagem "Initial revision of "git", the information manager from hell". Poucos meses depois, Junio C Hamano assumiu a manutenção do projeto, papel que continua exercendo, e a versão 1.0 saiu em dezembro de 2005.
E o nome? O README desse primeiro commit brinca que "git" pode significar qualquer coisa, dependendo do seu humor: de "global information tracker", quando tudo funciona e "anjos cantam", até um xingamento impublicável, quando quebra. Em gíria britânica, git também é uma forma pouco elogiosa de chamar alguém.
Duas décadas depois, o Git é, de longe, o sistema de controle de versão mais usado do mundo.
Git não é GitHub (nem GitLab, nem Bitbucket)
Essa é a confusão número um de quem está começando:
- Git é o programa que roda no seu computador. É software livre (licença GPLv2), funciona 100% offline e não depende de empresa nenhuma.
- GitHub é um serviço que hospeda repositórios Git na nuvem e adiciona ferramentas de colaboração: pull requests, issues, revisão de código, GitHub Actions, Pages, Projects e muito mais. Foi lançado em 2008 e comprado pela Microsoft em 2018.
Uma boa analogia: o Git está para o GitHub assim como um arquivo de vídeo está para o YouTube. O vídeo existe sem o YouTube; o YouTube hospeda, distribui e coloca uma camada social em cima.
O GitHub não é o único serviço desse tipo:
A boa notícia: tudo o que você aprender sobre Git vale para todos eles. Muda só a interface web.
Como o Git funciona por dentro
Você pode usar Git decorando comandos, mas vai sofrer na primeira situação fora do roteiro. Entender o modelo interno leva uns 15 minutos e transforma reset, rebase e "detached HEAD" de magia negra em algo lógico. Vale o investimento.
Snapshots, não diferenças
Muitos sistemas antigos guardam cada versão como uma lista de diferenças (deltas) em relação à anterior. O Git pensa de outro jeito: cada commit é um snapshot, uma fotografia completa de como todos os arquivos do projeto estavam naquele momento.
Isso não significa copiar o projeto inteiro a cada commit. Se um arquivo não mudou, o novo snapshot simplesmente aponta para o mesmo conteúdo já guardado. Veja três commits reais de um projeto pequeno:
Commit A Commit B Commit C
README.md blob 8ee0c0 ─────▶ (o mesmo) ───────▶ (o mesmo)
index.html blob b061ee blob 52caf0 ─────▶ (o mesmo)
style.css blob bc406f ─────▶ (o mesmo) blob acd365No commit B só o index.html mudou, então só ele ganhou um conteúdo novo; o resto é reaproveitado. (Por baixo dos panos, o Git ainda compacta tudo em packfiles com compressão por deltas para economizar disco, mas o modelo com que você raciocina é o de snapshots.)
O diretório .git: o repositório de verdade
Quando você roda git init ou git clone, surge uma pasta oculta chamada .git. Ela é o repositório. O resto da pasta do projeto é só a sua área de trabalho. Se você apagar a .git, os arquivos continuam lá, mas todo o histórico some.
.git/
├── HEAD → em que branch você está (ex.: ref: refs/heads/main)
├── config → configurações deste repositório (remotos, branches...)
├── description → usado só pelo GitWeb; pode ignorar
├── hooks/ → scripts que rodam em eventos (pre-commit, commit-msg...)
├── index → a staging area (um arquivo binário)
├── info/exclude → um .gitignore local, que não vai para o repositório
├── logs/ → para onde cada referência apontou ao longo do tempo (reflog)
├── objects/ → o banco de dados: blobs, trees, commits e tags
└── refs/
├── heads/ → suas branches locais
├── remotes/ → cópias das branches dos remotos (origin/main...)
└── tags/ → suas tagsCom o tempo também aparecem arquivos como COMMIT_EDITMSG (a mensagem do último commit), ORIG_HEAD (a posição anterior a um merge, rebase ou reset), FETCH_HEAD e packed-refs (referências compactadas em um único arquivo).
Os quatro tipos de objeto
Tudo o que o Git guarda em .git/objects é um de quatro tipos de objeto:
Você pode inspecionar qualquer objeto com git cat-file: a flag -t mostra o tipo, -s o tamanho e -p imprime o conteúdo de forma legível. Veja o último commit daquele projeto:
git cat-file -p HEADtree 8adf6bd09b522157a364106e006f15026d2e1be5
parent d3d359b2919025b172d5d372e0b2462b9d630afc
author Ana Souza <ana@exemplo.com> 1790195540 -0300
committer Ana Souza <ana@exemplo.com> 1790195540 -0300
feat: saudação completaO commit aponta para uma tree e para o commit parent. Descendo para a tree (as aspas simples evitam que o PowerShell interprete as chaves):
git cat-file -p 'HEAD^{tree}'100644 blob 8ee0c0ed567d2cef49d241aa892ad0293ae6bb2b README.md
040000 tree ee1af1465b1a72a8d2d37ad473fb8e4c3c2cd597 css
100644 blob 52caf059a80dd4ffa7e60f36ee519d648e9bab8f index.htmlOs números à esquerda são o modo do arquivo: 100644 é um arquivo comum, 100755 um executável, 040000 um diretório (outra tree) e 120000 um link simbólico. Repare que o blob do README.md (8ee0c0...) é exatamente o mesmo do commit anterior: é o reaproveitamento de snapshots em ação.
Hashes: a impressão digital de cada objeto
Cada objeto é identificado pelo hash SHA-1 do seu conteúdo, um número de 160 bits escrito como 40 caracteres hexadecimais. Como o hash depende só do conteúdo, o mesmo conteúdo gera o mesmo hash em qualquer computador do mundo. Faça o teste no Git Bash, no macOS ou no Linux:
echo 'hello' | git hash-object --stdince013625030ba8dba906f756967f9e9ca394464aVocê vai obter exatamente esse valor. O Git calcula o SHA-1 de um pequeno cabeçalho (blob 6 seguido de um byte nulo) mais o conteúdo, e grava o objeto em .git/objects/ce/013625...: os dois primeiros caracteres viram o nome da pasta, o resto vira o nome do arquivo.
Essa ideia simples tem consequências enormes:
- Integridade: se um único bit de um arquivo ou commit se corromper, o hash não bate mais e o Git percebe.
- Deduplicação: conteúdo idêntico é guardado uma única vez.
- Imutabilidade: um commit nunca é alterado. "Editar" um commit (com
--amendourebase) na verdade cria um commit novo, com hash novo. É por isso que se diz que esses comandos "reescrevem o histórico". - Abreviação: você raramente digita os 40 caracteres. Os primeiros 7 (ou mais, se houver ambiguidade) bastam:
git show d3d359b.
Sobre a segurança do SHA-1: desde 2017 existem ataques práticos de colisão contra ele, e o Git passou a usar uma versão "endurecida" do algoritmo, que detecta os ataques conhecidos. Além disso, desde a versão 2.29 o Git suporta repositórios com SHA-256 (git init --object-format=sha256, com hashes de 64 caracteres). Ainda não há interoperabilidade entre repositórios SHA-1 e SHA-256, e o ecossistema (bibliotecas, ferramentas e serviços de hospedagem) precisa acompanhar. A documentação do projeto já anuncia que o Git 3.0 deve adotar SHA-256 como padrão para repositórios novos. Na prática, hoje, quase todo repositório que você encontrar usa SHA-1.
Commits formam um grafo
Cada commit guarda o hash do(s) seu(s) pai(s). O primeiro commit do projeto não tem pai (é o root commit), um commit normal tem um pai e um merge commit tem dois (ou mais). Ligando os commits pelos pais, você obtém um grafo acíclico dirigido (DAG, directed acyclic graph):
D ─── E
/ \
A ─── B ─── C ──── F ← F é um merge commit: seus pais são C e EDois detalhes importantes:
- As ligações apontam para trás no tempo: cada commit conhece o pai, mas o pai não sabe quais filhos tem.
- Como o hash de um commit inclui o hash do pai, mudar qualquer commit antigo muda o hash de todos os descendentes. O histórico funciona como uma corrente.
Para se referir a ancestrais sem copiar hashes, o Git tem uma notação própria:
Refs, branches e HEAD
Hashes são ótimos para computadores e péssimos para humanos. Por isso o Git tem referências (refs): nomes que apontam para commits.
Uma branch é só isso: um arquivo com o hash de um commit.
cat .git/refs/heads/maind3d359b2919025b172d5d372e0b2462b9d630afcCriar uma branch é criar um arquivo de 41 bytes. Por isso branches no Git são instantâneas e baratas, e você deve usá-las sem economia. A característica que define uma branch é que ela se move sozinha: quando você faz um commit estando nela, o ponteiro avança para o commit novo.
O HEAD indica onde você está agora. Normalmente ele aponta para uma branch (é uma referência simbólica):
cat .git/HEADref: refs/heads/main HEAD
│
▼
main
│
▼
A ◀──── B ◀──── C ◀──── D
▲
│
feature/loginLeia o diagrama assim: main aponta para D, feature/login aponta para C, e o HEAD aponta para main, ou seja, você está na main. Se fizer um commit agora, surge um E cujo pai é D, e main passa a apontar para E; feature/login fica onde está.
Existem outros tipos de ref: tags (ponteiros fixos, que não andam), branches de rastreamento remoto como origin/main (a última posição conhecida da main no servidor) e o próprio HEAD. Quando o HEAD aponta direto para um commit, e não para uma branch, você está em detached HEAD, situação que veremos com calma mais adiante.
As três áreas: working tree, staging area e repositório
Este é o conceito mais importante do guia. Todo arquivo transita por três lugares:
- Working tree (diretório de trabalho): os arquivos que você vê e edita no seu editor.
- Staging area (também chamada de index): o rascunho do próximo commit. Fica em
.git/index. - Repositório: os commits já gravados em
.git/objects.
┌──────────────┐ git add ┌──────────────┐ git commit ┌──────────────┐
│ Working tree │ ─────────▶ │ Staging area │ ─────────▶ │ Repositório │
│ (seus │ │ (index) │ │ (.git) │
│ arquivos) │ │ │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
▲ │ ▲ │
│ git restore <arq> │ │ git restore --staged │
└───────────────────────┘ └────────────────────────┘git addcopia o estado atual de um arquivo do working tree para a staging area.git commitgrava o que está na staging area (e não o que está no working tree!) como um novo snapshot.git restore <arquivo>faz o caminho inverso: sobrescreve o arquivo no working tree com a versão da staging area, descartando o que você mudou.git restore --staged <arquivo>sobrescreve a staging area com a versão do último commit, "tirando o arquivo do palco" sem mexer no seu working tree.
Por que existe uma área intermediária? Porque ela permite montar commits com cuidado. Imagine que você corrigiu um bug e, no caminho, renomeou algumas variáveis em outro arquivo. Com a staging area você cria dois commits separados, cada um com um propósito claro, em vez de um commit "corrige bug e mais umas coisas".
Os estados de um arquivo
Do ponto de vista do Git, cada arquivo do seu projeto está em um destes estados:
O ciclo de vida fica assim:
untracked ──git add──▶ staged ──git commit──▶ unmodified ──(você edita)──▶ modified
▲ │
└───────────────────────── git add ──────────────────┘Guarde esse desenho. Quase todo comando do dia a dia é uma seta dele.
Instalando o Git
A forma recomendada é o Git for Windows, que traz o Git, o Git Bash (um terminal com comandos Unix) e o Git Credential Manager.
Baixe e instale
Baixe em git-scm.com/downloads/win ou, pelo terminal:
winget install --id Git.Git -e --source wingetEscolhas importantes do instalador
- Editor padrão: escolha o VS Code se você o usa; o padrão é o Vim.
- Nome da branch inicial: marque "Override" e digite
main. - PATH: mantenha a opção recomendada ("Git from the command line and also from 3rd-party software").
- Finais de linha: "Checkout Windows-style, commit Unix-style" (equivale a
core.autocrlf=true). - Credential helper: mantenha o Git Credential Manager.
Confira
Abra um terminal novo e rode
git --version.
O macOS oferece o Git junto com as ferramentas de linha de comando da Apple:
xcode-select --installPara ter sempre a versão mais recente, use o Homebrew:
brew install git
git --versionsudo apt update && sudo apt install git # Debian/Ubuntu
sudo dnf install git # Fedora
sudo pacman -S git # Arch
git --versionConfiguração inicial
Antes do primeiro commit, diga ao Git quem você é. Esses dados vão em todos os commits:
git config --global user.name "Seu Nome"
git config --global user.email "voce@exemplo.com"Use o mesmo e-mail da sua conta do GitHub para que os commits apareçam ligados ao seu perfil. Outras configurações que valem a pena:
git config --global init.defaultBranch main # novos repositórios começam na main
git config --global core.editor "code --wait" # VS Code como editor de mensagens
git config --global pull.rebase false # pull faz merge (padrão explícito, sem avisos)
git config --global color.ui autoFinais de linha
Windows usa CRLF no fim das linhas; Linux e macOS, LF. Para que o repositório guarde sempre LF:
git config --global core.autocrlf true # no Windows
git config --global core.autocrlf input # no macOS e no LinuxEm projetos de equipe, o mais robusto é um arquivo .gitattributes na raiz, que vale para todos independentemente da configuração de cada máquina:
* text=auto eol=lf
*.bat text eol=crlf
*.png binaryAliases
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.lg "log --oneline --graph --decorate --all"Agora git lg desenha o histórico de todas as branches.
Onde as configurações ficam
O nível mais específico vence. git config --list --show-origin mostra cada valor e de qual arquivo ele veio.
Criando repositórios
Há duas formas de começar:
# 1. Transformar uma pasta existente em repositório
cd meu-projeto
git init
# 2. Copiar um repositório existente (com todo o histórico)
git clone https://github.com/usuario/projeto.git
git clone git@github.com:usuario/projeto.git # mesma coisa, via SSH
git clone https://github.com/usuario/projeto.git outra-pastaO git init cria a pasta oculta .git. É nela que mora todo o repositório: objetos, branches, configurações. Apagar o .git transforma a pasta de volta em uma pasta comum; apagar os arquivos do projeto não apaga o histórico.
O ciclo básico, passo a passo
Crie ou altere um arquivo
echo "# Meu projeto" > README.mdVeja o estado
git statusOn branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) README.mdPrepare as mudanças (staging)
git add README.md # um arquivo git add . # tudo a partir da pasta atual git add -p # escolhe trecho por trecho, interativamenteGrave o commit
git commit -m "Adiciona README"Confira o histórico
git log --oneline3f2a1c9 (HEAD -> main) Adiciona README
git commit -am "mensagem" faz add e commit de uma vez, mas só para arquivos já rastreados; arquivos novos precisam de git add.
Boas mensagens de commit
Um histórico legível é documentação. As convenções mais aceitas:
- Assunto curto (até uns 50 caracteres), no imperativo: "Corrige cálculo de frete", não "corrigido" nem "corrigindo".
- Linha em branco e, se precisar, um corpo explicando o porquê, com linhas de até ~72 caracteres.
- Um commit, um propósito.
Corrige arredondamento no cálculo do frete
O valor era arredondado antes de aplicar o desconto, o que gerava
diferenças de um centavo em pedidos com cupom.Conventional Commits
Muitos projetos adotam um prefixo que diz o tipo da mudança. Ferramentas usam isso para gerar changelogs e versões automaticamente.
Mudanças que quebram compatibilidade levam !: feat!: remove endpoint v1.
Inspecionando o repositório
git status -s # status resumido (M = modificado, A = adicionado, ?? = novo)
git diff # o que mudou e ainda NÃO está na staging area
git diff --staged # o que está na staging area e vai no próximo commit
git diff main..feature # diferença entre duas branches
git log # histórico completo
git log --oneline --graph --all --decorate
git log -p arquivo.ts # histórico com o conteúdo das mudanças de um arquivo
git log --stat # quais arquivos cada commit alterou
git log --author="Ana" # commits de uma pessoa
git log --since="2 weeks ago"
git log -S "calculaFrete" # commits que adicionaram ou removeram esse texto
git show 3f2a1c9 # detalhes de um commit
git blame arquivo.ts # quem alterou cada linha por último
git shortlog -sn # quantidade de commits por autor.gitignore: o que não deve ser versionado
Dependências baixadas, builds, logs, arquivos do sistema e, principalmente, segredos não entram no repositório. Liste-os no .gitignore:
# Dependências e builds
node_modules/
dist/
.next/
*.o
*.exe
# Logs e temporários
*.log
.DS_Store
Thumbs.db
# Variáveis de ambiente (NUNCA versione)
.env
.env.local
!.env.exampleRegras da sintaxe:
O GitHub mantém modelos prontos para cada linguagem no repositório github/gitignore. Para arquivos que só você gera (configurações do seu editor, por exemplo), use um gitignore global:
git config --global core.excludesFile ~/.gitignore_globalDesfazendo coisas
Esta é a seção que você mais vai consultar. Os comandos estão em ordem, do mais seguro ao mais destrutivo.
Descartar mudanças no working tree
git restore arquivo.ts # volta o arquivo para a versão da staging area (ou do último commit)
git restore . # descarta TODAS as mudanças não preparadasAs mudanças descartadas assim não vão para lugar nenhum: não há como recuperá-las pelo Git.
Tirar da staging area
git restore --staged arquivo.ts # tira do "palco", mas mantém as mudanças no arquivoCorrigir o último commit
git commit --amend -m "Mensagem corrigida" # troca a mensagem
git add esquecido.ts
git commit --amend --no-edit # inclui um arquivo esquecido, mantendo a mensagemO --amend cria um commit novo no lugar do anterior. Se o commit já foi enviado ao remoto, evite: você reescreveria um histórico que outras pessoas já têm.
git reset: voltando a branch
git reset move a branch atual para outro commit. As três variações diferem no que acontece com as mudanças:
Antes: A ─ B ─ C ← main (HEAD)
git reset --soft HEAD~1 → main aponta para B; as mudanças de C ficam na STAGING AREA
git reset --mixed HEAD~1 → main aponta para B; as mudanças de C ficam no WORKING TREE (padrão)
git reset --hard HEAD~1 → main aponta para B; as mudanças de C são DESCARTADASHEAD~1 significa "um commit antes do HEAD"; HEAD~3, três antes. --soft é ótimo para refazer os últimos commits como um só; --hard apaga trabalho e deve ser usado com cuidado.
git revert: desfazer sem reescrever o histórico
git revert 3f2a1c9Cria um novo commit que faz o contrário do commit indicado. É a forma segura de desfazer algo que já está no remoto e que outras pessoas já baixaram.
git reflog: a rede de segurança
O Git registra por onde o HEAD passou nos últimos meses (por padrão, 90 dias para entradas alcançáveis). Mesmo depois de um reset --hard desastroso, os commits ainda existem:
git reflog9c1d2e3 HEAD@{0}: reset: moving to HEAD~2
7b8a9f0 HEAD@{1}: commit: Implementa checkout
5e4d3c2 HEAD@{2}: commit: Adiciona carrinhogit reset --hard 7b8a9f0 # volta para antes do reset
git branch recuperada 7b8a9f0 # ou cria uma branch apontando para láO mesmo vale para uma branch apagada por engano: ache o último commit dela no reflog e recrie a branch.
git clean: apagar arquivos não rastreados
git clean -n # simula: mostra o que seria apagado
git clean -fd # apaga arquivos e pastas não rastreadosQual comando usar?
Branches
Uma branch é apenas um ponteiro móvel para um commit. Criar uma branch custa praticamente nada: o Git grava um arquivo com o hash do commit. A cada novo commit, a branch em que você está (a que o HEAD aponta) avança.
┌── feature/login
▼
A ─ B ─ C ─ D
▲
└── maingit branch # lista as branches locais (* marca a atual)
git branch -a # inclui as remotas
git switch -c feature/login # cria e troca para a nova branch
git switch main # troca de branch
git switch - # volta para a branch anterior
git branch -m nome-novo # renomeia a branch atual
git branch -d feature/login # apaga (só se já foi mesclada)
git branch -D feature/login # apaga à forçagit switch foi criado em 2019 (Git 2.23) para separar as funções do antigo git checkout, que fazia de tudo. git checkout -b nome ainda funciona e aparece em muitos tutoriais.
Convenções de nome comuns: feature/descricao, fix/descricao, hotfix/descricao, chore/descricao, sempre em minúsculas e com hifens.
Merge: juntando branches
Fast-forward
Se a main não andou desde que a branch foi criada, o merge só avança o ponteiro:
Antes: A ─ B ← main
\
C ─ D ← feature
git switch main && git merge feature
Depois: A ─ B ─ C ─ D ← main, featureMerge de três vias
Se as duas branches receberam commits, o Git cria um commit de merge com dois pais:
Antes: A ─ B ─ E ← main
\
C ─ D ← feature
Depois: A ─ B ─ E ─ M ← main
\ /
C ─ Dgit merge --no-ff feature força a criação do commit de merge mesmo quando daria fast-forward, preservando no histórico o registro de que existiu uma branch.
Resolvendo conflitos, passo a passo
Quando as duas branches alteram as mesmas linhas, o Git para e pede sua decisão:
Auto-merging src/preco.ts
CONFLICT (content): Merge conflict in src/preco.ts
Automatic merge failed; fix conflicts and then commit the result.Veja quais arquivos estão em conflito
git statuslista os arquivos em "both modified".Abra o arquivo e encontre os marcadores
<<<<<<< HEAD const desconto = 0.10; ======= const desconto = 0.15; >>>>>>> feature/promocaoAcima do
=======está a versão da sua branch atual (HEAD); abaixo, a da branch que está entrando.Edite para o resultado final e apague os marcadores
O VS Code mostra botões "Accept Current", "Accept Incoming" e "Accept Both" sobre cada conflito.
Marque como resolvido e conclua
git add src/preco.ts git commit # a mensagem de merge já vem preenchida
Mudou de ideia no meio? git merge --abort volta tudo para antes do merge.
Rebase: reescrevendo a base
O rebase reaplica os commits da sua branch em cima de outra base, como se você tivesse começado a trabalhar a partir do ponto mais recente:
Antes: A ─ B ─ E ← main
\
C ─ D ← feature
git switch feature && git rebase main
Depois: A ─ B ─ E ← main
\
C' ─ D' ← featureC' e D' são commits novos (com hashes diferentes), com as mesmas mudanças de C e D. O resultado é um histórico linear, sem commit de merge.
Se houver conflito durante o rebase, resolva o arquivo e continue:
git add arquivo
git rebase --continue # segue para o próximo commit
git rebase --skip # descarta o commit atual
git rebase --abort # cancela e volta ao estado anteriorRebase interativo: arrumando commits antes de publicar
git rebase -i HEAD~3O Git abre o editor com a lista dos últimos 3 commits:
pick 1a2b3c4 Adiciona formulário de login
pick 5d6e7f8 wip
pick 9a0b1c2 Corrige typoTroque a palavra antes de cada commit:
Stash: guardando mudanças para depois
Você está no meio de uma tarefa e precisa trocar de branch para corrigir algo urgente? Guarde as mudanças numa pilha temporária:
git stash push -m "formulário pela metade" # guarda e limpa o working tree
git stash -u # inclui arquivos não rastreados
git stash list # lista o que está guardado
git stash show -p stash@{0} # mostra o conteúdo
git stash pop # reaplica o último e remove da pilha
git stash apply stash@{1} # reaplica sem remover
git stash drop stash@{0} # descarta um itemRepositórios remotos
Um remoto é uma cópia do repositório em outro lugar, normalmente no GitHub. Por convenção, o principal se chama origin.
git remote -v # lista os remotos
git remote add origin git@github.com:usuario/projeto.git
git remote set-url origin git@github.com:usuario/novo.git
git remote rename origin github
git remote remove githubfetch, pull e push
git fetch # baixa as novidades do remoto sem mexer nas suas branches
git pull # fetch + merge na branch atual
git pull --rebase # fetch + rebase (histórico linear)
git push # envia seus commits
git push -u origin main # primeira vez: envia e liga a branch local à remotaO -u (upstream) grava que a sua main acompanha a origin/main. Depois disso, git push e git pull sem argumentos já sabem para onde ir. git branch -vv mostra quem acompanha quem e quantos commits estão à frente ou atrás.
git push -u origin feature/login # publica uma branch nova
git push origin --delete feature/login # apaga a branch remota
git fetch --prune # remove referências a branches remotas que não existem maisForce push com segurança
Depois de um rebase, o push normal é recusado porque o histórico divergiu. Se a branch é só sua:
git push --force-with-leaseDiferente de --force, o --force-with-lease recusa o envio se alguém tiver enviado commits que você ainda não baixou, evitando apagar o trabalho dos outros.
GitHub, passo a passo
Criando a conta e o primeiro repositório
- Crie sua conta em github.com e ative a autenticação em dois fatores (Settings → Password and authentication).
- Crie um repositório com o botão New: escolha o nome, público ou privado, e se deseja um README, um
.gitignoree uma licença. - Opcional: crie um repositório com o mesmo nome do seu usuário e um
README.mdnele. Ele vira o README do seu perfil.
Publicando um projeto que já existe no seu computador
Transforme a pasta em repositório e faça o primeiro commit
cd meu-projeto git init git add . git commit -m "Primeiro commit"Crie o repositório no GitHub
Crie-o vazio (sem README, sem .gitignore, sem licença) para não haver conflito com o seu histórico local.
Conecte e envie
git remote add origin git@github.com:usuario/meu-projeto.git git branch -M main git push -u origin maingit branch -M maingarante que a branch se chamamain, caso o seu Git tenha criadomaster.
Autenticação: HTTPS, tokens e SSH
Desde agosto de 2021, o GitHub não aceita a senha da conta para operações Git por HTTPS. Existem duas alternativas.
HTTPS com Git Credential Manager ou token
O Git Credential Manager (instalado junto com o Git for Windows e disponível para macOS e Linux) abre o navegador na primeira vez e guarda a credencial com segurança. Sem ele, você usa um Personal Access Token (Settings → Developer settings → Personal access tokens) no lugar da senha. Prefira os tokens fine-grained, com acesso só aos repositórios necessários e data de expiração.
SSH, passo a passo
Gere a chave
ssh-keygen -t ed25519 -C "voce@exemplo.com"Aceite o caminho padrão e defina uma passphrase.
Carregue a chave no agente
No PowerShell como administrador, ative o serviço uma vez:
Get-Service ssh-agent | Set-Service -StartupType Automatic Start-Service ssh-agent ssh-add $env:USERPROFILE\.ssh\id_ed25519eval "$(ssh-agent -s)" ssh-add --apple-use-keychain ~/.ssh/id_ed25519eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519Copie a chave pública
cat ~/.ssh/id_ed25519.pubCopie a linha inteira (começa com
ssh-ed25519). Nunca compartilhe o arquivo sem.pub.Cadastre no GitHub
Settings → SSH and GPG keys → New SSH key. Cole a chave e salve.
Teste a conexão
ssh -T git@github.comHi usuario! You've successfully authenticated, but GitHub does not provide shell access.Troque um remoto HTTPS para SSH (se necessário)
git remote set-url origin git@github.com:usuario/projeto.git
GitHub CLI
A ferramenta oficial gh traz o GitHub para o terminal:
gh auth login # autentica (abre o navegador)
gh repo create meu-projeto --private --source=. --push # cria no GitHub e envia a pasta atual
gh repo clone usuario/projeto
gh pr create --fill # abre um pull request com os dados dos commits
gh pr list
gh pr checkout 42 # baixa o PR 42 para revisar localmente
gh pr merge 42 --squash
gh issue list
gh issue createColaborando: pull requests
Um pull request (PR) é um pedido para mesclar uma branch em outra, com espaço para revisão, comentários e verificações automáticas.
Clone ou fork?
- Clone: você tem permissão de escrita no repositório (seu ou da sua equipe). Crie branches nele mesmo.
- Fork: é uma cópia do repositório na sua conta. Usado para contribuir com projetos em que você não tem permissão de escrita, como projetos open source.
O fluxo completo
Atualize a main e crie uma branch
git switch main git pull git switch -c feature/filtro-por-dataTrabalhe em commits pequenos
git add . git commit -m "feat: adiciona filtro por data na listagem"Publique a branch
git push -u origin feature/filtro-por-dataAbra o pull request
O GitHub mostra um botão "Compare & pull request". Escreva o que mudou, por que e como testar. Use
Closes #12na descrição para fechar a issue 12 automaticamente quando o PR for mesclado. Marque como draft se ainda não estiver pronto para revisão.Responda à revisão
Faça novos commits na mesma branch e dê
git push: o PR é atualizado sozinho.Mescle e limpe
Depois de aprovado, mescle pelo GitHub e apague a branch. Localmente:
git switch main git pull git branch -d feature/filtro-por-data git fetch --prune
As três formas de mesclar no GitHub
Mantendo um fork atualizado
git remote add upstream https://github.com/projeto-original/repo.git
git fetch upstream
git switch main
git merge upstream/main
git push origin mainO botão Sync fork na página do seu fork faz o mesmo pelo navegador.
Fluxos de trabalho
- GitHub Flow: a
mainestá sempre pronta para produção; cada mudança nasce numa branch curta e entra por PR. Simples e suficiente para a maioria dos projetos. - Git Flow: branches
develop,releaseehotfixalém damain. Útil para software com versões lançadas em ciclos, mas pesado para aplicações web com deploy contínuo. - Trunk-based development: todos integram na
maincom muita frequência (branches de horas, não de semanas), usando feature flags para esconder o que não está pronto.
Recursos do GitHub que valem conhecer
- Issues: registro de bugs, tarefas e ideias, com labels (bug, enhancement...) e milestones para agrupar por entrega.
- Projects: quadros no estilo kanban ligados às issues e PRs.
- Discussions: fórum para perguntas e ideias, separado das issues.
- README.md: a página de apresentação do repositório.
- LICENSE: sem licença, ninguém tem permissão legal de reutilizar o código. A MIT é a mais simples e permissiva.
- CONTRIBUTING.md e templates em
.github/para padronizar issues e PRs. - CODEOWNERS: define quem revisa automaticamente cada parte do código.
- Regras de proteção de branch: exigem PR, revisão e testes passando antes de mesclar na
main.
Tags e releases
Tags marcam versões específicas do histórico:
git tag # lista
git tag -a v1.0.0 -m "Primeira versão" # tag anotada (com autor, data e mensagem)
git tag v1.0.1 # tag leve (só um nome)
git push origin v1.0.0 # tags não vão no push normal
git push origin --tags # envia todas
git tag -d v1.0.1 # apaga localmente
git push origin --delete v1.0.1 # apaga no remotoO padrão de numeração mais usado é o Semantic Versioning: MAJOR.MINOR.PATCH. Incremente o MAJOR quando quebrar compatibilidade, o MINOR ao adicionar funcionalidades compatíveis e o PATCH em correções. No GitHub, uma Release é uma tag com notas de versão e arquivos anexados.
Ferramentas avançadas
cherry-pick: trazer um commit específico
git switch main
git cherry-pick 9a0b1c2 # aplica só esse commit na branch atualbisect: encontrando o commit que introduziu um bug
O bisect faz uma busca binária no histórico. Com 1.000 commits, você encontra o culpado em cerca de 10 passos.
git bisect start
git bisect bad # a versão atual tem o bug
git bisect good v1.2.0 # essa versão funcionava
# O Git faz checkout de um commit no meio. Teste e responda:
git bisect good # ou: git bisect bad
# ... repita até o Git anunciar "abc1234 is the first bad commit"
git bisect reset # volta para onde você estavaSe houver um script de teste que retorna 0 quando funciona, automatize: git bisect run npm test.
worktree: duas branches abertas ao mesmo tempo
git worktree add ../projeto-hotfix hotfix/pagamento # outra pasta com outra branch
git worktree list
git worktree remove ../projeto-hotfixOutras ferramentas
- Submódulos (
git submodule add URL pasta): um repositório dentro de outro, fixado num commit. Poderosos, mas confusos para a equipe; considere alternativas antes. - Git LFS: guarda arquivos grandes (vídeos, modelos, binários) fora do repositório, deixando só ponteiros no histórico.
- Hooks: scripts executados em eventos (
pre-commit,commit-msg,pre-push), na pasta.git/hooks. Em projetos JavaScript, o Husky facilita compartilhá-los com a equipe. - Commits assinados: provam que o commit veio de você. É possível assinar com a própria chave SSH (
git config --global gpg.format ssh), e o GitHub mostra o selo "Verified".
GitHub Actions: automação e CI
Integração contínua (CI) é rodar testes e verificações automaticamente a cada push ou pull request. No GitHub, isso é feito com o GitHub Actions, configurado em arquivos YAML dentro de .github/workflows/.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run lint
- run: npm testOs resultados aparecem na aba Actions e como verificações no pull request. Combinado com a proteção de branch, impede que código com testes quebrados chegue à main. Outros recursos úteis: GitHub Pages (hospedagem gratuita de sites estáticos) e Dependabot (abre PRs atualizando dependências e alertando sobre vulnerabilidades).
Segurança: segredos no repositório
Nunca faça commit de senhas, tokens, chaves de API ou arquivos .env. Robôs varrem repositórios públicos atrás de credenciais em questão de minutos.
O GitHub ajuda com secret scanning e push protection, que bloqueiam o push quando detectam um token conhecido.
Erros comuns e como resolver
fatal: not a git repository. Você não está dentro de um repositório. Confira a pasta com pwd ou rode git init.
Author identity unknown / Please tell me who you are. Configure user.name e user.email com git config --global.
! [rejected] main -> main (fetch first). O remoto tem commits que você não tem. Rode git pull (resolva conflitos, se houver) e depois git push.
refusing to merge unrelated histories. Os dois históricos não têm commit em comum, geralmente porque o repositório do GitHub foi criado com README e o local foi iniciado separadamente. Se tiver certeza de que quer juntá-los: git pull origin main --allow-unrelated-histories.
You are in 'detached HEAD' state. Você fez checkout de um commit, não de uma branch. Para guardar o que fizer ali, crie uma branch: git switch -c nova-branch. Para sair: git switch main.
Permission denied (publickey). A chave SSH não está cadastrada no GitHub ou não foi carregada no agente. Teste com ssh -T git@github.com e revise a seção de SSH.
error: src refspec main does not match any. A branch main não existe localmente: ou ainda não há nenhum commit, ou a branch se chama master. Faça o primeiro commit ou use git branch -M main.
Your branch is ahead of 'origin/main' by 3 commits. Não é erro: há commits locais ainda não enviados. git push resolve.
Commitei na branch errada. Se ainda não enviou: crie a branch certa no ponto atual (git branch feature/certa), volte a atual (git reset --hard HEAD~1) e troque (git switch feature/certa).
LF will be replaced by CRLF. Aviso de conversão de finais de linha no Windows. Configure core.autocrlf e um .gitattributes como mostrado no início.
File is 120.00 MB; this exceeds GitHub's file size limit of 100.00 MB. O GitHub recusa arquivos acima de 100 MB. Remova o arquivo do histórico (não basta apagar num commit novo) e use Git LFS se precisar versioná-lo.
Tabela completa de comandos Git
Todos os comandos do guia, organizados para consulta rápida. Para situações de emergência explicadas uma a uma, veja também o artigo Git: comandos que salvam sua vida.
Git e GitHub: comandos essenciais
Do primeiro commit ao pull request.
Configuração
Uma vez por máquina
Criar e clonar
Começando um repositório
Ciclo básico
O dia a dia
Inspecionar histórico
O que mudou, quando e quem
Desfazer mudanças
Do mais seguro ao mais destrutivo
Branches
Linhas de trabalho independentes
Merge
Juntando branches
Rebase
Reescrevendo a base (só local)
Stash
Guarde mudanças temporariamente
Remotos e sincronização
Conversando com o GitHub
Tags e releases
Marcando versões
Ferramentas avançadas
Para situações específicas
GitHub CLI
O GitHub no terminal
SSH e autenticação
Acesso sem senha
Referências e leituras recomendadas
- Pro Git, livro oficial em português: o livro completo e gratuito.
- Documentação de referência do Git: todos os comandos e opções.
- GitHub Docs em português: documentação oficial do GitHub.
- GitHub Skills: cursos práticos dentro do próprio GitHub.
- Tutoriais de Git da Atlassian: explicações visuais de merge, rebase e fluxos de trabalho.
- Learn Git Branching: jogo interativo para praticar branches.
- Oh Shit, Git!?!: receitas para sair de enrascadas.
- Conventional Commits: a especificação das mensagens de commit.
- Semantic Versioning: a especificação de versionamento.
- Modelos de .gitignore: prontos para cada linguagem.
- git-filter-repo: reescrevendo o histórico com segurança.
- Manual do GitHub CLI: todos os comandos
gh. - Git Credential Manager: autenticação HTTPS segura.
- Trunk Based Development: o fluxo usado por grandes equipes.
Se você trabalha no terminal, o guia completo de Linux complementa este.
7 perguntas sobre Git e GitHub.
Qual a diferença entre git fetch e git pull?
Não foi dessa vez. Explicação:
git pull é um git fetch seguido de merge (ou rebase) na branch atual.



