Отмените Git-слияние, которое еще не было перенесено

В моей основной ветке я сделал git merge some-other-branch локально, но никогда не отправлял изменения в мастер-источник. Я не хотел сливаться, поэтому я хотел бы отменить это. При выполнении git status после моего слияния я получил это сообщение:

# On branch master
# Your branch is ahead of 'origin/master' by 4 commits.

На основании некоторых инструкций, которые я нашел, я попытался запустить

git revert HEAD -m 1

но теперь я получаю это сообщение с git status:

# On branch master
# Your branch is ahead of 'origin/master' by 5 commits.

Я не хочу, чтобы моя ветвь была впереди любого количества коммитов. Как мне вернуться к этому моменту?

36 ответов

Решение

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

git reset --hard commit_sha

Есть и другой способ:

git reset --hard HEAD~1

Это вернет вам 1 коммит.

Имейте в виду, что любые измененные и незафиксированные / неснятые файлы будут сброшены в их неизмененное состояние. Чтобы сохранить их или спрятать изменения или посмотреть --merge Вариант ниже.


Как @Velmont предложил ниже в своем ответе, в этом прямом случае используется:

git reset --hard ORIG_HEAD

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


Еще один совет заключается в использовании --merge переключаться вместо --hard поскольку он не сбрасывает файлы без необходимости:

git reset --merge ORIG_HEAD

--merge

Сбрасывает индекс и обновляет файлы в рабочем дереве, которые отличаются между и HEAD, но сохраняет те, которые отличаются между индексом и рабочим деревом (т. Е. С изменениями, которые не были добавлены).

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

git reset --hard origin/master

Тогда ваш местный master ветвь должна выглядеть идентично origin/master,

См. Главу 4 в книге Git и оригинальный пост Линуса Торвальдса.

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

git revert -m 1 commit_hash

Обязательно отмените возврат, если вы снова делаете ветку, как сказал Линус.

Странно, что пропала самая простая команда. Большинство ответов работают, но отмена слияния, которое вы только что сделали, это простой и безопасный способ:

git reset --merge ORIG_HEAD

Ссылка ORIG_HEAD будет указывать на исходный коммит до слияния.

(The --merge Опция не имеет ничего общего со слиянием. Это так же, как git reset --hard ORIG_HEAD, но безопаснее, поскольку не затрагивает незафиксированные изменения.)

В более новых версиях Git, если вы еще не совершили слияние и у вас есть конфликт слияния, вы можете просто сделать:

git merge --abort

От man git merge:

[Это] можно запустить только после того, как слияние привело к конфликтам. git merge --abort прервет процесс слияния и попытается восстановить состояние перед слиянием.

Вы должны сбросить до предыдущего коммита. Это должно работать:

git reset --hard HEAD^

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

В случае, если у вас возникли проблемы и в вашей основной ветке не было локальных изменений, вы можете сбросить на origin/master,

В последнее время я использую git reflog чтобы помочь с этим. Это в основном работает только в том случае, если произошло слияние, и оно было на вашей машине.

git reflog может вернуть что-то вроде:

fbb0c0f HEAD@{0}: commit (merge): Merge branch 'master' into my-branch
43b6032 HEAD@{1}: checkout: moving from master to my-branch
e3753a7 HEAD@{2}: rebase finished: returning to refs/heads/master
e3753a7 HEAD@{3}: pull --rebase: checkout e3753a71d92b032034dcb299d2df2edc09b5830e
b41ea52 HEAD@{4}: reset: moving to HEAD^
8400a0f HEAD@{5}: rebase: aborting

Первая строка указывает, что произошло слияние. 2-я строка - время до моего слияния. Я просто git reset --hard 43b6032 заставить эту ветвь отследить до слияния и продолжить.

Если вы находитесь в процессе слияния, вы всегда можете прервать егоgit merge --abort

Если ветки срезаны и не задвинуты. Затем команда git reset будет работать, чтобы отменить слияние:git reset --merge ORIG_HEAD

С современным Git вы можете:

git merge --abort

Старый синтаксис:

git reset --merge

Старая школа:

git reset --hard

Но на самом деле стоит отметить, что git merge --abort только эквивалентно git reset --merge При условии MERGE_HEAD настоящее. Это можно прочитать в справке Git для команды слияния.

git merge --abort is equivalent to git reset --merge when MERGE_HEAD is present.

После неудачного слияния, когда нет MERGE_HEADнеудачное слияние может быть отменено git reset --merge, но не обязательно с git merge --abortТаким образом, они не только старый и новый синтаксис для одного и того же.

Лично я нахожу git reset --merge гораздо более мощный и полезный в повседневной работе, поэтому я всегда использую его.

Хорошо, ответы, которые мне дали другие люди, были близки, но это не сработало. Вот что я сделал.

Делая это...

git reset --hard HEAD^
git status

... дал мне следующий статус.

# On branch master
# Your branch and 'origin/master' have diverged,
# and have 3 and 3 different commit(s) each, respectively.

Затем я должен был ввести то же самое git reset команда еще несколько раз. Каждый раз, когда я это делал, сообщение менялось на единицу, как вы можете видеть ниже.

> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 3 and 3 different commit(s) each, respectively.
> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 2 and 3 different commit(s) each, respectively.
> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 1 and 3 different commit(s) each, respectively.
> git reset --hard HEAD^
HEAD is now at [...truncated...]
> git status
# On branch master
# Your branch is behind 'origin/master' by 3 commits, and can be fast-forwarded.

В этот момент я увидел, что сообщение о статусе изменилось, поэтому я попытался сделать git pullи это, похоже, сработало:

> git pull
Updating 2df6af4..12bbd2f
Fast forward
 app/views/truncated |    9 ++++++---
 app/views/truncated |   13 +++++++++++++
 app/views/truncated |    2 +-
 3 files changed, 20 insertions(+), 4 deletions(-)
> git status
# On branch master

Короче говоря, мои команды сводились к следующему:

git reset --hard HEAD^
git reset --hard HEAD^
git reset --hard HEAD^
git reset --hard HEAD^
git pull

Вы должны изменить свою ГОЛОВУ, Не свою конечно, но мерзавца ГОЛОВУ....

Поэтому, прежде чем ответить, давайте добавим немного фона, объясняя, что это HEAD,

First of all what is HEAD?

HEAD это просто ссылка на текущий коммит (последний) в текущей ветке.
Там может быть только один HEAD в любой момент времени. (без учета git worktree)

Содержание HEAD хранится внутри .git/HEAD и он содержит 40 байтов SHA-1 текущего коммита.


detached HEAD

Если вы не на последнем коммите - это означает, что HEAD указывает на предыдущий коммит в истории его называется detached HEAD,

В командной строке это будет выглядеть так - SHA-1 вместо имени ветви, так как HEAD не указывает на конец текущей ветви

Несколько вариантов того, как восстановить систему с отключенной HEAD:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

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

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

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

Каждый раз, когда ГОЛОВКА изменяется, в reflog

git reflog
git checkout HEAD@{...}

Это вернет вас к желаемой фиксации


git reset --hard <commit_id>

"Переместите" ГОЛОВУ назад к желаемому коммиту.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
  • Примечание: ( начиная с Git 2.7)
    Вы также можете использовать git rebase --no-autostash также.

git revert <sha-1>

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

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Эта схема иллюстрирует, какая команда что делает.
Как вы можете видеть там reset && checkout изменить HEAD,

Это можно сделать несколькими способами.

1) Прервать слияние

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

git merge --abort

2) Сбросить ГОЛОВУ в удаленную ветку

Если вы работаете из удаленной ветки разработки, вы можете сбросить HEAD до последнего коммита в удаленной ветке, как показано ниже:

git reset --hard origin/develop

3) Удалить текущую ветку и снова оформить заказ из удаленного хранилища.

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

git checkout master 
##to delete one branch, you need to be on another branch, otherwise you will fall with the branch :) 

git branch -D develop
git checkout -b develop origin/develop

если нет конфликта и слияние завершено, то:

        git reset --hard HEAD~1

если при выполнении слияния возник конфликт, то прерывание удалит вас из последних изменений слияния:

      git merge --abort

или если вы хотите вернуться к определенному идентификатору фиксации.

       git reset --hard <commit-id>

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

В частности,

$ git reflog
$ git reset --hard HEAD@{0}

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

git reset --hard remotes/origin/HEAD

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

Если вы еще не совершили это, вы можете использовать только

$ git checkout -f

Это отменит слияние (и все, что вы сделали).

Добрался до этого вопроса также, пытаясь вернуться к исходному совпадению (т. Е. NO фиксирует перед началом координат). Исследуя дальше, обнаружили, что есть reset Команда именно для этого:

git reset --hard @{u}

Замечания: @{u} это сокращение для origin/master, (И, конечно, вам нужен этот удаленный репозиторий, чтобы это работало.)

Отвечая на вопрос "Отменить слияние Git, которое еще не было отправлено"

Вы можете использовать git reset --hard HEAD~1

Рассмотрим следующую ситуацию, когда есть 2 ветки master и feature-1:


$ git log --graph --oneline --all

Сделайте слияние Git

$ git merge feature-1

$ git log --graph --oneline --all

Отменить слияние Git

$ git reset --hard HEAD~1

$ git log --graph --oneline --all

Самый простой ответ тот, который дал Одино - Велмонт

Сначала сделай git reset --merge ORIG_HEAD

Для тех, кто хочет выполнить сброс после того, как внесены изменения, сделайте это (потому что это первый пост, увиденный для всех вопросов по git reset слиянию)

git push origin HEAD --force

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

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

  1. git reset --hard commitHash (вы должны использовать коммит, который хотите перезапустить, например, 44a587491e32eafa1638aca7738)
  2. git push origin HEAD --force (Отправка новой локальной ветки master в origin/master)

Удачи и вперед!

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

git слияние --abort

Просто чтобы посмотреть на дополнительную опцию, я в основном следовал описанной здесь модели ветвления: http://nvie.com/posts/a-successful-git-branching-model/ и поэтому объединялся с --no-ff (нет перемотки вперед) обычно.

Я только что прочитал эту страницу, так как я случайно слил ветку тестирования вместо своей ветки релиза с мастером для развертывания (веб-сайт, мастер - это то, что работает). Ветвь тестирования объединена с двумя другими ветвями и насчитывает около шести коммитов.

Таким образом, чтобы вернуть весь коммит, мне просто нужен был один git reset --hard HEAD^ и это отменило все слияние. Так как слияния не были быстро переданы, слияние было блоком, и один шаг назад - "ветвь не слитая".

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

Например, я случайно слил разрабатываемую ветку в master и хотел отменить это. Используя следующие шаги:

git checkout develop
git branch -D master
git branch -t master origin/master

Вуаля! Мастер находится на той же стадии, что и источник, и ваше неправильно объединенное состояние стирается.

Стратегия: создать новую ветку, где все было хорошо.

Обоснование: отменить слияние сложно. Существует слишком много решений, в зависимости от многих факторов, таких как то, совершили ли вы слияние или отменили его, или были ли новые коммиты с момента вашего слияния. Кроме того, вам все еще нужно иметь относительно глубокое понимание git, чтобы адаптировать эти решения к вашему случаю. Если вы слепо следуете некоторым инструкциям, вы можете получить "пустое слияние", при котором ничто не будет объединено, и дальнейшие попытки слияния заставят Git сказать вам "Уже в курсе".

Решение:

Допустим, вы хотите объединить dev в feature-1,

  1. Найдите ревизию, которую вы хотите получить слияние:

    git log --oneline feature-1
    a1b2c3d4 Merge branch 'dev' into 'feature-1' <-- the merge you want to undo
    e5f6g7h8 Fix NPE in the Zero Point Module <-- the one before the merge, you probably want this one
    
  2. Проверьте это (вернитесь во времени):

    git checkout e5f6g7h8
    
  3. Создайте новую ветку оттуда и проверьте ее:

    git checkout -b feature-1
    

Теперь вы можете перезапустить слияние:

  1. Объединение: git merge dev

  2. Исправьте ваши конфликты слияния.

  3. Commit: git commit

  4. Когда вы будете удовлетворены результатами, удалите старую ветку: git branch --delete feature-1

Просто создайте новую ветку, а затем выберите нужные коммиты.

Это делает его более простым и безопасным после сброса, описанного во многих ответах выше

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

Если вы новичок, вам может понравиться графический подход:

  1. Подать мяч gitk (из командной строки или щелкните правой кнопкой мыши в браузере файлов, если у вас есть)
  2. Вы можете легко найти коммит слияния там - первый узел сверху с двумя родителями
  3. Перейдите по ссылке на первого / левого родителя (та, что в вашей текущей ветке до слияния, обычно красная для меня)
  4. На выбранном коммите щелкните правой кнопкой мыши "Reset branch to here", выберите там hard reset

Я думаю, что вы можете сделать git rebase -i [hash] [branch_name] где [hash] является идентифицирующим хешем для того места, которое вы хотите перемотать назад, плюс один (или сколько коммитов вы хотите вернуться), а затем удалите строки для коммитов в редакторе, которые вам больше не нужны. Сохраните файл. Выход. Молиться. И это должно быть перемотано. Возможно, вам придется сделать git reset --hard, но это должно быть хорошо на этом этапе. Вы также можете использовать это для извлечения определенных коммитов из стека, если вы не хотите сохранять их в своей истории, но это может оставить ваш репозиторий в состоянии, которое вы, вероятно, не хотите.

  1. Сначала убедитесь, что вы все совершили.

  2. Затем верните ваш репозиторий в предыдущее рабочее состояние:

    $ git reset f836e4c1fa51524658b9f026eb5efa24afaf3a36
    

    или используя --hard (это удалит все локальные, не зафиксированные изменения!):

    $ git reset f836e4c1fa51524658b9f026eb5efa24afaf3a36 --hard
    

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

  3. Проверьте, какие коммиты вы хотите повторно зафиксировать в верхней части предыдущей правильной версии:

    $ git log 4c3e23f529b581c3cbe95350e84e66e3cb05704f
    
    commit 4c3e23f529b581c3cbe95350e84e66e3cb05704f
    
    ...
    
    commit 16b373a96b0a353f7454b141f7aa6f548c979d0a
    
    ...
    
  4. Примените свои правые коммиты в верхней части правой версии вашего хранилища:

    • Используя cherry-pick (изменения, внесенные некоторыми существующими коммитами)

          git cherry-pick ec59ab844cf504e462f011c8cc7e5667ebb2e9c7
      
    • Или путем выбора диапазона коммитов:

      • Сначала проверьте правильные изменения, прежде чем объединить их:

        git diff 5216b24822ea1c48069f648449997879bb49c070..4c3e23f529b581c3cbe95350e84e66e3cb05704f
        
      • Сначала проверьте правильные изменения, прежде чем объединить их:

        git cherry-pick 5216b24822ea1c48069f648449997879bb49c070..4c3e23f529b581c3cbe95350e84e66e3cb05704f
        

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

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

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