TL; DR
Используйте git cherry-pick
и (позже), git reset
или git branch -f
, в зависимости от ситуации.
Long
Потерпи немного, пока я проверяю вещи, которые ты уже знаешь, так как они сделают ответ намного яснее.
Rebase работает путем копирования коммитов. То есть, учитывая граф фиксации вида:
...--o--*--I <-- mainline
\
F--G--H <-- branch (HEAD)
мы часто хотим, чтобы последовательность фиксации F-G-H
шла после commit I
в главной строке. Для этого мы запускаем git checkout branch && git rebase mainline
, который копирует фиксирует F-G-H
для новых и улучшенных. Затем, сделав копии, он перемещает имя текущей ветви , чтобы указать на последнюю такую копию:
F'-G'-H' <-- branch (HEAD)
/
...--o--*--I <-- mainline
\
F--G--H [abandoned, sort of]
Как вы уже знаете, мы можем отделить набор коммитов, которые должны быть скопированы от точки, в которой они должны приземлиться, используя git rebase --onto
. Например, вместо того, чтобы копировать все F-G-H
, мы могли бы оставить позади F
, копируя только G
и H
:
G'-H' <-- branch (HEAD)
/
...--o--*--I <-- mainline
\
F <-- ???
\
G--H [abandoned, sort of]
Когда мы делаем это, хорошей идеей будет добавить какое-либо имя к коммиту (-ам), которые мы оставляем за до , когда Git переместит имя branch
. Мы можем сделать это, используя git branch
, например, или git checkout -B
, или что-то еще. Ключ, однако, заключается в том, что мы используем --onto
для выбора коммита I
- целевого коммита, после которого будут идти копии - и другого аргумента для ограничения того, какие коммиты копируются. Коммиты, которые копируются , - это те, которые после аргумента, который мы даем, вплоть до аргумента в HEAD
ветви.
Аргументы для этого --onto
случая выглядят так:
git rebase --onto $target $stop
, где $stop
, например, коммит F
.
Как внутренне работает rebase
Способ, которым фактически выполняется rebase, довольно прост:
Запоминает текущую ветвь (или для отдельного HEAD, ID хэша): saved_branch=$(git symbolic-ref -q --short HEAD)
, более или менее.
В нем перечислены хэш-идентификаторы коммитов для копирования. Он использует git rev-list
с множеством причудливых опций для этого, чтобы обрабатывать все сложные случаи, но для наших простых случаев мы можем просто использовать $stop..HEAD
.
Он работает git checkout --detach $onto
(или что-то более или менее эквивалентное).
работает git cherry-pick <saved list>
, т.е. git cherry-pick $stop..$saved_branch
.
Как только все вишневые пики завершены, включая любые остановки для разрешения конфликтов, обработанные человеком, он запускает git branch -f $saved_branch HEAD
(или что-то более или менее эквивалентное), чтобы имя сохраненной ветви указывало на последнее скопированное совершить. (Есть также немного дурака со специальным названием ORIG_HEAD
.)
Что вы хотите
В вашем случае, вы уже на --onto
части. То есть шаг 1 не имеет значения, а шаг 2 не может происходить совершенно одинаково. Шаг 3 не должен происходить вообще - мы хотим, чтобы новые коммиты увеличивали текущую ветку!
Это оставляет нам только шаги 4 и 5 для достижения.
Шаг 4 особенно прост в любом современном Git, где git cherry-pick
может обрабатывать последовательность: мы просто запускаем:
git cherry-pick $stop..$branch
где $stop
- фиксация остановки (фиксация, которую мы не хотим скопировать), а $branch
- ветка, которая указывает на последний фиксацию, которую мы делаем хочу скопировать. Если мы сейчас находимся на mainline
, это git cherry-pick <hash-of-F>..branch
или git cherry-pick branch~2..branch
, для копирования коммитов G
и H
.
Чтобы выполнить шаг 5 - заставить $branch
указать назад на фиксацию F
- мы оставим это на потом, когда наши копии будут готовы. Когда эти копии сделаны , мы можем запустить:
git checkout $branch && git reset --hard $stop
или
git branch -f $branch $stop
пока мы находимся mainline
.