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


964

git revert <commit_hash>один не будет работать. -mдолжен быть указан, и я довольно смущен этим.

Кто-нибудь испытывал это раньше?


3
Посмотрите на ответ на этот вопрос: stackoverflow.com/questions/2318777/…
— eugen


Ссылка здесь - лучший пример, который иллюстрирует возврат объединенного коммита: christianengvall.se/undo-push-merge-git
— SK Venkat

Это пример того, где дизайн gitне соответствует git-flowрабочему процессу -ish, который все используют. Если вы сделали developэто, вы, конечно , хотите вернуть ветку 2-коммитов, в которой появилась ошибка, а не ветка с общим временем разработки. Чувствует себя нелепо, когда его нужно выбрать -m 1.
— Пкамб

2
Еще одно предложение, которое мне никогда не приходило в голову - если один из списков коммитов веток мал, вам может быть удобнее возвращать отдельные коммиты вместо целой ветви коммитов.
— Шридхар Сарнобат

Ответы:


1156

-mПараметр определяет родительский номер . Это потому, что коммит слияния имеет более одного родителя, и Git не знает автоматически, какой родитель был основной линией, и какой родитель был ветвью, которую вы хотите удалить.

Когда вы просматриваете коммит слияния в выводе git log, вы увидите его родителей в строке, которая начинается с Merge:

commit 8f937c683929b08379097828c8a04350b9b8e183
Merge: 8989ee0 7c6b236
Author: Ben James <ben@example.com>
Date:   Wed Aug 17 22:49:41 2011 +0100

Merge branch 'gh-pages'

Conflicts:
    README

В этой ситуации, git revert 8f937c6 -m 1вы получите дерево, как оно было 8989ee0, и git revert -m 2восстановите дерево, как оно было 7c6b236.

Чтобы лучше понять родительские идентификаторы, вы можете запустить:

git log 8989ee0 

а также

git log 7c6b236

127
из двух чисел 8989ee0, 7c6b236какой из них идти. Как бы я понял?
— Arup Rakshit

12
После возврата я не думаю, что кто-то сможет легко исправить код в ветке с исходным кодом и снова объединить? kernel.org/pub/software/scm/git/docs/howto/...
— IsmailS

10
Находясь в поисках лучшего объяснения, я нашел эту статью, которая, как мне показалось, отлично проработала детали. После прочтения я обнаружил, что на самом деле я искал команду RESET с последующим принудительным нажатием. Может быть, это поможет кому-то еще. atlassian.com/git/tutorials/…
— Funktr0n

46
@ArupRakshit, если вы запускаете, git log 8989ee0и git log 7c6b236вы должны знать ответ.
— BMW

4
git log - объединяет для просмотра всех слияний и git log --no-merges для просмотра истории без слияний. Слияние ветки приводит историю слитой ветки к цели и
— усложняет

370

Вот полный пример в надежде, что это кому-то поможет:

git revert -m 1 <commit-hash> 
git push -u origin master

Где <commit-hash>находится хеш коммита слияния, который вы хотели бы вернуть, и, как указано в пояснении к этому ответу , -m 1указывает, что вы хотите вернуться к дереву первого родителя до слияния.

git revert ...Линия по существу совершает изменения в то время как вторая линия делает ваши изменения общественности, вставив их в удаленный филиал.


21
Я полагал, что git revertкоманда уже передала созданный объект коммита. Чтобы этого не произошло, вам нужно будет ввести --no-commitфлаг
— Delfic

2
Как упомянул @Delfic, коммит уже управляется первой строкой (мне нужно было: wq для его проверки), поэтому вторая строка не нужна.
— eka808

1
это смущает. там только 2 строки и нет git commit .. может кто-нибудь пожалуйста отредактировать.
— Джей Рэндом

176

Бен рассказал вам, как отменить коммит слияния, но очень важно, чтобы вы понимали, что при этом

«... объявляет, что вы никогда не захотите изменений дерева, внесенных слиянием. В результате, последующие слияния будут вносить только изменения дерева, внесенные коммитами, которые не являются предками ранее отмененного слияния. Это может или не может быть что вы хотите. " (страница справочника git-merge) .

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


81
Но вы можете отменить возврат, чтобы вернуть их, если это действительно необходимо.
— Далоре

5
Спасибо. Очень полезно знать, как вариант использования для отмены слияния - скажем, из-за ошибки - и затем повторного слияния всей ветви, как только ошибка исправлена, является распространенным.
— Компонент 10

3
Если вы похожи на меня и позже хотите слияния, вы можете либо отменить возврат, либо черри выбрать изменение, которое вы отменили.
— UnitasBrooks

В моей ситуации я столкнулся с необходимостью «отменить возврат», чтобы получить свои изменения назад. Сбор вишни может быть аккуратнее? Я попробую это в следующий раз ...
— Стивен Андерсон

79

Вы можете выполнить эти шаги, чтобы отменить неправильные коммиты или вернуть удаленную ветвь обратно в правильный HEAD / состояние.

  1. Оформить заказ на удаленную ветку в локальный репо.
    git checkout development
  2. скопировать хеш коммита (т.е. идентификатор коммита непосредственно перед неправильным коммитом) из журнала git git log -n5

    вывод:

    commit 7cd42475d6f95f5896b6f02e902efab0b70e8038 «Слить ветвь« неправильная фиксация »в« развитие »»
    commit f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 «это неправильный
    коммит»

  3. сбросить ветку на хеш коммита, скопированный на предыдущем шаге
    git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)

  4. запустить, git statusчтобы показать все изменения, которые были частью неправильного коммита.
  5. просто запустите, git reset --hardчтобы отменить все эти изменения.
  6. принудительно перенесите вашу локальную ветку на удаленный компьютер и обратите внимание, что история ваших изменений чиста, как это было до ее загрязнения.
    git push -f origin development

4
Что, если за это время 20 разработчиков вытащили последнее слияние разработчиков?
— Эвокс

2
Я бы не стал форсировать ветку разработки, когда в команде используется 20 разработчиков. :) В этом случае разумно просто сделать возврат.
— ссаси

4
Это очень хорошее решение, когда вы работаете самостоятельно или уверены, что другие разработчики не потянули коммиты, которые вы испортили
— Kyle B


30

Чтобы сохранить журнал в чистоте, так как ничего не произошло (с некоторыми недостатками при таком подходе (из-за нажатия -f)):

git checkout <branch>
git reset --hard <commit-hash-before-merge>
git push -f origin HEAD:<remote-branch>

'commit-hash-before-merge' приходит из журнала (git log) после слияния.


Подсказка: если вы делаете это в своей компании, у вас может не быть разрешения.
— eneski

3
никогда не делайте push -fрепо в общем репо
— Батист Милле-Матиас

17

Иногда самый эффективный способ отката - сделать шаг назад и заменить.

git log

Используйте хэш 2-го коммита (полный хеш, тот, к которому вы хотите вернуться, до того, как ошибка была указана), а затем выполните повторную разметку оттуда.

git checkout -b newbranch <HASH>

Затем удалите старую ветку, скопируйте новую ветку на место и перезапустите оттуда.

git branch -D oldbranch
git checkout -b oldbranch newbranch

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


4
Предупреждение о трансляции должно быть более ясным о том, насколько ужасна эта идея. Это повредит все версии этой ветки и будет действительно полезным, только если вы работаете с удаленным репозиторием (github / bitbucket), к которому только у вас есть доступ.
— RobbyD

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

5

Если вы хотите отменить mergeкоммит, вот что вам нужно сделать.

  1. Сначала проверьте, git logчтобы найти идентификатор вашего слияния. Вы также найдете несколько родительских идентификаторов, связанных с объединением (см. Изображение ниже).

введите описание изображения здесь

Запишите идентификатор фиксации слияния, показанный желтым цветом. Родительские идентификаторы - это те, которые записаны в следующей строке как Merge: parent1 parent2. Сейчас же...

Короткий рассказ:

  1. Переключитесь на ветку, на которой было произведено слияние. Затем просто сделайте то, git revert <merge commit id> -m 1что откроет viконсоль для ввода сообщения коммита. Написать, сохранить, выйти, готово!

Длинная история:

  1. Переключитесь на ветку, на которой было произведено слияние. В моем случае это testветка, и я пытаюсь удалить feature/analytics-v3из нее ветку.

  2. git revertэто команда, которая отменяет любой коммит Но при отмене mergeкоммита есть неприятный трюк . Вам нужно ввести -mфлаг, иначе он не удастся. С этого момента вам необходимо решить, хотите ли вы вернуть свою ветку и сделать так, чтобы она выглядела именно так, как она была, parent1или parent2через нее :

git revert <merge commit id> -m 1(возвращается к parent2)

git revert <merge commit id> -m 2(возвращается к parent1)

Вы можете git войти эти родители, чтобы выяснить, каким путем вы хотите пойти, и это корень всей путаницы.


5

Все ответы уже охватывали большинство вещей, но я добавлю свои 5 центов. Короче говоря, отменить коммит слияния довольно просто:

git revert -m 1 <commit-hash>

Если у вас есть разрешение, вы можете отправить его прямо в «главную» ветку, в противном случае просто переместите его в свою «возвратную» ветку и создайте запрос на извлечение.

Вы можете найти более полезную информацию по этому вопросу здесь: https://itcodehub.blogspot.com/2019/06/how-to-revert-merge-in-git.html


1

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

Скажем, ветвь кода, которую вы объединили в master, была mycodebranch.

  1. Оформить заказ мастеру.
  2. Создайте полное двоичное обратное исправление между мастером и резервной копией. git diff --binary master..master_bk_01012017 > ~/myrevert.patch
  3. Проверьте ваш патч git apply --check myrevert.patch
  4. Применить патч с подписью git am --signoff < myrevert.patch
  5. Если вам нужно будет снова ввести этот код, как только он будет исправлен, вам нужно будет разветвиться от возвращенного мастера и извлечь ветку исправлений git branch mycodebranch_fix git checkout mycodebranch_fix
  6. Здесь вам нужно найти ключ SHA для возврата и отменить возврат git revert [SHA]
  7. Теперь вы можете использовать mycodebranch_fix для исправления проблем, фиксации и повторного слияния с мастером после завершения.

1

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

Допустим, у нас есть ветви A и B .. Вы слили ветвь A в ветвь B и подтолкнули ветвь B к себе, так что теперь слияние является частью этого ... Но вы хотите вернуться к последнему коммиту перед слиянием .. Что делать вы делаете?

  1. Перейдите в корневую папку вашего git (обычно это папка проекта) и используйте git log
  2. Вы увидите историю последних коммитов - коммиты имеют свойства commit / author / date, а слияния также имеют свойство merge - так что вы видите их так:

    commit: <commitHash> Merge: <parentHashA> <parentHashB> Author: <author> Date: <date>

  3. Используйте git log <parentHashA>и git log <parentHashB>- вы увидите истории коммитов этих родительских веток - первые коммиты в списке самые последние

  4. Возьмите <commitHash>коммит, который вы хотите, перейдите в вашу корневую папку git и используйте git checkout -b <newBranchName> <commitHash>- это создаст новую ветку, начиная с того последнего коммита, который вы выбрали до слияния. Вуаля, готово!


0

Я также столкнулся с этой проблемой на PR, который был объединен с главной веткой репозитория GitHub.

Так как я только хотел изменить некоторые измененные файлы , но не целые изменения ПР принесло, я должен был с .amendmerge commitgit commit --am

шаги:

  1. Перейдите в ветку, в которую вы хотите изменить / отменить некоторые измененные файлы.
  2. Сделайте необходимые изменения в соответствии с измененными файлами
  3. запустить git add *илиgit add <file>
  4. запустить git commit --amи проверить
  5. запустить git push -f

Почему это интересно:

  • Сохраняет фиксацию автора PR без изменений
  • Это не ломает мерзкое дерево
  • Вы будете помечены как коммиттер (автор коммитов слияния останется без изменений)
  • Git действует так, как будто вы разрешаете конфликты, он удаляет / изменяет код в измененных файлах, как если бы вы вручную указали GitHub не объединять его как есть.

0

Я нашел хорошее объяснение для « Как отменить слияние» по этой ссылке, и я скопировал и вставил объяснение ниже, и это было бы полезно на тот случай, если ссылка ниже не работает.

Как отменить ошибочное слияние, Алан (alan@clueserver.org) сказал:

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

История сразу после «возврата к слиянию» будет выглядеть так:

---o---o---o---M---x---x---W
              /
      ---A---B

где A и B находятся на стороне развития, которая была не очень хорошей, M - это слияние, которое вносит эти преждевременные изменения в основную линию, x - это изменения, не связанные с тем, что боковая ветвь сделала и уже сделала на основной линии, а W - это " возврат слияния М "(разве W не смотрит на М с ног на голову?). IOW, "diff W ^ .. W" похож на "diff -RM ^ .. M".

Такой «возврат» слияния может быть сделан с помощью:

$ git revert -m 1 M После того, как разработчики боковой ветки исправят свои ошибки, история может выглядеть так:

---o---o---o---M---x---x---W---x
              /
      ---A---B-------------------C---D

где C и D должны исправить то, что было сломано в A и B, и у вас уже могут быть некоторые другие изменения в магистрали после W.

Если вы объедините обновленную боковую ветвь (с D на кончике), ни одно из изменений, сделанных в A или B, не будет в результате, потому что они были отменены W. Это то, что Алан видел.

Линус объясняет ситуацию:

Отмена обычного коммита просто эффективно отменяет то, что сделал этот коммит, и довольно проста. Но возвращение коммита слияния также отменяет данные, которые коммит изменил, но это абсолютно не влияет на историю, которую слияние имело. Таким образом, слияние все еще будет существовать, и оно все равно будет рассматриваться как объединение двух ветвей, и будущие слияния будут видеть, что слияние является последним общим состоянием - и обратный процесс, который отменил введенное слияние, не повлияет на это вообще. Таким образом, «возврат» отменяет изменения данных, но это очень не«отмена» в том смысле, что она не отменяет влияния коммита на историю репозитория. Так что если вы думаете о «возврате» как об «отмене», то вы всегда будете пропускать эту часть возвратов. Да, это отменяет данные, но нет, это не отменяет историю. В такой ситуации вы бы хотели сначала вернуть предыдущий возврат, что бы история выглядела следующим образом:

---o---o---o---M---x---x---W---x---Y
              /
      ---A---B-------------------C---D

где Y - это возврат к W. Такой «возврат к возврату» можно сделать с помощью:

$ git revert W Эта история (игнорируя возможные конфликты между изменениями W и W..Y) будет эквивалентна отсутствию W или Y в истории:

---o---o---o---M---x---x-------x----
              /
      ---A---B-------------------C---D

и повторное объединение боковой ветви не приведет к конфликту, связанному с более ранним возвратом и восстановлением возврата.

---o---o---o---M---x---x-------x-------*
              /                       /
      ---A---B-------------------C---D

Конечно, изменения, сделанные в C и D, все еще могут конфликтовать с тем, что было сделано любым из x, но это обычный конфликт слияния.


-2

Как упоминал Райан, это git revertможет затруднить слияние в будущем, поэтому, git revertвозможно, это не то, что вы хотите. Я обнаружил, что использование git reset --hard <commit-hash-prior-to-merge>команды более полезно здесь.

После того, как вы выполнили часть аппаратного сброса, вы можете принудительно нажать на удаленную ветвь, то есть там git push -f <remote-name> <remote-branch-name>, где <remote-name>часто называют origin. С этого момента вы можете повторно объединить, если хотите.


4
Все, что связано с форсированными ударами, является плохой идеей, если вы не единственный, кто использует репо, и вы точно не знаете, что делаете. Возврат с помощью git revert, а затем, возможно, возвращение возврата с помощью git revert (если вам нужно вернуть вещи обратно) - гораздо более безопасная альтернатива.
— Ойвинд
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.