“Commit cedo, commit frequentemente” é um mantra popular no desenvolvimento de software ao usar Git. Fazendo isso, garante-se que cada mudança está bem documentada, melhora a colaboração e facilita o acompanhamento da evolução do projeto. No entanto, isso também pode levar a uma abundância excessiva de commits.
É aí que entra a importância de ajuntar commits. Ajuntar commits é o processo de combinar várias entradas de commit em um único commit coeso.
Por exemplo, vamos dizer que estamos trabalhando em uma funcionalidade implementando um formulário de login e criamos os seguintes quatro commits:
Una vez que a funcionalidade está concluída, para o projeto geral, esses commits são muito detalhados. Não precisamos saber no futuro que nos deparamos com um bug que foi corrigido durante o desenvolvimento. Para garantir um histórico limpo no ramo principal, mesclamos esses commits em um único commit:
Como Mesclar Commits no Git: Rebase Interativo
A maneira mais comum de mesclar commits é usando um rebase interativo. Iniciamos usando o comando:
git rebase -i HEAD~<number_of_commits>
Substitua <number_of_commits>
pelo número de commits que queremos mesclar.
No nosso caso, temos quatro commits, então o comando é:
git rebase -i HEAD~4
Executar esse comando abrirá um editor de linha de comando interativo:
A seção superior exibe os commits, enquanto a parte inferior contém comentários sobre como mesclar commits.
Vemos quatro commits. Para cada um, devemos decidir qual comando executar. nos importamos com os comandos pick
(p
) e squash
(s
). Para mesclar esses quatro commits em um único commit, podemos escolher o primeiro e mesclar os outros três.
Aplicamos os comandos modificando o texto que precede cada commit, especificamente mudando pick
para s
ou squash
para os segundos, terceiros e quartos commits. Para fazer essas edições, precisamos entrar no modo “INSERT” do editor de texto de linha de comando pressionando a tecla i
no teclado:
Após pressionar i
, o texto -- INSERT --
aparecerá na parte inferior, indicando que entramos no modo de inserção. Agora, podemos mover o cursor com as teclas de seta, deletar caracteres e digitar como faríamos em um editor de texto padrão:
Uma vez satisfeitos com as mudanças, precisamos sair do modo de inserção pressionando a tecla Esc
no teclado. O下一步 é salvar nossas mudanças e sair do editor. Para fazer isso, primeiramente pressionamos a tecla :
para sinalizar ao editor que pretendemos executar um comando:
No fundo do editor, agora vemos um ponto e vírgula :
nos incentivando a inserir um comando. Para salvar as mudanças, usamos o comando w
, que significa “escrever”. Para fechar o editor, usamos q
, que significa “sair”. Esses comandos podem ser combinados e digitados juntos wq
:
Para executar o comando, pressionamos a tecla Enter
. Esta ação fechará o editor atual e abrirá um novo, permitindo-nos inserir a mensagem de commit para o commit recém amalgamado. O editor exibirá uma mensagem padrão que compreende as mensagens dos quatro commits que estamos amalgamando:
Recomendo modificar a mensagem para refletir com precisão as mudanças implementadas por esses commits combinados — afinal, o propósito de mesclar é manter um histórico limpo e facilmente legível.
Para interagir com o editor e editar a mensagem, pressionamos i
novamente para entrar no modo de edição e editamos a mensagem conforme desejarmos.
Neste caso, substituímos a mensagem do commit por “Implementar formulário de login.” Para sair do modo de edição, pressionamos Esc
. Em seguida, salvamos as mudanças pressionando :
, digitando o comando wq
e pressionando Enter
.
Como Ver o Histórico de Commits
Normalmente, lembrar o histórico completo de commits pode ser desafiador. Para visualizar o histórico de commits, podemos usar o comando git log
. No exemplo mencionado, antes de realizar o squash, executar o comando git log
mostraria:
Para navegar pela lista de commits, use as teclas de seta para cima e para baixo. Para sair, pressione q
.
Podemos usar o git log
para confirmar o sucesso do squash. Executá-lo após o squash exibirá um único commit com a nova mensagem:
Enviar commit mesclado
O comando acima agirá no repositório local. Para atualizar o repositório remoto, precisamos enviar nossas mudanças. No entanto, porque alteramos o histórico de commits, precisamos fazer um push forçado usando a opção --force
:
git push --force origin feature/login-form
Um push forçado sobrescreverá o histórico de commits no ramo remoto e pode potencialmente perturbar outros que estão trabalhando nesse ramo. É uma boa prática comunicar-se com a equipe antes de fazer isso
Uma maneira mais segura de fazer um push forçado, 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 faremos um push forçado apenas se o ramo remoto não foi atualizado desde nossa última busca ou pull.
Squashing commits específicos
Imagine que temos cinco commits:
Suponhamos que queremos manter os commits 1, 2 e 5 e mesclar os commits 3 e 4.
Ao usar rebase interativo, os commits marcados para squashing serão combinados com o commit imediatamente anterior. Neste caso, significa que queremos mesclar o Commit4
para que ele se funda no Commit3
.
Para fazer isso, devemos iniciar uma rebase interativa que inclua esses dois commits. Neste caso, três commits são suficientes, então usamos o comando:
git rebase -i HEAD~3
Em seguida, definimos Commit4
para s
para que ele seja mesclado com o Commit3
:
Após executar esse comando e listar os commits, observamos que os commits 3 e 4 foram mesclados enquanto o restante permanece inalterado.
Mesclar a partir de um commit específico
No comando git rebase -i HEAD~3
, a parte HEAD
é um atalho para o commit mais recente. A sintaxe ~3
é usada para especificar um ancestral de um commit. Por exemplo, HEAD~1
se refere ao pai do commit HEAD
.
Em uma rebase interativa, os commits considerados incluem todos os commits ancestrais que levam ao commit especificado no comando. Note que o commit especificado não está incluído:
Em vez de usar HEAD, podemos especificar diretamente o hash de um commit. Por exemplo, Commit2
tem um hash de dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
, então o comando:
git rebase -i dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
iniciaria uma rebase considerando todos os commits feitos após o Commit2
. Portanto, se quisermos iniciar uma rebase em um commit específico e incluí-lo, podemos usar o comando:
git rebase -i <commit-hash>~1
Resolvendo Conflitos ao Mesclar Commits
Quando mesclamos commits, combinamos várias mudanças de commit em um único commit, o que pode levar a conflitos se as mudanças se sobrepuserem ou divergirem significativamente. Aqui estão alguns cenários comuns onde conflitos podem surgir:
- Mudanças sobrepostas: Se dois ou mais commits que estão sendo mesclados modificaram as mesmas linhas de um arquivo ou linhas estreitamente relacionadas, o Git pode não ser capaz de reconciliar automaticamente essas mudanças.
- Estado de mudanças diferentes: Se um commit adiciona um pedaço de código e outro commit modifies ou exclui o mesmo pedaço de código, mesclar esses commits pode levar a conflitos que precisam ser resolvidos.
- Renomear e modificar: Se um commit renomeia um arquivo e commits subsequentes fazem mudanças no nome antigo, mesclar esses commits pode confundir o Git, causando um conflito.
- Mudanças em arquivos binários: Arquivos binários não se mesclam bem usando ferramentas de diff baseadas em texto. Se vários commits alteram o mesmo arquivo binário e tentamos mesclá-los, pode ocorrer um conflito porque o Git não pode reconciliar automaticamente essas mudanças.
- Histórico complexo: Se os commits têm um histórico complexo com várias mesclas, ramificações ou rebase entre eles, mesclá-los pode resultar em conflitos devido à natureza não linear das mudanças.
Ao mesclar, o Git tentará aplicar cada mudança uma por uma. Se encontrar conflitos durante o processo, ele pausará e nos permitirá resolverlos.
Conflitos serão marcados com marcadores de conflito <<<<<<
e >>>>>>
. Para lidar com os conflitos, precisamos abrir os arquivos e resolver manualmente cada um, selecionando qual parte do código desejamos manter.
Após resolver os conflitos, precisamos adicionar os arquivos resolvidos usando o comando git add
. Depois disso, podemos continuar o rebase usando o seguinte comando:
git rebase --continue
Para mais sobre conflitos no Git, confira este tutorial sobre como resolver conflitos de mesclagem no Git.
Alternativas ao Achatamento com Rebase
O comando git merge --squash
é uma método alternativo ao git rebase -i
para combinar vários commits em um único commit. Este comando é especialmente útil quando queremos mesclar mudanças de um branch principal para o branch principal, squashando todos os commits individuais em um. Aqui está um resumo de como fazer o squash usando git merge
:
- Navegamos até o branch de destino no qual queremos incorporar as mudanças.
- Executamos o comando
git merge --squash <nome-do-branch>
substituindo<nome-do-branch>
pelo nome do branch. - Commitamos as mudanças com
git commit
para criar um único commit que representa todas as mudanças do branch de recursos.
Por exemplo, digamos que queremos incorporar as mudanças do branches feature/login-form
no 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 de controle: Menos controle sobre commits individuais. Com o rebase podemos escolher quais commits mesclar, enquanto o merge força combinar todas as mudanças em um único commit.
- História intermediária: Ao usar mesclagem, o histórico individual de commits da branch de feature é perdido na branch principal. Isso pode tornar mais difícil rastrear as mudanças incrementais que foram feitas durante o desenvolvimento da feature.
- Revisão pré-commit: Como ele estagia todas as mudanças como um único conjunto de mudanças, não podemos revisar ou testar cada commit individualmente antes de mesclar, ao contrário de durante uma rebase interativa, onde cada commit pode ser revisado e testado em sequência.
Conclusão
Incorporar commits frequentes e pequenos no fluxo de trabalho de desenvolvimento promove colaboração e documentação clara, mas também pode poluir o histórico do projeto. Mesclar commits atingi um equilíbrio, preservando os marcos importantes enquanto elimina o ruído das mudanças iterativas menores.
Para aprender mais sobre Git, recomendo esses recursos:
Source:
https://www.datacamp.com/tutorial/git-squash-commits