Como alterar o commit anterior para incluir um arquivo perdido?

98

Fiz uma alteração e esqueci de adicionar um arquivo ao conjunto de alterações. Depois de outros commits, percebi que agora o arquivo está faltando em um HEAD^4commit.

Como reescrevo um commit anterior para incluir o arquivo ausente?

Kolrie
fonte
você empurrou esses 4 commits?
mvp de
@mvp não, eles estão apenas no meu repositório git local.
kolrie

Respostas:

53

Use git rebase --interactive HEAD~4e defina a editopção para o commit que você gostaria de alterar.

Lembre-se de que você não deve modificar os commits enviados ao repositório remoto desta forma. É melhor adicionar um novo commit com o arquivo ausente nesse caso.

Rafał Rawicki
fonte
Obrigado. Esse é o caso mesmo se eu for o único usuário do repo remoto? Isso não me permitiria fazer isso git push -fse eu tivesse certeza de que o upstream não mudou?
kolrie
1
Se você for o único usuário do repo remoto, não há problema em fazer o push forçado.
Rafał Rawicki
7
Acho que essas instruções não são detalhadas o suficiente. Ao tentar pela primeira vez, me disseram "Não é possível realocar: seu índice contém alterações não confirmadas." Eu já havia addinstalado os arquivos ausentes, então fiz um commit com "xxx" na mensagem. Então fiz o comando rebase e mudei o commit "xxx" de "pick" para "edit". Então eu fiz "git rebase --continue". Agora, quando olho para o histórico, tenho "xxx" como o commit mais recente, e o commit anterior ao qual eu queria adicioná-los permanece inalterado! Eu me pergunto onde foi meu erro?
Darren Cook
2
Eliminar o último commit não colocará o arquivo no HEAD ~ 4.
Justin
1
git add editedFiles; git commit -m "Blah"; git rebase -i HEAD ~ 5; // como agora um novo commit adicionado, então precisamos rebase com 5 em vez de 4. agora mova o commit "Blah" para a segunda linha e mude de "Pick" para "s" (squash) que irá esmagar o commit com HEAD ~ 5 conforme os comandos são executados de cima para baixo
zstring
274

Sei que as pessoas podem pesquisar no Google e vir aqui para encontrar uma resposta mais simples: e se fosse apenas o último commit? (A pergunta de OP é para consertar o 4º commit na história)

No caso de você se comprometer e perceber que esqueceu de adicionar algum arquivo imediatamente , basta fazer:

# edited file-that-i-remember.txt
git add file-that-i-remember.txt
git commit

# realize you forgot a file
git add file-that-i-forgot.txt
git commit --amend --no-edit

Onde --no-editmanterá a mesma mensagem de commit.

Mole-mole!

Dr. Beco
fonte
21
Esta é a resposta.
Adam Bittlingmayer
5
Vale a pena mencionar, se os commits não forem enviados para o remoto.
Ram Patra
1
Sim, vale a pena mencionar aqui nos comentários: É para usar antes do push . Obrigado por apontar isso.
Dr. Beco de
2
Um aviso é que os commits antes e depois --amendtêm hashes diferentes
sonlexqt
5
Obrigado, mas não pode ser: OP solicitado HEAD^4. Tudo bem do jeito que está, apenas como um adendo para referência. ;)
Dr. Beco
11

Se você NÃO enviou esses 4 commits, você pode fazer isso da seguinte maneira:

Crie arquivos de patch para todos esses commits:

git format-patch -4

Retroceda em 4 confirmações:

git reset --hard HEAD~4

Adicionar arquivo ausente:

git add missing-file

Comprometa-se com --amend:

git commit --amend

Aplique todos os patches salvos de volta:

git am *.patch

Se você tiver feito push, NÃO deve usar este método. Em vez disso, apenas admita seu erro e crie mais um commit no HEAD que corrige esse problema.

mvp
fonte
Se você quiser fazer isso passo a passo, é mais fácil selecionar os commits após o modificado, do que exportá-los como um patch.
Rafał Rawicki
1
Isso é uma questão de gosto. Eu gosto git format-patch/ git ammuito melhor. E o mais importante, dá a você mais confiança se você errar em algo - commit salvo como patch em arquivo físico é sua melhor rede de segurança.
mvp
A verdadeira confiança reside no fato de que, ao operar em um repositório git, você nunca remove nada. Os commits antigos estão disponíveis até você executar git gc:)
Rafał Rawicki
Isso é trivial e óbvio para você e para mim. Mas, para o usuário que está apenas começando e provavelmente não entende nada sobre o git - esse fato não é nada óbvio.
mvp de
2
Essas instruções pareciam prolixas, mas eram bastante simples e fáceis de seguir. Obrigado. (Eu tinha acabado de adicionar uma etapa final: rm *.patch)
Darren Cozinhe
9

Embora a resposta aceita esteja correta, faltam instruções detalhadas sobre como executar a edição de um commit durante um processo de rebase.

  • Primeiro, inicie um processo de rebase:

    git rebase --interactive HEAD~4
    
  • Uma lista de commits será apresentada, escolha um commit que você deseja editar mudando a palavra pickpara edite salve o arquivo.

  • Faça as modificações necessárias em seu código (lembre-se de invocar git addpara novos arquivos)

  • Depois que todas as modificações forem feitas, problema git commit --amend- isso irá corrigir um commit marcado comoedit

  • Invoque git rebase --continueque encerrará o processo (se houver mais commits marcados como edit, as etapas acima precisam ser repetidas)

Anotações importantes:

  • NÃO remova as linhas marcadas como pickque você não deseja editar - deixe-as como estão. A exclusão dessas linhas resultará na exclusão de commits relacionados

  • O GIT força você a fazer o stashrebase antes do rebase, se seu diretório de trabalho não estiver limpo; você pode, no entanto, git stash pop / git stash applydurante o rebase, a fim de corrigir essas alterações (ou seja, alterações armazenadas antes de iniciar o processo de rebase) para um commit marcado comoedit

  • se algo deu errado e você deseja reverter as alterações feitas durante o processo de rebase antes de terminar (ou seja, você deseja reverter ao ponto antes de iniciar o rebase), use git rebase --abort- também leia: Como abortar um rebase interativo se --abort não ' t funciona?

  • Conforme dito na resposta aceita:

    Lembre-se de que você não deve modificar os commits enviados ao repositório remoto desta forma. É melhor adicionar um novo commit com o arquivo ausente nesse caso.

    A resposta está no livro Git (parágrafo intitulado " The Perils of Rebasing "):

    Não realoque commits que existem fora do seu repositório.

    Se você seguir essa diretriz, ficará bem. Do contrário, as pessoas irão odiá-lo e você será desprezado por amigos e familiares.

    Quando você rebase coisas, está abandonando commits existentes e criando novos que são semelhantes, mas diferentes. Se você enviar commits em algum lugar e outros puxá-los para baixo e trabalhar com base neles, e então você reescrever esses commits com git rebase e colocá-los novamente, seus colaboradores terão que re-fundir seus trabalhos e as coisas ficarão confusas quando você tentar coloque o trabalho deles de volta no seu.

    [...]

Dominik
fonte