Восстановление нарушенного слияния и повторное применение выбранных коммитов с помощью Git - PullRequest
8 голосов
/ 04 февраля 2012

Недавно мы столкнулись с проблемой, когда слияние каким-то образом привело к тому, что все изменения привели к тому, что один родитель был отменен в коммите слияния, и после этого было применено несколько коммитов. Все это было отправлено в наш общий репозиторий происхождения. Я хочу иметь возможность отменить неудачное слияние и повторно применить другие наборы изменений. Какой самый простой способ сделать это?

ASCII art example:

    A-B-C-D
   /       \
P-Q         M-X-Y-Z
   \       /
    1-2-3-4

  ---time--->

В коммит M включены изменения от A до D, но все изменения с 1 по 4 были отменены этим коммитом.

Я бы хотел вернуться назад, например. изменить набор 4 и повторно применить изменения A-D и X к Z, в идеале без повторного изменения каждого изменения вручную.

Если это невозможно, я хотел бы услышать о лучших обходных путях - например. может быть ответвление от 4, снова слить в D, стараясь ничего не сломать, а затем вручную применить X к Z?

(Идеальная ситуация - знать, как это сделать, используя TortoiseGit, хотя знание командной строки должно позволить мне вывести остальное.)

1 Ответ

10 голосов
/ 04 февраля 2012

Вы хотите получить что-то, чей логический эффект:

P-Q-1-2-3-4-A-B-C-D-X-Y-Z

Есть несколько подходов.Давайте предположим, что мастер указывает на Z прямо сейчас.


Отменить коммит слияния (самый безопасный, но более сложный)

Сначала нам нужно отменить прерванный коммит слияния M.Мы сделаем это с:

git revert -m 1 M

Далее, давайте снова применим отмененные или потенциально отрицательные коммиты:

git cherry-pick 1
git cherry-pick 2
git cherry-pick 3
git cherry-pick 4
git cherry-pick A
git cherry-pick B
git cherry-pick C
git cherry-pick D

Теперь у нас есть это:

# M reverts some of A..D and 1..4
# !M undoes the logical effect of the merge

    A-B-C-D
   /       \
P-Q         M-X-Y-Z-!M-1'-2'-3'-4'-A'-B'-C'-D'
   \       /
    1-2-3-4

Это самый безопасный подход, потому что он не затрагивает никакую предыдущую историю, поэтому никто из репозиториев не будет затронут.Но это также самый грязный, потому что он оставляет много детрита.Увы, это цена, которую вы платите за небрежное слияние.


Выбросьте сломанную историю (наиболее опасную, но самую чистую)

Для этого потребуется получить согласие от всех, что ваш подход обоснован,Вы сломаете историю людей, и любой, у кого есть испорченный коммит слияния M, будет испытывать проблемы, если вы сделаете это таким образом.Но ваша история будет выглядеть чище.

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

Сначала мы перематываем мастер обратно на Q, последний коммит без проблем.

git checkout master
git reset --hard Q       # rewind `master` branch to `Q`

Далее нам нужно интерактивно перебазировать нарушенную последовательность коммитов.

git checkout Z         # move to Z
git branch tmp         # make a branch pointing to Z
git rebase -i master   # rework this branch onto master

Вам будет представлен список коммитов в вашем редакторе, который выглядит следующим образом:

pick aaaaaaa Commit message one
pick bbbbbbb Commit message two
pick ccccccc Commit message three
# ...

Одним из них будет прерванный коммит слияния.Удалить эту строку;сохраните и закройте.

Git теперь будет применять коммиты в том порядке, в каком вы просили, и вы будете на tmp.Если все выглядит хорошо, сбросьте мастер:

git checkout master
git reset --hard tmp

Теперь принудительно нажмите на источник:

git push -f origin master

Сломанная история будет удалена, и мастер укажет на новую историю.


Наконец

Вы должны попросить свою команду использовать что-то вроде nitie git-flow , чтобы избежать беспорядка в будущем и предотвратить snafus.

Обновление: Похоже, что nvie отказалась от поддержки git-flow.Тем не менее, Питер ван дер Доу раздвоил и поддерживает версию

...