Пересадка истории в * текущую * ветку - PullRequest
0 голосов
/ 14 сентября 2018

Я знаю, как использовать git rebase --onto для пересадки истории. Тем не менее, мне часто хочется перенести историю из другой ветви в мою текущую ветвь без промежуточного отсоединенного состояния HEAD.

Другими словами:

git checkout foo
git rebase --onto foo A^ B
# results in a detached HEAD
# how to avoid this last step?
git checkout -B foo

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

@ Торек отлично ответил, но я хотел опубликовать TL; DR; на вопрос, который я специально задал, а именно:

git cherry-pick A..B

Ключевой информацией из ответа @ torek, разумеется, является то, что cherry-pick принимает диапазон.

0 голосов
/ 14 сентября 2018

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, довольно прост:

  1. Запоминает текущую ветвь (или для отдельного HEAD, ID хэша): saved_branch=$(git symbolic-ref -q --short HEAD), более или менее.

  2. В нем перечислены хэш-идентификаторы коммитов для копирования. Он использует git rev-list с множеством причудливых опций для этого, чтобы обрабатывать все сложные случаи, но для наших простых случаев мы можем просто использовать $stop..HEAD.

  3. Он работает git checkout --detach $onto (или что-то более или менее эквивалентное).

  4. работает git cherry-pick <saved list>, т.е. git cherry-pick $stop..$saved_branch.

  5. Как только все вишневые пики завершены, включая любые остановки для разрешения конфликтов, обработанные человеком, он запускает 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...