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

964

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

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

Yaz
источник
3
Посмотрите на ответ на этот вопрос: stackoverflow.com/questions/2318777/…
eugen
2
Связанный: Отменить слияние Git? ,
Ссылка здесь - лучший пример, который иллюстрирует возврат объединенного коммита: 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 ...Линия по существу совершает изменения в то время как вторая линия делает ваши изменения общественности, вставив их в удаленный филиал.

Saheed
источник
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
ssasi
источник
4
Что, если за это время 20 разработчиков вытащили последнее слияние разработчиков?
Эвокс
2
Я бы не стал форсировать ветку разработки, когда в команде используется 20 разработчиков. :) В этом случае разумно просто сделать возврат.
ссаси
4
Это очень хорошее решение, когда вы работаете самостоятельно или уверены, что другие разработчики не потянули коммиты, которые вы испортили
Kyle B
52
git revert -m 1 <merge-commit>
Нирадж Кумар
источник
2
В этом ответе недостаточно деталей. Может быть, поэтому это хорошо.
Густаво Штраубе
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

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

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

saran3h
источник
5

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

git revert -m 1 <commit-hash>

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

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

xproph
источник
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 для исправления проблем, фиксации и повторного слияния с мастером после завершения.
alexplain
источник
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, но это обычный конфликт слияния.

MK446
источник
-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 (если вам нужно вернуть вещи обратно) - гораздо более безопасная альтернатива.
Ойвинд