Pular para o conteúdo principal

Git Squash Commits: Um guia com exemplos

Saiba como fazer squash de commits em um branch usando o rebase interativo, que ajuda a manter um histórico de commits limpo e organizado.
Actualizado 5 de nov. de 2024  · 7 min de leitura

"Commit early, commit often" é um mantra popular no desenvolvimento de software quando você usa o Git. Dessa forma, você garante que cada alteração seja bem documentada, melhora a colaboração e facilita o acompanhamento da evolução do projeto. No entanto, isso também pode levar a uma superabundância de commits.

É aqui que entra em jogo a importância de eliminar os commits. Squashing commits é o processo de combinar várias entradas de commit em um único commit coeso.

Torne-se um engenheiro de dados

Torne-se um engenheiro de dados por meio do aprendizado avançado de Python
Comece a aprender de graça

Por exemplo, digamos que trabalhamos em um recurso que implementa um formulário de login e criamos os quatro commits a seguir:

Exemplo de um histórico de commits do Git

Depois que o recurso é concluído, para o projeto geral, esses commits são muito detalhados. Não precisamos saber no futuro que encontramos um bug que foi corrigido durante o desenvolvimento. Para garantir um histórico limpo na ramificação principal, juntamos esses commits em um único commit:

Exemplo de como esmagar commits do Git

Como reduzir os commits no Git: Rebase interativo

O método mais comum para reduzir os commits é usar um rebase interativo. Nós o iniciamos usando o comando:

git rebase -i HEAD~<number_of_commits>

Substitua pelo número de commits que você deseja esmagar.

No nosso caso, temos quatro commits, portanto, o comando é:

git rebase -i HEAD~4

Ao executar esse comando, você abrirá um editor de linha de comando interativo:

Editor da CLI após o git rebase -i

A seção superior exibe os commits, enquanto a parte inferior contém comentários sobre como esmagar os commits.

Temos quatro compromissos. Para cada um deles, precisamos decidir qual comando executar. O que nos interessa são os comandos pick (p) e squash (s). Para esmagar esses quatro commits em um único commit, podemos escolher o primeiro e esmagar os três restantes.

Aplicamos os comandos modificando o texto que precede cada commit, alterando especificamente pick para s ou squash para o segundo, terceiro e quarto commits. Para fazer essas edições, precisamos entrar no modo "INSERT" no editor de texto de linha de comando pressionando a tecla i no teclado:

Entrar no modo de inserção no editor da CLI

Depois de pressionar i, o texto -- INSERT -- aparecerá na parte inferior, indicando que você entrou no modo de inserção. Agora, podemos mover o cursor com as teclas de seta, excluir caracteres e digitar como faríamos em um editor de texto padrão:

Interagir com o editor de texto da CLI

Quando estivermos satisfeitos com as alterações, precisaremos sair do modo de inserção pressionando a tecla Esc no teclado. A próxima etapa é salvar nossas alterações e sair do editor. Para fazer isso, primeiro pressionamos a tecla : para sinalizar ao editor que pretendemos executar um comando:

Digite um comando no editor de texto da CLI

Na parte inferior do editor, agora vemos um ponto e vírgula : solicitando que você insira um comando. Para salvar as alterações, usamos o comando w, que significa "write" (escrever). Para fechar o editor, use q, que significa "quit" (sair). Esses comandos podem ser combinados e digitados juntos wq:

Como salvar e sair do editor de texto da CLI

Para executar o comando, pressionamos a tecla Enter. Essa ação fechará o editor atual e abrirá um novo, permitindo que você insira a mensagem de commit para o commit recém-esmagado. O editor exibirá uma mensagem padrão que inclui as mensagens dos quatro commits que estamos eliminando:

Edição da mensagem de confirmação de squash

Recomendo que você modifique a mensagem para refletir com precisão as alterações implementadas por esses commits combinados - afinal, o objetivo do squashing é manter um histórico limpo e de fácil leitura. 

Para interagir com o editor e editar a mensagem, pressione i novamente para entrar no modo de edição e editar a mensagem de acordo com sua preferência.

Exemplo de mensagem de commit do Squash

Nesse caso, substituímos a mensagem de confirmação por "Implementar formulário de login". Para sair do modo de edição, pressione Esc. Em seguida, salve as alterações pressionando :, inserindo o comando wq e pressionando Enter.

Como visualizar o histórico de confirmações

Em geral, recuperar todo o histórico do commit pode ser um desafio. Para visualizar o histórico de confirmações, você pode usar o comando git log. No exemplo mencionado, antes de realizar o squash, a execução do comando git log exibiria o seguinte:

Histórico de commits do registro do Git antes do squash

Para navegar na lista de commits, use as teclas de seta para cima e para baixo. Para sair, pressione q.

Podemos usar o site git log para confirmar o sucesso do squash. Ao executá-lo após o squash, você verá um único commit com a nova mensagem:

Histórico do commit do registro do Git após o squash

Empurrando o commit esmagado

O comando acima atuará no repositório local. Para atualizar o repositório remoto, precisamos enviar nossas alterações. No entanto, como alteramos o histórico do commit, precisamos forçar o push usando a opção --force:

git push --force origin feature/login-form

Forçar o push substituirá o histórico de commits no ramo remoto e, potencialmente, interromperá outras pessoas que estejam trabalhando nesse ramo. É uma boa prática comunicar-se com a equipe antes de fazer isso

Uma maneira mais segura de forçar o envio, que reduz o risco de interromper os colaboradores, é usar a opção --force-with-lease:

git push --force-with-lease origin feature/login-form

Essa opção garante que só forçaremos o envio se a ramificação remota não tiver sido atualizada desde a última busca ou extração.

Eliminação de commits específicos

Imagine que temos cinco compromissos:

Exemplo de registro do Git com cinco commits

Suponhamos que você queira manter os commits 1, 2 e 5 e eliminar os commits 3 e 4.

Eliminação de qualquer commit

Ao usar o rebase interativo, os commits marcados para squashing serão combinados com o commit diretamente anterior. Nesse caso, isso significa que queremos esmagar o Commit4 para que ele se funda com o Commit3.

Para fazer isso, precisamos iniciar um rebase interativo que inclua esses dois commits. Nesse caso, três commits são suficientes, portanto, usamos o comando:

git rebase -i HEAD~3

Em seguida, definimos Commit4 como s para que ele seja esmagado com Commit3:

Exemplo de commit do Squash Two

Depois de executar esse comando e listar os commits, observamos que os commits 3 e 4 foram esmagados juntos, enquanto os demais permanecem inalterados.

Registro do Git depois de esmagar

Squashing de um commit específico

No comando git rebase -i HEAD~3, a parte HEAD é uma abreviação do commit mais recente. A sintaxe ~3 é usada para especificar um ancestral de um commit. Por exemplo, HEAD~1 refere-se à origem do commit HEAD.

Ilustração da notação ~

Em um rebase interativo, os commits considerados incluem todos os commits ancestrais que levam ao commit especificado no comando. Observe que o commit especificado não está incluído:

Como os commits são incluídos em um rebase interativo

Em vez de usar o HEAD, podemos especificar um hash de confirmação diretamente. Por exemplo, Commit2 tem um hash de dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf, portanto, o comando:

git rebase -i dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf

iniciaria um rebase considerando todos os commits feitos após Commit2. Portanto, se quisermos iniciar um rebase em um commit específico e incluir esse commit, podemos usar o comando:

git rebase -i <commit-hash>~1

Resolução de conflitos ao esmagar commits

Quando fazemos squash de commits, combinamos várias alterações de commit em um único commit, o que pode gerar conflitos se as alterações se sobrepuserem ou divergirem significativamente. Aqui estão alguns cenários comuns em que podem surgir conflitos:

  1. Mudanças sobrepostas: Se dois ou mais commits que estão sendo esmagados modificaram as mesmas linhas de um arquivo ou linhas estreitamente relacionadas, o Git pode não ser capaz de reconciliar automaticamente essas alterações. 
  2. Estado de alterações diferentes: Se um commit adiciona um determinado trecho de código e outro commit modifica ou exclui esse mesmo trecho de código, esmagar esses commits pode levar a conflitos que precisam ser resolvidos.
  3. Renomeando e modificando: Se um commit renomear um arquivo e os commits subsequentes fizerem alterações no nome antigo, esmagar esses commits pode confundir o Git, causando um conflito.
  4. Alterações em arquivos binários: Os arquivos binários não se fundem bem usando ferramentas de comparação baseadas em texto. Se vários commits alterarem o mesmo arquivo binário e tentarmos esmagá-los, poderá ocorrer um conflito porque o Git não pode reconciliar automaticamente essas alterações.
  5. História complexa: Se os commits tiverem um histórico complexo com várias mesclagens, ramificações ou rebases entre eles, esmagá-los pode resultar em conflitos devido à natureza não linear das alterações.

Ao esmagar, o Git tentará aplicar cada alteração uma a uma. Se encontrar conflitos durante o processo, ele fará uma pausa e nos permitirá resolvê-los. 

O conflito será marcado com os marcadores de conflito <<<<<< e >>>>>>. Para lidar com os conflitos, precisamos abrir os arquivos e resolver cada um manualmente, selecionando a parte do código que queremos manter. 

Depois de resolver os conflitos, precisamos preparar os arquivos resolvidos usando o comando git add. Em seguida, podemos continuar o rebase usando o seguinte comando:

git rebase --continue

Para saber mais sobre conflitos no Git, confira este tutorial sobre como resolver conflitos de mesclagem no Git.

Alternativas ao Squashing com Rebase

O comando git merge --squash é um método alternativo ao git rebase -i para combinar vários commits em um único commit. Esse comando é particularmente útil quando você deseja mesclar as alterações de uma ramificação na ramificação principal e, ao mesmo tempo, esmagar todos os commits individuais em um só. Aqui está uma visão geral de como você pode fazer squash usando git merge:

  1. Navegamos até o ramo de destino no qual queremos incorporar as alterações.
  2. Executamos o comando git merge --squash substituindo pelo nome da filial.
  3. Confirmamos as alterações com git commit para criar uma única confirmação que represente todas as alterações do ramo de recursos.

Por exemplo, digamos que você queira incorporar as alterações da ramificação feature/login-form em main como um único commit:

git checkout main
git merge --squash feature-branch
git commit -m "Implement login form"

Essas são as limitações dessa abordagem em comparação com git rebase -i:

  • Granularidade do controle: Menos controle sobre commits individuais. Com o rebase, podemos escolher quais commits serão mesclados, enquanto o merge força a combinação de todas as alterações em um único commit.
  • Histórico intermediário: Ao usar a mesclagem, o histórico individual de commits do ramo de recursos é perdido no ramo principal. Isso pode dificultar o rastreamento das alterações incrementais feitas durante o desenvolvimento do recurso.
  • Revisão pré-compromisso: Como ele prepara todas as alterações como um único conjunto de alterações, não podemos revisar ou testar cada commit individualmente antes de esmagá-lo, ao contrário do que ocorre durante um rebase interativo, em que cada commit pode ser revisado e testado em sequência.

Conclusão

A incorporação de commits frequentes e pequenos no fluxo de trabalho de desenvolvimento promove a colaboração e a documentação clara, mas também pode sobrecarregar o histórico do projeto. A redução de commits atinge um equilíbrio, preservando os marcos importantes e eliminando o ruído de pequenas alterações iterativas.

Para saber mais sobre o Git, recomendo estes recursos:


François Aubry's photo
Author
François Aubry
LinkedIn
Ensinar sempre foi minha paixão. Desde meus primeiros dias como estudante, eu buscava ansiosamente oportunidades para dar aulas particulares e ajudar outros alunos. Essa paixão me levou a fazer um doutorado, onde também atuei como assistente de ensino para apoiar meus esforços acadêmicos. Durante esses anos, encontrei imensa satisfação no ambiente tradicional da sala de aula, promovendo conexões e facilitando o aprendizado. Entretanto, com o advento das plataformas de aprendizagem on-line, reconheci o potencial transformador da educação digital. Na verdade, participei ativamente do desenvolvimento de uma dessas plataformas em nossa universidade. Estou profundamente comprometido com a integração dos princípios tradicionais de ensino com metodologias digitais inovadoras. Minha paixão é criar cursos que não sejam apenas envolventes e informativos, mas também acessíveis aos alunos nesta era digital.
Temas

Aprenda engenharia de dados com estes cursos!

programa

Associate Data Engineer

30 hours hr
Learn the fundamentals of data engineering: database design and data warehousing, working with technologies including PostgreSQL and Snowflake!
Ver DetalhesRight Arrow
Iniciar curso
Ver maisRight Arrow
Relacionado
Git

blog

O que é Git? Manual completo do Git

Saiba mais sobre o sistema de controle de versão mais conhecido e por que é uma ferramenta de colaboração indispensável para cientistas de dados e programadores.
Summer Worsley's photo

Summer Worsley

14 min

tutorial

Tutorial de push e pull do GIT

Saiba como realizar solicitações Git PUSH e PULL por meio do GitHub Desktop e da linha de comando.

Olivia Smith

13 min

tutorial

Git Rename Branch: Como renomear uma filial local ou remota

Saiba como renomear ramificações locais e remotas do Git usando o terminal ou a interface gráfica do usuário (GUI) de clientes populares como o GitHub.
François Aubry's photo

François Aubry

tutorial

Tutorial do GitHub e do Git para iniciantes

Um tutorial para iniciantes que demonstra como funciona o controle de versão do Git e por que ele é crucial para projetos de ciência de dados.
Abid Ali Awan's photo

Abid Ali Awan

17 min

tutorial

Git Pull Force: Como substituir uma ramificação local por uma remota

Saiba por que o git pull --force não é a melhor maneira de substituir uma ramificação local pela versão remota e descubra o método adequado usando git fetch e git reset.
François Aubry's photo

François Aubry

tutorial

Git Prune: O que é o Git Pruning e como usar o Git Prune

O Git prune é um comando do Git que remove objetos do repositório que não são mais acessíveis a partir de qualquer commit ou branch, ajudando a liberar espaço em disco.
François Aubry's photo

François Aubry

5 min

Ver maisVer mais