Author: Pedro Lucas Porcellis <porcellis@eletrotupi.com>
Add article about git-rebase I have an english version but will finish later
content/blog/a-magia-do-git-rebase.md | 130 +++++++++++++++++++++++++++++
diff --git a/content/blog/a-magia-do-git-rebase.md b/content/blog/a-magia-do-git-rebase.md new file mode 100644 index 0000000000000000000000000000000000000000..f8318465907057dc2624585c1a723fea142b1201 --- /dev/null +++ b/content/blog/a-magia-do-git-rebase.md @@ -0,0 +1,130 @@ +--- +title: "A Magia do git rebase" +date: 2021-04-29 +draft: false +tags: ["git"] +--- + +Uma das ferramentas que eu acho das mais úteis no `git`, e da qual eu [mais +amo][toot] é o `git-rebase(1)`. Ela se baseia na premissa que trouxe o git para +o mundo dos sistemas de controle de versão: de que você *pode e deve* alterar a +história do teu repositório de forma a tornar ele mais legível e organizado. + +[toot]: https://alfazema.club/@eletrotupi/105984543604974096 + +Ao redor do mundo, cada projeto estabelece sua convenção de como trabalhar com o +git, alguns utilizam técnicas como o git-flow, outros, apenas uma branch e tags, +outros dividem em duas branchs principais, `master/main` e `production/release`. +A convenção no meu trabalho, é de sempre criar uma _branch_ específica para +trabalhar em uma nova funcionalidade, correção de algum erro ou melhoria num +geral. Feito isso, eu adoto a disciplina de (1) manter ela atualizada com a +_branch_ pai usando `git pull --rebase origin/branch_pai` e (2) conforme for +trabalhando nela, procuro manter ela organizada usando o `git rebase -i HEAD~N`. + +## Rebase + +O rebase tem um funcionamento bastante simples, em essência, ao contrário de +executar um _merge_ (ou seja, criar um _commit_ que puxa as mudanças para a tua +branch), o rebase: temporariamente remove os teus patches, puxa os commits da +_branch_ pai e aplica os teus patches em sequência em cima dessa branch +atualizada. O benefício aqui é claro: a branch contém apenas uma lista de +commits com o seu trabalho novo, de forma legível e coerente. + +Utilizando o parâmetro `-i` no comando, inicia um _rebase_ interativo, iniciando +a partir do commit que eu pedi. [^1] Por exemplo, `git rebase -i HEAD~5`, vai +iniciar um rebase até os meus últimos 5 commits. O _rebase_ vai mostrar o +seguinte: + +[^1]: `HEAD~N`, significa essencialmente a partir do ultimo commit disponível + nessa branch até N commits anteriormente. + +```ini +pick 9fdd140 hooks: Flesh out the Hook::Destroy service test +pick ecb296e resource: Flesh out the resource test +pick bd115a0 hooks: Generate token when subscribing +pick fcaba9d hooks: Flesh out the handler test +pick 3926d4b doc: Add document about testing + +# Rebase bed2ff9..3926d4b onto bed2ff9 (5 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 <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 <commit> | -c <commit>] <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. +# +# These lines can be re-ordered; they are executed from top to bottom. +# +# If you remove a line here THAT COMMIT WILL BE LOST. +# +# However, if you remove everything, the rebase will be aborted. +# +``` + +Ou seja, vai me mostrar meus últimos 5 commits, e me pedir para eu escolher +(`pick` ou `break`), remover (`drop`), editar (`edit` ou `reword`) ou juntar +eles (`fixup` e `squash`). + +## Disciplina + +Meu plano de trabalho, em geral é o seguinte: conforme eu avanço no trabalho que +eu tenho que fazer, commito localmente de forma que eu vagamente consiga +explicar o que eu fiz. No momento que eu sinto que terminei e já vale de +apresentar esse trabalho, utilizo o _rebase_ para reescrever, quebrar/juntar +esses commits em uma narrativa de passos sequenciais mais legíveis e coerente. + +Buscando escrever essa narrativa, procuro criar mudanças atômicas que por si só +sejam responsáveis por responder a pergunta de _o que foi feito?_ e _isso não +quebra os testes?_ [^2]. + +[^2]: Ou seja, sem `fix tests`, `fix typo`. + +Uma experiência que me ajudou muito nesse propósito, foi o de contribuir com +projetos que utilizam uma lista de e-mail pública [^3], enviando patches ao +invés de criar _Pull Requests_. Em especial, porque a lógica de trabalho muda. A +forma de trabalho apresentada por ferramentas como Github, GitLab e outras +derivações similares é de criar um PR e infinitamente seguir adicionando commits +nele até que o trabalho esteja pronto, a interface que se vire para mostrar o +que realmente é importante. Contudo, a lógica de trabalho do git por email é de +tentar ser o mais assertivo possível, evitando patchsets [^4] gigantes e com +mudanças em lugares diferentes. Uma das vantagens de lidar com patchsets, é que +é possível aplicar uma seleção de patches de um patchset, e pedir revisão dos +outros que não façam sentido ou necessitem de um trabalho maior naquele momento. + +[^3]: Que é o formato de contribuição do qual o git foi feito para funcionar + originalmente, exemplos notáveis são a lista de email do Kernel, Alpine Linux, + git, cgit, dwm e outras ferramentas da suite suckless, coreutils, musl-libc, + freedesktop, vim, emacs, sistemas BSD e por aí vai. + +[^4]: Um patch é um commit, e um patchset é uma coleção de patches. + +Aplicando essa lógica, procuro sempre quebrar um patchset muito grande em vários +patchsets diferentes [^5] e com patches atômicos, descritivos, geralmente +organizado em módulos ou partes [^6]. + +[^5]: Um exemplo aqui poderia ser organizar _includes_, remover espaços em + branco, ou qualquer mudança estética no código que não tenha relação direta + com a funcionalidade/correção que seja necessário originalmente. + +[^6]: Geralmente adoto o seguinte formato para meus commits: `módulo: + sub-módulo, descrição do que esse commit faz` e descrevo o porque essa mudança + foi feita e qual abordagem eu tomei no corpo do commit. + +Em resumo, o git é fantástico. Ter disciplina para utilizá-lo e aprender como +ele funciona é uma obrigação de qualquer programador. Uma das principais +diferenças dele para os outros sistemas de controle de versão que vieram antes é +justamente que a história de trabalho daquele repositório não é escrita em +pedra, não é apenas leitura. Aliado a isso o git tem uma extensiva documentação +offline, disponível para você estudá-lo a respeito nos seus manuais. Recomendo +fortemente a leitura dessas man pages: git-rebase(1), giteveryday(7) e +gitrevisions(7).