Чем отличаются мерзавцы и замены? (Сейчас трансплантаты устарели?)
На git очень мало вопросов и ответов grafts
против replace
, Поиск [git] +grafts +replace нашел только два, которые считались актуальными из 5. /questions/19100893/dlya-chego-nuzhnyigitinfografts и /questions/39295464/git-chto-takoe-transplantat-ili-identifikator-transplantata. Есть также заметка на https://git.wiki.kernel.org/index.php/GraftPoint
Являются ли трансплантаты полностью настигшими replace
а также filter-branch
или они все еще нужны для некоторых особых угловых случаев (и обратной совместимости)?
В общем, как они отличаются (например, которые транспортируются между репо), и как они в целом одинаковы? Я видел, что Линус, похоже, в настоящее время не заботится о трансплантатах в дискуссии о количестве поколений коммитов (от максимального числа родителей до любого корневого сорта): "Трансплантаты уже ненадежны".
РЕДАКТИРОВАТЬ: больше информации найдено.
Поиск www.kernel.org/pub/software/scm/git/docs для graft
найдено только 3 результатов:
- ГИТ-фильтр-ветвь (1),
- v1.5.4.7 / GIT-фильтр-ветвь (1),
- v1.5.0.7 / ГИТ-SVN (1).
Найден чуть более широкий поиск RelNotes / 1.6.5.txt, который содержит:
- refs / replace / иерархия предназначена для использования в качестве замены механизма "прививки", с дополнительным преимуществом, что он может быть передан через репозитории.
К сожалению, gitrepository-layout(5) еще не обновлен ни с информацией о макете refs / replace / repository (и примечаниями), ни с примечанием об устаревании информации / прививок.
Это становится ближе к поддержке того, что я думал, но я бы приветствовал любое подтверждение или разъяснение.
3 ответа
В том же обсуждении о коммитном поколении, о котором вы упомянули, Jakub Narębski подтверждает, что прививки являются скорее проблемой, чем решением:
прививки настолько ужасны, что я был бы не против выключения номеров поколений, если они используются.
В случае замены объектов вам нужны как не замененные, так и замененные номера генерации DAG.
[...] Трансплантаты не подлежат передаче, и если вы используете их для отбраковки, а не для добавления истории, они небезопасны для сбора мусора... Я думаю.
(публикация всегда была о git filter-branch
, как показано в этой теме 2008 года о процессе трансплантации.)
Различие между прививками и заменой git лучше всего иллюстрируется этим SO-вопросом "Установка указателя родительского элемента git на другого родителя" и комментариями (снова Якуба) ответа.
Это включает ссылку на Git1.6.5
Из того, что я понимаю (из GraftPoints),
git replace
вытеснилgit grafts
(если у вас есть git 1.6.5 или более поздняя версия)(Якуб:)
- если вы хотите переписать историю, то
grafts
+git-filter-branch
(или интерактивная перебазировка, или быстрый экспорт + например, reposurgeon) - способ сделать это.- Если вы хотите / должны сохранить историю, то
git-replace
намного превосходит прививку
Если вам нужно переписать родительский коммит, используя git replace
Вот как это сделать.
Как отметил Филип Окли, git replace просто заменяет один коммит другим. Чтобы привить родителя к существующему коммиту, вам нужно сначала создать фальшивый коммит с правильным родителем.
Скажем, у вас есть две ветки git, которые вы хотите привить:
(a)-(b)-(c) (d)-(e)-(f)
Теперь мы хотим (d) быть родителем (c). Таким образом, мы создаем замену для (c) с правильным родителем (назовем это c1), затем git replace
(в) с (с1). На этих шагах каждая буква относится к хешу SHA1, представляющему этот коммит.
Чтобы создать новый коммит:
git checkout d
git rm -rf * # remove all files from working direcotry
git checkout c -- . # commit everything from c over top of it
GIT_AUTHOR_DATE="..." GIT_COMMITTER_DATE="..." git commit -m "..." # create replacement commit with date author
Теперь у вас есть коммит (c1), у которого правильный родитель (d). Поэтому все, что нам нужно сделать, это заменить существующий (c) на (c1):
git replace c c1
Теперь ваша история выглядит так:
(a)-(b)-(c1)-(d)-(e)-(f)
Бинго!
РЕДАКТИРОВАТЬ: git replace --graft <commit> [<parent>…]
делает то же самое, что и прививки, и может добавлять или удалять родителей. В документации сказано:
Создайте прививку. Новый коммит создается с тем же содержимым, что и
, за исключением того, что его родители будут [ …] вместо родителей . Затем создается ссылка на замену на вновь созданный коммит.
(Я оставляю старый ответ ниже в качестве ссылки.)
AFAIK, есть один случай использования, который grafts
может справиться, но replace
не может: добавление или удаление родителей. Это мощный инструмент для рефакторинга истории.
Например, если вы импортируете историю из старого SVN-репозитория в Git, информация о слиянии отсутствует. Что вы можете сделать (и я делал это много раз), это прочитать сообщения коммита, чтобы узнать, где было выполнено SVN-слияние, и затем использовать Git-трансплантаты, чтобы добавить родителя в коммит слияния.
IIRC, у меня также было несколько случаев, когда я удалял родителя коммита, чтобы сделать его первым коммитом в истории. Создание чистой истории, основанной на нескольких хаотичных старых хранилищах, иногда требует решительных мер (в моем блоге есть опыт переноса проектов в Git).
Затем, после того как вы очистите всю историю, вы должны сделать git filter-branch
перед публикацией нового репозитория Git.