Git Squash Commits

Azhar Bashir Khan 30 janeiro 2023
  1. Usando a ferramenta interativa git rebase para reprimir os commits do Git
  2. Usando git merge -squash para Squash Git Commits
Git Squash Commits

Aprenderemos a compressão Git neste tutorial. A ideia básica é pegar vários commits contínuos e comprimi-los em um.

A intenção principal é condensar muitos commits em alguns commits relevantes. Portanto, fazer isso faz com que o histórico do git pareça conciso e claro.

Outra maneira de ver isso é que fazemos vários commits relacionados a alguma tarefa. Depois de um tempo, quando alcançamos um estado satisfatório, as muitas mensagens de commit atrapalham o histórico do git.

Neste ponto, podemos querer combinar os diferentes commits em um para que o histórico do git pareça claro e reflita melhor a tarefa realizada.

Outro caso de uso é fazer a compressão ao fazer a mesclagem de ramos. Normalmente, criamos um branch de recurso do branch principal para algum desenvolvimento de recurso.

Após a conclusão do recurso, mesclamos o branch do recurso com o branch principal. Aqui também, podemos querer espremer as várias mensagens de commit feitas no branch de recursos em uma ao mesclar com o branch principal.

Observe que não há comando git squash.

Existem duas maneiras de obter o esmagamento do Git:

  • git rebase -i como uma ferramenta interativa usada para esmagar commits
  • git merge -squash usando a opção -squash durante a fusão

Usando a ferramenta interativa git rebase para reprimir os commits do Git

Considere o seguinte trecho de git log, que mostra os últimos quatro commits de HEAD que estamos interessados ​​em eliminar.

25c38c4 remove .class files
da66e6a Delete version.ini
f4e3f09 Delete .log
b0e6655 Delete .lock
da66e6a github git notes

Podemos ver no log as primeiras quatro mensagens de confirmação, significando as operações de exclusão de diferentes arquivos irrelevantes. Agora, vamos esmagar esses quatro commits em um.

A seguir está a sintaxe do comando para esmagar os últimos X commits usando a ferramenta de rebase interativa.

git rebase -i HEAD~[X]

Assim, para esmagar os quatro commits, faríamos como a seguir.

$ git rebase -i HEAD~4

Depois de emitir este comando, o Git irá invocar o editor padrão com detalhes de commits para squash, como mostrado abaixo.

pick b0e6655 Delete .lock
pick f4e3f09 Delete .log 
pick da66e6a Delete version.ini
pick 25c38c4 remove .class files

# Rebase 652d2fe..25c38c4 onto 652d2fe (4 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# 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.
#
# Note that empty commits are commented out

O editor mostra os vários commits com o comando pick. Ele também mostra informações sobre os comandos disponíveis. Estaremos usando o comando squash (ou s).

Como mostrado abaixo, nós manteremos o primeiro commit com o comando pick e mudaremos do comando pick para s (para squash) para os três commits restantes.

pick b0e6655 Delete .lock
s f4e3f09 Delete .log 
s da66e6a Delete version.ini
s 25c38c4 remove .class files

# Rebase 652d2fe..25c38c4 onto 652d2fe (4 command(s))
#
...

Os commits marcados com squash (ou s) serão mesclados ao commit principal viz. aquele marcado com pick.

Agora, vamos salvar as alterações no editor e sair. Depois disso, a ferramenta rebase -i abrirá outro editor para inserir a mensagem de confirmação, conforme abaixo:

# This is a combination of 4 commits. The first commit's message is:

Delete .lock

# This is the 2nd commit message:

Delete .log 

# This is the 3rd commit message:

Delete version.ini

# This is the 4th commit message:

remove .class files

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sun Jan 3 16:39:23 2021 +0530
#
# interactive rebase in progress; onto 652d2fe
# Last commands done (4 commands done):
#    pick b0e6655 Delete .lock
#    s f4e3f09 Delete .log 
#    s da66e6a Delete version.ini
#    s 25c38c4 remove .class files
# No commands remaining.
# You are currently editing a commit while rebasing branch 'master' on '652d2fe'.
#
# Changes to be committed:
#       new file:   github-git-notes.txt
#

Agora, vamos adicionar a nova mensagem de confirmação no topo da primeira mensagem de confirmação.

Deleted irrelevant files

# This is a combination of 4 commits. The first commit's message is:

Delete .lock

# This is the 2nd commit message:

Delete .log
...

Depois de salvar e sair do editor, a ferramenta rebase -i imprimirá a seguinte mensagem.

HEAD~2
Rebasing (2/2)


[detached HEAD caab6e8] Deleted irrelevant files
 Date: Sun Jan 3 16:39:23 2021 +0530
 1 file changed, 54 insertions(+)
 create mode 100644 github-git-notes.txt
Successfully rebased and updated refs/heads/master.

Agora, verificaremos o git log e veremos a mensagem de confirmação única (ou seja,) ao invés das quatro mensagens de confirmação.

$ git log --oneline
25c38c4 Deleted irrelevant files
da66e6a github git notes
...

Usando git merge -squash para Squash Git Commits

A seguir está a sintaxe do comando para mesclar um branch com o branch atual (geralmente main) e esmagar os commits do branch de origem.

git merge --squash <source_branch_name_to_squash>

Vamos agora mesclar o ramo viz do recurso. feature1 com esmagamento com o ramo main.

Em primeiro lugar, faremos o checkout para o ramo main.

$ git checkout main
Switched to branch 'main'

Em seguida, faremos um git merge com a opção squash da seguinte forma.

$ git merge --squash feature1
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested

Quando fazemos uma opção merge com --squash, o Git não cria um commit de merge no branch de destino, como faz em uma merge normal. Em vez disso, o Git pega todas as mudanças no branch de origem viz. feature1 e coloca como alterações locais na cópia de trabalho do ramo de destino viz. main.

Por favor veja abaixo.

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   config.ini

Aqui, o arquivo config.ini contém as alterações feitas no ramo feature1.

Agora, tudo o que resta é comprometer as mudanças para o branch main como abaixo.

$ git commit -am 'Merged and squashed the feature1 branch changes'
[main 573b923] Squashed and merged the feature1 branch
 1 file changed, 4 insertions(+)

Assim, agora mesclamos as mudanças no branch feature1 no branch main, juntamente com a compressão das mensagens de commit do branch feature1. Agora temos apenas uma única mensagem de confirmação no branch main.

Artigo relacionado - Git Rebase

Artigo relacionado - Git Merge