Como posso esmagar meu último X confirmado em um commit usando o Git?
git
rebase
squash
git-squash
markdorison
fonte
fonte
Respostas:
Use
git rebase -i <after-this-commit>
e substitua "pick" na segunda confirmação e subseqüentes por "squash" ou "fixup", conforme descrito no manual .Neste exemplo,
<after-this-commit>
é o hash SHA1 ou o local relativo do HEAD da ramificação atual a partir da qual as confirmações são analisadas para o comando rebase. Por exemplo, se o usuário desejar visualizar 5 confirmações do HEAD atual no passado, o comando serágit rebase -i HEAD~5
.fonte
<after-this-commit>
?<after-this-commit>
é commit X + 1, ou seja, pai do commit mais antigo que você deseja esmagar.rebase -i
abordagem ereset --soft
é,rebase -i
permite que eu retenha o autor do commit, enquantoreset --soft
me permite confirmar novamente. Às vezes, preciso esmagar os commits de solicitações pull, mantendo as informações do autor. Às vezes, preciso redefinir o software por conta própria. Upvotes para ambas as grandes respostas de qualquer maneira.Você pode fazer isso facilmente, sem
git rebase
ougit merge --squash
. Neste exemplo, esmagaremos os últimos 3 commits.Se você deseja escrever a nova mensagem de confirmação do zero, basta:
Se você deseja começar a editar a nova mensagem de confirmação com uma concatenação das mensagens de confirmação existentes (ou seja, semelhante ao que uma
git rebase -i
lista de instruções de pick / squash / squash /… / squash o iniciaria), será necessário extrair essas mensagens e passar elesgit commit
:Ambos os métodos esmagam os três últimos commits em um único novo commit da mesma maneira. A reinicialização suave apenas aponta novamente HEAD para o último commit que você não deseja esmagar. Nem o índice nem a árvore de trabalho são tocados pela reinicialização suave, deixando o índice no estado desejado para seu novo commit (ou seja, ele já possui todas as alterações dos commits que você está prestes a "jogar fora").
fonte
git rebase --squash-recent
, ou atégit commit --amend-many
.branch@{upstream}
(ou apenas@{upstream}
para o ramo atual; em ambos os casos, a última parte poderá ser abreviada para@{u}
; veja gitrevisions ). Isso pode diferir do seu "último envio por push" (por exemplo, se alguém enviou algo que foi construído sobre o envio mais recente e você o buscou), mas parece que pode estar próximo do que você deseja.push -f
mas, caso contrário, era adorável, obrigado.git push --force
mais tarde para que ele leva a cometerVocê pode usar
git merge --squash
isso, que é um pouco mais elegante quegit rebase -i
. Suponha que você esteja no mestre e queira compactar os últimos 12 commits em um.AVISO: Primeiro, certifique-se de comprometer o seu trabalho - verifique se
git status
está limpo (poisgit reset --hard
descartará alterações em etapas e em etapas)Então:
A documentação para
git merge
descreve a--squash
opção com mais detalhes.Atualização: a única vantagem real desse método sobre a mais simples
git reset --soft HEAD~12 && git commit
sugerida por Chris Johnsen em sua resposta é que você recebe a mensagem de confirmação pré-preenchida com todas as mensagens de confirmação que você está esmagando.fonte
git rebase -i
, mas não explica por que. Provisivamente, coloquei isso em prática porque me parece que, de fato, o oposto é verdadeiro e isso é um truque; você não está executando mais comandos do que o necessário apenas para forçargit merge
a executar uma das coisasgit rebase
especificamente projetadas?git merge --squash
também é mais fácil de usar em um script. Essencialmente, o raciocínio era que você não precisa da "interatividade"git rebase -i
para isso.git merge --squash
é menos provável que produza conflitos de mesclagem em face de movimentos / exclusões / renomeações em comparação com a reestruturação, especialmente se você estiver mesclando de uma ramificação local. (disclaimer: com base em apenas uma experiência, corrija-me se isso não é verdade no caso geral!)HEAD@{1}
apenas estar do lado seguro, por exemplo, quando seu fluxo de trabalho é interrompido por uma hora por uma queda de energia, etc.Eu recomendo evitar
git reset
sempre que possível - especialmente para iniciantes no Git. A menos que você realmente precise automatizar um processo com base em uma série de confirmações, existe uma maneira menos exótica ...git merge --squash (working branch name)
git commit
A mensagem de confirmação será pré-preenchida com base na squash.
fonte
gitk
para rotular a linha de código que você está esmagando e também rotular a base sobre a qual apertar. No caso normal, esses dois rótulos já existem, portanto, a etapa (1) pode ser ignorada.git branch your-feature && git reset --hard HEAD~N
a maneira mais conveniente. No entanto, envolve git reset novamente, o que esta resposta tentou evitar.Baseado na resposta de Chris Johnsen ,
Adicione um alias global de "squash" do bash: (ou Git Bash no Windows)
... ou usando o prompt de comando do Windows:
Seu
~/.gitconfig
agora deve conter este alias:Uso:
... Que esmaga automaticamente os últimos
N
commits, inclusive.Nota: A mensagem de confirmação resultante é uma combinação de todas as confirmações esmagadas, em ordem. Se você não estiver satisfeito com isso, poderá sempre
git commit --amend
modificá-lo manualmente. (Ou edite o alias para corresponder ao seu gosto.)fonte
git squash -m "New summary."
e terN
determinado automaticamente como o número de confirmações não enviadas.git commit --amend
para alterar ainda mais a mensagem, mas esse alias permite que você tenha um bom começo sobre o que deve estar na mensagem de confirmação.Graças a este post útil, descobri que você pode usar este comando para esmagar os últimos 3 commits:
Isso é útil, pois funciona mesmo quando você está em uma filial local sem informações de rastreamento / repositório remoto.
O comando abrirá o editor de rebase interativo, que permite reordenar, esmagar, reformular, etc., normalmente.
Usando o editor de rebase interativo:
O editor de rebase interativo mostra os três últimos commits. Essa restrição foi determinada
HEAD~3
ao executar o comandogit rebase -i HEAD~3
.A confirmação mais recente,,
HEAD
é exibida primeiro na linha 1. As linhas que começam com a#
são comentários / documentação.A documentação exibida é bastante clara. Em qualquer linha, você pode alterar o comando de
pick
para um comando de sua escolha.Prefiro usar o comando,
fixup
pois isso "esmaga" as alterações do commit no commit na linha acima e descarta a mensagem do commit.Como o commit na linha 1 é
HEAD
, na maioria dos casos você deixaria isso comopick
. Você não pode usarsquash
oufixup
porque não há outro commit para compactar o commit.Você também pode alterar a ordem dos commits. Isso permite esmagar ou consertar confirmações que não são adjacentes cronologicamente.
Um exemplo prático do dia a dia
Eu comprometi recentemente um novo recurso. Desde então, cometi duas correções de bugs. Mas agora eu descobri um bug (ou talvez apenas um erro de ortografia) no novo recurso que cometi. Que irritante! Não quero um novo commit poluindo meu histórico de commit!
A primeira coisa que faço é corrigir o erro e fazer um novo commit com o comentário
squash this into my new feature!
.Em seguida, executo
git log
ougitk
obtenho o commit SHA do novo recurso (neste caso1ff9460
).Em seguida, trago o editor de rebase interativo com
git rebase -i 1ff9460~
. O~
SHA após o commit diz ao editor para incluir esse commit no editor.Em seguida, movo a confirmação que contém o fix (
fe7f1e0
) para embaixo da confirmação do recurso e mudopick
parafixup
.Ao fechar o editor, a correção será compactada no commit do recurso e meu histórico de commit ficará bonito e limpo!
Isso funciona bem quando todos os commits são locais, mas se você tentar alterar qualquer commit já enviado para o controle remoto, poderá realmente causar problemas para outros desenvolvedores que fizeram check-out no mesmo ramo!
fonte
pick
na linha 1. Se você escolhersquash
oufixup
para o commit na linha 1, o git mostrará uma mensagem dizendo "erro: não é possível 'consertar' sem um commit anterior". Então, você terá a opção de corrigi-lo: "Você pode corrigir isso com 'git rebase --edit-todo' e, em seguida, execute 'git rebase --continue'." ou você pode simplesmente abortar e começar de novo: "Ou você pode abortar o rebase com 'git rebase --abort'.".Se você usa o TortoiseGit, pode executar a função
Combine to one commit
:Show Log
Combine to one commit
no menu de contextoEssa função executa automaticamente todas as etapas necessárias de git único. Infelizmente disponível apenas para Windows.
fonte
Para fazer isso, você pode usar o seguinte comando git.
n (= 4 aqui) é o número da última confirmação. Então você tem as seguintes opções,
Atualize como abaixo de
pick
um commit esquash
os outros para os mais recentes,Para detalhes, clique no link
fonte
Com base neste artigo , achei esse método mais fácil para o meu caso de usuário.
Meu ramo 'dev' estava à frente de 'origin / dev' em 96 confirmações (portanto, essas confirmações ainda não foram enviadas para o controle remoto).
Eu queria esmagar esses commits em um antes de fazer a alteração. Eu prefiro redefinir a ramificação para o estado 'origin / dev' (isso deixará todas as alterações dos commits do 96 sem estágio) e depois confirmar as alterações de uma só vez:
fonte
No ramo em que você gostaria de combinar as confirmações, execute:
exemplo:
Isso abrirá o editor de texto e você deverá alternar a 'seleção' na frente de cada confirmação com 'squash' se desejar que esses commits sejam mesclados. Da documentação:
p, escolha = use confirmação
s, squash = use commit, mas mescla no commit anterior
Por exemplo, se você deseja mesclar todos os commits em um, o 'pick' é o primeiro commit que você fez e todos os futuros (colocados abaixo do primeiro) devem ser configurados como 'squash'. Se estiver usando o vim, use : x no modo de inserção para salvar e sair do editor.
Em seguida, para continuar a rebase:
Para saber mais sobre essa e outras maneiras de reescrever seu histórico de consolidação, consulte este post útil
fonte
--continue
e vim:x
faz.git add
a configuração correta nos arquivos ser usadagit rebase --continue
para passar para a próxima confirmação e começar a mesclar.:x
é um comando que salvará as alterações do arquivo ao usar o vim, veja istoA resposta das anomias é boa, mas me senti insegura quanto a isso, então decidi adicionar algumas capturas de tela.
Etapa 0: log do git
Veja onde você está
git log
. Mais importante, encontre o hash de confirmação do primeiro commit que você não deseja esmagar. Portanto, apenas o:Etapa 1: git rebase
Execute
git rebase -i [your hash]
, no meu caso:Etapa 2: escolha / esmague o que quiser
No meu caso, quero compactar tudo o que foi confirmado pela primeira vez. A ordem é do primeiro ao último, de maneira exatamente oposta
git log
. No meu caso, eu quero:Etapa 3: ajustar as mensagens
Se você selecionou apenas uma confirmação e esmagou o restante, poderá ajustar uma mensagem de confirmação:
É isso aí. Depois de salvar este (
:wq
), está pronto. Dê uma olhada nisso comgit log
.fonte
git log
Procedimento 1
1) Identifique o hash curto de confirmação
Aqui mesmo
git log --oneline
também pode ser usado para obter hash curto.2) Se você quiser esmagar (mesclar) os dois últimos commit
3) Isso abre um
nano
editor para mesclar. E parece que abaixo4) Renomeie a palavra
pick
àsquash
qual está presente antesabcd1234
. Depois de renomear, deve ser como abaixo.5) Agora salve e feche o
nano
editor. Pressionectrl + o
e pressioneEnter
para salvar. E então pressionectrl + x
para sair do editor.6) Então
nano
editor é aberto novamente para atualizar os comentários, se necessário, atualize-o.7) Agora é esmagado com sucesso, você pode verificá-lo verificando os logs.
8) Agora pressione para repo. Nota para adicionar
+
sinal antes do nome da filial. Isso significa empurrão forçado.Nota: Isso se baseia no uso do git no
ubuntu
shell. Se você estiver usando sistemas operacionais diferentes (Windows
ouMac
), os comandos acima serão os mesmos, exceto o editor. Você pode obter um editor diferente.Procedimento 2
--fixup
opção eOLDCOMMIT
deve estar na qual precisamos mesclar (squash) esse commit.Agora, isso cria um novo commit no topo do HEAD
fixup1 <OLDCOMMIT_MSG>
.OLDCOMMIT
.Aqui
^
significa o compromisso anterior comOLDCOMMIT
. Esterebase
comando abre uma janela interativa em um editor (vim ou nano), em que não precisamos fazer nada, basta salvar e sair é suficiente. Como a opção passada para isso moverá automaticamente a confirmação mais recente para a confirmação antiga e alterará a operação parafixup
(equivalente a squash). Em seguida, o rebase continua e termina.Procedimento 3
--amend
possível usar os meiosgit-commit
.Aqui
--amend
mescla as novas mudanças para a última confirmaçãocdababcd
e gera um novo ID de confirmação1d4ab2e1
Conclusão
fonte
Para compactar os últimos 10 commit em um único commit:
Se você também deseja atualizar a ramificação remota com o commit compactado:
fonte
Se você estiver em uma ramificação remota (chamada
feature-branch
) clonada de um Repositório de Ouro (golden_repo_name
), então aqui está a técnica para compactar seus commits em um:Finalize o repo de ouro
Crie um novo ramo a partir dele (repositório de ouro) da seguinte maneira
O Squash se mescla com a filial local que você já possui
Confirme suas alterações (esse será o único commit que ocorre no ramo dev)
Envie a ramificação para o seu repositório local
fonte
O que pode ser realmente conveniente:
encontre o hash de confirmação que você deseja esmagar, digamos
d43e15
.Agora usa
fonte
Isso é super-duper kludgy, mas de uma maneira legal, então eu vou jogá-lo no ringue:
Tradução: forneça um novo "editor" para o git que, se o nome do arquivo a ser editado for
git-rebase-todo
(o prompt de rebase interativo), alterará tudo, exceto o primeiro "pick" para "squash", e gerará o vim - de modo que quando você for solicitado Para editar a mensagem de confirmação compactada, você obtém o vim. (E, obviamente, eu estava esmagando os últimos cinco commits no ramo foo, mas você pode mudar isso da maneira que quiser.)Eu provavelmente faria o que Mark Longair sugeriu , no entanto.
fonte
Se você deseja compactar cada commit em um único commit (por exemplo, ao liberar um projeto publicamente pela primeira vez), tente:
fonte
2020 Solução simples sem rebase:
git reset --soft HEAD~2
git commit -m "new commit message"
git push --force
2 significa que os dois últimos commits serão esmagados. Você pode substituí-lo por qualquer número
fonte
Eu acho que a maneira mais fácil de fazer isso é criar uma nova ramificação fora do master e fazer uma mesclagem - sucata da ramificação de recursos.
Então você tem todas as alterações prontas para confirmar.
fonte
Uma linha simples que sempre funciona, já que você está atualmente no ramo que deseja esmagar, master é o ramo de origem e o commit mais recente contém a mensagem e o autor do commit que você deseja usar:
fonte
se, por exemplo, você deseja compactar as últimas 3 confirmações em uma única confirmação em uma ramificação (repositório remoto), por exemplo: https://bitbucket.org
O que eu fiz é
fonte
WAR️ AVISO: "Meu último X confirma" pode ser ambíguo.
Nesta história muito abreviada do repositório https://github.com/fleetwood-mac/band-history, você abriu uma solicitação pull para mesclar o commit de Bill Clinton no commit original (
MASTER
) do Fleetwood Mac.Você abriu uma solicitação de recebimento e no GitHub você vê isso:
Quatro confirmações:
Pensando que ninguém se importaria em ler a história completa do repositório. (Na verdade, existe um repositório, clique no link acima!) Você decide esmagar esses commits. Então você vai e corre
git reset --soft HEAD~4 && git commit
. Então vocêgit push --force
no GitHub para limpar seu PR.E o que acontece? Você acabou de fazer um único commit que vai de Fritz a Bill Clinton. Porque você esqueceu que ontem estava trabalhando na versão Buckingham Nicks deste projeto. E
git log
não corresponde ao que você vê no GitHub.AL MORAL DA HISTÓRIA
git checkout
elesgit reset --soft
quegit commit
que entorte diretamente do de para o parafonte
Se você não se importa com as mensagens de confirmação das confirmações intermediárias, pode usar
fonte
Se você estiver trabalhando com o GitLab, basta clicar na opção Squash na solicitação de mesclagem, como mostrado abaixo. A mensagem de confirmação será o título da solicitação de mesclagem.
fonte
onde o número de ^ é X
(neste caso, esmague os dois últimos commits)
fonte
Além de outras excelentes respostas, gostaria de acrescentar como
git rebase -i
sempre me confunde com a ordem de confirmação - da mais antiga para a mais recente ou vice-versa? Portanto, este é o meu fluxo de trabalho:git rebase -i HEAD~[N]
, em que N é o número de confirmações nas quais quero ingressar, começando pela mais recente . Entãogit rebase -i HEAD~5
, significa "esmagar os últimos 5 commits em um novo";Fontes e leituras adicionais: # 1 , # 2 .
fonte
Que tal uma resposta para a pergunta relacionada a um fluxo de trabalho como este?
merge --squash
após o PR, mas a equipe achou que isso atrasaria o processo.)Não vi um fluxo de trabalho como esse nesta página. (Talvez sejam meus olhos.) Se eu entendi
rebase
corretamente, várias mesclagens exigiriam várias resoluções de conflito . Eu não quero nem pensar nisso!Então, isso parece funcionar para nós.
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
// coloca todas as alterações no estágiogit commit 'one message to rule them all'
git push
fonte
Acho que uma solução mais genérica não é especificar confirmações 'N', mas sim o ID da filial / confirmação que você deseja esmagar. Isso é menos propenso a erros do que contar as confirmações até uma confirmação específica - basta especificar a tag diretamente ou, se você realmente quiser contar, pode especificar HEAD ~ N.
No meu fluxo de trabalho, inicio uma ramificação, e meu primeiro commit nessa ramificação resume o objetivo (ou seja, geralmente é o que enviarei como a mensagem 'final' do recurso para o repositório público.) Então, quando terminar, tudo Eu quero fazer é
git squash master
voltar para a primeira mensagem e então estou pronto para enviar.Eu uso o alias:
Isso irá despejar o histórico que está sendo compactado antes de fazê-lo - isso lhe dá uma chance de recuperar, pegando um ID de confirmação antigo do console, se você quiser reverter. (Os usuários do Solaris observam que ele usa a
-i
opção GNU sed ; os usuários de Mac e Linux devem ficar bem com isso.)fonte
git squash master
quando fazemos check-out no escravo. o que isso vai acontecer? vamos esconder o conflito?Em questão, pode ser ambíguo o que se entende por "último".
por exemplo,
git log --graph
gera o seguinte (simplificado):As últimas confirmações por hora são H0, mesclagem, B0. Para esmagá-los, você precisará refazer sua ramificação mesclada no commit H1.
O problema é que H0 contém H1 e H2 (e geralmente confirma mais antes da mesclagem e após a ramificação), enquanto B0 não. Então você precisa gerenciar as alterações de H0, mesclagem, H1, H2, B0 pelo menos.
É possível usar o rebase, mas de maneira diferente das respostas mencionadas nas outras:
rebase -i HEAD~2
Isso mostrará as opções de escolha (conforme mencionado em outras respostas):
Coloque a abóbora em vez de escolher para H0:
Após salvar e sair, o rebase aplicará confirmações sucessivas após H1. Isso significa que ele solicitará que você resolva conflitos novamente (onde HEAD será H1 no início e depois acumulará confirmações à medida que forem aplicadas).
Após o término da recuperação, você pode escolher a mensagem para H0 e B0 esmagados:
PS Se você apenas redefinir o BO: (por exemplo, usar
reset --mixed
isso é explicado em mais detalhes aqui https://stackoverflow.com/a/18690845/2405850 ):então você esmaga as alterações B0 de H0, H1, H2 (perdendo completamente as confirmações após as ramificações e antes da mesclagem).
fonte
1) git reset --soft HEAD ~ n
n - Número do commit, é necessário esmagar
2) git commit -m "nova mensagem de confirmação"
3) origem do push do git branch_name --force
fonte