Pedro's dev blog

Guia de Referência Git: Comandos, Fluxos de Trabalho e Técnicas Avançadas

Git.jpg
Published on
/21 mins read/---

Este documento é um guia extremamente detalhado sobre comandos Git. Cobriremos desde o básico até cenários avançados, com explicações minuciosas, exemplos práticos, e diagramas Mermaid (gitGraph e outros) para visualização.

Sumário

  1. Configuração Inicial (com Verificação)
  2. Criando Repositórios (Local vs. Remoto)
  3. Ciclo de Vida Detalhado de um Arquivo
  4. Branches (Ramificações - O Poder do Git)
  5. Desfazendo Alterações (O "Undo" do Git)
  6. Trabalhando com Repositórios Remotos (Colaboração)
  7. Tags (Pontos de Referência)
  8. Stashing (Salvando o Trabalho em Andamento)
  9. Rebase (Reescrevendo a História - Com Cuidado!)
  10. Logs e Inspeção (Investigando o Histórico)
  11. Comandos Avançados (Para Usuários Experientes)
  12. Alias (Atalhos Personalizados)
  13. Exemplos de Diagramas GitGraph (Cenários Comuns)

1. Configuração Inicial (com Verificação)

git config --global user.name "Seu Nome Completo"
git config --global user.email "seu.email@exemplo.com"
git config --global core.editor "nano"  # Ou "vim", "code -w", etc.
git config --global init.defaultBranch main # Define 'main' como branch padrão
 
# VERIFICAÇÃO:
git config --list  # Exibe TODAS as configurações
git config user.name #Verifica apenas o nome do usuario
  • core.editor: Define o editor de texto padrão para mensagens de commit, rebase interativo, etc. code -w é para VS Code (o -w faz o Git esperar o VS Code fechar antes de continuar).
  • init.defaultBranch: Define o nome padrão do branch inicial (main é o recomendado atualmente).
  • Verificação: Use git config --list para ver todas as configurações, não apenas as globais. Isso ajuda a identificar configurações específicas de repositório que podem estar sobrescrevendo as globais.

2. Criando Repositórios (Local vs. Remoto)

  • Local:

    mkdir meu-projeto
    cd meu-projeto
    git init

    Diagrama (nenhum diagrama é realmente necessário aqui, é muito básico).

  • Remoto (Clonando):

    git clone https://github.com/usuario/repositorio.git  # Clonagem via HTTPS
    git clone git@github.com:usuario/repositorio.git   # Clonagem via SSH (requer configuração de chave SSH)
    git clone --depth=1 https://github.com/usuario/repositorio.git # Clone raso, apenas o commit mais recente
    • --depth=1: Faz um "clone raso", baixando apenas o histórico mais recente (útil para repositórios grandes, economiza tempo e espaço).

3. Ciclo de Vida Detalhado de um Arquivo

graph LR
    A[Untracked] --> B(Modified);
    B --> C{Staged};
    C --> D[Committed];
    B --> D;
    A --> C;
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style B fill:#ccf,stroke:#333,stroke-width:2px
    style C fill:#9ff,stroke:#333,stroke-width:2px
    style D fill:#9f9,stroke:#333,stroke-width:2px
  • Untracked: O arquivo existe no seu sistema de arquivos, mas o Git não o conhece.
  • Modified: O arquivo foi alterado desde o último commit.
  • Staged: As alterações foram marcadas para serem incluídas no próximo commit.
  • Committed: As alterações foram salvas permanentemente no histórico do Git.
  • As setas mostram as transições possíveis entre os estados.

Status (e suas nuances)

git status
git status -s  # Status curto (mais conciso)
git status --ignored # Mostra arquivos ignorados (.gitignore)
  • -s: Formato curto, ideal para scripts ou para ter uma visão geral rápida. Usa códigos de duas letras:

    • ??: Untracked
    • M: Modified (no staging area)
    • M : Modified (no staging area, com espaço antes significa que o arquivo já estava sendo rastreado)
    • A : Added (no staging area)
    • AM: Modified após ser adicionado ao staging area
    • MM: Modified no working directory e no staging area, mas com alterações diferentes.
    • D : Deleted (no staging area)
    • R : Renamed (no staging area)
    • C : Copied (no staging area)
    • UU: Unmerged (conflito durante um merge)
  • --ignored: Mostra os arquivos que estão sendo ignorados pelo .gitignore. Útil para depurar o .gitignore.

Adicionando Mudanças (Staging Area Detalhado)

git add <arquivo1> <arquivo2> ...  # Adiciona arquivos específicos
git add .                        # Adiciona TUDO no diretório atual e subdiretórios
git add -u                       # Adiciona arquivos modificados e deletados (mas NÃO novos)
git add -A                       # Adiciona TUDO (modificados, deletados E novos)
git add -p                       # Modo interativo: permite selecionar *partes* de um arquivo para adicionar
git add --patch                  # O mesmo que -p
  • -p (ou --patch): Extremamente útil. O Git mostra cada "hunk" (pedaço de alteração) e pergunta se você quer adicioná-lo, ignorá-lo, ou fazer outras ações (editar, adiar, etc.). Permite criar commits muito granulares.

    Stage this hunk [y,n,q,a,d,s,e,?]?
    y - yes (adiciona)
    n - no (não adiciona)
    q - quit (sai do interactive add)
    a - stage this and all later hunks in the file (adiciona este e todos os pedaços seguintes neste arquivo)
    d - do not stage this hunk nor any of the later hunks in the file (não adiciona este nem os seguintes)
    s - split the current hunk into smaller hunks (divide o pedaço atual em pedaços menores)
    e - manually edit the current hunk (abre o editor para editar o pedaço manualmente)
    ? - print help (mostra a ajuda)
    

Commit (Anatomia de um Commit)

git commit -m "Mensagem concisa e descritiva"
git commit -am "Mensagem" # Atalho: adiciona arquivos modificados e commita (NÃO inclui novos)
git commit --amend -m "Nova mensagem" # Modifica o ÚLTIMO commit (mensagem ou conteúdo)
git commit --amend --no-edit  # Modifica o último commit, adicionando o que está no stage, mas sem alterar a mensagem.
  • Anatomia de um commit:

    • SHA-1 hash: Um identificador único de 40 caracteres hexadecimais (ex: a1b2c3d4...).
    • Autor: Nome e email de quem fez o commit.
    • Data do autor: Quando o commit foi originalmente criado.
    • Committer: Nome e email de quem aplicou o commit (pode ser diferente do autor, ex: em rebase).
    • Data do committer: Quando o commit foi aplicado (pode ser diferente da data do autor).
    • Mensagem do commit: Descrição das alterações.
    • Parent(s): O(s) commit(s) anterior(es). Commits normais têm um pai. Commits de merge têm dois ou mais pais. O commit inicial de um repositório não tem pai.
    • Tree: Uma "fotografia" do estado do repositório naquele commit.
    • Blobs: O conteúdo dos arquivos.
  • --amend: Permite modificar o último commit. Use com cuidado, pois reescreve o histórico. Não use em commits que já foram enviados para um repositório remoto.

  • --no-edit: Complemento do --amend, é muito útil para quando se esquece de adicionar arquivos ao último commit.

4. Branches (Ramificações - O Poder do Git)

Criação e Estratégias de Branching

git branch <nome-do-branch>      # Cria um novo branch, mas NÃO muda para ele
git checkout -b <nome-do-branch>  # Cria E muda para o novo branch
git branch -b <novo> <commit> # Cria o branch a partir de um commit específico.
git branch --track <novo> <remoto/branch>  #Cria um branch local rastreando um branch remoto
git branch -vv #Lista os branchs com mais informações
  • Estratégias de Branching (Workflows):
    • Gitflow: Um modelo bem definido, com branches main, develop, feature/*, release/*, e hotfix/*. Bom para projetos com ciclos de lançamento formais.
    • GitHub Flow: Mais simples. Usa main como branch principal e branches de feature. Ideal para projetos com deploy contínuo.
    • Trunk-Based Development: Todos trabalham diretamente em main (ou em branches de curtíssima duração). Requer testes automatizados robustos e integração contínua.
git checkout <nome-do-branch>  # Muda para o branch
git checkout <commit-hash>    # Muda para um commit específico (DETACHED HEAD)
git checkout -               # Volta para o branch anterior
git switch <nome-do-branch>    # Alternativa mais recente (e recomendada) ao checkout para branches
  • DETACHED HEAD: Quando você faz checkout de um commit específico (em vez de um branch), você entra no estado DETACHED HEAD. Isso significa que você não está em um branch nomeado. Se você fizer commits nesse estado, eles não pertencerão a nenhum branch e poderão ser perdidos quando você mudar para outro branch (a menos que você crie um branch a partir deles).

  • git switch: Introduzido em versões mais recentes do Git, git switch é uma alternativa mais clara ao git checkout para mudar de branch. Evita a confusão do checkout que também é usado para desfazer alterações.

    graph LR
        A[Branch 'main'] --> B(Commit 1)
        B --> C(Commit 2)
        C --> D(Commit 3)
        D --> E[HEAD (main)]
        F[Branch 'feature'] --> C
        F --> G(Commit F1)
        G --> H[HEAD (feature)]
     
    subgraph "DETACHED HEAD"
         I(Commit 2) --> J[HEAD]
    end
     
    style E stroke:#f00,stroke-width:3px
    style H stroke:#0f0,stroke-width:3px
    style J stroke:#ff0,stroke-width:3px

Merging (Fast-Forward, Three-Way, Squash, e Rebase)

git checkout <branch-destino>  # Ex: git checkout main
git merge <branch-origem>    # Ex: git merge feature/nova-funcionalidade
git merge --no-ff <branch-origem>   # Força a criação de um commit de merge (mesmo se pudesse ser fast-forward)
git merge --squash <branch-origem>  # Combina todos os commits do branch de origem em UM novo commit no branch de destino
  • Fast-Forward Merge: Ocorre quando o branch de destino não tem commits novos desde que o branch de origem foi criado. O ponteiro do branch de destino simplesmente "avança" para o último commit do branch de origem. Não há commit de merge.

    gitGraph
        commit
        commit
        branch feature
        checkout feature
        commit
        commit
        checkout main
        merge feature  # Fast-forward
  • Three-Way Merge: Ocorre quando ambos os branches têm commits novos. O Git cria um novo commit de merge que combina as alterações. Usa um algoritmo de "três vias" (daí o nome), comparando os dois branches com um ancestral comum.

    gitGraph
        commit
        commit
        branch feature
        checkout feature
        commit
        checkout main
        commit
        merge feature  # Three-way merge
  • --no-ff: Força a criação de um commit de merge, mesmo que um fast-forward fosse possível. Útil para manter um histórico mais claro, mostrando quando um branch de feature foi mesclado.

  • --squash: Combina todos os commits do branch de origem em um único novo commit no branch de destino. Cria um histórico mais limpo, mas perde a granularidade dos commits individuais do branch de origem. É como se voce tivesse feito um git add . e git commit -m "Mensagem" com todas as alterações do branch de origem.

Conflitos de Merge (e como resolvê-los)

Quando o Git não consegue mesclar automaticamente as alterações (porque o mesmo trecho de código foi modificado nos dois branches), ele marca o arquivo como unmerged e insere marcadores de conflito:

<<<<<<< HEAD
Código do branch atual (ex: main)
=======
Código do branch que você está mesclando (ex: feature)
>>>>>>> feature/nome-do-branch

Resolução:

  1. Abra o arquivo com conflitos em um editor de texto.
  2. Edite o arquivo para remover os marcadores (<<<<<<<, =======, >>>>>>>) e deixar apenas o código que você quer.
  3. Salve o arquivo.
  4. Adicione o arquivo ao staging area (git add <arquivo>).
  5. Faça o commit (git commit). O Git já preenche a mensagem de commit para você.

Exclusão (Segura vs. Forçada)

git branch -d <nome-do-branch>  # Exclusão segura (só exclui se já foi mesclado)
git branch -D <nome-do-branch>  # Exclusão forçada (exclui mesmo se não foi mesclado - CUIDADO!)
  • -d: "Delete". Só exclui o branch se ele já foi completamente mesclado em outro branch. É uma operação segura.
  • -D: "Delete" (forçado). Exclui o branch mesmo que ele não tenha sido mesclado. Use com cautela, pois você pode perder commits.

5. Desfazendo Alterações (O "Undo" do Git)

Descartando Alterações Locais (Checkout e Clean)

git checkout -- <arquivo>  # Descarta alterações em um arquivo específico
git checkout .            # Descarta TODAS as alterações não commitadas no diretório atual (e subdiretórios)
git clean -fd             # Remove arquivos UNTRACKED (não rastreados) e diretórios vazios
git clean -f -n           # "Dry run" do clean: mostra o que SERIA removido, sem remover
git clean -fdx            # Remove arquivos UNTRACKED, incluindo os ignorados (.gitignore)
  • checkout -- <arquivo>: Reverte o arquivo para o estado do último commit.
  • git clean: Remove arquivos untracked. É irreversível, então use com cuidado. O -n (dry run) é altamente recomendado antes de executar o clean de verdade. O -x remove até mesmo arquivos que estão no .gitignore.

Desfazendo Commits (Reset, Revert, Amend)

git reset --soft HEAD~1    # Desfaz o último commit, mas mantém as alterações no staging area
git reset --mixed HEAD~1   # Desfaz o último commit e remove as alterações do staging area (mas mantém no working directory)
git reset --hard HEAD~1    # Desfaz o último commit E descarta as alterações (PERMANENTE)
git reset <commit-hash>  # Reseta para um commit específico (use --soft, --mixed ou --hard)
 
git revert <commit-hash>   # Cria um NOVO commit que desfaz as alterações do commit especificado
git revert -n <commit-hash> # desfaz, porem mantem as mudanças no stage.
 
git commit --amend       # Modifica o último commit
  • reset: Move o ponteiro do branch atual para um commit anterior. É como se os commits mais recentes nunca tivessem existido (no seu branch local).
    • --soft: Menos destrutivo. Mantém as alterações no staging area.
    • --mixed: Padrão. Remove as alterações do staging area, mas mantém no working directory.
    • --hard: Mais destrutivo. Descarta completamente as alterações (tanto do staging area quanto do working directory). Use com EXTREMA cautela.
  • revert: Não reescreve o histórico. Cria um novo commit que desfaz as alterações de um commit anterior. É a maneira segura de desfazer commits que já foram enviados para um repositório remoto.
  • revert -n: Não cria o commit. Faz o revert e adiciona as mudanças no stage, assim como o --soft.

Reflog - a rede de segurança

git reflog

O reflog é um registro de todas as movimentações do HEAD (a ponta do seu branch atual). Mesmo commits que foram "perdidos" por causa de um reset --hard ou um rebase mal feito ainda estarão no reflog por um tempo (por padrão, 30 dias para referências "dangling" e 90 dias para outras).

e7a9519 HEAD@{0}: rebase -i (finish): returning to refs/heads/feature/minha-feature
e7a9519 HEAD@{1}: rebase -i (pick): Adiciona funcionalidade X
2a5c89f HEAD@{2}: rebase -i (pick): Corrige bug Y
b3d7f2a HEAD@{3}: rebase -i (start): checkout b3d7f2a
f1c4d7e HEAD@{4}: commit: Mensagem do commit original
...

Você pode usar o reflog para encontrar o hash de um commit "perdido" e recuperá-lo:

git checkout <hash-do-reflog>   # Recupera o commit (DETACHED HEAD)
git branch <novo-branch> <hash-do-reflog>  # Cria um novo branch a partir do commit recuperado

6. Trabalhando com Repositórios Remotos (Colaboração)

Clonando (e suas opções)

git clone <URL>
git clone <URL> <diretorio> # Clona para um diretório com nome diferente
git clone --branch <nome-do-branch> <URL>  # Clona um branch específico
git clone --depth=1 <URL>     # Clone raso (apenas o último commit)

Remotos (Múltiplos Remotos)

git remote -v           # Lista os remotos e suas URLs
git remote add <nome> <URL>  # Adiciona um novo remoto
git remote rename <antigo> <novo> # Renomeia um remoto
git remote remove <nome>  # Remove um remoto
git remote set-url <nome> <nova-URL> # Muda a URL de um remoto
git remote show <nome>   # Mostra informações sobre um remoto específico
  • Você pode ter múltiplos remotos. Isso é comum em fluxos de trabalho colaborativos, onde você pode ter um remoto origin (seu fork) e um remoto upstream (o repositório principal).

Push (e suas armadilhas)

git push <remoto> <branch>   # Ex: git push origin main
git push -u origin <branch>  # "-u" (upstream) configura o rastreamento (só precisa fazer na primeira vez)
git push --force           # FORÇA o envio (sobrescreve o histórico remoto - PERIGOSO!)
git push --force-with-lease # Uma versão MAIS SEGURA do force push.
git push --all <remoto>      # Envia todos os seus branches locais
git push --tags            # Envia as tags
  • --force: Use com extrema cautela. Sobrescreve o histórico no repositório remoto. Só use se você tiver absoluta certeza do que está fazendo e se você sabe que ninguém mais está trabalhando naquele branch. Pode causar perda de trabalho para outras pessoas.

  • --force-with-lease: Uma alternativa muito mais segura ao --force. Ele só força o envio se o seu branch local estiver atualizado em relação ao branch remoto. Se alguém tiver enviado commits para o remoto, o --force-with-lease falhará, evitando que você sobrescreva o trabalho de outras pessoas. É a opção recomendada se você realmente precisar forçar um push.

Fetch vs. Pull (A diferença crucial)

git fetch <remoto>         # Baixa as informações do remoto, mas NÃO aplica as mudanças
git fetch <remoto> <branch> #Baixa as informações de um branch do remoto.
git fetch --all            # Baixa as informações de TODOS os remotos
git fetch --prune          # Remove branches remotos que não existem mais no servidor
git pull <remoto> <branch>  # Equivale a git fetch + git merge
git pull --rebase <remoto> <branch> # Equivale a git fetch + git rebase.
  • fetch: Baixa os metadados (commits, branches, tags) do repositório remoto, mas não modifica seus arquivos de trabalho. É como dar uma "espiada" no que mudou no remoto. Você pode então usar git merge ou git rebase para integrar as mudanças.

  • pull: Faz um fetch e um merge (ou rebase, se você usar --rebase). É um atalho conveniente, mas pode ser menos flexível do que fazer fetch e merge separadamente.

  • --prune: Limpa referências a branches remotos que foram excluídos no servidor. Mantém sua lista de branches remotos locais organizada.

  • pull --rebase: É, em muitos casos, uma melhor alternativa ao pull padrão. Em vez de criar um commit de merge, ele reaplica seus commits locais em cima dos commits remotos. Resulta em um histórico mais linear. No entanto, as mesmas regras de cuidado sobre rebase se aplicam: não use em branches compartilhados.

Git Workflows (Fluxos de Trabalho)

  • Centralizado: Todos trabalham diretamente em um único repositório, no mesmo branch. Simples, mas não recomendado para equipes.
  • Feature Branch: Para cada nova funcionalidade ou correção, cria-se um novo branch. Ao finalizar, faz-se o merge com o branch principal (geralmente main ou develop).
  • Gitflow:: Um workflow mais estruturado, com branches dedicados para desenvolvimento (develop), releases (release/*), hotfixes (hotfix/*) e features (feature/*). O branch main contém apenas o código estável em produção.
  • Forking: Cada desenvolvedor faz um "fork" (cópia) do repositório principal. Trabalha em seu fork e, ao finalizar, envia um "Pull Request" para o repositório original. Comum em projetos open source.
  • Trunk-Based Development: Todos trabalham em um único branch (o "trunk", geralmente main). Requer alta disciplina, testes automatizados e integração contínua.

7. Tags (Pontos de Referência)

git tag <nome-da-tag>             # Cria uma tag "leve" (lightweight)
git tag -a <nome-da-tag> -m "Mensagem"  # Cria uma tag anotada (annotated - RECOMENDADO)
git tag -a v1.0.0 -m "Versão 1.0.0"  # Exemplo
git tag                             # Lista as tags
git tag -l "v1.*"                   # Lista tags que correspondem a um padrão
git show <nome-da-tag>             # Mostra informações sobre uma tag
git push origin <nome-da-tag>       # Envia uma tag específica
git push origin --tags             # Envia todas as tags
git tag -d <nome-da-tag>              #Exclui uma tag localmente
git push origin --delete <nome-da-tag> #Exclui uma tag no repositório remoto.
  • Tags leves (lightweight): São apenas um ponteiro para um commit específico.
  • Tags anotadas (annotated): São objetos completos no banco de dados do Git. Armazenam o nome do autor da tag, email, data e uma mensagem (como um commit). São recomendadas para releases, pois contêm mais informações.
  • -l: Lista tags, opcionalmente utilizando wildcards para filtrar por padrão.

8. Stashing (Salvando o Trabalho em Andamento)

git stash                        # Guarda as alterações (modificadas e staged)
git stash push -m "Mensagem"    # Guarda com uma mensagem descritiva
git stash list                   # Lista os stashes
git stash show                   #Mostra o diff do ultimo stash
git stash show -p stash@{2}      #Mostra o stash na posição 2
git stash apply                  # Aplica o último stash (mas NÃO o remove da lista)
git stash apply stash@{2}        # Aplica um stash específico
git stash pop                    # Aplica o último stash E o remove da lista
git stash drop stash@{2}         # Remove um stash específico
git stash clear                  # Remove TODOS os stashes
git stash branch <nome-branch>  #Cria um novo branch com base no stash e o remove da lista.
  • stash: Guarda as alterações modificadas e staged em uma "pilha" de stashes. Seu working directory fica limpo (como se você tivesse feito um git reset --hard).
  • stash list: Mostra a pilha de stashes. Cada stash tem um identificador: stash@{0}, stash@{1}, etc.
  • stash apply: Aplica um stash, mas não o remove da pilha. Você pode aplicar o mesmo stash várias vezes.
  • stash pop: Aplica o stash e o remove da pilha. É o mais comum.
  • stash branch: Cria um novo branch a partir do stash, e então remove o stash. Uma maneira muito prática de começar a trabalhar em um novo branch a partir de um estado salvo.

9. Rebase (Reescrevendo a História - Com Cuidado!)

git checkout <seu-branch>
git rebase <branch-base>  # Ex: git rebase main
git rebase -i HEAD~3     # Rebase INTERATIVO dos últimos 3 commits
git rebase --continue    # Continua o rebase após resolver conflitos
git rebase --abort       # Aborta o rebase (volta ao estado anterior)
git rebase --skip        # Pula o commit atual (se não houver conflitos para ele)
  • rebase: Reaplica seus commits em cima de outro branch. Cria um histórico linear, como se você tivesse começado a trabalhar a partir do último commit do branch base.
  • NUNCA faça rebase em commits que já foram enviados para um repositório remoto compartilhado! Isso causará problemas para outros colaboradores. Use rebase apenas em seus branches locais.
  • -i (interativo): Permite editar o histórico. Você pode:
    • Reordenar commits.
    • Remover commits.
    • Editar mensagens de commit.
    • Combinar commits (squash).
    • Dividir um commit em vários.

Rebase Interativo

Quando você executa git rebase -i, o Git abre um editor de texto com uma lista de commits e comandos:

pick a1b2c3d Mensagem do commit 1
pick d4e5f6g Mensagem do commit 2
pick g7h8i9j Mensagem do commit 3
 
# Rebase 1234567..g7h8i9j onto 1234567 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C] <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -C <commit> to reword the commit message.

Para:

  • Reordenar: Mova as linhas para cima ou para baixo.
  • Remover: Mude pick para drop (ou d).
  • Editar mensagem: Mude pick para reword (ou r).
  • Combinar (squash): Mude pick para squash (ou s). O commit será combinado com o commit anterior.
  • Fixup: Como o squash, mas descarta a mensagem do commit.
  • Editar commit (amend): Mude pick para edit (ou e). O rebase vai parar, e voce pode fazer alterações com git commit --amend.

Salve e feche o editor. O Git aplicará as mudanças. Se houver conflitos, resolva-os como em um merge normal e use git rebase --continue.

10. Logs e Inspeção (Investigando o Histórico)

git log                     # Histórico completo
git