Команда, которую вы ищете, это git rebase
, в частности опция -i/--interactive
.
Я собираюсь предположить, что вы хотите оставить коммит c в ветви A, и вы действительно хотите, чтобы вы переместили другие коммиты в другие ветви, а не слияния, поскольку слияния выполняются просто. Давайте начнем с манипулирования ветвью А.
git rebase -i <SHA1 of commit a>^ branchA
^
означает предыдущий коммит, поэтому эта команда говорит, чтобы перебазировать ветвь A, используя коммит перед "a" в качестве основы. Git представит вам список коммитов в этом диапазоне. Измените их порядок и скажите git раздавить соответствующие:
pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f
Теперь история должна выглядеть так:
c - [a+d+e+g] - [b+f] (branchA)
/
--o-x-x-x-x-x-x-x-x-x-x (master)
Теперь давайте возьмем недавно сжатый коммит b + f для ветви B.
git checkout branchB
git cherry-pick branchA # cherry-pick one commit, the tip of branchA
И то же самое для a + d + e + g для мастера:
git checkout master
git cherry-pick branchA^
Наконец, обновите ветку A, чтобы она указала на c:
git branch -f branchA branchA^^
Теперь у нас должно быть:
c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
/
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
\
x-x-x-x-x-[b+f] (branchB)
Обратите внимание, что если у вас было несколько коммитов, которые вы хотите перемещать между ветвями, вы могли бы снова использовать rebase (не в интерактивном режиме):
# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB
Наконец, отказ от ответственности: вполне возможно изменить порядок коммитов таким образом, что некоторые из них больше не будут применяться корректно. Это может быть связано с тем, что вы выбрали неправильный порядок (установка патча перед коммитом, представляющего исправленную функцию); в этом случае вы захотите прервать ребаз (git rebase --abort
). В противном случае вам придется разумно исправлять конфликты (так же, как вы делаете это с конфликтами слияний), добавлять исправления, а затем запускать git rebase --continue
, чтобы двигаться дальше. Эти инструкции также предоставляются сообщением об ошибке, которое выводится при возникновении конфликта.