Como faço para esmagar duas confirmações não consecutivas?

183

Sou um pouco novo em todo o recurso de rebasing do git. Digamos que eu fiz os seguintes commits:

A -> B -> C -> D

Posteriormente, percebo que Dcontém uma correção que depende de algum novo código adicionado Ae que esses commits pertencem um ao outro. Como faço para esmagar A& Djuntos e licença B& Csozinho?

Nik Reiman
fonte

Respostas:

258

Você pode executar git rebase --interactivee reordenar D antes de B e esmagar D em A.

O Git abrirá um editor e você verá um arquivo como este, por exemplo: git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (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
#
# 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

Agora você altera o arquivo com a seguinte aparência:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

E o git agora mescla as mudanças de A e D em um commit e coloca B e C depois. Quando você não deseja manter a mensagem de confirmação de D, em vez de squash, você usaria a fixuppalavra - chave. Para mais informações fixup, consulte os git rebasedocumentos ou confira esta pergunta, que tem boas respostas.

Rudi
fonte
5
Inicialmente, eu li como "rebase D em A, squash D em A e depois rebase B em DA". Não está claro pela resposta que isso pode ser feito reorganizando as linhas em um editor de texto.
Victor Sergienko
3
Se sua filial for local, você receberá um There is no tracking information for the current brancherro ao fazer o novo rebase. Neste caso, você precisa especificar o número de commits que você quer trabalhar com, como este: git rebase -i HEAD~4. Veja esta resposta .
Johndodo 16/0518
1
Uso o modo interativo (git rebase -i) há anos, só percebi que ele pode ser reordenado . Obrigado 🤟🏻
CalvinChe
43

Nota: Você não deve alterar confirmações que foram enviadas para outro repositório de forma alguma, a menos que conheça as consequências .

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

Tipo i(Coloque o VIM no modo de inserção)

Altere a lista para ficar assim (você não precisa remover ou incluir a mensagem de confirmação). Não cometa erros ortográficos squash! :

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

Digite Escthen ZZ(Salvar e sair do VIM)

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

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

Tipo i

Altere o texto para o que você deseja que a nova mensagem de confirmação seja exibida. Eu recomendo que seja uma descrição das alterações no commit Ae D:

new_commit_message_for_A_and_D

Digite EscentãoZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

Você criou um novo commit E. Confirma Ae Dnão está mais em sua história, mas não se foi. Você ainda pode recuperá-los neste momento e por um tempo git rebase --hard D( git rebase --hardirá destruir as alterações locais! ).

Nate
fonte
3

Para aqueles que usam o SourceTree :

Verifique se você ainda não enviou os commit.

  1. Repositório> Rebase Interativo ...
  2. Arraste D (o commit mais recente) para ficar diretamente acima de A (o commit mais antigo)
  3. Verifique se o commit D está realçado
  4. Clique Squash with previous
Adam Johns
fonte
1

A rebase interativa funciona bem até que você tenha uma ramificação grande de recursos com 20 a 30 confirmações e / ou duas mesclagens do mestre ou da correção de conflitos enquanto você estava confirmando em sua ramificação. Mesmo encontrando meus commits através da história e substituindo pickpor squash, não funcionou aqui. Então, eu estava procurando uma outra maneira e encontrei este artigo . Fiz minhas alterações para trabalhar isso em um ramo separado:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

Antes disso, recebi minha solicitação de recebimento com aproximadamente 30 confirmações com 2-3 mesclagens de conflitos principais e de correção. E depois disso, consegui um PR claro com um commit.

PS aqui é o script bash para executar essas etapas no automode.

Oleksiy Guzenko
fonte
A primeira solução nesse artigo é muito legal, obrigado pelo link
Hoody
-1

mestre de checkout do $ git

$ git log --oneline

D
C
B
A

$ git rebase --onto HEAD ^^^ HEAD ^

$ git log --oneline

D
A
Algodão
fonte
Eu acho que você quer dizer --oneline? E parece que você caiu Ce B, o que não é o que o OP pretendia.
bstpierre
Não funcionou para mim. Ele moveu meu HEAD e master até A, mas não mesclou D em A ( git show A) e D, C e B foram perdidos no meu ref-log. Tinha que git rebase Dvoltar.
Nate