Что использует git rebase для определения общего предка?

Я знаю, что я могу использовать git merge-base определить общего предка при выполнении git merge, но, похоже, это не так для git rebase

Вот мои настройки до rebase:
master branch: A--Y--(C)
а также dev branch: A-----C--D
(C) результат того, что я перебросил A- C на A- Y, то же содержание, но другое сообщение о коммите

git merge-base master dev вернет А, и если я сделаю git merge devЯ увижу оба (С) и С в моей истории

git rebase master, результат: A- Y- (C) -(D), где (D) - D после перебазирования

Есть ли git rebase Считаете (С) общим предком? (Это довольно сложно сделать в коде) Я предполагаю, что он все еще использует A, но когда он выбирает C,D, чтобы добавить в конец master, C закончил как неоперативный?

1 ответ

Давайте начнем с этого: что git rebase делает, чтобы скопировать некоторые коммиты. В вашем случае, кажется, копировать коммит C но не совершать D, (Я думаю, что вы спрашиваете почему, но предполагаете, что это как-то связано с базой слияния, и это, вероятно, не правильно.)

Набор коммитов, который git rebase выбор для копирования в значительной степени, но не полностью, определяется результатом:

git rev-list <upstream>..HEAD

где <upstream> аргумент, который вы передаете git rebase или настроенный апстрим. Например, с git rebase master, <upstream> в этой последовательности master, Ваш текущий филиал dev, так HEAD относится к dev, Таким образом, набор коммитов для копирования указан так:

git rev-list master..dev

(хотя добавлены дополнительные опции; см. ниже).

Если я читаю ваш график правильно, входной график:

...--A--Y   <-- master
      \
       C--D   <-- dev

так что выход этого git rev-list это совершает C а также D,

Затем Rebase продолжает:

  1. git checkout верхушка целевой ветви (коммит Y Вот);
  2. бежать - иногда буквально, иногда в переносном смысле - git cherry-pick по коммитам C а также D; и наконец
  3. бежать checkout -B dev HEAD (не совсем буквально, но тот же эффект: вернуться на dev после переезда dev указать на окончательный скопированный коммит).

Я думаю, что вы спрашиваете непосредственно - что, я думаю, не то, о чем вы должны спрашивать - это то, какой коммит используется в качестве базы слияния во время сбора вишни. Ответ здесь заключается в том, что основа объединения вишневого пика - это родительский коммит этого вишневого пика. Так что для git cherry-pick C шаг, база слияния, если слияние требуется, это коммит A: C родитель. Для git cherry-pick D шаг, база слияния, если слияние требуется, это коммит C: D родитель.

Теперь я перечислил несколько пунктов "дополнительных опций" выше. Они относятся к процессу выбора фиксации. Особенно, git rebase есть два способа выбросить коммиты из списка "скопировать". (Он также генерирует список в порядке, обратном обычному обратному порядку, так что вместо этого он выполняет сбор вишни в прямом порядке.)

Основным способом устранения коммитов из списка копий является использование git patch-id, git patch-id Команда вычисляет идентификационный номер, который, как мы надеемся, является "достаточно уникальным", основываясь на найденном патче, сравнивая коммит с его непосредственным родителем, отбрасывая номера строк и некоторые другие элементы, которые могут повлиять на выбранный вишней коммит. Результатом должен быть тот же идентификатор, если коммит уже был выбран, но другой, если нет.

Следовательно, после перечисления C а также D, Git продолжает сравнивать их идентификаторы патчей с идентификатором патча коммита Y,

Есть две команды, которые делают подобные вещи, одна предназначена для пользователей, а другая - git rev-list, Пользовательская команда git cherry, но я думаю, что здесь, ориентированный на компьютер git rev-list на самом деле гораздо проще объяснить.

Интересно, что Git знает, как сравнивать все идентификаторы патчей коммитов C а также D к ID патча коммита Y, когда начальный граф тот, который мы показали выше. Или, если бы график был:

...--A--E--F--G   <-- master
      \
       C--D   <-- dev

два набора идентификаторов патчей для сравнения будут такими же для (C, D) и для (E, F, G). Если бы мы имели:

          H--I
         /    \
...--A--G---J--K   <-- master
      \
       \   D
        \ / \
         C   F   <-- dev
          \ /
           E

наборы для сравнения будут (C, D, E, F) против (G, H, I, J, K). И это, я думаю, делает всю концепцию намного более ясной: git rev-list может исследовать обе ветви до точки, где они встречаются, и собирать набор коммитов для каждой "стороны".

Как мы получаем git rev-list сделать это:

git rev-list --left-right master...dev

(обратите внимание на три точки здесь и порядок dev а также master имеет значение только для определения того, какие коммиты являются "левой стороной" - на master но не на dev И которые "правая сторона", на dev но не на master).

Это обозначение из трех точек, наряду с этим --left-right Идея в том, как Git выясняет, какие коммиты помещать в какой набор, чтобы вычислить полный набор идентификаторов патчей. Если идентификатор исправления левого коммита совпадает с идентификатором исправления правого коммита, эти коммиты, хотя и отличаются в некотором смысле, представляют собой одно и то же изменение: они являются вишнями, которые уже были выбраны вишней.

git rebase команда пропускает эти предварительно собранные вишни. Он только выбирает оставшиеся коммиты, какие бы они ни были. git rebase код работает git rev-list --right-only --cherry-pick --no-merges <upstream>..HEAD чтобы получить список коммитов для копирования на шаге 2. Затем он выполняет эти три шага. Если идентификатор патча либо C или же D соответствует идентификатору патча для коммита Y, этот коммит никогда не копируется вообще. В этом случае, казалось бы, что совершить D ID патча может совпадать с коммитом Y "S.

Я также сказал, что есть два способа устранения коммитов. Это обнаружение вишни является одним из них, и я подозреваю, что именно поэтому вы видите результат, я думаю, вы видите. Другой - это то, что Git называет --fork-point, что немного сложнее. Я не собираюсь освещать это здесь. Он только пропускает "ранние" коммиты, то есть находит точку вдоль C--D chain - это было бы более разумно, если бы цепочка была длиннее - что она думает, что она должна быть отброшена, и после этой точки копирует только коммиты. С момента совершения C копируется, это не может быть код форк-пойнта, вызывающий коммит D чтобы опустить.

Другие вопросы по тегам