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上で操作を始める前に、次の内容を確認します。
| 確認項目 | 確認する理由 |
|---|---|
| 対象のプルリクエスト | 別の変更を誤って戻さないため |
| マージ先ブランチ | main、developなど対象環境を確認するため |
| リポジトリの書き込み権限 | 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用プルリクエストをマージし、デプロイしたあとは、次の順序で確認します。
- デプロイが正常に完了していることを確認する
- 対象の不具合が解消していることを確認する
- 主要な既存機能が動作していることを確認する
- エラーログや監視アラートを確認する
- データベースや外部サービスの状態を確認する
- 関係者へ対応完了を共有する
- 原因調査と修正版の作成を進める
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”

コメント