Отменить слияние без отмены коммитов, выполненных после - PullRequest
0 голосов
/ 04 сентября 2018

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

В настоящее время у меня есть эта модель:

enter image description here

Мне бы хотелось:

enter image description here

Я проверил документы и выполнил поиск в Интернете, но не нашел разумного решения. У тебя есть идея для меня? Я бы позаботился о моем слиянии на будущее!

Большое спасибо!

Ответы [ 3 ]

0 голосов
/ 04 сентября 2018

Поскольку вы ничего не упоминаете о ветках, которые у вас есть в настоящее время, я предлагаю вам начать с создания некоторых веток (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. Это некрасиво, но это самое безопасное решение.

0 голосов
/ 04 сентября 2018

Вы можете переписать историю веток или отменить слияние. У каждого есть свои плюсы и минусы.

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

A -- B -- C <--(branch>
           \
  M -- N -- O -- P -- Q <--(master)

Вы не показывали никаких ссылок, поэтому я предполагаю, что master указывает на Q. Если у вас нет ветки на branch, вам, вероятно, следует создать ее (если только вы не отменяете постоянно изменения A, B и C). Кроме того, небольшая нотация, но я переключил все коммиты на буквы (так как иногда это может быть понятнее).


Переписать историю

Все обычные предупреждения о переписывании истории применимы к этому подходу. В основном это означает, что если master был выдвинут так, что кто-то еще «видел» коммит O как часть истории master, то вам придется согласовать с ними успешную перезапись истории. Переписанный переводит их копию master в плохое состояние, из которого они должны будут восстановиться, и если они сделают это неправильно - как они могли бы, если бы вы не сообщили о том, что происходит - тогда ваша работа может быть отменена , Для получения дополнительной информации см. «Восстановление из исходной перебазировки» в git 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)

Старые коммиты все еще существуют (и, как я показал здесь, reflog все еще может достигать их - на данный момент локально). Поскольку большинство инструментов не следуют журналу, вы, вероятно, увидите что-то более похожее на

A -- B -- C <--(branch>

M -- N -- P' -- Q` <--(master)

И действительно, после истечения срока действия reflog это именно то, что останется (если вы не сделаете что-то, чтобы сохранить старые коммиты за это время). На этом этапе, чтобы нажать master, вам нужно сделать принудительный толчок. Самый безопасный способ сделать это -

git push --force-with-lease

Обычно люди рекомендуют просто параметр -f, но это менее безопасно, так как это может привести к прерыванию коммитов, о которых вы не знаете, на пульте master. В любом случае, после принудительного толчка наступает момент, когда кому-либо еще с копиями master необходимо будет восстановиться после условия «восходящего восстановления».

Другие способы перезаписи (например, путем сброса и последующего выбора вишни) функционально эквивалентны (за исключением нескольких странных крайних случаев), но они более ручные и, следовательно, более подвержены ошибкам. Стоит повторить, что, хотя такие альтернативы могут не использовать команду 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
0 голосов
/ 04 сентября 2018

Один из способов - сбросить хард на последнем коммите до 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.

Хотя существуют механизмы, с помощью которых вы можете изменить идентификаторы коммитов, выполнив или перебазировку, или тривиальное изменение просто ради изменения идентификаторов, что приведет к конфликту слияния при идентичных изменениях. Лично я бы не рекомендовал такой подход.

...