Перебазирования филиала после гитхаб "сквош и объединения" на мастер


Предположим, я разработал функцию на branch1 и отправил ее на проверку кода с помощью запроса на вытягивание GitHub. Пока он пересматривается, я делаю некоторые последующие работы над branch2.

 branch2                   -> D --> E --> F
                          /    
 branch1  -> A --> B --> C
         /
 master M
Мой рецензент любит мою работу! Никаких изменений не требуется. Я объединяю запрос pull для branch1 с помощью функции GitHub Squash и merge.

После запуска git pull на master и удаления branch1, я остаюсь с этой ситуацией:

 branch2  -> A --> B --> C -> D --> E --> F
         /
 master M --> S

Чтобы отправить чистый пиар для branch2, я бы например, чтобы мое дерево фиксации выглядело так:

 branch2        -> D' --> E' --> F'
               /
 master M --> S

Код в S (фиксация, сгенерированная "сквошем и слиянием" для branch1) идентичен C, так как это просто раздавленная версия A --> B --> C.

Один из способов достичь этого-запустить последовательность, подобную этой, на branch2:

git reset --hard S
git cherry-pick D E F
Но перечисление всех коммитов таким образом становится утомительным, и это действительно похоже на перебазирование. git rebase master не будет работать, конечно, так как совершает A, B и C нужно исчезнуть.

Как лучше всего перебазировать ветвь от раздавленной версии одного из ее предковых коммитов?

1 3

1 ответ:

Используйте git rebase с --onto. Это все еще немного сложно, поэтому, чтобы сделать его легким, вы захотите сделать одну вещь по-другому, раньше.

Я думаю, что лучше, кстати, нарисовать эти графики с именами ветвей в правой части , указывая на один конкретный коммит. Это происходит потому, что в Git коммиты находятся на нескольких ветвях, а имена ветвей действительно делают просто указывают на один конкретный коммит. Также стоит обратить вспять внутренние стрелки (потому что Git действительно хранит их таким образом) или просто используя соединительные линии, чтобы не подразумевать неправильное направление.

Отсюда:

          D--E--F   <-- branch2
         /    
  A--B--C       <-- branch1
 /
M          <-- master

Коммиты A через C действительно находятся на обоих branch1 и еще branch2, в то время как коммиты D через F являются только на branch2. (Коммиты M и ранее находятся на всех трех ветвях.)

Что git rebase upstream делает, это выбирает все1 коммиты достижимы из текущей ветви, но недостижимы из текущей ветви. upstream затем скопируйте их (с git cherry-pick или эквивалентом) так, чтобы они шли сразу после аргумента.upstream совершить.

После сквоша - "слияние" (не совсем слияние), если вы запустите git fetch, а затем перемотаете вперед ваш master, у вас будет то же самое, что вы нарисовали, но я оставляю branch1 и ставлю метки слева и добавляю origin/master здесь:

          D--E--F   <-- branch2
         /    
  A--B--C       <-- branch1
 /
M--S       <-- master, origin/master

(или, если вы еще не перемотали свой master, только origin/master указывает на фиксацию S).

Теперь вы хотите рассказать Git чтобы скопировать D-E-F с помощью cherry-pick, затем переместите метку branch2, чтобы указать на последний скопированный коммит. Вы не хотите копировать A-B-C, поскольку они включены в S. Вы хотите, чтобы копии шли после S, на что origin/master теперь указывает-обновили вы master или нет. Отсюда:

git checkout branch2
git rebase --onto origin/master branch1

upstream Теперь branch1 вместо master, но --onto говорит Git, где разместить копии: branch1 служит только для разграничения того, что не копировать . Так что теперь git копирует D-E-F и изменяет branch2, чтобы указать туда:

          D--E--F   [abandoned]
         /    
  A--B--C       <-- branch1
 /
M--S       <-- master?, origin/master
    \
     D'-E'-F'   <-- branch2

И теперь Вы можете удалить имя branch1. (И теперь вы можете перемотать вперед master, Если вы этого еще не сделали-на самом деле не имеет значения, когда вы это сделаете, и на самом деле вам вообще не нужна ваша собственная master.)


1точнее, rebase выбирает коммиты, которые (а) не являются коммитами слияния и (б) не имеют того же git patch-id, что и некоторые коммиты в исключенном множестве, используя симметричную разницу. То есть, скорее, чем upstream..HEAD, Git фактически запускает git rev-list на upstream...HEAD, с --cherry-mark или подобным, чтобы выбрать коммиты. Реализации немного различаются в зависимости от конкретного вида ребаза.