ブログに戻る

gitで直前のコミットを取り消す:変更を残して安全に

2026年9月3日

TL;DR:直前のコミットを取り消して変更を残すなら git reset --soft HEAD~1(変更はステージされたまま)、git reset HEAD~1(変更はワーキングツリーに残ります)。すでにpush済みのコミットなら代わりに git revert HEAD を実行しましょう。履歴を書き換えず、取り消し内容を新しいコミットとして作ります。 この3つのコマンドで「コミットが早すぎた」場面のほとんどをカバーできます。以降では各ケースをコピペできるコマンドとともに順に説明し、--soft--mixed--hard の違いを解説し、失敗したときの復旧方法も示します。以下のコマンドは、Linux、macOS、Windowsのいずれの最近のGitでも動作します。

直前のコミットを取り消しつつ、変更は残すには?

いちばんよくある状況です。コミットした直後にtypoを見つけた、ファイルを入れ忘れた、そもそも別のコミットに分けるべきだと気づいた。まだ何もpushしていない。コミットを取り消して、すべて元の場所に戻しましょう:

# コミットはどこにも残らない — 変更はステージングエリアに戻る
git reset --soft HEAD~1

# 変更をワーキングツリーに戻す(アンステージ状態)ならこちら
git reset HEAD~1

HEAD~1 は「HEADが今指している場所より1つ前のコミット」です。どちらのコマンドでもファイルはディスク上で無傷のまま。動くのはブランチポインタだけです。git status で確認できます。--soft なら変更は ステージ済み で、修正を混ぜて再コミットする態勢です。フラグがなければ変更は アンステージ なので、まず自由に編集できます。

直前のコミットにファイルを追加したいだけなら、取り消さないほうが安全な習慣があります:

git add forgotten-file.txt
git commit --amend --no-edit

--amend は直前のコミットをその場で置き換えます(ここではメッセージは変更していません)。なお、 push済みの コミットをamendすると履歴を書き換えることになります。これについては後述します。

—soft、—mixed、—hardの違いは?

ここだけは暗記する価値があります。フラグが変更の行き先を決め、失われるかどうかも決めるからです:

フラグコミットは取り消される?ディスク上の変更ステージされる?典型的な用途
--softはい残るはい小さな修正を加えて再コミット
--mixed(デフォルト)はい残るいいえ変更を組み直し、選んでステージし直す
--hardはい削除作業を完全に捨てる
git revertいいえ(新しいコミット)残るpush済みコミットの取り消し

危険なのは --hard だけです。コミット 変更の両方を捨てます。reset --hard の前には、今あるものをstashするかブランチに退避させてください:

git branch backup-before-reset   # 安い保険
git reset --hard HEAD~1          # コミット + 変更が消える

すでに危険なほうを実行してしまっても、まだ終わりではありません。git reflog はHEADが辿った場所を覚えています:

git reflog                       # 失ったコミットのハッシュを探す
git reset --hard HEAD@{1}        # または: git reset --hard <hash>

reflogはぶら下がったコミットをデフォルトで約90日間保持します。「間違えてhard-resetした」は、ガベージコレクションより先に手を動かせばほぼ常に復旧可能だということです。

すでにpush済みのコミットだったら?

コミットが共有ブランチ(自分のフィーチャーブランチ以外のすべて)に乗っているなら、履歴を書き換えては いけませんgit revert を使いましょう。逆の変更を計算してコミットしてくれます:

git revert HEAD
git push

pullした人は全員、古いコミットの変更を打ち消す新しいコミットを受け取るだけです。force-pushもなければ、壊れる同僚もいません。連続したコミットを取り消すなら、範囲でrevertします:git revert --no-commit HEAD~3..HEAD && git commit

代替手段の git reset --hard HEAD~1 && git push --force-with-lease は、誰も土台にしていないブランチでしか許容されません。そして --force-with-lease(素の --force は決して使わない)だけが唯一の安全な形です。誰かが間にpushしていたら上書きを拒否してくれます。共有ブランチへのforce-pushは、チームがコミットを失い、CIのログが誰のチェックアウトとも謎に一致しなくなる原因です。

直前のコミットを取り消しつつ、あとで使えるよう残しておくには?

コミット自体は良い仕事なのに、住所が間違っていることがあります。間違ったブランチにあるか、早すぎたか。取り消す代わりに、引っ越しさせましょう:

git branch stash-commit          # コミットを新しいブランチに退避
git reset --hard HEAD~1          # それから現在のブランチをきれいにする

現在のブランチに触れずに、そのコミットだけ別のブランチへ持っていくなら:

git cherry-pick <hash>           # 移動先のブランチにいる状態で

reset --softcherry-pickrevert の間に、移動も無効化もできないコミットは存在しません。コツは、フラグに手を伸ばす前に「移動」と「取り消し」を選ぶことです。

どの取り消しを使うべき?判断の早見表

  1. 未push、修正して再コミットしたいgit reset --soft HEAD~1
  2. 未push、選んでステージし直したいgit reset HEAD~1(mixed)
  3. 変更を完全に消したいgit reset --hard HEAD~1(後悔してもreflogが覚えています)
  4. すでに共有ブランチへpush済みgit revert HEAD
  5. コミットは別のブランチにあるべき → 取り消さずに cherry-pick

最後にひとつ、運用上のヒントを。悪いコミットがサーバーまで届いていたら、デプロイフックの失敗や、force-push後のCIランナーの不調などは、次に見るべき場所がGitではなくマシンのログです。systemdのマシンなら journalctl -u <service> -n 100 で何がいつ実行されたか正確に分かります。journalctlチートシートにコピペ用のパターンを揃えてあります。また、diffレビューにローカルLLMを組み込むなら、主要2ツールをOllama vs LM Studioで比較しています。

— mrsaynothing

Get the next one by email

One email per post. No spam, no algorithms.

self-hosted · no third parties · one-click unsubscribe

what is this?

journalctlチートシート:Linuxログの表示、絞り込み、ライブ追跡

記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談