Utilizando rebase e squash para melhorar o histórico do Git

Daniel Faccini
CWI Software
Published in
4 min readApr 9, 2019

Durante o desenvolvimento de software, são consideradas boas práticas realizar commits frequentes e fazer merges da branch para onde será feito o pull request sempre que possível, a fim de evitar maiores conflitos no futuro. Ao fazermos isso, geramos um número alto de commits que acabam poluindo o histórico o Git se não fizermos nada, como no exemplo abaixo.

Nesse caso, seria muito melhor se tivéssemos apenas um commit com todas as alterações feitas. Em uma visão de maior escala, esses commits parciais não agregam muito valor e não dão uma visão do que realmente foi implementado. Para isso, podemos utilizar um comando muito poderoso (e também perigoso caso usado sem cuidado) do Git — o comando rebase.

O rebase é parecido com o comando merge: recebe por parâmetro uma branch (ou commit específico) e atualiza a árvore de commits da branch atual com os commits dessa branch, porém ele faz de maneira diferente. Em vez de criar um commit de intersecção entre a branch atual e a branch de destino, o rebase utiliza como base a branch de destino, e depois aplica um a um os commits da branch atual que forem diferentes. Desta forma, torna-se desnecessário o commit de merge, porém o histórico de commits é reescrito. Por isso, é recomendado utilizar o rebase apenas em branches que ainda não foram compartilhadas no ambiente remoto (origin).

Para entender melhor, vamos utilizar um exemplo. Na imagem abaixo, temos a branch exemplo-rebase que foi criada a partir da branch master. Porém, na master, entraram dois novos commits que precisamos atualizar na branch exemplo-rebase.

Se utilizarmos o merge, através do comando git merge master, iremos gerar um commit de merge desnecessário que não agrega valor algum e polui ainda mais o histórico de commits.

Se utilizarmos o rebase, através do comando git rebase master, teremos um histórico muito mais linear e fácil de entender.

Com isso, temos uma branch sem commits de merge desnecessários, mas que ainda tem os vários commits que foram gerados durante o desenvolvimento. Nesse caso, podemos realizar o squash, que é o ato de transformar vários commits em um só. Assim, podemos ter um único commit que contempla toda a funcionalidade em desenvolvimento e, se necessário, modificarmos a mensagem para uma mais condizente ao que foi implementado.

Para realizarmos o squash, primeiro precisamos identificar quais os commits que queremos unificar e então pegar o hash do commit anterior ao primeiro commit que será unificado. Considerando nosso exemplo, iremos realizar o squash dos 3 últimos commits, então precisamos pegar o hash do 4º commit conforme imagem abaixo.

Com o hash em mãos, iremos novamente utilizar o rebase, mas dessa vez usando o modo interativo. Esse modo permite várias alterações interessantes na maneira que o comando será realizado. Iremos utilizar como parâmetro de destino o commit do qual copiamos o hash. Dessa forma o Git irá voltar o histórico para o ponto deste commit e iremos escolher como os commits que sobraram serão aplicados.

O resultado é uma tela com a lista de commits que serão aplicados e as opções do que podemos fazer para cada commit. Podemos alterar a ordem que os commits serão aplicados simplesmente trocando as linhas de lugar (a ordem de aplicação é de cima para baixo). No nosso caso, iremos utilizar a opção "s" ou "squash". O squash joga o conteúdo do commit atual junto com o conteúdo do commit acima dele. Isso pode ser feito em sequência, como vou mostrar aqui, porém é necessário manter pelo menos um commit como pick no topo para que o conteúdo dos outros commits sejam jogados nele.

Após selecionar as opções, salvar e fechar, os commits são aplicados e aparece outra tela onde podemos alterar a mensagem do commit gerado a partir do squash.

Após alterada a mensagem, o rebase é finalizado e temos como resultado um único commit no histórico.

Adotando essa prática, conseguimos ter menos e mais relevantes commits no histórico da master enquanto mantemos as práticas de commits e merges (agora rebases) frequentes nas nossas branches locais.

Espero ter ajudado vocês a conhecerem ou perderem o medo de utilizar o rebase e que se sintam mais confortáveis para utilizarem esse comando que acho muito útil no dia a dia.

--

--