Как избежать перебазирования ада, когда коммиты слияния мешают?

У меня в git tree есть следующая ситуация:

1 -- 2 -- 3 -- 4 <-- master
      \         \
       5 -- 6 -- 7 -- 8 -- 9 <-- feature

Я хочу перебазировать и раздавить все из функции, чтобы я мог продвинуть мастер с помощью одного коммита добавленной функции.

Поскольку этот коммит 7 уже является слиянием, которое решает все конфликты, я попробовал следующее:

git rebase -i -p master

Единственными вариантами, которые мне здесь предоставили, были коммиты 7, 8 и 9. "Имеет смысл", подумала я, "поскольку слияние уже включает в себя 5 и 6, их можно просто отбросить". Я продолжил раздавливать 7, 8 и 9 в одном коммите, который мы назовем "789". (Я знаю, я творческий тип.)

После этого мое дерево выглядело так:

1 -- 2 -- 3 -- 4 <-- master
                \
                 5 -- 6 -- 789 <-- feature

Наличие 5 и 6 в одной и той же ветке смутило меня, но, опять же, поскольку они уже были включены в 7 (а теперь в 789), я мог просто отказаться от них.

Так что я git rebase -i master снова и на этот раз я отбросил 5 и 6.

Однако конфликты возникали тут и там, поэтому я все прервал.

В данный момент я нахожусь на этом этапе, но моя удаленная ветвь не была обновлена, поэтому я могу также сделать сброс в исходное состояние.

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

2 ответа

Решение

Я хочу перебазировать и раздавить все из функции, чтобы я мог продвинуть мастер с помощью одного коммита добавленной функции.

Это именно то, что --squash вариант для git merge делает. Из документов для git merge:

--сквош

Создайте рабочее дерево и состояние индекса, как если бы произошло реальное слияние (за исключением информации о слиянии), но на самом деле не выполняйте коммит, не перемещайте HEAD и не записывайте $GIT_DIR/MERGE_HEAD, чтобы следующая команда git commit создала слияние совершить. Это позволяет вам создать один коммит поверх текущей ветви, эффект которого такой же, как и слияние другой ветви (или более в случае осьминога).

(Акцент мой)

Пример использования:

git checkout master
git merge feature --squash
git commit

Альтернатива решению Ajedi32

Решение Ajedi32, безусловно, является лучшим и самым простым на данный момент, но я просто хотел показать альтернативу, которая работает примерно так же, для полноты картины. --squash вариант для git merge в основном реплицирует все состояние окончательного коммита ветви, в которую вы сливаетесь, поскольку каждый коммит представляет собой полный снимок 1.

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

git rm -rf -- .
git checkout feature -- .
git add . # Is this necessary?
git commit

# Verify that the current working copy state
# is identical to the feature branch's
git diff feature

1 Еще один способ думать об этом - это сумма всех различий, которые ведут от вершины базовой ветви к вершине объединяемой ветви.

Альтернативное решение 2 (это был мой старый ответ)

Итак, две вещи:

  1. У меня есть решение, которое должно работать на вас.
  2. Объяснение того, почему то, что вы сейчас делаете, не работает, может быть неверным.

Решение? (это на самом деле будет работать?)

Мы перебазируем вашу ветку функций по частям. --preserve-merges флаг git rebase работает лучше всего, когда родители этих коммитов слияния не включают коммиты из другой ветви, которые вы пытаетесь перебазировать поверх, как я уверен, вы уже видели.

Для краткости я опущу некоторые пояснения о том, что такое синтаксис для следующих команд, поскольку вы можете посмотреть это в документации:

  1. Во-первых, создайте резервную ветвь на случай, если что-то пойдет ужасно, ужасно неправильно, и нам в конечном итоге потребуется полный сброс обратно в исходное состояние ваших ветвей функций:

    git branch backup feature
    
  2. Затем мы раздавим 5 и 6, а затем выберем их master (Вы можете перебазировать их на master тоже, но так как мы просто выбираем один коммит, ребаз для этого немного переборщит):

    git checkout -b temp 6
    git reset --soft HEAD^
    git commit -m "Enter your squash commit message here"
    

    Теперь, когда мы разделили 5 и 6 вместе, мы хотим выбрать их master, Так как вы упомянули, что слияние совершить 7 разрешенные конфликты, это означает, что вишня выбирает новый сдавленный коммит (назовем его S) также приведет к конфликтам. Однако, так как вы уже разрешили эти конфликты в 7 мы должны иметь возможность просто скопировать эти резолюции в новый выбранный вишней коммит (хотя я не уверен на 100% в этом):

    git checkout master
    git cherry-pick S
    
    # If no conflicts, then great!
    # Otherwise, completely delete the conflicted working directory tree,
    # and replace it with the directory tree from commit 7 instead:
    git rm -rf -- .
    git checkout 7 -- .
    git add . # Is this necessary?
    git cherry-pick --continue
    
    # When the cherry-pick is done, compare master to 7 and
    # verify that there are no differences
    git diff 7
    
  3. Далее мы перебазируем 8 и 9. Мы пропускаем 7, потому что это просто представляет событие синхронизации master в feature ветвление в точке коммита 6; тем не менее, потому что вишня сбор / перебазирование 5 и 6 на вершине master создает эквивалентное конечное состояние, как при слиянии, при этом фиксация слияния больше не требуется.

    git rebase --onto master 7 feature
    
    # Squash 8 and 9 into S (which should be the tip of master).
    # You might have been able to do this in the above step too,
    # but I don't have time to double-check, so this will also work:
    git reset --soft master
    git commit --amend --no-edit
    
    # Once again, verify that the final result
    # matches that original of the feature branch:
    git diff backup
    

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

git reset --hard backup
git checkout master
git reset --hard 4

Объяснение того, почему то, что вы делали раньше, не сработало, как ожидалось

Итак, полное объяснение займет у меня некоторое время, чтобы написать. Если у меня будет время, я вернусь к этому ответу и выпишу все детали. Однако, прежде чем я уйду, я просто хочу отметить, что:

  • Rebase повторно применяет коммиты как патчи. Когда вы пропускаете коммит из перебазирования, изменения, вносимые в этот коммит, теряются при всех последующих коммитах-потомках (они не сохраняются в потомках).

Обязательные ссылки на документацию

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