Редактирование патча прошлого коммита - PullRequest
2 голосов
/ 24 мая 2019

У меня есть предыдущий коммит, содержащий в своем патче строку, которая должна быть частью более нового коммита. Можно ли как-нибудь переместить эту строку из одного патча в другой? Я пытаюсь поиграть с опцией edit, равной git rebase --interactive, но не могу изменить патч. Это вообще возможно?

Ответы [ 2 ]

2 голосов
/ 24 мая 2019

Вы можете получить то, что хотите, используя интерактивный ребаз. Но обратите внимание на ключевой момент: коммиты - это не патчи! Коммиты снимки . Каждый коммит имеет полную копию всего вашего источника , хотя git log -p показывает каждый коммит в виде патча.

Причина, по которой git log -p показывает коммиты как патчи, хотя они и являются моментальными снимками, очевидна: гораздо более полезно , чтобы сказать, commit # 12, немного отличается от commit # 11, и вот разница , чем это можно сказать: Вот коммит # 12, и ха! Ты сам по себе, присоска! Итак, git log -p работает, извлекая коммит # 11 и коммит # 12, сравнивая их и показывая, что изменилось .

Вы должны всегда помнить об этом: коммиты - это моментальные снимки; но Git любит сравнить два снимка, чтобы показать вам, что изменилось между ними. Вы можете выбрать любые два снимка для сравнения, но git log -p и другие команды Git будут автоматически использовать две смежные родительские / дочерние фиксации. Копирование Rebase также работает следующим образом: он сравнивает родителя и потомка, чтобы увидеть, что делать. (Для сравнения двух коммитов по вашему выбору вы можете использовать git diff и просто назвать два коммита для сравнения.)

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


С учетом всего вышесказанного, вот как это сделать с помощью интерактивной перебазировки. Начните с выбора коммита перед теми, которые вам нужно улучшить:

9abcdef  Subject of last commit
00afde4  Second to last commit that should have more stuff
badc0de  The commit that you dislike
ba5eba5  A commit you're OK with

Здесь, коммит badc0de - это тот, который вы хотели бы заменить новым и улучшенным коммитом. Вы хотите, чтобы какое-то изменение, которое вы сделали в badc0de, перешло в 00afde4, поэтому вы действительно хотите заменить badc0de другим коммитом, который делает меньше, а 00afde4 другим коммитом, который делает больше Затем, поскольку 9abcdef говорит мой родитель 00afde4, вам придется скопировать 9abcdef в новый и улучшенный коммит, единственным улучшением которого будет мой родитель _______ , где пробел заполняется созданием новой и улучшенной версии 00afde4.

Итак, теперь вы запустите git rebase -i ba5eba5 и получите в своем редакторе лист инструкций, который гласит:

pick badc0de The commit that you dislike
pick 00afde4 Second to last commit that should have more stuff
pick 9abcdef Subject of last commit

(Обратите внимание, что хотя git log начинается с end и работает в обратном направлении, лист инструкций для git rebase -i начинается с первого коммита, который вы скопируете, но измените при копировании, и переместится вперед .)

Поскольку мы предполагаем, что вы хотите поместить строку из badc0de в 00afde4, вы должны теперь изменить эти две из трех pick команд на чтение edit, оставив 9abcdef - что Вы хотите скопировать без изменений - как команда pick. Запишите лист инструкций обратно в любой файл, в котором он находится, и выйдите из редактора, чтобы запустить Git, следуя инструкциям.

Git теперь будет копировать коммит badc0de, но затем остановится, чтобы вы могли изменить копию. Технически, он на самом деле еще не скопировал badc0de - он просто настроил так, чтобы то, что вы делаете дальше , скопировало бы , но в более сложных случаях, как мы чтобы увидеть, он сделал временную копию. Теперь вы находитесь в том, что Git называет режим detached HEAD - совсем не в какой-либо ветке - и все, что вы делаете, - запускаете свой редактор, чтобы редактировать любые файлы, которые должны отличаться, изменять их, записывать их и выйдите из редактора. Затем вы git add каждый такой файл и запустить:

git commit --amend

Это исключает badc0de из этой ветки отсоединенного HEAD, делая копию badc0de коммита, которая теперь является хорошим коммитом. Мы не знаем, какой хеш-идентификатор у него будет , но давайте назовем его 1111111 с целью ссылки на него.

Теперь гоу вас есть 1111111 с родительским ba5eba5 и с нужным снимком, выполните:

git rebase --continue

для возобновления интерактивной перебазировки. Теперь Git копирует 00afde4 для нового временного коммита. На этот раз действительно нужно сделать временную копию, потому что 00afde4 говорит , мой родитель badc0de, и ему нужна копия, которая говорит , мой родитель 1111111. Git делает эту временную копию путем сравнения 00afde4 с badc0de, чтобы увидеть, что изменилось, и внесения тех же самых изменений в 1111111. Мы не знаем или не заботимся о том, какой хеш-идентификатор получает этот новый временный коммит, потому что теперь пришло время снова запустить ваш редактор и восстановить недостающие строки, которые вы удалили в прошлый раз.

Если вы забыли, что такое пропущенные строки, вы можете легко их найти: сравните коммит badc0de - который все еще находится в вашем хранилище - с его родителем, используя, например, git show badc0de. Вставьте эти строки в ваши файлы рабочего дерева, используя ваш редактор. Выпишите файлы, выходя из редактора. Запустите git add для этих файлов, а затем снова выполните git commit --amend.

Git теперь отбрасывает временный коммит, делая новый коммит, похожий на 00afde4, за исключением того, что:

  • перечисляет 1111111 в качестве родителя и
  • восстановлены строки из badc0de.

Опять же, мы понятия не имеем, какой хеш будет , но давайте просто назовем его 2222222 здесь, чтобы мы могли поговорить об этом.

Теперь вы можете запустить git rebase --continue снова. На данный момент единственной оставшейся инструкцией является pick 9abcdef. Так что Git сравнивает 9abcdef со своим родителем 00afde4, чтобы увидеть, что изменилось, вносит те же самые изменения в снимок в 2222222 и делает из этого новый коммит. Давайте назовем новую копию 3333333.

Git теперь имеет:

3333333  (copy of) Subject of last commit
2222222  (copy of) Second to last commit (now with more stuff)
1111111  (copy of) The commit that you disliked (but now improved)
ba5eba5  A commit you're OK with

Фиксация 3333333 на самом деле идентична 9abcdef, за исключением , что 3333333 говорит мой родитель 2222222.

Поскольку копирование теперь завершено, git rebase выполняет свой последний трюк: он делает имя вашей ветви, каким бы оно ни было, идентифицирует копию 3333333 вместо оригинального 9abcdef и выходит из режима отсоединенного HEAD, проверяя перенастроенная ветка. Так что теперь, если Git просматривает коммиты, начинающиеся с текущего и работающие в обратном направлении, вы видите 3333333, затем 2222222, затем 1111111, затем ba5eba5 и так далее. Оригинальные коммиты - те, которые вы заменили новыми и улучшенными копиями - невидимы. Они по-прежнему в вашем хранилище (около месяца), но их сейчас трудно найти.

0 голосов
/ 24 мая 2019

@ karlosss git rebase -i commit-id-test , покажет все commit-id и commit-msg после commit-id-test, затем вы можете отредактировать commit-msg избежать ctrl-c / v.

, например

pick 90b83db Replace QtMultimedia usage with 4A + gstreamer
pick f55e504 autobuild: introduce autobuild scripts
...