すでにリモートブランチにプッシュされているマージコミットを元に戻すにはどうすればよいですか?


962

git revert <commit_hash>単独では機能しません。-m指定する必要があり、私はそれについてかなり混乱しています。

誰もがこれを以前に経験しましたか?


3
この質問に対する答えを見てみましょう:stackoverflow.com/questions/2318777/...
オイゲン・


ここにあるリンクは、マージされたコミットを元に戻す方法を示す最良の例です:christianengvall.se/undo-pushed-merge-git
SK Venkat

これは、のデザインが誰もが使用するようなワークフローとgit一致しない例ですgit-flowdevelopチェックアウトしている場合は、当然、長年の共有開発ブランチではなく、バグが発生した2コミット機能ブランチを元に戻します。とそれを選ぶ必要があるとんでもない感じ-m 1
pkamb 2017年

2
以前に私に思いつかなかったもう1つの提案-ブランチのコミットのリストの1つが小さい場合、コミットのブランチ全体ではなく、個々のコミットを元に戻すほうが快適だと感じるかもしれません。
Sridhar Sarnobat

回答:


1153

-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

親IDをよりよく理解するには、次を実行します。

git log 8989ee0 

そして

git log 7c6b236

127
二つの数字から8989ee07c6b236どこへ行く1、。どのように理解できますか?
Arup Rakshit 2014年

12
元に戻した後、ソースブランチのコードを簡単に修正して再度マージできるとは思いませんか?kernel.org/pub/software/scm/git/docs/howto/...
IsmailS

10
より良い説明を探してグーグルで検索しているときに、この記事を見つけました。詳細を検討するのに非常に役立ったと思いました。読んだところ、本当に探していたのはRESETコマンドとそれに続く強制プッシュでした。多分それは誰かを助けるでしょう。atlassian.com/git/tutorials/...
Funktr0n

46
@ArupRakshitを実行するgit log 8989ee0git log 7c6b236、答えがわかるはずです。
BMW

4
git log --mergesはすべてのマージを表示し、git log --no-mergesはマージなしの履歴を表示します。ブランチをマージすると、マージされたブランチの履歴がターゲットに取り込まれ、プレーンなgitログを使用して作成するのが難しくなります
Alex Punnen

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が必要だった)ので、2行目は必要ありません。
eka808

1
これは紛らわしいです。2行しかなく、git commitはありません。誰かが編集してください。
ジェイランダム

176

ベンはコミットマージを元に戻すためにどのように語っていますが、それはだ非常に重要そうすることであなたがいることを実感します

"... は、マージによってツリーの変更が発生することは決してないことを宣言します。その結果、後のマージでは、以前に元に戻されたマージの祖先ではないコミットによって導入されたツリーの変更のみが発生します。これは、そうでない場合もあります。何をしたい。(gitのマージmanページ)

記事/メーリングリストメッセージ manページからリンクされているが関与しているメカニズムと考慮事項について詳しく説明します。マージコミットを元に戻した場合、後でブランチを再びマージして、同じ変更が戻されることを期待できないことを理解してください。


81
ただし、本当に必要な場合は、元に戻すことで元に戻すことができます。
ダロア2014年

5
ありがとう。バグなどのためにマージを元に戻し、バグが修正されたらブランチ全体を再マージするユースケースとしてよく知られています。
コンポーネント10

3
あなたが私のようで、後でマージが必要になった場合は、元に戻すか、元に戻した変更を選択することができます。
UnitasBrooks

私の状況では、変更を元に戻すために、「元に戻す」という必要に迫られました。さくらんぼ狩りはもっとすてきな方法かもしれませんか?次回はそれを試してみる...
Steven Anderson

79

これらの手順に従って、誤ったコミットを元に戻すか、リモートブランチをリセットして正しいHEAD /状態に戻すことができます。

  1. リモートブランチをローカルリポジトリにチェックアウトします。
    git checkout development
  2. git logからコミットハッシュ(つまり、間違ったコミットの直前のコミットのID)をコピーする git log -n5

    出力:

    commit 7cd42475d6f95f5896b6f02e902efab0b70e8038 "ブランチ 'wrong-commit'を 'development'にマージします。"
    commit f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 "これは間違ったコミットです"
    commit 3779ab50e72908da92d7athisthiscommitthisdmitacomdthisacomddthiscdcdccf72

  3. ブランチを前の手順でコピーしたコミットハッシュにリセットする
    git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)

  4. を実行しgit statusて、間違ったコミットの一部であったすべての変更を表示します。
  5. 単に実行git reset --hardして、すべての変更を元に戻します。
  6. ローカルブランチを強制的にリモートにプッシュし、汚染される前のコミット履歴がクリーンであることを確認します。
    git push -f origin development

4
その間に20人の開発者が最新の開発マージをプルした場合はどうなりますか?
Ewoks 2018

2
開発ブランチを使用しているチームに20人の開発者がいる場合、開発ブランチを強制的にプッシュすることはしません。:)その場合、単にコミットを元に戻すのが賢明です。
ssasi

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ログ)から取得されます。


ヒント:会社でこれを行っている場合、権限がない可能性があります。
eneski

3
push -f共有リポジトリでは絶対に行わないでください
Baptiste Mille-Mathias

17

ロールバックする最も効果的な方法は、ステップバックして置換することです。

git log

2番目のコミットハッシュ(完全なハッシュ、リストされたミスの前に元に戻したいハッシュ)を使用し、そこから再分岐します。

git checkout -b newbranch <HASH>

次に、古いブランチを削除し、新しいブランチをその場所にコピーして、そこから再起動します。

git branch -D oldbranch
git checkout -b oldbranch newbranch

ブロードキャストされている場合は、すべてのリポジトリから古いブランチを削除し、やり直したブランチを最も中央にプッシュして、すべてに戻します。


4
放送に関する警告は、これがどれほどひどい考えであるかについて、より明確にすべきです。これはそのブランチのすべてのバージョンを破壊し、あなただけがアクセスできるリモートリポジトリ(github / bitbucket)で作業している場合にのみ本当に役立ちます。
RobbyD 2017年

変更された構成ファイルをダウンストリームで本番環境にプッシュするほど悪くはありません。破損することはなく、以前のコミットからの再分岐なので、ブランチポインタを以前のバージョンに移動するための迂回的な方法です。うまくいけば、それはローカルリポジトリにのみ影響します
ppostma1 '12

5

mergeコミットを元に戻したい場合は、次のようにします。

  1. 最初に、git logマージコミットのIDを確認します。マージに関連する複数の親IDも見つかります(下の画像を参照)。

ここに画像の説明を入力してください

黄色で示されているマージコミットIDを書き留めます。親IDは、次の行にとして記述されているものですMerge: parent1 parent2。さて...

ショートストーリー:

  1. マージが行われたブランチに切り替えます。次に、コミットメッセージを入力するためgit revert <merge commit id> -m 1viコンソールを開くを実行します。書き込み、保存、終了、完了!

長い話:

  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 logして、どちらの方法にしたいかを判断できます。これがすべての混乱の原因です。


5

すべての回答はすでにほとんどのことをカバーしていますが、5セントを追加します。つまり、マージコミットを元に戻すのは非常に簡単です。

git revert -m 1 <commit-hash>

権限がある場合は、「master」ブランチに直接プッシュできます。それ以外の場合は、単に「revert」ブランチにプッシュしてプルリクエストを作成します。

このテーマに関するより役立つ情報は、https//itcodehub.blogspot.com/2019/06/how-to-revert-merge-in-git.htmlにあります。


1

2つの既知のエンドポイント間にリバースパッチを作成し、そのパッチを適用するとうまくいくことがわかりました。これは、masterブランチからスナップショット(タグ)を作成したか、masterブランチのバックアップでmaster_bk_01012017を作成したことを前提としています。

マスターにマージしたコードブランチが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プロパティがあり、マージにもマージプロパティがあるため、次のように表示されます。

    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

GitHubリポジトリのマスターブランチにマージされたPRでもこの問題に直面しました。

PRによってもたらされたすべての変更ではなく、いくつかの変更されたファイルを変更したかったので、私はwith を使用する必要がamendありmerge commitましたgit commit --am

手順:

  1. 変更/変更したファイルを元に戻すブランチに移動します
  2. 変更されたファイルに従って必要な変更を行います
  3. 実行git add *またはgit add <file>
  4. 実行git commit --amして検証
  5. 走る git push -f

なぜそれが興味深いのですか?

  • PRの作成者のコミットを変更せずに維持します
  • gitツリーを壊さない
  • コミッターとしてマークされます(マージコミットの作成者は変更されません)
  • Gitは競合を解決したかのように動作し、手動でGitHubにそのままマージしないように指示する場合と同様に、変更されたファイルのコードを削除または変更します

0

このリンクからマージを元に戻す方法についての良い説明が見つかりました。以下の説明をコピーして貼り付けました。以下のリンクが機能しない場合に備えて、参考になります。

誤ったマージを元に戻す方法 Alan(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 -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によって元に戻されたため、結果には反映されません。

ライナスは状況を説明します:

通常のコミットを元に戻すと、そのコミットが行ったことが元に戻され、かなり簡単です。ただし、マージコミットを元に戻すと、コミットによって変更されたデータも取り消されますが、マージによる履歴への影響はまったくありません。したがって、マージは引き続き存在し、2つのブランチを結合していると見なされます。今後のマージでは、そのマージが最後の共有状態として認識されます。また、差し込まれたマージを元に戻した復帰は、その影響をまったく受けません。したがって、「元に戻す」はデータの変更を元に戻しますが、それはほとんどありませんリポジトリの履歴に対するコミットの影響を取り消さないという意味での「取り消し」。したがって、「元に戻す」を「元に戻す」と考えると、この部分の復帰を常に見逃すことになります。はい、データは元に戻されますが、履歴は元に戻されません。このような状況では、最初に前の状態に戻す必要があります。これにより、履歴は次のようになります。

---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を使用して元に戻す(場合によっては、元に戻す必要がある場合)の方がはるかに安全な方法です。
oyvind 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.