Перебазирование комита Git Merge

183

Возьмите следующий случай:

У меня есть работа в ветке тем, и теперь я готов вернуться к мастеру:

* eb3b733 3     [master] [origin/master]
| * b62cae6 2   [topic]
|/  
* 38abeae 1

Я выполняю слияние с мастером, разрешаю конфликты и теперь у меня есть:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | eb3b733 3                     [origin/master]
|/  
* 38abeae 1

Теперь слияние заняло у меня некоторое время, поэтому я сделал еще одну выборку и заметил, что в удаленной главной ветке появились новые изменения:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
| | * e7affba 4                   [origin/master]
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Если я пытаюсь «git rebase origin / master» от master, я вынужден снова разрешать все конфликты и также теряю коммит слияния:

* d4de423 2       [master]
* e7affba 4       [origin/master]
* eb3b733 3
| * b62cae6 2     [topic]
|/  
* 38abeae 1

Есть ли чистый способ перебазировать коммит слияния, чтобы я получил историю, подобную той, что я показываю ниже?

*   51984c7 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | e7affba 4                     [origin/master]
* | eb3b733 3
|/  
* 38abeae 1
jipumarino
источник
74
TL; DR:git rebase --preserve-merges origin/master
Илья К.
6
Что касается необходимости повторного разрешения конфликтов, вы можете взглянуть на git rerere .
Паркер Коутс
git config --global pull.rebase preserve всегда сохранять коммиты слияния во время
ребазинга
4
Предупреждение: начиная с Git 2.18 (Q2 2018, 5 лет спустя), в git --rebase-mergesконечном итоге заменит старый git --preserve-merges. Посмотрите, что именно Git's « rebase --preserve-merges» делает (и почему?)
VonC
1
--preserve-mergesустарела. Использованиеgit rebase --rebase-merges origin/master
Арджун

Ответы:

127

Здесь есть два варианта.

Одним из них является интерактивная перебазировка и редактирование коммита слияния, повторное слияние вручную и продолжение перебазирования.

Другим вариантом является использование --rebase-mergesопции on git rebase, которая описана в руководстве следующим образом: «По умолчанию, rebase просто удалит коммиты слияния из списка задач и поместит перебазированные коммиты в одну линейную ветвь. С --rebase- слияния, вместо этого ребаз будет пытаться сохранить структуру ветвления внутри коммитов, которые должны быть перебазированы, путем воссоздания коммитов слияния. Любые разрешенные конфликты слияния или ручные поправки в этих коммитах слияния должны быть разрешены / повторно применены вручную. "

siride
источник
16
Я попробовал параметр -p, и он действительно оставляет историю коммитов так, как я хотел, но это вынуждает меня снова разрешать конфликты, даже в файлах, которые не были отредактированы в origin / master. Что касается вашего первого предложения, какой будет точная последовательность команд?
jipumarino
2
@jipumarino: git rebase -i (скажите, чтобы он редактировал коммит слияния), когда он добирается до коммита слияния, git reset --hard HEAD ^, git merge, исправление конфликтов, git commit, git rebase --continue. Возможно, вы также захотите взглянуть на git rerere, который должен помочь с такими вещами (но я никогда не использовал, поэтому я не могу предложить какой-либо совет или помощь).
Сирида
2
Спасибо. Я включил rerere и попробовал с rebase -p, и он работает как надо.
Джипумарино
3
Вот отличное сообщение в блоге, описывающее эту точную ситуацию:
Перебазирование слияний
1
rere не является решением, так как вам все равно придется разрешать слияния вручную в первый раз.
Flimm
29

Хорошо, это старый вопрос, и он уже принял ответ @siride, но в моем случае этого ответа было недостаточно, поскольку --preserve-mergesвы заставляете разрешать все конфликты во второй раз. Мое решение основано на идее, @Tobi Bно с точными пошаговыми командами

Итак, мы начнем с такого состояния на основе примера в вопросе:

*   8101fe3 Merge branch 'topic'  [HEAD -> master]
|\  
| * b62cae6 2                     [topic]
| |
| | * f5a7ca8 5                   [origin/master]
| | * e7affba 4
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Обратите внимание, что у нас есть 2 коммитов впереди, мастер, поэтому вишневый пик не сработает.

  1. Прежде всего, давайте создадим правильную историю, которую мы хотим:

    git checkout -b correct-history # create new branch to save master for future
    git rebase --strategy=ours --preserve-merges origin/master
    

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

    История будет выглядеть так (без учета мастера):

    *   51984c7 Merge branch 'topic'  [HEAD -> correct-history]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
  2. Давайте получим правильный индекс сейчас.

    git checkout master # return to our master branch
    git merge origin/master # merge origin/master on top of our master
    

    Мы можем получить некоторые дополнительные конфликты слияния здесь, но это будет только конфликты из файлов, измененных между 8101fe3и f5a7ca8, но не включает уже разрешенные конфликты изtopic

    История будет выглядеть так (без учета правильной истории):

    *   94f1484 Merge branch 'origin/master'  [HEAD -> master]
    |\  
    * | f5a7ca8 5                   [origin/master]
    * | e7affba 4
    | *   8101fe3 Merge branch 'topic'
    | |\  
    | | * b62cae6 2                     [topic]
    |/ /
    * / eb3b733 3
    |/  
    * 38abeae 1
    
  3. Последний этап - объединить нашу ветку с правильной историей и ветку с правильным индексом.

    git reset --soft correct-history
    git commit --amend
    

    Мы используем, reset --softчтобы сбросить нашу ветку (и историю), чтобы исправить историю, но оставить индекс и рабочее дерево как есть. Затем мы используем, commit --amendчтобы переписать наш коммит слияния, который раньше имел неправильный индекс, с нашим хорошим индексом от master.

    В конце у нас будет такое состояние (обратите внимание на другой идентификатор top commit):

    *   13e6d03 Merge branch 'topic'  [HEAD -> master]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
Иван Найдонов
источник
Это здорово и очень помогло! Но я не понял, с коммитом --amend: не могли бы вы добавить больше информации об этом? Что именно происходит после того, как вы запустите его - я заметил, что изменился SHA коммита - но почему? Или что произойдет, если вы не запустите это?
ZenJ
1
@ZenJ git commit --amendдобавляет изменения в последний коммит (HEAD, в данном случае коммит слияния). Поскольку содержимое фиксации изменяется, хэш обновляется.
Дрис Сталенс
1
Для людей, у которых не было включено «rerere» до устранения конфликтов, это решение отлично подходит, потому что оно избавляет вас от необходимости исправлять конфликты снова. Спасибо!
Шеклфорд
6

Учитывая, что я просто потерял день, пытаясь выяснить это, и на самом деле нашел решение с помощью коллеги, я подумал, что должен вмешаться.

У нас большая база кода, и нам приходится иметь дело с двумя ветвями, которые сильно изменяются одновременно. Существует основной филиал и дополнительный филиал, если вы, который.

Пока я объединяю вторичную ветвь с основной ветвью, работа в основной ветке продолжается, и к тому времени, когда я это сделаю, я не могу отложить свои изменения, потому что они несовместимы.

Поэтому мне нужно «перебазировать» мое «слияние».

Вот как мы наконец сделали это:

1) запишите SHA. Пример: c4a924d458ea0629c0d694f1b9e9576a3ecf506b

git log -1

2) Создайте правильную историю, но это нарушит слияние.

git rebase -s ours --preserve-merges origin/master

3) запишите SHA. пример: 29dd8101d78

git log -1

4) Теперь сбросьте, где вы были раньше

git reset c4a924d458ea0629c0d694f1b9e9576a3ecf506b --hard

5) Теперь объедините текущий мастер в вашу рабочую ветку

git merge origin/master
git mergetool
git commit -m"correct files

6) Теперь, когда у вас есть правильные файлы, но неправильная история, добавьте правильную историю поверх ваших изменений с помощью:

git reset 29dd8101d78 --soft

7) А затем - изменить результаты в исходном коммите слияния

git commit --amend

Вуаля!

Клод Пелокин
источник
1

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

git checkout master      # Let's make sure we are on master branch
git reset --hard master~ # Let's get back to master before the merge
git pull                 # or git merge remote/master
git merge topic

Это даст вам то, что вы хотите.

Антуан Пелисс
источник
4
При включенном rerere это, по-видимому, дает тот же результат, что и решение rebase -p, приведенное выше siride.
jipumarino
0
  • От вашего слияния совершить
  • Cherry-выбрать новое изменение, которое должно быть легко
  • копируй свои вещи
  • повторить слияние и разрешить конфликты, просто скопировав файлы из локальной копии;)
Тоби Б
источник
1
Этот ответ выглядит хорошо, но он был бы более полезен, если бы вы дали реальные команды git, если пользователь новичок в git
Луиза Дэвис