Во-первых, +1 на вопрос о графике фиксации. :-) Во-вторых, не очень важно, но вы должны подумать об этом как о двух ветвях: master
и branch1
. Нет ничего особенного в master
, это просто ветка, как и любая другая.
Теперь, в центре всего, что находится в git, это факт, что вы никогда не сможете изменить фиксацию.Это происходит из того, как git на самом деле называет каждый фиксатор, с использованием большого уродливого 40-символьного SHA-1: SHA-1 производится путем вычисления криптографической контрольной суммы содержимого коммита. Поэтому, если вы (попытаетесь) изменить что-либо, изменится «истинное имя», и у вас будет новая и другая фиксация.
Вместо этого вы можете сделать копию старой фиксации на новую, внесение желаемых изменений по пути непосредственно перед завершением коммита (и, следовательно, с учетом его «истинного имени»). Мы сделаем копии.
Вы нарисовали этот график:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
F--G--H--I--J--K--L... branch1
, но это не может быть вполне правы, потому что при каждой фиксации точки родительскому фиксации, и здесь F
точки обратно E
, когда мы смотрим на master
, но F
указывает на D
, если посмотреть на branch1
. (Я предполагаю здесь, что каждая отдельная буква стоит за SHA-1 «истинное имя» данного коммита.) Я собираюсь предположить, что те, которые в настоящее время находятся на branch1
, являются копиями, хотя это не имеет большого значения который, пока вы довольны деревьями, связанными с этими коммитами (то есть, что вы получаете, когда один из них git checkout
).
То, что вы хотите выглядеть следующим образом:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
J'-K'-L'... branch1
Здесь "премьер" в знак J'
, K'
и L'
показывают, что эти копии фиксаций J
, K
и L
.
Пока я писал этот ответ, некоторые из них предложили использовать git cherry-pick
, чтобы сделать эти копии. Это работает очень хорошо, но на самом деле мы можем использовать git rebase
, чтобы сделать трюк, потому что где cherry-pick
- простой топор, который вы можете использовать для вырубки одного вишневого дерева, rebase
- полностью автоматическая цепная пила, которую вы можете использовать, чтобы сделать все это одним махом.
Начнем с того, что сообщило rebase
для работы на branch1
; простой способ заключается в git checkout branch1
(или мы могли бы просто добавить branch1
в качестве последнего аргумента в git rebase
, но я буду использовать «простой способ»).
Далее, нам нужно знать, где мы хотим, чтобы коммиты отделились, т. Е. Имели некоторый способ назвать фиксацию I
. Из вышесказанного мы могли бы сказать master~3
: счетчик назад совершает четыре фиксации от master
(имена которых фиксируют Y
), и вы проходите через X
, затем Z
, затем достигаете I
. Или вы можете сделать это с помощью истинного SHA-1, который всегда работает, но часто требует вырезания и вставки, чтобы получить право. Для приведенной ниже команды я просто напишу I
. Мы укажем rebase на базу --onto
, которая зафиксирует.
Наконец, мы должны получить rebase
, чтобы выбрать коммиты J
, K
и L
для копирования. Есть множество способов сделать это. Возможно, самым простым является использование git rebase -i
, которое предложит переустановить все на branch1
, а затем вы можете удалить строки pick
для первых четырех коммитов.Или, мы можем сказать rebase
, чтобы исключить определенные коммиты, но я предполагаю, что вы будете использовать интерактивный метод.
Таким образом, окончательная команда здесь git rebase -i --onto I master
(после выполнения команды git checkout branch1
). -i
делает это интерактивным, так что вы можете сбросить коммиты; --onto
выбирает цель для новой серии фиксаций, которую rebase
будет вишневым; и часть master
сообщает rebase
, которая обязывает исключать: в частности, любые фиксации достижимы от имени master
. (Это исключает совершает A
, B
, C
и D
, но не скопированные версии F'
, G'
, H'
и I'
, которые появляются на branch
: вы просто удалить «подцепить» линию для тех, кто).
После того, как git rebase
закончил свою команду git cherry-pick
, последнее, что он делает, - это переместить ярлык ветки для вашей текущей ветви (branch1
) на самую новую команду. Таким образом, это дает:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
J'-K'-L'... branch1
при условии, что все идет хорошо. (. Если она идет плохо, вы можете git rebase --abort
, чтобы остановить процесс и поставить все обратно, как это было прежде, чем вы начали)
Мы могли бы быть фантазером и (при условии, I'
это branch1~3
) сделать:
git rebase --onto master~3 branch1~3 branch1
без каких-либо начальных git checkout
, но это предполагает, что отсчет 3 здесь верен (дляи branch1
). Это в основном так же, как и раньше, с тремя модификациями:
- добавить
branch1
на конце, чтобы сделать rebase
проверить его в качестве первого шага
- мы используем
branch1~3
вместо master
в качестве аргумента, который говорит «исключить этот материал, только перебазироваться вещи, которые не достижимы отсюда»
- мы опускаем
-i
, так как нам не нужно редактировать прочь„выбрать“команды на этот раз
Это требует аЬ это более тщательный подсчет коммитов, чтобы убедиться, что все выражения ~
правы (или, опять же, вы можете работать с ними с помощью сырых идентификаторов SHA-1).
Ну, есть несколько специальных вещей о master
: для одной стороны, это отделение вы надеть, когда вы делаете git init
и это создает новое хранилище. Другим является то, что git merge
слегка изменяет сообщение о слиянии. Но оба они довольно тривиальны.
Это действительно справедливо для всех четырех объектов, найденных в репозитории git (commit, tree, аннотированный тег и «blob» - последний хранит содержимое файла). Каждый хранится в репозитории, а затем задается имя, которое состоит из собственной криптографической контрольной суммы.
Более точно, каждый фиксации имеет ноль или более строк parent ...
в ней, с каждым parent
давая SHA-1 идентификатор соответствующего родительского фиксации. Первый родитель, как правило, является «главной линией» ветви, и все дополнительные родители указывают, что это коммит слияния. Конец без родителей, например A
, является «корневой фиксацией».
Почему бы не просто 'git checkout branch1', а' git rebase COMMIT_HASH_FOR_I'? Или просто 'git rebase COMMIT_HASH_FOR_I branch1'? ('COMMIT_HASH_FOR_I' для версии в master). Я думаю, что это немного более прямолинейно, хотя ваша версия является полной, канонической формой. :-) Хороший ответ, кстати. Очень полно. – jszakmeister
@jszakmeister: Я не пробовал, но думаю (предполагая, что исходный граф должен читать 'F'-G'-H'-I '-...' простой 'git rebase' скопируйте 'F'' в' F''' и т. д., и не сможете увидеть, что они уже находятся. Rebase действительно знает, что в некоторых случаях удалять одинаковые коммиты, поэтому может работать. –
torek
Пока diff то же самое, тогда и патч-идентификатор будет, и Git обязательно их убьет. Это становится проблемой только в том случае, если 'F'',' G'' и 'H'' имеют дополнительные модификации из-за конфликта или изменения в некоторых Кстати, FWIW, я сделал это более чем несколько раз. :-) – jszakmeister