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

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

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

22 ответа

Решение

-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

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

git revert -m 1 <commit-hash> 
git commit -m "Reverting the last commit which messed the repo."
git push -u origin master

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

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

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

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

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

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

    выход:

    commit 7cd42475d6f95f5896b6f02e902efab0b70e8038 "Объединить ветку 'неправильная фиксация' в 'разработка'"
    commit f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 "это неверный коммит"
    commit 3779ab50e72908da92d2cfcd72256d7a09f446ba "это правильный коммит"

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

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

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

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

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

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

git revert -m 1 <commit-hash>

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

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

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

git log

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

git checkout -b newbranch <HASH>

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

git branch -D oldbranch
git checkout -b oldbranch newbranch

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

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

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

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

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

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

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

где A и B относятся к побочной разработке, которая была не очень хорошей, M - это слияние, которое вносит эти преждевременные изменения в основную ветку, x - это изменения, не связанные с тем, что было сделано боковой ветвью и уже внесенные в основную ветку, и W - откат слияния M" (разве W не смотрит на M вверх ногами?). IOW, "diff W^..W" похож на "diff -R M^..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, но это просто нормальный конфликт слияния.

Когда вы просматриваете фиксацию слияния на выходе 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

Возьмите резервную ветку

      git checkout -b mybackup-brach

git reset --hard 8989ee0 
git push origin -u mybackup-branch

Итак, теперь у вас есть изменения перед слиянием. Если все в порядке, перейдите в предыдущую ветку и выполните сброс с помощью резервной ветки.

      git reset --hard origin/mybakcup-branhc

Принятый ответ и некоторые другие ответы демонстрируют, как отменить фиксацию слияния с помощьюgit revertкоманда. Однако возникла некоторая путаница в отношении родительских коммитов. Этот пост призван прояснить это с помощью графического представления и реального примера.

Отменить фиксацию слияния не так просто, как в случаеgit revert <commit-hash>, так как Git запутывается, оглядываясь на коммит слияния из-за его двух родительских коммитов. Чтобы указать желаемого родителя, используется-mфлаг. Поскольку git не может определить, какой родитель является основной линией, а какая — ветвью, которую нужно автоматически разъединить, это необходимо указать.

Филиал был объединен с , создавMerge Commit, . было двое родителей и .

Нужно вернутьсяC6и верните репозиторий в его состояние в . Поэтому он должен указать, какой родитель использовать для команды возврата.

  • Для этого проверьте ( здесь представлен фактический хэш коммита с кодовыми именами из графика )

            > git log
    
    commit C6
    Merge: C4 C5
    Author: Mozz <mozz@example.com>
    Date:   Wed Feb 29 23:59:59 2020 +0100
    
    Merge branch 'iss53' to master
    ...
    
  • Из вывода запишите родительские идентификаторы, которые поставляются сMerge: - -. Он будет в форматеMerge: parent1 parent2, здесьMerge: C4 C5.

  • The C4commit находится в ветке, и нам нужно вернуться к тому, что является родителем 1 и требуется здесь (используйтеgit log C4для проверки предыдущих коммитов для подтверждения родительской ветки).

  • Переключитесь на ветку, на которой было выполнено слияние (этоmasterветвь здесь, и мы стремимся удалитьiss53ответвление от него)

    Верните git с помощью-m 1флаг.

            # To revert to C4 in master branch
    git revert C6 -m 1
    
    # C6 - is the merge commit hash
    

В некоторых других случаях при необходимости вернитесь кC5,

      # revert to C5 in iss53 branch
git revert C6 -m 2

# General
git revert <merge commit id> -m 1 (reverts to parent1)
git revert <merge commit id> -m 2 (reverts to parent2)
# remember to check and verify the parent1 and parent2 with git log command.

Практический пример

Создал новую ветку в существующем проекте, который имеет только ветку. График фиксации теперь выглядит так.

(Для графического представления коммитов используйте —graphс [ SO ans ref] ИЛИ это более интерактивное расширение кода VS - git graph)

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

Затем создал запрос на извлечение из GitHub и объединил ветку с .

Я хочу отменить фиксацию слияния и вернуться к последней фиксации в ветке, которая

Обратите внимание, что коммит слияния -2ec06d9теперь имеет двух родителей - (вmain) и15bde47revert-test), проверкаgit logсейчас,

      > git log

commit 2ec06d9d315a3f7919ffe4ad2c2d7cec8c8f9aa3 (HEAD -> main, origin/main, origin/HEAD)
Merge: 12a7327 15bde47
Author: Akshay <63786863+akshay@users.noreply.github.com>
Date:   Sun Feb 5 00:41:13 2023 +0530

    Merge pull request #1 from Akshay/revert-test
    
    Revert test

Чтобы отменить фиксацию слияния и вернуться к12a7327нужно сделать,

      # To the First parent
git revert 2ec06d9 -m 1

Теперь в редакторе появится сообщение фиксации, в котором указаны детали, проверка и проверка.

Таким образом, создается фиксация Revert, которая выполняет обратные изменения коммита слияния.

Наконец, внесите изменения. Теперь изменения фиксации слияния исчезли, и журнал будет выглядеть так:

Очень простой ответ, если вы хотите отменить внесенное вами изменение:

      commit 446sjb1uznnmaownlaybiosqwbs278q87
Merge: 123jshc 90asaf


git revert -m 2 446sjb1uznnmaownlaybiosqwbs278q87 //does the work

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

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

Итак, если история GIT такая:

  • d
  • c
  • b <<< слияние
  • а
  • ...

Я создаю новую ветку из a, выбираю вишню c и d, а затем новая ветка очищается от b. Я когда-нибудь снова смогу решить объединить "b" в своей новой ветке. Старая ветка становится устаревшей и будет удалена, если "b" больше не нужен или все еще находится в другой ветке (функция / исправление).

Единственная проблема - это одна из самых сложных задач в компьютерных науках: как вы назовете новую ветку?;)

Хорошо, если вы не справились. в devel вы создаете newdevel, как указано выше, удаляете старую devel и переименовываете newdevel в devel. Миссия выполнена. Теперь вы можете снова объединить изменения, когда захотите. Как будто никогда раньше не сливались....

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

Допустим, у нас есть ветви 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> - это создаст новую ветку, начиная с последнего коммита, который вы выбрали до слияния. Вуаля, готово!

git doc о git revert -m предоставьте ссылку, чтобы объяснить это: https://github.com/git/git/blob/master/Documentation/howto/revert-a-faulty-merge.txt

-m1 - это последний родительский элемент текущей ветки, который исправляется, -m 2 - исходный родительский элемент ветки, которая была слита с этой.

Tortoise Git также может помочь здесь, если командная строка сбивает с толку.

Я обнаружил, что создание обратного патча между двумя известными конечными точками и применение этого патча будет работать. Это предполагает, что вы создали моментальные снимки (теги) из своей основной ветви или даже резервную копию своей основной ветви, скажем, 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_fixgit checkout mycodebranch_fix
  6. Здесь вам нужно найти ключ SHA для возврата и отменить возвратgit revert [SHA]
  7. Теперь вы можете использовать mycodebranch_fix для исправления проблем, фиксации и повторного слияния с мастером, как только закончите.

Это работает для меня ::

  1. Выписка из ветки. Вы хотите отменить предыдущий (последний) мерж-реквест.
  2. Терминал Opem 3. Запустите командуgit reset --merge HEAD~1.

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

Поскольку я просто хотел изменить некоторые измененные файлы, но не все изменения, которые принес PR, мне пришлось amend то merge commit с git commit --am.

Шаги:

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

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

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

Для меня у меня был PR в репозитории от моей разветвленной главной ветки до origin master, когда я загружал вверх по течению, он добавил фиксацию слияния, потому что моя ветка была на 1 фиксацию впереди и несколько 100 фиксаций позади. Таким образом, он автоматически добавляет фиксацию слияния.

Чтобы удалить это, мне нужно было просто запустить эту команду:

      git reset --hard HEAD~1
git push -f origin

Он удалил это слияние. И все вернулось на круги своя.

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

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

Другие вопросы по тегам