Если в прошлом у меня был коммит, который указывает на одного из родителей, но я хочу сменить родителя, на которого он указывает, как мне поступить?
Использование git rebase
. Это общая команда «принять коммиты и добавить их / их к другому родителю (базе)» в Git.
Однако некоторые вещи нужно знать:
Поскольку в SHA для фиксации участвуют их родители, при смене родителя для данного коммита его SHA изменится, равно как и SHA для всех коммитов, которые последуют за ним (более поздним, чем он) в линии разработки.
Если вы работаете с другими людьми и уже опубликовали коммит в том месте, где его вытащили, изменение коммитов, вероятно, является Плохой идеей ™. Это происходит из-за # 1, и, таким образом, возникает путаница, с которой сталкиваются репозитории других пользователей, когда пытаются выяснить, что произошло из-за того, что ваши SHA больше не соответствуют их для "тех же" коммитов. (Подробности см. В разделе «ВОССТАНОВЛЕНИЕ ОТ UPSTREAM REBASE» на связанной странице man.)
Тем не менее, если вы в настоящее время находитесь на ветке с некоторыми коммитами, которые вы хотите переместить к новому родителю, это будет выглядеть примерно так:
git rebase --onto <new-parent> <old-parent>
Это переместит все после <old-parent>
текущей ветки <new-parent>
вместо того, чтобы сидеть сверху .
--onto
используется для! Документация всегда была мне совершенно неясна, даже после прочтения этого ответа!git rebase --onto <new-parent> <old-parent>
Скажи мне все о том, как использоватьrebase --onto
все остальные вопросы и документы, которые я до сих пор читал».rebase this commit on that one
илиgit rebase --on <new-parent> <commit>
. Использование старого родителя здесь не имеет смысла для меня.git rebase <new-parent>
.Если окажется, что вам нужно избегать перебазирования последующих коммитов (например, потому что переписывание истории было бы несостоятельным), тогда вы можете использовать git replace (доступно в Git 1.6.5 и более поздних версиях).
С установленной выше заменой любые запросы к объекту B фактически возвращают объект C. Содержимое C в точности совпадает с содержимым B, за исключением первого родителя (те же самые родители (кроме первого), того же дерева, то же самое сообщение коммита).
Замены активны по умолчанию, но их можно отключить, используя
--no-replace-objects
опцию git (перед именем команды) или установивGIT_NO_REPLACE_OBJECTS
переменную среды. Замены могут быть разделены нажатиемrefs/replace/*
(в дополнение к обычномуrefs/heads/*
).Если вам не нравится фиксация (сделанная с помощью sed выше), вы можете создать свой коммит с помощью команд более высокого уровня:
Большая разница в том, что эта последовательность не распространяется на дополнительных родителей, если B является коммитом слияния.
источник
refs/replace/
иерархии ссылок. Если вам просто нужно сделать это один раз, то вы можете сделать этоgit push yourremote 'refs/replace/*'
в исходном хранилище иgit fetch yourremote 'refs/replace/*:refs/replace/*'
в целевом хранилище. Если вам нужно сделать это несколько раз, вы можете вместо этого добавить эти refspecs вremote.yourremote.push
переменную config (в исходном хранилище) иremote.yourremote.fetch
переменную config (в целевых хранилищах).git replace --grafts
. Не уверен, когда это было добавлено, но его цель - создать замену, аналогичную коммиту B, но с указанными родителями.Обратите внимание, что изменение коммита в Git требует, чтобы все коммиты, которые следуют за ним, также должны быть изменены. Это не рекомендуется, если вы опубликовали эту часть истории, и кто-то мог построить свою работу на истории, какой она была до изменений.
Альтернативное решение,
git rebase
упомянутое в ответе Амбер, заключается в использовании механизма пересадки (см. Определение прививки Git в глоссарии Git и документации по.git/info/grafts
файлу в документации по макету репозитория Git ), чтобы изменить родительский элемент коммита, проверить, что он исправился с помощью некоторого средства просмотра истории (gitk
,git log --graph
и т. д.), а затем используйтеgit filter-branch
(как описано в разделе «Примеры» на его man-странице), чтобы сделать его постоянным (а затем удалите трансплантат и, при необходимости, удалите исходные ссылки, сохраненные вgit filter-branch
резервном хранилище, или отмените репозиторий):НОТА !!! Это решение отличается от решения по перебазированию тем,
git rebase
что перебазирует / пересаживает изменения , в то время как решение на основе трансплантатов просто перечитывает коммиты как есть , не принимая во внимание различия между старым родителем и новым родителем!источник
git replace
произошла замена git-трансплантатов (при условии, что у вас есть git 1.6.5 или более поздняя версия).git-replace
+git-filter-branch
? В моем тесте,filter-branch
казалось, уважал замену, перебирая все дерево.git filter-branch
переписывает историю, уважая трансплантаты и замены (но в переписанной истории замены будут спорными); толчок приведет к изменению не вперед-назад.git replace
создает на месте переносимые замены; push будет ускоренной перемоткой вперед, но вам нужно будет нажать,refs/replace
чтобы перенести замены, чтобы исправить историю. HTHЧтобы уточнить ответы выше и бесстыдно подключить свой собственный скрипт:
Это зависит от того, хотите ли вы «перебазировать» или «переучить». Перебазироваться , как это было предложено Амбер , движется вокруг посмотреть различия . Reparent , как это было предложено Якуба и Крис , движется вокруг снимков из целого дерева. Если вы хотите переписать, я предлагаю использовать
git reparent
вместо того, чтобы делать работу вручную.сравнение
Предположим, у вас есть изображение слева, и вы хотите, чтобы оно выглядело как изображение справа:
Как перебазирование, так и повторное воспитание дают одинаковую картину, но определение
C'
отличается. Сgit rebase --onto A B
,C'
не будет содержать никаких изменений, внесенныхB
. Сgit reparent -p A
,C'
будет идентичнымC
(за исключением того, чтоB
не будет в истории).источник
reparent
сценарием; это прекрасно работает; настоятельно рекомендуется . Я также рекомендовал бы создать новуюbranch
метку и дляcheckout
этой ветви перед вызовом (как метка ветви будет перемещаться).Ответ @ Jakub определенно помог мне несколько часов назад, когда я пытался сделать то же самое, что и OP.
Тем не менее,
git replace --graft
теперь это более простое решение относительно трансплантатов. Кроме того, одной из основных проблем этого решения было то, что ветвь фильтра заставляла меня терять каждую ветку, которая не была объединена с веткой HEAD. Затемgit filter-repo
выполнил работу отлично и без нареканий.Для получения дополнительной информации: ознакомьтесь с разделом «Пересадка истории» в документации.
источник