Чем отличаются мерзавцы и замены? (Сейчас трансплантаты устарели?)

На 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. ГИТ-фильтр-ветвь (1),
  2. v1.5.4.7 / GIT-фильтр-ветвь (1),
  3. 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.

Другие вопросы по тегам