Как добавить измененный файл в более старый (не последний) коммит в Git

461

За последний час я изменил несколько вещей и сделал их шаг за шагом, но я только что понял, что забыл добавить измененный файл несколько коммитов назад.

Журнал выглядит так:

    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

Любые идеи?

Leen
источник
1
По сути дубликат Как изменить указанный коммит?
Дан Даскалеску

Ответы:

694

Использование git rebase. В частности:

  1. Используйте git stashдля хранения изменений, которые вы хотите добавить.
  2. Используйте git rebase -i HEAD~10(или сколько коммитов вы хотите увидеть).
  3. Пометьте коммит в вопросе ( a0865...) для редактирования, изменив слово pickв начале строки на edit. Не удаляйте другие строки, так как это приведет к удалению коммитов. [^ Vimnote]
  4. Сохраните файл rebase, и git вернется в оболочку и будет ждать, пока вы исправите этот коммит.
  5. Вставьте тайник с помощью git stash pop
  6. Добавьте свой файл с помощью git add <file>.
  7. Изменить коммит с git commit --amend --no-edit.
  8. Сделайте, git rebase --continueчто перезапишет остальные ваши коммиты против нового.
  9. Повторите, начиная с шага 2, если вы пометили более одного коммита для редактирования.

[^ vimnote]: если вы используете, vimвам нужно будет нажать Insertклавишу для редактирования, затем Escввести текст, :wqчтобы сохранить файл, выйти из редактора и применить изменения. Кроме того, вы можете настроить удобный редактор git commit с помощью git config --global core.editor "nano".

Грег Хьюгилл
источник
23
Что делать, если у вас есть неотмеченные изменения, которые вы хотите добавить в редактирование? Если бы я их спрятал, я бы не смог git add.
Сэм
15
Сэм, вы можете просто распаковать изменения во время коммита, это будет работать нормально.
omnikron
17
Примечание. Когда вы помечаете фиксацию edit, НЕ УДАЛЯЙТЕ другие коммиты, перечисленные в файле. Если вы это сделаете, коммиты будут удалены, и вам нужно будет выполнить следующие шаги, чтобы вернуть их.
Дэвид Туит
2
Что касается того, что сказал @DavidTuite, моя привычка, когда я делаю что-то на git, что я не уверен, чем это закончится, это создание ветки "branchname-ref" для сохранения текущего состояния временной шкалы на случай, если я все испорчу. Когда я закончу, я удаляю это.
Рафаэль
1
На шаге 6 включите. (точка) в команде. Правильная команда:git add .
Том
323

Чтобы «исправить» старый коммит с небольшим изменением, не изменяя сообщение коммита старого коммита, где- OLDCOMMITто вроде 091b73a:

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

Вы также можете использовать git commit --squash=OLDCOMMITдля редактирования старого сообщения коммита во время перебазирования.


  • git rebase --interactiveвызовет текстовый редактор (который можно настроить ) для подтверждения (или редактирования) последовательности команд rebase . В файле есть информация об изменениях инструкции rebase ; просто сохраните и выйдите из редактора ( :wqinvim ), чтобы продолжить ребазинг.
  • --autosquashавтоматически поместит любые --fixup=OLDCOMMITкоммиты в желаемом порядке. Обратите внимание, что --autosquashдействует только при использовании этой --interactiveопции.
  • ^В OLDCOMMIT^помощи это ссылка на коммит непосредственно перед OLDCOMMIT.

Вышеуказанные шаги полезны для проверки и / или изменения последовательности команд rebase , но также можно пропустить / автоматизировать интерактивный текстовый редактор rebase:

Смотрите git commit и git rebase . Как всегда, при переписывании истории git вы должны только фиксировать или фиксировать коммиты, которые вы еще никому не публиковали (включая случайных пользователей интернета и серверы сборки).

Джоэл Пурра
источник
18
Гораздо понятнее, чем другие варианты, и это сработало как шарм
Крис Митчелмор
5
@Jonah: редактор открывается не для редактирования сообщения о коммите , а для подтверждения (или редактирования) шагов перебазирования . Этого нельзя избежать; --autosquashдействует только при использовании --interactiveопции .
Джоэл Пурра
5
Используя это решение, я застреваю в GIT, показывая VIM. Моя проблема в том, что я не знаю, как использовать VIM. Как, черт возьми, я выхожу из этого, как я могу контролировать эту вещь, это так запутанно.
Неон Warge
2
@NeonWarge: выбранный редактор настраивается с использованием, например git config --global core.editor "pico". Есть несколько других способов настроить git и / или изменить редактор вашей системы по умолчанию и так далее.
Джоэл Пурра
2
одна строка хороший псевдоним здесь
idanp
61

с git 1.7 есть действительно простой способ использования git rebase:

подготовить ваши файлы:

git add $files

создать новый коммит и повторно использовать сообщение коммита вашего «битого» коммита

git commit -c master~4

добавьте fixup!в строку темы (или squash!если вы хотите редактировать коммит (сообщение)):

fixup! Factored out some common XPath Operations

использовать, git rebase -i --autosquashчтобы исправить ваш коммит

knittl
источник
2
+1. Хорошее использование новой директивы исправления (1.7+): stackoverflow.com/questions/2302736/trimming-git-checkins/…
VonC
@knittl Я опробовал твой метод, чтобы добавить другой файл в мой старый коммит (не выдвинутый), но когда я делаю ребазинг, я получаю, You asked me to rebase without telling me which branch you want to rebase against, and 'branch.master.merge'и если я использую его, git rebase -i --autosquashя просто получаю noopстроку темы, перебрасывая коммит на себя. Есть идеи, что я делаю не так?
Ощренк
7
@oschrenk: вам нужно предоставить коммит, к которому вы хотите перебазировать, напримерgit rebase -i --autosquash HEAD~10
knittl
1
Хороший ответ, но мне также нужно было добавить коммит, против которого нужно выполнить ребазинг. Было бы здорово, если бы вы могли обновить его.
Пол Одеон
@PaulOdeon: я не понимаю ваш вопрос. Что вы пытаетесь сделать, и где у вас проблемы?
knittl
8

Вы можете попробовать rebase --interactiveсеанс, чтобы изменить свой старый коммит (при условии, что вы еще не передали эти коммиты в другое репо).

Иногда вещь исправлена ​​в п.2. нельзя исправить до не совсем совершенного коммита, который он исправляет, потому что этот коммит глубоко скрыт в серии патчей .
Это именно то, для чего нужен интерактивный ребаз: используйте его после множества «а» и «б», переставляя и редактируя коммиты, и объединяя несколько коммитов в один.

Запустите его с последним коммитом, который вы хотите сохранить как есть:

git rebase -i <after-this-commit>

Редактор будет запущен со всеми коммитами в вашей текущей ветке (игнорируя коммиты слияния), которые идут после данного коммита.
Вы можете переупорядочить коммиты в этом списке на свое усмотрение, и вы можете удалить их. Список выглядит примерно так:

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

Онлайновые описания предназначены исключительно для вашего удовольствия; git rebase будет смотреть не на них, а на имена коммитов (в этом примере «deadbee» и «fa1afe1»), поэтому не удаляйте и не редактируйте имена.

Заменив команду «pick» на команду «edit», вы можете указать git rebase прекратить работу после применения этого коммита, чтобы вы могли редактировать файлы и / или сообщение о коммите, изменять коммит и продолжать перебазирование .

VonC
источник