Как добавить измененный файл в более старый (не последний) коммит в 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

Любые идеи?


Ответы:


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чтобы исправить ваш коммит


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 прекратить работу после применения этого коммита, чтобы вы могли редактировать файлы и / или сообщение о коммите, изменять коммит и продолжать перебазирование .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.