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

Рассмотрим следующий сценарий:

  1. Я проверил ветку от мастера
  2. я сделал несколько коммитов
  3. Я слил обновленный мастер
  4. Я сделал еще несколько коммитов
  5. Теперь я хочу перебазировать коммиты из пункта 4, чтобы коммиты из пункта 2 не пострадали.

Итак, если у меня изначально:

     (1)         (2)
x--x--x--x--x--x--x         master
       \     \
        y--y--Y--y--y       dev
          (2)(3)   (4)

Я хочу получить:

     (1)         (2)
x--x--x--x--x--x--x             master
       \           \
        y--y--------Y'--y'--y'  dev
          (2)      (5)     (5)

Если я просто сделаю git rebase master, он перебазирует коммиты как из 2, так и из 4 и удалит слияние из 3. Это не то, что я хочу.

Существует также возможность выполнить git merge master, затем git rebase -i -p перед фиксацией слияния из 3 и переместить последнее слияние после слияния из 3 и выполнить исправление/раздавить его в слияние из 3. Обновление: это не работать так легко. Git отказывается раздавливать два слияния. Эта проблема: git rebaseinteractive: слияние сквоша выполняется вместе.


person Alexey    schedule 24.09.2013    source источник
comment
Мой ответ остается в силе (за исключением того, что я ошибочно называю операцию merge --onto: это rebase --onto). Я обновил ответ, чтобы приблизиться ближе к тому, что вы хотите.   -  person VonC    schedule 25.09.2013
comment
Какое сообщение об ошибке вы получаете?   -  person VonC    schedule 25.09.2013
comment
Отказ от слияния: 987ab25995f24554cc7ce1451919327e09c5a18b   -  person Alexey    schedule 25.09.2013
comment
Ok. Я предполагаю, что reset --soft, упомянутый в stackoverflow.com/q/1725708/6309, может помочь. Хотя не уверен в точной последовательности.   -  person VonC    schedule 25.09.2013


Ответы (3)


Использование моих ответов и ответов VonC сделало более автоматизированное решение:

git checkout -b tmp Y
git merge master 
git reset --soft HEAD^^ 
git rev-parse master > .git/MERGE_HEAD 
git commit -C Y
git checkout -
git rebase --onto tmp Y
git branch -d tmp

Y - расширяемый коммит слияния.

И это работает так:

x--x--x--x--x--x--x         master
       \     \
        y--y--Y--y--y       dev

x--x--x--x--x--x--x         master
      |      \
       \      Y             tmp
        \    / \
         y--y   y--y        dev

x--x--x--x--x--x--x         master
      |      \     \
       \      Y-----Y'      tmp
        \    / \
         y--y   y--y        dev

x--x--x--x--x--x--x         master
      |            \
      |      -------Y'      tmp
       \    /
        y--y--Y--y--y       dev

x--x--x--x--x--x--x         master
      |            \
      |      -------Y'      tmp
       \    /        \
        y--y          y--y  dev

x--x--x--x--x--x--x           master
       \           \
        y--y--------Y'--y--y  dev
person Alexey    schedule 25.09.2013

Вы начинаете с:

     (1)
x--x--x--x--x--x--x         master
       \     \
        y--y--Y--y--y       dev
          (2)(3)   (4)

Выполните git rebase --onto:

git branch dev1 Y^
git rebase --onto master Y^ dev

Y^ ссылается на первого родителя коммита слияния Y: здесь 'y', а не 'x'.
См. "Ссылки на предков":

Первый родитель — это ветка, в которой вы находились при слиянии, а второй — фиксация ветки, в которую вы влились.

В итоге вы получите:

     (1)
x--x--x--x--x--x--x             master
       \           \
        y--y        Y'--y'--y'  dev
          (2)      (5)
        (dev1)

Это разделит вашу начальную ветку dev на две части и применит только последние dev коммитов поверх master, сохранив при этом первые dev коммиты без изменений, на которые теперь ссылается ветвь dev1.

Вы можете попробовать:

git rebase -p --onto master `Y^` dev

чтобы увидеть, сохранит ли это связь между y (то есть Y^) и недавно перебазированным Y'. Но я сомневаюсь, что это возможно.

-p для --preserve-merge.

person VonC    schedule 24.09.2013

В итоге я делаю следующее:

git rebase -i -p Y^

В файл добавить следующую строку за строкой с слиянием (предположительно после первой строки):

exec sh -c "git merge master; git reset --soft HEAD^^; git rev-parse master > .git/MERGE_HEAD; git commit -C `git rev-parse HEAD`"
person Alexey    schedule 25.09.2013
comment
Великолепно звучат. Точнее, чем мой ответ. +1 - person VonC; 26.09.2013