Отменить слияние без отмены коммитов, выполненных после
У меня маленькая проблема. Мне нужно отменить слияние в моем удаленном хранилище без отмены коммитов, сделанных после. Смотрите ниже мою модель и мои ожидания.
В настоящее время у меня есть эта модель:
Я хотел бы это:
Я проверил документы и поиск в Интернете, но я не нашел никакого умного решения. У тебя есть идея для меня? Я бы позаботился о моем слиянии на будущее!
Большое вам спасибо!
3 ответа
Вы можете либо переписать историю веток, либо отменить слияние. У каждого есть свои плюсы и минусы.
Сначала давайте начнем с немного измененной копии вашей диаграммы текущего состояния, которая отражает немного больше того, что происходит.
A -- B -- C <--(branch>
\
M -- N -- O -- P -- Q <--(master)
Вы не показывали никаких ссылок, поэтому я предполагаю master
указывает на Q
, Если у вас нет филиала в branch
вы, вероятно, должны создать его (если вы не отменяете изменения A
, B
, а также C
). Кроме того, незначительная нотация, но я переключил все коммиты на буквы (так как иногда это может быть понятнее).
Переписать историю
Все обычные предупреждения о переписывании истории применимы к этому подходу. В основном это означает, что если master
толкнул так, что кто-то еще уже "видел" совершить O
как часть master
с историей, то вам придется согласовать с ними, чтобы успешно сделать переписывание истории. Переписать поставлю свою копию master
в плохом состоянии, из которого они должны будут оправиться, и если они сделают это неправильно - как они могли бы, если бы вы не сообщили о том, что происходит - тогда ваша работа может быть отменена. См. "Восстановление после восходящей версии" в git rebase
документы для получения дополнительной информации, так как это является применимым условием независимо от того, используете ли вы на самом деле rebase
Команда для выполнения перезаписи.
Если вы действительно хотите переписать, rebase - самый простой способ. Вам понадобятся либо идентификаторы коммитов N
а также O
или выражения, которые разрешают коммиты N
а также O
, В этом примере мы можем использовать master~3
за N
а также master~2
за O
,
git rebase --onto master~3 master~2 master
Это примет изменения, достижимые от master
, но не достижимо от O
и переиграть их N
; а затем двигаться master
к переписанным коммитам.
A -- B -- C <--(branch>
\
M -- N -- O -- P -- Q <--(master@{1})
\
P' -- Q` <--(master)
Старые коммиты все еще существуют (и, как я показал здесь, рефлог все еще может достигать их - в данный момент локально). Поскольку большинство инструментов не следуют журналу, вы, вероятно, увидите что-то более похожее
A -- B -- C <--(branch>
M -- N -- P' -- Q` <--(master)
И действительно, после истечения срока действия reflog это именно то, что останется (если вы не сделаете что-то, чтобы сохранить старые коммиты тем временем). На данный момент, чтобы подтолкнуть master
вам придется сделать силовой толчок. Самый безопасный способ сделать это
git push --force-with-lease
Обычно люди просто рекомендуют -f
вариант, но это менее безопасно, так как он может совершать коммиты, о которых вы не знаете, на удаленном master
, В любом случае после принудительного толчка наступает момент, когда кто-либо еще с копиями master
должен будет восстановиться после состояния "upstream rebase".
Другие способы перезаписи (например, путем сброса и последующего выбора вишни) функционально эквивалентны (за исключением нескольких странных крайних случаев), но они более ручные и, следовательно, более подвержены ошибкам. Стоит повторить, хотя такие альтернативы могут не использовать rebase
команда, ситуация "восходящая перебазировка" все равно будет применяться точно так же.
Без переписать
Если перезапись истории неосуществима - как это часто бывает в широко распространенных репозиториях - альтернативой является отмена фиксации слияния. Это создает новый коммит, который "отменяет" изменения, внесенные слиянием. Использовать revert
на коммит слияния, вы должны дать -m
вариант (который говорит revert
к какому из родителей вернуться; если вы пытаетесь отменить эффект слияния, это обычно -m 1
).
Опять же, вам нужен идентификатор или выражение, которое разрешает, O
; мы будем использовать master~2
в примере.
git checkout master
git revert -m 1 master~2
Теперь у вас есть
A -- B -- C <--(branch>
\
M -- N -- O -- P -- Q -- !O <--(master)
где !O
отменяет изменения, которые O
применительно к N
,
Как отмечено в другом месте, Git видит branch
как "уже учтено" - это не отслеживает !O
изменения были задуманы как возврат / откат O
или что-нибудь в этом роде. Так что если вы позже хотите сказать git merge branch
пропустит коммиты A
, B
, а также C
,
Один из способов исправить это с rebase -f
, Например, после возврата можно сказать,
git rebase -f master~3 branch
и все коммиты достижимы из branch
, но не достижимо от master
до слияния в O
, будет переписан. Конечно, это переписать branch
, Так как вы могли использовать revert
подход, чтобы избежать переписывания master
Вы также можете не захотеть переписывать branch
, (Если переписать branch
, и если branch
передается другим репозиториям, тогда вам придется push --force-with-lease
и другие пользователи должны были бы восстановиться после восходящей перебазировки.)
Другой вариант, в точке, где вы хотите объединить branch
Вернуться в master
, чтобы "отменить возврат". Предположим, прошло некоторое время с тех пор, как вы отменили слияние, и у вас есть
A -- B -- C -- D -- E <--(branch>
\
M -- N -- O -- P -- Q -- !O -- R -- S <--(master)
Теперь слить branch
в master
ты мог бы сказать
git checkout master
git revert master~2
git merge branch
Один из способов - сбросить настройки последнего коммита до O
который не принадлежит ветви 5 - 6 - 7
т.е. N
в этом случае. Тогда вишня выберет все необходимые коммиты т.е. P
& Q
, Пример ниже:
git reset --hard N #This resets the branch to N & removes all commits of merged branch `5 - 6 - 7`
git cherry-pick P #Manually add the 2 commits you want back.
git cherry-pick Q
Другой способ - отменить коммит слияния с помощью следующей команды:
git revert -m 1 O . #this is alphabet O - merge commit id. Not Numeric zero.
Это добавит новый коммит поверх Q
- Давайте назовем это как O'
, где
O'
обратный коммитO
предостережение: если вы попытаетесь внести некоторые изменения в 5 - 6 - 7
переходите в будущее и объединяйте его снова - он не объединит коммиты 5
, 6
, 7
потому что эти идентификаторы коммитов уже находятся в этой ветке, а также существует обратный коммит этих коммитов поверх них.
Это означает, что вы никогда не сможете объединять коммиты 5
, 6
, 7
,
Хотя существуют механизмы, с помощью которых вы можете изменить идентификаторы коммитов, выполнив перебазировку или сделав тривиальное изменение просто ради изменения идентификаторов, это приведет к конфликту слияния при идентичных изменениях. Лично я бы не рекомендовал такой подход.
Поскольку вы ничего не упоминаете о ветках, которые у вас есть, я предлагаю начать с создания веток (b7
а также bkp
) в существующих коммитах, чтобы они оставались видимыми после изменения:
+----- b7
v
5 - 6 - 7 +-------- bkp
| v
M - N - O - P - Q
^
+-------- bQ
Так как у вас, вероятно, есть ветка, которая указывает на Q
Вы можете использовать его вместо bQ
в командах ниже.
затем git rebase
коммиты P
а также Q
на вершине коммита N
чтобы получить структуру, которую вы желаете.
Команды:
# Preparations
# Create the branch `b7` to keep the commit `7` reachable after the change
git branch b7 7
# Create the branch `bkp` for backup (the commit `Q` will be discarded)
git branch bkp Q
# The operation
# Checkout `bQ` to start the change
git checkout bQ
# Move the `P` and `Q` commits on top of `N`
# Change "2" and "3" in the command below with the actual number of commits
# 2 == two commits: P and Q, 3 == 2 + 1
# Or you can use commit hashes (git rebase --onto N O)
git rebase --onto HEAD~3 HEAD~2
Теперь хранилище выглядит так:
+----- b7
v
5 - 6 - 7 +-------- bkp
| v
M - N - O - P - Q
|
P'- Q'
^
+-------- bQ
Старый O
, P
а также Q
коммиты все еще там, и они все еще доступны, пока ветвь bkp
или любые другие ветви, которые указывают на Q
все еще существует. Если вы довольны изменением, вы можете удалить bkp
и любые другие ветви, которые указывают на Q у вас есть.
Команда:
git branch -D bkp
Вы, вероятно, не хотите удалять b7
если у вас уже нет другой ветки, которая указывает на фиксацию 7
уже. В этом случае вам даже не нужно создавать b7
,
После этой операции репо выглядит так:
+----- b7
v
5 - 6 - 7
M - N - P'- Q'
^
+-------- bQ
Обратите внимание, что коммиты P'
а также Q'
отличаются от оригинальных коммитов P
а также Q
,
Предупреждение
Если совершает O
, P
а также Q
были уже переданы в удаленный репозиторий (через bQ
ветка), толкая bQ
опять не получится. Толкание может быть принудительным (git push --force origin bQ
) но это действие запутает ваших коллег, которые уже получили текущую позицию bQ
ветвь (она содержит O
, P
а также Q
совершает).
Если вам действительно нужно выполнить этот трюк, обязательно сообщите всем об этом изменении.
Лучшим подходом в этой ситуации является git revert -m
совершить O
, Это создает новый коммит поверх Q
вносит изменения, отменяющие изменения, внесенные коммитом O
, Это некрасиво, но это самое безопасное решение.