◀ 32.

Git pushした変更の取消し|revert・履歴変更と保護の注意点

▶

Git pushした変更の取消しは、revertで打ち消す方法と公開済み履歴の変更を区別します。push拒否をサーバー直接操作で回避しません。

Git pushした変更の取消しは、revertで打ち消す方法と公開済み履歴の変更を区別します。push拒否をサーバー直接操作で回避しません。

この記事の要点
  • pushした変更を打ち消す方法と、公開済み履歴そのものを書き換える方法を区別する
  • 共有済みの変更はまずrevertを検討。resetとforce pushを基本手順にしない
  • push拒否は設定・保護・権限等を確認。サーバー側resetで保護を迂回しない
  • 本番運用のブランチ(main / master / release)には原則 force push しない。代わりに git revert で打ち消しコミットを積む
  • 実施前に必ずバックアップブランチ(git branch backup-YYYYMMDD)を取り、チームに事前告知する
公開済み履歴の強制更新に注意。revertは旧履歴を残して新commitを追加しますが、resetと強制pushは参照を書き換えます。利用者/PR/CI・保護・退避/復旧を確認し、拒否を回避するために直接サーバーを操作しません。

push 取り消しの 3 つの方針

方針概要安全性
A. revert で打ち消す新しいコミットで変更を相殺。履歴は残る共有履歴を残せるが、競合/依存の検証と通常のreviewが必要
B. reset --hard + push -f履歴を強制的に過去へ巻き戻す個人名のbranchでも共有済みの場合がある。強制更新は別途合意/保護/復旧が必要
C. リモート側で reset旧案内の訂正対象。サーバー直接操作をpush拒否の回避手段にしない× 共有リポへの直接操作

A. 推奨:git revert で打ち消す

revertは対象変更を打ち消す新commitを追加し、旧commitは履歴に残します。共有履歴を巻き戻さずに済みますが、後続変更との競合・依存やアプリ動作への影響があるため、無事故の保証ではありません。

この旧例の前提と注意:IDとmasterは実在する対象へ読み替える旧例です。revertは端末では既定でメッセージeditorが開き得ます。競合時はstatus/内容を確認し、解決後にcontinue、中断ならabortを選びます。pushも送り先と保護/CIを確認して行います。

# 取り消したいコミット ID を確認
git log --oneline -10

# 打ち消しコミットを作成(メッセージは自動生成)
git revert c545149

# リモートへ通常 push
git push origin master

c545149..HEADはc545149から到達可能なcommitを除外した集合で、c545149自体を含みません。範囲の一覧と順序を先に確認します。merge commitには主線とする親番号の選択が必要で、-m 1を常に使うわけではありません。mergeのrevertは後の再mergeで取り込まれる変更にも影響します。

B. 強制:reset --hard + push -f

reset/force pushは秘密値やcommitを全コピーから完全に消す操作ではありません。個人用branchでも既に他者/PR/CIが利用している場合があります。main / master / release 等の共有ブランチではやらないでください。

この旧例の前提と注意:この旧ブロックをそのまま実行しません。退避branchは未コミット作業の保存ではなく、hard resetは手元の作業を失い得ます。forceは共有refを書き換え、leaseも無条件の安全保証ではありません。今回は実強制更新を検証しません。

# まずバックアップブランチを作る
git branch backup-before-reset

# 戻したいコミット(=この時点を最新にする)まで巻き戻す
git reset --hard c545149

# リモートへ強制 push
git push -f origin master

c545149 の部分には戻したいコミットのリビジョン番号を指定します。HEAD~1 のように相対指定も可能(直前 1 件を取り消し)。

reflogはローカルの参照記録で、期限/到達可能性/設定やオブジェクト保持に依存します。90日間の完全復旧保証ではなく、HEAD@{1}も固定の復旧先ではありません。未コミット作業や相手側のコピーを復元できるとは扱いません。

C. --shared 付きベアリポジトリで拒否されるとき

git init --sharedでは既定でreceive.denyNonFastForwardsが有効になります。一方、単なるclone --bareだけで同じ拒否設定になるとは限りません。実際の設定/保護/hook等を確認します。非fast-forward更新とforceという要求も同義ではなく、forceを付けてもサーバー側の拒否を解除できません。次は拒否出力の一例です。

remote: error: denying non-fast-forward refs/heads/master (you should pull first)

次の旧サーバー操作は推奨しません。保護で拒否された更新を直接ref操作で迂回せず、管理者と運用方針を確認します。HEADがどのbranchを指すかも不明なままresetしません。

この旧例の前提と注意:この旧例は保護を迂回するための手順として使いません。SSH接続/サーバーresetは今回実行しません。bareのHEADと対象branchを混同せず、必要な管理操作は権限・合意・退避/復旧を含む別の運用計画に従います。

# サーバへ SSH 接続
ssh user@git-server

# ベアリポジトリへ移動
cd /srv/git/myrepo.git

# 該当コミットへ HEAD を巻き戻す
git reset --soft c545149

bareでもsoft resetがref等を変更し得ることと、共有運用上安全であることは別です。他者へ一律のhard resetを依頼せず、各自の未共有commit/未保存作業と旧新履歴を確認して復旧を設計します。サーバーの保護設定も弱めません。

事前に必ずやること

  • git branch backup-YYYYMMDD でバックアップブランチを作る
  • 関係するメンバーに force push の時刻と対象ブランチを事前告知する
  • 取り消し対象のコミット ID(git log --oneline)と戻したい先のコミット ID をメモする
  • 退避branchはcommitのみ、通常stashは追跡対象の作業/indexが中心。未追跡/ignored/submodule等も別に確認し、全作業が保存されたと決めつけない

force push 後のチームメンバーの復旧手順

force push後のpullが必ず競合するわけではありません。旧新の履歴・手元の独自commitと作業状態を確認します。残したい変更を退避し、必要なcommitの再適用などを個別に判断します。次の一律hard resetは標準復旧手順ではありません。

この旧例の前提と注意:次は必要な未共有commit/未保存作業を破棄し得ます。強制更新後の全員共通の復旧命令ではありません。先に各自の状態と旧新OIDを確認し、保全した作業をどの履歴へ再適用するか判断します。

# 最新を取得
git fetch origin

# ローカルブランチを破棄してリモートに合わせる
git reset --hard origin/master

関連

履歴を残して打ち消す新規repoの練習

Bash/Git 2.28以降、空フォルダ用です。既存のrevert-practice/revert-remote.gitがあれば停止します。実サービスに接続せず、新規ローカルbareへ通常pushだけを行います。練習commitをrevert --no-editで打ち消し、元commitが祖先として残ることを確認します。

set -eu
for path in revert-practice revert-remote.git; do
    if [ -e "$path" ] || [ -L "$path" ]; then printf 'Already exists: %s\n' "$path" >&2; exit 1; fi
done
git init -b main revert-practice
git -C revert-practice config user.name Training
git -C revert-practice config user.email training@example.invalid
printf 'base\n' > revert-practice/sample.txt
git -C revert-practice add sample.txt
git -C revert-practice commit -m Base
git init --bare -b main revert-remote.git
git -C revert-practice remote add origin ../revert-remote.git
printf 'changed\n' > revert-practice/sample.txt
git -C revert-practice commit -am Change
change=$(git -C revert-practice rev-parse HEAD)
git -C revert-practice push -u origin main
git -C revert-practice revert --no-edit "$change"
git -C revert-practice merge-base --is-ancestor "$change" HEAD
git -C revert-practice push origin main
cat revert-practice/sample.txt
git -C revert-practice log --oneline -3

最終ファイルはbaseとなり、元の変更commitと打ち消しcommitの両方が残ります。秘密値をcommitした場合はこれで消せたとは扱わず、まず資格情報の失効/ローテーションと利用先への影響を確認します。履歴の書換えは別の合意・作業です。

複数変更・mergeを打ち消す前の確認

通常commitのrevertはその親との差分を逆適用するため、後続の変更があると競合したり意図しない状態になることがあります。対象集合をrev-list/log等で確認し、mergeならshow --no-patch --format=%P等で親を調べ、どの親から見た差分を取り消すかを決めます。-mは親番号であって、branch名や常に1という定数ではありません。

共有先へ反映する前に差分・ビルド/テスト・reviewと保護条件を確認します。revertのcontinue/abortも進行中の状態に応じた代替操作で、全て続けて実行しません。

取込みはpull、履歴編集はrebase、送信条件はpushへ。Git親ページの子ページから選択してください。

公式資料と検証範囲

2026年10月にreset・revert・initのshared・clean・restore・checkout・stash・reflog・submoduleを確認しました。旧コードを保持し誤案内は近接訂正しています。新規ローカルrepoで追加例とrestore/revert・設定/範囲を検査しましたが、hard reset・実clean・強制更新/サーバー直接変更、実サービスの権限/保護/CIや全submodule構成を検証したものではありません。

子ページ

子ページはありません

同階層のページ
  1. 用語一覧
  2. エラー一覧
  3. git本体のインストール(Linux)
  4. Linuxサーバーへのgit導入とクライアントのセットアップ
  5. リモートリポジトリをローカルリポジトリとしてクローンする方法
  6. リモートとローカルのリポジトリを同期(pull)する方法
  7. 設定の確認
  8. gitユーザー名とemailの設定
  9. リモートリポジトリの作成
  10. ローカルリポジトリの作成
  11. 新規ファイル/ディレクトリをインデックスに登録
  12. インデックスの登録状態を確認
  13. ローカルリポジトリの変更をコミット
  14. コミット履歴の確認
  15. クライアントからリモートリポジトリの接続設定、確認、削除
  16. リポジトリへのプッシュ
  17. リモートリポジトリからクライアントへのSSHクローン
  18. リモートとローカルの差分表示
  19. バージョンの確認
  20. プロキシの設定
  21. ローカルをリモートリポジトリの状態に戻す
  22. ブランチの作成, 一覧表示, 切り替え
  23. ブランチのマージと削除
  24. リベース
  25. .gitignoreの書き方
  26. .gitignoreの設定が反映されない場合
  27. 特定のファイルをgitの管理から外す方法
  28. 参照(ORIG_HEAD, HEAD, FETCH_HEAD)
  29. git rm [-r --cached] の取り消し
  30. 一部のディレクトリ/ファイルのみをリポジトリから復元する方法
  31. ローカルとリモートリポジトリの有無を同期
  32. pushの取消し方法
  33. マージツールの起動方法
  34. Gitで「MERGING」の状態

最近更新/作成されたページ