Estágio 01 · 01-09
LockedA maioria dos devs usa Git como mágica. git pull, git push, copia comandos do Stack Overflow quando algo dá errado. Quando há merge conflict complexo, rebase interativo, ou precisa recuperar commits "perdidos", trava.
Git é uma estrutura de dados: uma DAG (directed acyclic graph) de commits, cada um apontando pra trees, que apontam pra blobs. Tudo identificado por SHA-1 (em transição pra SHA-256). É um sistema de versionamento de conteúdo distribuído com modelo simples e operações compostas a partir desse modelo.
Entender Git por dentro significa:
reflog.merge é melhor que rebase (e vice-versa).Tudo no Git é objeto identificado por hash SHA-1 (40 chars hex) do conteúdo. Mesmo conteúdo → mesmo hash. Conteúdo diferente → hash diferente (com altíssima probabilidade, colisões SHA-1 são teoricamente possíveis, na prática negligenciáveis).
3 tipos de objetos:
$ echo "hello" | git hash-object --stdin
ce013625030ba8dba906f756967f9e9ca394464a
(mode, type, hash, name). Aponta pra blobs (arquivos) e outras trees (subdiretórios).Tudo armazenado em .git/objects/:
.git/objects/ab/cdef1234... # primeiros 2 chars como diretório
Conteúdo é zlib-compressed. Cabeçalho: <type> <size>\0<content>.
Packfiles: depois de N objetos soltos, Git compacta em packfiles (.pack) com delta compression (cada objeto codificado como diff de outro similar). Otimização de espaço/transferência.
Refs são ponteiros nomeados pra commits.
refs/heads/<name>. main é só o ponteiro refs/heads/main → commit X.refs/tags/<name>. Lightweight tag = ref direto. Annotated tag = objeto tag com mensagem.refs/remotes/origin/main → última posição conhecida do remote..git/HEAD. Aponta pra branch ativo (ref: refs/heads/main) ou direto pra commit (detached HEAD).git checkout <branch> muda HEAD. git checkout <commit> deixa HEAD detached.
Git rastreia mudanças em 3 lugares:
Working directory ──add──► Index (staging) ──commit──► HEAD
▲ │ │
│ │ │
└────checkout───reset────────┴───────reset HEAD──────┘
Comandos:
git add <file>: WD → Index.git commit: Index → novo commit; HEAD avança.git reset --soft <commit>: HEAD muda; Index preservado; WD preservado.git reset --mixed <commit> (default): HEAD muda; Index reset; WD preservado.git reset --hard <commit>: HEAD, Index e WD resetam. Destrutivo.git checkout -- <file>: descarta mudanças de WD.git status mostra diferenças entre os 3.
Branch é só um ponteiro mutável. Criação é O(1), só cria um ref novo.
Merge (default --ff quando possível): se branch atual está em ancestral do branch-alvo, fast-forward (move o ponteiro pra frente). Se há divergência, cria merge commit com 2 parents.
--no-ff força merge commit mesmo quando fast-forward seria possível, mantém histórico explícito de "houve um branch aqui".
--squash: combina mudanças em 1 commit linear. Não cria merge commit. Histórico fica mais limpo, mas perde info de branching.
git rebase <base>: pega commits do branch atual desde divergência com base, e os reaplica em cima de base.
Resultado: histórico linear, sem merge commits. Bom pra branches de feature antes de merge na main.
Antes:
A---B---C---D (main)
\
E---F (feature)
Depois de `git rebase main` em feature:
A---B---C---D (main)
\
E'---F' (feature)
E' e F' são novos commits (hashes diferentes). E e F antigos viram lixo (coletado eventualmente).
Regra de ouro: NUNCA rebase commits que já foram pushados pra branch compartilhado. Você reescreve história, outros têm divergência.
Rebase interativo (git rebase -i <base>): abre editor com lista de commits. Você pode:
pick (manter)reword (mudar message)edit (parar pra modificar)squash (juntar com anterior)fixup (squash sem manter message)drop (descartar)Ferramenta poderosíssima pra limpar histórico antes de PR.
Merge:
Rebase:
Política comum em times maduros:
--no-ff ou squash merge (preferência do time).git reflog é log local de toda movimentação de HEAD (e de cada branch). Cada checkout, commit, reset, rebase é registrado por ~90 dias.
$ git reflog
abc1234 HEAD@{0}: rebase: ...
def5678 HEAD@{1}: checkout: moving from feature to main
...
Se você fez git reset --hard e perdeu commits: git reflog mostra. git checkout <hash> e você está de volta.
reflog é a razão pela qual quase nada se perde de verdade no Git. Os objetos só somem após GC (git gc), tipicamente >30-90 dias.
git stash: salva mudanças não-commitadas em uma pilha de stashes. Útil quando precisa trocar de branch.
git stash push -m "msg"git stash listgit stash pop (aplica e remove)git stash apply (aplica, não remove)git stash dropStash é commit normal com 2-3 parents (working tree + index, opcional untracked) em refs/stash.
git cherry-pick <hash>: aplica as mudanças de um commit específico no branch atual. Cria novo commit. Útil pra portar bugfix entre branches.
git revert <hash>: cria novo commit que desfaz as mudanças. Não reescreve história. Seguro em branches públicos.
Diferente de reset que apaga o commit.
git remote -v lista remotes. Cada remote tem ref refs/remotes/<remote>/<branch>.
git fetch: traz objetos do remote, atualiza refs/remotes/. Não muda branches locais.
git pull = fetch + merge (ou --rebase).
git push: envia commits locais ao remote.
git push --force sobrescreve remote, perigoso. git push --force-with-lease: só sobrescreve se remote ainda está onde você esperava (não houve push de outro entre seu fetch e push).
Rule: NUNCA --force em main/master/develop. Use --force-with-lease mesmo em feature branches.
Scripts em .git/hooks/ executados em eventos. Úteis: pre-commit (lint), commit-msg (validação de mensagem), pre-push (testes), post-merge.
Husky (Node) automatiza setup de hooks. lint-staged roda lint só em arquivos staged.
.gitignore: padrões de paths a ignorar.
.gitattributes: metadados por path (line endings, diff personalizado, lock, filtros).
Cuidado: .gitignore não unstage arquivos já tracked. Use git rm --cached <file>.
git checkout -b feat/x.git fetch origin && git rebase origin/main.git push -u origin feat/x.--force-with-lease se rebased).Pra passar o Portão Conceitual, sem consultar:
git reset --soft|--mixed|--hard.git revert vs git reset em termos de histórico.git reset --hard perdido.--force-with-lease e por que é mais seguro que --force.cherry-pick é melhor que merge/rebase.Implementar mygit, uma versão minimalista do Git em TypeScript.
CLI que suporta:
mygit init: cria diretório .mygit/ com estrutura mínima (objects/, refs/heads/, HEAD).mygit hash-object [-w] <file>: lê arquivo, computa hash SHA-1 (com cabeçalho blob <size>\0<content>), opcionalmente escreve em .mygit/objects/.mygit cat-file -p <hash>: lê objeto, descomprime (zlib), imprime conteúdo.mygit write-tree: pega o index (você implementa simples, ex: arquivo index.json com lista de paths + hashes), constrói tree object recursivamente, escreve.mygit commit-tree <tree-hash> [-p <parent>] -m <msg>: cria commit object apontando pra tree, opcionalmente parent.mygit log: percorre HEAD pra trás imprimindo commits.mygit branch <name>: cria ref em refs/heads/.mygit checkout <branch|hash>: muda HEAD; popula working dir baseado no tree do commit.node:fs, node:crypto, node:zlib, node:path. Sem libs externas.cat-file consegue ler objetos do Git real.init cria estrutura.hash-object é determinístico e bate com git hash-object real.write-tree produz mesma tree pra mesmo conteúdo.commit-tree cria commit consistente.log percorre histórico em ordem.mygit hash-object foo.txt produz mesmo SHA que git hash-object foo.txt.<type> <size>\0.write-tree: como recursa.checkout (escreve blobs como files, recria tree como dirs).mygit merge simples (3-way merge com diff conflito básico)..pack do Git real).mygit init + commits + git status mostra estado correto.Esse desafio é particularmente valioso porque você nunca mais vai mistificar Git depois de ter implementado os 90% do core.
*.lock) pra serializar escritas. fork/exec ao chamar editor (commit -m vs sem -m).git log --oneline | head), aliases.git log --graph --oneline --all: visualizar DAG.Documentation/technical/ tem docs internas excelentes.Encerramento: após 01-09 você nunca mais "tem medo" de comandos Git. Você raciocina sobre o que cada operação faz no DAG, e operações antes "perigosas" (rebase interativo, force push com lease) viram ferramentas naturais.
Destrava
01-09 é prereq dos seguintes módulos: