GitHubでマージ済みPRをRevertする方法|履歴を消さずに変更を戻す

GitHubでマージ済みのプルリクエストに不具合が見つかり、共有履歴を残したまま変更を戻したい場合は、元のプルリクエストにある「Revert」を使います。

Revertは元のプルリクエストやコミット履歴を削除する機能ではありません。元のマージコミットによる変更を打ち消すための新しいプルリクエストを作成します。作成されたプルリクエストの差分を確認し、テストとレビューを行ってからマージするのが基本的な流れです。([GitHub Docs][1])

ただし、Revertボタンを押しただけでは変更は戻りません。新しく作成されたプルリクエストをマージし、必要に応じて本番環境へデプロイする必要があります。また、データベースや外部サービスに書き込まれたデータは、コードのRevertだけでは元に戻らない点にも注意が必要です。

目次

GitHubのRevertは履歴を削除せずに変更を打ち消す

GitHubのRevertは、すでに共有されている履歴を書き換えるのではなく、元の変更を打ち消す新しい変更を追加する仕組みです。

流れを整理すると、次のようになります。

元のプルリクエストをマージ
        ↓
不具合を確認
        ↓
元のプルリクエストでRevertを選択
        ↓
変更を打ち消す新しいプルリクエストが作成される
        ↓
差分確認・テスト・レビュー
        ↓
新しいプルリクエストをマージ
        ↓
必要に応じて本番環境へデプロイ

元のプルリクエスト、レビュー、マージの記録は残ります。そのうえで、取り消しの理由や作業内容も新しいプルリクエストとして記録できるため、複数人で利用しているリポジトリでは追跡しやすい方法です。

Gitのgit revertも同様に、過去のコミットを消すのではなく、その効果を反転させる新しいコミットを記録します。([Git][2])

Revertを使うべきケースを判断する

マージ後の不具合を見つけたからといって、必ずしもすべてのケースで即座にRevertするとは限りません。障害の影響と、変更を戻すリスクを比較して判断します。

状況選択の目安
特定のプルリクエストを境に明確な不具合が発生したRevertが有力
本番サービスが停止するなど影響が大きい原因調査より先にRevertを検討
数行の修正で安全に直せる修正用プルリクエストも選択肢
Revertすると、その後に追加した正常な変更まで壊れる差分を限定した修正を検討
データベースの変更を含んでいるコード以外の状態も個別に確認
複数のプルリクエストが相互に依存している取り消す順序と影響範囲を確認
機能フラグで無効化できる先に機能を停止し、落ち着いて修正

判断に迷う場合は、まず機能フラグや設定変更で影響を止め、その後にRevertする方法もあります。

重要なのは、Revertを「原因究明の代わり」にしないことです。緊急回避として変更を戻したあと、元の不具合の原因を確認し、修正版を別のプルリクエストとして作成します。

Revertを実行する前に確認すること

GitHub上で操作を始める前に、次の内容を確認します。

確認項目確認する理由
対象のプルリクエスト別の変更を誤って戻さないため
マージ先ブランチmaindevelopなど対象環境を確認するため
リポジトリの書き込み権限Revertには書き込み権限が必要なため
マージ後に追加された変更Revertとの競合や依存関係を確認するため
CIのテスト内容取り消し後の状態を自動確認できるか判断するため
デプロイ方法マージ後に本番へ反映される条件を確認するため
データベースや外部サービスコード以外に残る変更を確認するため

GitHubのRevertを利用するには、対象リポジトリへの書き込み権限が必要です。権限がない場合は、リポジトリ管理者や担当者に操作を依頼します。([GitHub Docs][1])

マージ済みプルリクエストをRevertする手順

対象のプルリクエストを開く

対象リポジトリを開き、「Pull requests」から取り消したいマージ済みプルリクエストを選択します。

この段階で、次の点を再確認します。

  • 不具合が発生した時刻とマージ時刻が一致しているか
  • 対象プルリクエストが正しいブランチへマージされているか
  • 取り消したい変更が、そのプルリクエストに含まれているか
  • 別の変更に依存されていないか

プルリクエストのタイトルだけで判断せず、「Files changed」やマージされたコミットも確認することが重要です。

プルリクエスト下部のRevertを選ぶ

マージ済みプルリクエストの下部にある「Revert」を選択します。

GitHubは、元のマージコミットによる変更を打ち消すための新しいプルリクエストを作成します。Revertを選択した時点では、まだマージ先ブランチの変更は確定していません。([GitHub Docs][1])

Revertが表示されない場合は、まず書き込み権限を確認してください。GitHubの公式ドキュメントでも、Revertが表示されない場合はリポジトリ管理者へ書き込み権限を依頼するよう案内されています。([GitHub Docs][1])

作成された新しいプルリクエストの差分を確認する

Revertによって新しいプルリクエストが作成されたら、そのままマージせず、必ず差分を確認します。

確認すべきポイントは次のとおりです。

確認項目見る内容
取り消し対象元のプルリクエストの変更が打ち消されているか
意図しない変更関係のないコードが削除されていないか
後続変更との関係マージ後に追加された機能を壊していないか
設定ファイル環境変数や設定値が不整合にならないか
API古い呼び出し方法に戻って問題がないか
データ構造現在のデータベース構造と互換性があるか
テスト自動テストや手動確認が成功するか

特に注意したいのが、元のプルリクエストがマージされたあとに、別の開発者が関連コードを変更しているケースです。

例えば、元のプルリクエストで関数の引数を変更し、その後のプルリクエストが新しい引数に合わせて実装されている場合、元の変更だけをRevertすると後続コードが動かなくなる可能性があります。

テストとレビューを行う

Revert用プルリクエストも、通常の変更と同じようにテストとレビューを行います。

最低限、次の状態を確認します。

  • 不具合が再現しなくなった
  • 既存の正常な機能が壊れていない
  • ビルドや自動テストが成功した
  • ステージング環境で主要な操作を確認した
  • データベースとの互換性に問題がない
  • デプロイ後の確認方法が決まっている

保護ブランチでレビューやステータスチェックが必須になっている場合は、Revert用プルリクエストでも設定された条件を満たす必要があります。([GitHub Docs][3])

緊急時でも、差分を見ずに即マージするのは危険です。すべてのテストを実行できない場合は、少なくとも対象機能と主要機能の動作確認を行い、未確認範囲をプルリクエストに記録します。

Revert用プルリクエストをマージする

差分、テスト、レビューに問題がなければ、新しく作成されたプルリクエストをマージします。GitHub公式の手順でも、Revertで作成されたプルリクエストを最後にマージする必要があります。([GitHub Docs][1])

マージ後は、対象ブランチに元の変更を打ち消すコミットが追加されます。

ただし、ここで完了とは限りません。利用している開発環境によっては、さらに次の作業が必要です。

  • 本番環境へのデプロイ
  • コンテナイメージの再作成
  • CDNやアプリケーションキャッシュの削除
  • サービスやワーカーの再起動
  • 機能フラグの切り替え
  • デプロイ完了後の動作確認

CI/CDによる自動デプロイを利用している場合も、ワークフローの成功と本番反映の完了を別々に確認してください。

Revertボタンを押しただけでは本番の変更は戻らない

GitHubのRevert操作で起きることは、基本的に「変更を打ち消す新しいプルリクエストの作成」です。

次の状態は、それぞれ別の段階です。

段階状態
Revertを選択新しいプルリクエストが作成される
Revert用PRをマージリポジトリの対象ブランチに反映される
デプロイを実行サーバーやサービスへ反映される
動作確認を完了実際に不具合が解消したことを確認できる

「Revertを押したのに本番環境が変わらない」という場合は、Revert用プルリクエストが未マージになっていないか、デプロイが実行されているかを確認します。

Revertボタンが使えない場合の対処方法

書き込み権限を確認する

Revertが表示されない場合は、対象リポジトリへの書き込み権限を確認します。

権限がない場合は、次の情報をリポジトリ管理者へ伝えるとスムーズです。

対象リポジトリ:
対象プルリクエスト:
対象ブランチ:
Revertが必要な理由:
影響している環境:
希望する対応時刻:

緊急だからといって、個人の権限を一時的に過剰に広げる必要はありません。既存の運用ルールに従い、権限を持つ担当者にRevert用プルリクエストの作成を依頼します。

マージ競合が発生していないか確認する

元のプルリクエストをマージしたあとに同じファイルが変更されていると、単純に変更を打ち消せないことがあります。

GitHub公式ドキュメントでは、Revertによって競合が発生する場合や、元のプルリクエストがGitHub上でマージされていない場合、個別のコミットを取り消す必要があると案内されています。([GitHub Docs][1])

この場合は、ローカル環境でgit revertを実行し、競合を解決したブランチからプルリクエストを作成します。

コマンドで変更を取り消す方法

GitHub上のRevertを利用できない場合は、git revertで対象コミットを打ち消す方法があります。

次のコマンドは説明用の例です。ブランチ名、プルリクエスト番号、コミットIDは対象リポジトリに合わせて置き換えてください。

git fetch origin
git switch -c revert-pr-123 origin/main
git revert <commit-sha>
git push -u origin revert-pr-123

プッシュ後、revert-pr-123からmainへ向けたプルリクエストを作成します。

通常のコミットを取り消す場合は、次の形式です。

git revert <commit-sha>

マージコミットを直接取り消す場合は、メインラインとなる親コミットを指定する必要があります。

git revert -m 1 <merge-commit-sha>

-m 1は、最初の親をメインラインとして扱う指定です。リポジトリのマージ構造によって適切な親番号が異なる可能性があるため、内容を確認せず機械的に実行しないでください。Gitの公式ドキュメントでも、マージコミットのRevertではメインラインとなる親の指定が必要と説明されています。([Git][2])

競合が発生した場合

git revertの実行中に競合が発生した場合は、競合箇所を修正してから処理を続けます。

git status

競合したファイルを修正したあと、ステージングしてRevertを続行します。

git add <修正したファイル>
git revert --continue

処理を中止して、Revert開始前の状態へ戻す場合は次を実行します。

git revert --abort

--continue--abortは、競合が発生したRevert処理を続行または中止するための正式なサブコマンドです。([Git][2])

競合を解消するときは、単純に「元の状態へ戻す」だけでなく、現在のコードと整合する形に調整する必要があります。

データベースや外部状態は別に戻す必要がある

コードのRevertで取り消せるのは、Gitで管理されている変更です。

次のような状態は、プルリクエストをRevertしても自動的には元に戻りません。

対象必要になる可能性がある対応
データベースのデータ修正SQL、復元処理、データ移行
テーブルやカラム互換性を確認したマイグレーション
オブジェクトストレージ作成・更新したファイルの確認
メッセージキュー未処理メッセージや重複実行の確認
外部API外部サービス側で作成されたデータの取消
キャッシュキャッシュ削除や再生成
環境変数設定値の復元
シークレットローテーションや再設定
機能フラグ有効・無効状態の確認

例えば、プルリクエストによって新しいカラムへデータを書き込み始めた場合、コードをRevertしても書き込まれたデータは残ります。

反対に、Revertに合わせてカラムをすぐ削除すると、旧バージョンと新バージョンのアプリケーションが一時的に混在する環境では障害が広がることがあります。データベース変更はコードのRevertと切り離し、現在稼働しているアプリケーションとの互換性を確認してから実施します。

履歴を消す強制プッシュで代用しない

共有ブランチの変更を戻す目的で、git resetと強制プッシュを安易に使うのは避けます。

Revertと強制プッシュでは、目的が異なります。

方法履歴共有ブランチでの扱い
GitHubのRevert取り消し用の履歴が追加される適している
git revert取り消し用コミットが追加される適している
git reset後の強制プッシュ公開済み履歴を書き換える原則として避ける
修正用プルリクエスト問題部分を追加修正する状況に応じて有効

保護ブランチでは、初期設定として強制プッシュを無効にでき、レビューやステータスチェックを必須にできます。共有ブランチの安全性を保つためにも、取り消し作業はプルリクエストとして記録する運用が適しています。([GitHub Docs][3])

履歴を書き換えると、すでにブランチを取得している開発者のローカル履歴と食い違う可能性があります。元の変更が存在したことや、なぜ取り消したのかも追跡しにくくなります。

Revert用プルリクエストに記載しておきたい内容

Revert用プルリクエストには、単に「不具合があったため戻します」と書くだけでなく、判断材料を残します。

次のテンプレートを利用できます。

## Revertする理由

対象の変更をマージ後、〇〇の操作で△△の不具合が発生したため、
影響を止める目的で変更を取り消します。

## 対象

- 元のプルリクエスト:
- 対象ブランチ:
- 影響している環境:
- 影響している機能:

## 確認内容

- [ ] Revert後の差分を確認した
- [ ] 自動テストが成功した
- [ ] 対象機能を確認した
- [ ] 主要な既存機能を確認した
- [ ] データベースへの影響を確認した
- [ ] 外部サービスへの影響を確認した

## デプロイ後の確認

- 確認する画面・API:
- 確認担当者:
- 問題が残った場合の対応:

## 今後の修正

原因:
修正版のプルリクエスト:
再発防止策:

この情報を残しておくと、後から「なぜ元に戻したのか」「どこまで確認したのか」を判断しやすくなります。

Revert後に確認するチェックリスト

Revert用プルリクエストをマージし、デプロイしたあとは、次の順序で確認します。

  1. デプロイが正常に完了していることを確認する
  2. 対象の不具合が解消していることを確認する
  3. 主要な既存機能が動作していることを確認する
  4. エラーログや監視アラートを確認する
  5. データベースや外部サービスの状態を確認する
  6. 関係者へ対応完了を共有する
  7. 原因調査と修正版の作成を進める

GitHubでマージ済みプルリクエストの変更を履歴を消さずに戻す場合は、元のプルリクエストからRevertを選び、新しく作成されたプルリクエストを確認してマージします。

次に行うべきことは、対象のマージ済みプルリクエストを開き、書き込み権限、変更差分、後続変更との依存関係を確認することです。そのうえでRevert用プルリクエストを作成し、テスト、レビュー、マージ、デプロイ後の確認までを一つの作業として進めてください。
[1]: https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/reverting-a-pull-request “Reverting a pull request – GitHub Docs”
[2]: https://git-scm.com/docs/git-revert “Git – git-revert Documentation”
[3]: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches “About protected branches – GitHub Docs”

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次