Восстановление фиксации слияния Git

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

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

* 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 от мастера, мне придется снова разрешить все конфликты, и я также потеряю фиксацию слияния:

* 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

person jipumarino    schedule 24.01.2011    source источник
comment
Что касается повторного разрешения конфликтов, вы можете взглянуть на git rerere .   -  person Parker Coates    schedule 19.02.2016
comment
git config --global pull.rebase preserve, чтобы всегда сохранять коммиты слияния во время перебазирования   -  person galath    schedule 19.01.2018
comment
короче еще git rebase -p origin/master   -  person Felipe Alvarez    schedule 18.08.2018
comment
--preserve-merges устарел. Используйте git rebase --rebase-merges origin/master   -  person Arjun Sreedharan    schedule 11.10.2019


Ответы (5)


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

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

Другой вариант - использовать параметр --rebase-merges на git rebase, который описан в руководстве следующим образом:

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

person siride    schedule 24.01.2011
comment
Я попробовал опцию -p, и она действительно оставляет историю коммитов, как я хотел, но заставляет меня снова разрешать конфликты, даже в файлах, которые не редактировались в origin / master. Что касается вашего первого предложения, какова будет точная последовательность команд? - person jipumarino; 24.01.2011
comment
@jipumarino: git rebase -i (скажите ему отредактировать фиксацию слияния), когда он перейдет к фиксации слияния, git reset --hard HEAD ^, git merge, исправить конфликты, git commit, git rebase --continue. Вы также можете взглянуть на git rerere, который должен помочь с подобными вещами (но я никогда не использовал, поэтому не могу дать никаких советов или помощи). - person siride; 24.01.2011
comment
Спасибо. Я включил rerere и попробовал использовать rebase -p, и он работает как надо. - person jipumarino; 24.01.2011
comment
Вот отличное сообщение в блоге, описывающее именно эту ситуацию: Изменение позиции коммитов слиянием в Git - person kynan; 23.04.2012
comment
rere не является решением, так как вам все равно придется разрешать слияния вручную в первый раз. - person Flimm; 07.01.2014
comment
@Flimm: это часть долгосрочного решения, если вы часто делаете ребазинг. Обратите внимание, что я сказал, что никогда не использовал его (что было правдой в то время), так что простите за мой оптимистичный взгляд. - person siride; 07.01.2014
comment
Когда я делаю git rebase -i (интерактивно), он отфильтровывает все коммиты слияния. Действительно ли git rebase позволяет редактировать или изменять коммиты слияния? Вместо этого у меня сработало следующее решение: stackoverflow.com/a/4138485/1172352 - person peterflynn; 01.04.2016
comment
@peterflynn: вы включили опцию -p? - person siride; 02.04.2016
comment
В справке для git (версия 2.19.2) указано, что --preserve-merges и --interactive - несовместимые параметры. - person Jeff Evans; 28.01.2019
comment
@JeffEvans Похоже, что -p перестала быть ошибкой, была запрещена с -i, а теперь стала устаревшей в пользу --rebase-merges. Я обновлю свой ответ соответственно. - person siride; 11.01.2020
comment
git rebase -i --rebase-merges сделал именно то, что мне нужно. Могу изменить порядок моих собственных коммитов, сохранив существующие коммиты слияния. - person GameSalutes; 11.12.2020

Хорошо, это старый вопрос, и он уже принял ответ от @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, чтобы переписать нашу фиксацию слияния, которая раньше имела неправильный индекс, с нашим хорошим индексом от мастера.

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

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

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

У нас большая кодовая база, и нам приходится иметь дело с двумя ветвями, которые сильно модифицируются одновременно. Есть основная ветка и дополнительная ветка, если вам это нужно.

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

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

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

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

Вуаля!

person Claude Peloquin    schedule 14.12.2017

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

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

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

person Antoine Pelisse    schedule 24.01.2011
comment
При включенном rerere это, похоже, дает тот же результат, что и решение rebase -p, данное выше siride. - person jipumarino; 24.01.2011

  • Из вашего коммита слияния
  • Выбери новое изменение, которое должно быть легким
  • скопируйте свой материал
  • повторить слияние и разрешить конфликты, просто скопировав файлы из вашей локальной копии;)
person Tobi B    schedule 30.11.2017
comment
Этот ответ выглядит хорошо, но был бы более полезным, если бы вы указали фактические команды git, на случай, если пользователь не знаком с git. - person Louise Davies; 30.11.2017