Как git трансплантаты и замена отличаются? (Сейчас ли прививки теперь устарели?)
Есть очень мало Q & A на git grafts
по сравнению с replace
. Поиск [git] + grafts + replace только нашел два, которые считались релевантными 5. what-are-git-info-grafts-for и git-what-is-a-graftcommit-or-a-graft-id. Также есть примечание к git.wiki.kernel.org: GraftPoint
Являются ли теперь трансплантаты полностью обгоняемыми replace
и filter-branch
, или они все еще необходимы для некоторых специальных угловых случаев (и обратной совместимости)?
В целом, как они различаются (например, которые транспортируются между репозиториями), и как они в целом одинаковы?
Я видел, что Linus, похоже, не заботится о трансплантатах в настоящее время в обсуждении чисел генерации фиксации (от максимального числа родителей до любого корневого сорта) "Прививки уже ненадежны" .
EDIT: больше информации найдено.
Поиск в www.kernel.org/pub/software/scm/ git/docs для graft
нашел только 3 результата:
- git -filter-branch (1),
- v1.5.4.7/ git -filter-ветвь (1),
- v1.5.0.7/ git -svn (1).
Несколько более широкий поиск найден RelNotes/1.6.5.txt, который содержит:
- refs/replace/hierarchy предназначена для использования в качестве замены механизма "трансплантатов", с дополнительным преимуществом, что это может быть передается через репозитории.
К сожалению, gitrepository-layout (5) еще не обновлен с информацией о компоновке refs/replace/repository (и примечаниями), а также никакое примечание об инфо/трансплантации.
Это приближается к поддержке того, о чем я думал, но я бы приветствовал любое подтверждение или разъяснение.
Ответы
Ответ 1
В то же обсуждение о Commit Generation Number, о котором вы упоминаете, Jakub Narębski подтверждает, что трансплантаты являются более привлекательными, чем решение:
трансплантаты настолько ужасны, что я не буду против выключения чисел поколений, если они будут использоваться.
В случае замены объектов вам нужны как замененные, так и замененные Числа генерации DAG.
[...] Прививки непередаваемы, и если вы используете их для отбраковки, а не для добавления истории они небезопасны против сбора мусора... Я думаю.
(публикация всегда позаботилась git filter-branch
, как показано на рисунке поток 2008 года на рабочем процессе трансплантатов.)
Разница между трансплантатами и git заменить лучше всего иллюстрируется этим вопросом SO "Установка родительского указателя git для другого родителя" и комментариев (Якуб снова) ответ.
Он включает ссылку на Git1.6.5
Из того, что я понимаю (из GraftPoints), git replace
заменил git grafts
(если у вас есть git 1.6.5 или позже)
(Jakub:)
- если вы хотите переписать историю, тогда
grafts
+ git-filter-branch
(или интерактивная переадресация, или быстрый экспорт +, например, reposurgeon) - это способ сделать это. - Если вам нужно/нужно сохранить историю, то
git-replace
намного превосходит трансплантат
Ответ 2
Если вам нужно переписать родительский фиксатор с помощью git replace
, вот как это сделать.
Как отметил Филипп Оукли, git replace просто заменяет одно commit другим. Чтобы трансформировать родителя в существующую фиксацию, вам нужно сначала создать фальшивый коммит с правильным родителем.
Скажем, у вас есть две ветки git, которые вы хотите пересадить:
(a)-(b)-(c) (d)-(e)-(f)
Теперь мы хотим (d) быть родителем (c). Таким образом, мы создаем замену для (c) с правильным родителем (позвольте этому c1), затем git replace
(c) с (c1). На этих этапах каждое из букв ссылается на хэш SHA1, представляющий это commit.
Чтобы создать новый коммит:
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
Теперь у вас есть commit (c1), у которого есть правильный родитель (d). Итак, все, что нам нужно сделать, это заменить существующий (c) на (c1):
git replace c c1
Теперь ваша история выглядит так:
(a)-(b)-(c1)-(d)-(e)-(f)
Бинго!
Ответ 3
EDIT: git replace --graft <commit> [<parent>...]
выполняет то же самое, что и трансплантаты, и может добавлять или удалять родителей. документация говорит:
Создайте фиксацию трансплантата. Новый коммит создается с тем же содержимым, что и <commit> за исключением того, что его родители будут [<parent> ...] вместо родителей <commit> . Затем создается замена ref для замены вновь созданной фиксацией.
(Я оставляю старый ответ ниже в качестве справки.)
AFAIK, существует один вариант использования grafts
, но replace
не может: добавление или удаление родителей. Это инструмент для рефакторинга.
Например, если вы импортируете историю из старого репозитория SVN в Git, информация о слиянии отсутствует. То, что вы можете делать (и я делал это много раз), - это прочитать сообщения фиксации, чтобы узнать, где было выполнено слияние SVN, а затем использовать Git трансплантаты, чтобы добавить родителя к фиксации слияния.
IIRC, у меня также были случаи, когда я удалил родителя коммита, чтобы сделать его первым фиксатором в истории. Создание чистой истории, основанной на множественных хаотичных хранилищах, иногда требует радикальных мер (есть некоторый опыт миграции проектов к Git в моем блоге).
Затем, после того как вы очистили всю историю, перед публикацией нового репозитория Git вы сделаете git filter-branch
.