Como adicionar um arquivo alterado a um commit mais antigo (não o último) no Git

461

Alterei várias coisas na última hora e as comprometi passo a passo, mas acabei de perceber que esqueci de adicionar um arquivo alterado algumas confirmações atrás.

O Log fica assim:

    GIT TidyUpRequests u:1 d:0> git log 
    commit fc6734b6351f6c36a587dba6dbd9d5efa30c09ce 
    Author: David Klein <> 
    Date:   Tue Apr 27 09:43:55 2010 +0200

        The Main program now tests both Webservices at once

    commit 8a2c6014c2b035e37aebd310a6393a1ecb39f463 
    Author: David Klein <>
    Date:   Tue Apr 27 09:43:27 2010 +0200

        ISBNDBQueryHandler now uses the XPath functions from XPath.fs too

    commit 06a504e277fd98d97eed4dad22dfa5933d81451f 
    Author: David Klein <> 
    Date:   Tue Apr 27 09:30:34 2010 +0200

        AmazonQueryHandler now uses the XPath Helper functions defined in XPath.fs

    commit a0865e28be35a3011d0b6091819ec32922dd2dd8 <--- changed file should go here
    Author: David Klein <> 
    Date:   Tue Apr 27 09:29:53 2010 +0200

        Factored out some common XPath Operations

Alguma ideia?

leen
fonte
1
Essencialmente uma duplicata de Como modificar uma confirmação especificada?
Dan Dascalescu 9/01/19

Respostas:

694

Use git rebase. Especificamente:

  1. Use git stashpara armazenar as alterações que você deseja adicionar.
  2. Use git rebase -i HEAD~10(ou o número de confirmações que você deseja ver).
  3. Marque o commit na pergunta ( a0865...) para edição, alterando a palavra pickno início da linha para edit. Não exclua as outras linhas, pois isso excluiria as confirmações. [^ Vimnote]
  4. Salve o arquivo rebase, e o git retornará ao shell e esperará que você corrija esse commit.
  5. Estale o estoque usando git stash pop
  6. Adicione seu arquivo com git add <file>.
  7. Alterar o commit com git commit --amend --no-edit.
  8. Faça um git rebase --continueque reescreva o restante dos seus commit contra o novo.
  9. Repita da etapa 2 em diante se você tiver marcado mais de um commit para edição.

[^ vimnote]: Se você estiver usando vim, precisará pressionar a Inserttecla para editar Esce digitar :wqpara salvar o arquivo, sair do editor e aplicar as alterações. Alternativamente, você pode configurar um git user-friendly cometer editor com git config --global core.editor "nano".

Greg Hewgill
fonte
23
E se você tiver alterações não organizadas que deseja adicionar à edição? Se eu os esconder, não posso git add.
Sam
15
Se você pode simplesmente descompactar as alterações enquanto estiver no commit em questão, ele funcionará bem.
Omnikron # /
17
Nota: Ao marcar o commit com edit, NÃO DELETE os outros commit listados no arquivo. Se o fizer, as confirmações serão excluídas e você precisará seguir estas etapas para recuperá-las.
precisa
2
Com relação ao que o @DavidTuite disse, um hábito meu ao fazer coisas no git que não tenho certeza de como será a criação de um ramo "branchname-ref" para salvar o estado atual da linha do tempo, caso eu estrague tudo. Quando termino, apago.
Raphael
1
Na etapa 6, inclua o. (ponto) no comando Comando correto:git add .
Tom
323

Para "consertar" um commit antigo com uma pequena alteração, sem alterar a mensagem de commit do commit antigo, onde OLDCOMMITé algo como 091b73a:

git add <my fixed files>
git commit --fixup=OLDCOMMIT
git rebase --interactive --autosquash OLDCOMMIT^

Você também pode usar git commit --squash=OLDCOMMITpara editar a antiga mensagem de confirmação durante o rebase.


  • git rebase --interactiveexibirá um editor de texto (que pode ser configurado ) para confirmar (ou editar) a sequência de instruções rebase . Há informações para alterações das instruções de rebase no arquivo; apenas salve e feche o editor ( :wqinvim ) para continuar com o rebase.
  • --autosquashcolocará automaticamente quaisquer --fixup=OLDCOMMITconfirmações na ordem desejada. Observe que --autosquashsó é válido quando a --interactiveopção é usada.
  • O ^in OLDCOMMIT^significa que é uma referência ao commit logo antes OLDCOMMIT.

As etapas acima são boas para verificar e / ou modificar a sequência de instruções de rebase , mas também é possível pular / automatizar o editor de texto de rebase interativo da seguinte maneira:

Veja git commit e git rebase . Como sempre, ao reescrever o histórico do git , você só deve consertar ou confirmar squash que ainda não publicou para mais ninguém (incluindo usuários aleatórios da Internet e criar servidores).

Joel Purra
fonte
18
Muito mais clara do que as outras opções, e funcionou como um encanto
Chris Mitchelmore
5
@ Jonah: o editor não está aberto para editar a mensagem de confirmação , mas para confirmar (ou editar) as etapas de rebase . Não pode ser evitado; --autosquashé válido apenas quando a --interactiveopção é usada .
Joel Purra
5
Usando esta solução, eu estou preso git mostrando VIM. Meu problema é que não sei usar o VIM. Como diabos eu saio disso, como posso controlar essa coisa, isso é tão confuso.
Neon Warge
2
@NeonWarge: o editor de sua escolha é configurável usando, por exemplo git config --global core.editor "pico". Existem várias outras maneiras de configurar o git e / ou alterar o editor padrão do seu sistema, etc.
Joel Purra
2
alias nice de uma linha aqui
idanp 27/04
61

com o git 1.7, existe uma maneira muito fácil de usar git rebase:

prepare seus arquivos:

git add $files

crie uma nova confirmação e reutilize a mensagem de confirmação da confirmação "quebrada"

git commit -c master~4

coloque um prefixo fixup!na linha de assunto (ou squash!se você deseja editar o commit (mensagem)):

fixup! Factored out some common XPath Operations

use git rebase -i --autosquashpara corrigir seu commit

knittl
fonte
2
+1. Bom uso da nova diretiva de correção (1.7+): stackoverflow.com/questions/2302736/trimming-git-checkins/…
VonC
@knittl Eu estava tentando o seu método para adicionar outro arquivo a um commit antigo (não pressionado), mas ao fazer o rebaseamento recebo You asked me to rebase without telling me which branch you want to rebase against, and 'branch.master.merge'e, se eu usar git rebase -i --autosquash, recebo apenas uma nooplinha de assunto, rebatizando o commit para si mesmo. Alguma idéia do que eu faço de errado?
Oschrenk #
7
@oschrenk: Você precisa fornecer uma confirmação para o qual você deseja rebase, por exemplogit rebase -i --autosquash HEAD~10
knittl
1
Boa resposta, mas eu também precisava adicionar uma confirmação contra a qual refazer a recuperação. Seria ótimo se você pudesse atualizá-lo.
Paul Odeon
@PaulOdeon: Eu não entendo sua pergunta. O que você está tentando fazer e onde está tendo problemas?
knittl
8

Você pode tentar uma rebase --interactivesessão para alterar seu commit antigo (desde que você ainda não os envie por push para outro repo).

Às vezes, a coisa fixada em b.2. não pode ser alterado para o commit não muito perfeito que ele corrige, porque esse commit está enterrado profundamente em uma série de patches .
É exatamente para isso que o rebase interativo se destina: use-o após muitos "a" se "b" s, reorganizando e editando confirmações e esmagando várias confirmações em uma.

Inicie-o com o último commit que você deseja manter como está:

git rebase -i <after-this-commit>

Um editor será acionado com todas as confirmações em sua ramificação atual (ignorando as confirmações de mesclagem), que vêm após a confirmação especificada.
Você pode reordenar os commits nesta lista para o conteúdo do seu coração e pode removê-los. A lista é mais ou menos assim:

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

As descrições on-line são puramente para seu prazer; O git rebase não olhará para eles, mas para os nomes de confirmação ("deadbee" e "fa1afe1" neste exemplo), portanto, não exclua ou edite os nomes.

Substituindo o comando "pick" pelo comando "edit", você pode dizer ao git rebase para parar após aplicar esse commit, para que você possa editar os arquivos e / ou a mensagem de commit, alterar o commit e continuar com o rebase .

VonC
fonte