#git
#мерзавец #git
Вопрос:
У меня есть несколько сложных сценариев, которые я хотел бы решить, но я не знаю как. Я надеюсь, что вы сможете помочь:
A - B - C - D - E
. Я хотел бы добавить изменения из commitD
в commitB
и удалить commitD
. Как?A - B - C
. CommitB
содержит файл, который не должен был быть зафиксирован. Как удалить его из коммита?A - B - C - D
. CommitC
содержит файл , который должен был быть зафиксирован с помощью commitB
. Как удалить его из commitC
и добавить в commitB
?
Пожалуйста, обратите внимание: все коммиты находятся только в моей рабочей копии и еще не были отправлены.
Ответ №1:
Мои ответы используют git rebase
довольно много. Если вы новичок в перебазировании, обязательно сначала прочтите эту главу о перебазировании git
Как вы сами выяснили, вы могли быgit cherry-pick B
rebase -i A
поменять местами ревизии C / D, объединив B D.-
git rebase -i B
отметьте B как редактировать и примените изменения.Теперь, чтобы действительно удалить объекты из базы данных объектов, вам может потребоваться использовать filter-branch для всех ветвей, содержащих объект. После этого может потребоваться некоторое время для истечения срока действия reflogs. Если объект чувствителен к безопасности, вы можете захотеть
git gc --aggressive --prune=tomorrow
. В книге Pro Git есть раздел, посвященный удалению объектов.git-filter-branch
Всегда не забывайте включать--tag-name-filter cat
, смотрите Комментарии -
Если весь C должен быть в B, просто
git rebase -i B
отметьтеC
с помощью squash, и это сведет коммиты воедино.Если это становится более сложным, чем это (только один файл из C должен быть отправлен в B), я бы разделил задание на более мелкие шаги, которые могут быть решены, как в описанных выше шагах:
- разделите фиксацию C на две части: одну (C.1), чтобы объединить в фиксацию B, и одну (C.2), чтобы сохранить как новую ‘C’. Например, из главы Git Pro, на которую ссылались выше:
Например, если вы хотите разделить коммит, вы должны указать ‘edit’ для этого коммита:
pick fc62e55 added file_size pick 9824bf4 fixed little thing edit 21d80a5 added number to log pick 76b9da6 added the apply command pick c264051 Revert "added file_size"
И затем, когда вы попадаете в командную строку, вы отменяете этот коммит и создаете два (или более) новых. Допустим, 21d80a5 изменил два файла, file1 и file2, и вы хотели разделить их на отдельные коммиты. Вы могли бы сделать это после перебазирования в командную строку :
$ git reset HEAD^ $ git add file1 $ git commit 'first part of split commit' $ git add file2 $ git commit 'second part of split commit' $ git rebase --continue
И теперь вместо 5 коммитов у вас будет 6.
- перебазирование для объединения коммитов B и C.1
- разделите фиксацию C на две части: одну (C.1), чтобы объединить в фиксацию B, и одну (C.2), чтобы сохранить как новую ‘C’. Например, из главы Git Pro, на которую ссылались выше:
Комментарии:
1. Не могли бы вы подробнее рассказать о «не забудьте включить
--tag-name-filter cat
«? Почему это опасно, и почему это всегда должно быть включено?2. @unutbu: по умолчанию ветвь фильтра git «перезаписывает» (фильтрует) дерево версий, доступное по ссылкам, указанным в командной строке (или HEAD). Использование
--all
вы можете привести к перезаписи почти всех имен ссылок). Однако имена тегов не перезаписываются, если не присутствует--tag-name-filter
параметр. Это может привести к неприятным сюрпризам при забвении (внезапно теги преобразуются в «устаревшие» коммиты, которые уже давно были переписаны). Теперь на практике теги используются в основном для опубликованных версий, и вам все равно не следует переписывать опубликованную историю .3. … Тем не менее, мне бы понравилось, если бы
cat
фильтр имен тегов был установлен по умолчанию.4. Большое спасибо за ваш подробный ответ. Я прочитаю это и предоставленные ссылки и проверю позже.
5. @sehe: Спасибо. 1) выбор вишни не сработал, это создало конфликты. Тем не менее, я использовал
git rebase -i
и переключал коммитыC
иD
и изменилpick
передD
наf
.