Как избежать перебазирования ада, когда коммиты слияния мешают?
У меня в 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 (это был мой старый ответ)
Итак, две вещи:
- У меня есть решение, которое должно работать на вас.
- Объяснение того, почему то, что вы сейчас делаете, не работает, может быть неверным.
Решение? (это на самом деле будет работать?)
Мы перебазируем вашу ветку функций по частям. --preserve-merges
флаг git rebase
работает лучше всего, когда родители этих коммитов слияния не включают коммиты из другой ветви, которые вы пытаетесь перебазировать поверх, как я уверен, вы уже видели.
Для краткости я опущу некоторые пояснения о том, что такое синтаксис для следующих команд, поскольку вы можете посмотреть это в документации:
Во-первых, создайте резервную ветвь на случай, если что-то пойдет ужасно, ужасно неправильно, и нам в конечном итоге потребуется полный сброс обратно в исходное состояние ваших ветвей функций:
git branch backup feature
Затем мы раздавим 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
Далее мы перебазируем 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 повторно применяет коммиты как патчи. Когда вы пропускаете коммит из перебазирования, изменения, вносимые в этот коммит, теряются при всех последующих коммитах-потомках (они не сохраняются в потомках).