← ブログに戻る

Gitコミットを取り消す方法(作業を失わずに)

tutorial git

取り消したいcommitをしてしまった。どうする?

誰にでも起こることです。早すぎるタイミングでcommitしてしまったり、含めるべきでないファイルを含めてしまったり、間違ったメッセージを書いてしまったり、コードが正しくないことに気づいたり。良いニュース:Gitはこれに対処できるように設計されています。さらに良いニュース:ほとんどの場合、作業を失うことなくcommitを取り消すことができます。

適切なアプローチは一つの重要な質問に依存します:そのcommitはリモートにpushされたものか、それともまだローカルにあるか?ローカルのcommitはより柔軟に対応できます。pushされたcommitは、他の人がすでにあなたの変更をpullしている可能性があるため、より安全な戦略が必要です。

git resetでローカルcommitを取り消す

commitがまだローカル(pushされていない)の場合、git resetが最も一般的なツールです。branchポインタを以前のcommitに戻し、事実上commitを「取り消し」ます。それらの変更に何が起こるかは、使用するフラグによります。

git reset --soft:commitを取り消し、すべてをstagingに保持

git reset --soft HEAD~1

これは最後のcommitを取り消しますが、すべての変更はstaging(インデックス)に保持されます。作業ディレクトリは影響を受けません。git commitを実行しなかったかのように、commitする直前の状態に戻ります。

使用するタイミング:commitメッセージを変更したい、commitにもっとファイルを追加したい、またはcommitをより小さなものに分割したい場合。コードは問題なく、単に早くcommitしすぎただけです。

git reset --mixed:commitを取り消し、変更をstagingから外す

git reset --mixed HEAD~1

これはgit resetのデフォルトの動作です(--mixedフラグは省略可能)。commitを取り消し、変更をstagingから外しますが、ファイルは作業ディレクトリで変更されたまま残ります。何も失われません。

使用するタイミング:commitに何を含めるか再考したい場合。一部のファイルだけをstagingに入れたい、またはcommitし直す前に変更を確認したい場合。

git reset --hard:commitを取り消し、すべての変更を破棄

git reset --hard HEAD~1

これはcommitを取り消し、すべての変更を完全に削除します。作業ディレクトリは前のcommitに一致するようにリセットされます。通常のGit操作では破棄された変更を復元する方法はありません。

使用するタイミング:変更がまったく不要だと確信している場合。うまくいかなかった実験的なコードをcommitしてしまい、白紙の状態に戻したい場合。注意して使用してください。

さらに前に戻す

HEAD~1は現在のHEADの1つ前のcommitを指します。さらに前に戻ることもできます:

git reset --soft HEAD~3

これは直近3つのcommitを取り消し、すべての変更をstagingエリアにまとめます。特定のcommitをハッシュで指定することもできます:

git reset --soft a1b2c3d

git commit --amendで最後のcommitを修正する

最後のcommitを完全に取り消すのではなく、微調整するだけなら、--amendが最も速い選択肢です:

git commit --amend -m "corrected commit message"

これは前のcommitを新しいものに置き換えます。忘れたファイルを追加するためにも使えます:

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

--no-editフラグは元のcommitメッセージを保持します。結果として、「おっと」commitの連続ではなく、単一の修正されたcommitになります。

重要:resetと同様に、amendも履歴を書き換えます。pushされていないcommitにのみ使用してください。pushされたcommitをamendしてforce-pushすると、元のcommitをpullした人全員に問題が発生します。

git revertでpushされたcommitを取り消す

commitが共有リモートにpushされた後は、履歴を書き換えるべきではありません。他の開発者がそのcommitに基づいて作業している可能性があります。代わりにgit revertを使用してください:

git revert HEAD

これは対象のcommitの正確に逆の操作を行う新しいcommitを作成します。元のcommitが行を追加した場合、revert commitはそれを削除します。ファイルを削除した場合、revert commitはそれを復元します。元のcommitは履歴に残り、revertがその上に追加されます。

使用するタイミング:すでにpushされたcommitを取り消す必要がある場合。履歴を書き換えるのではなく追加するため、安全です。

古いcommitをrevertする

最新のcommitだけでなく、任意のcommitをrevertできます:

git revert a1b2c3d

Gitはその特定のcommitの変更のみを取り消すrevert commitを作成しようとします。それらの変更がその後の作業と重なる場合、手動で解決が必要なコンフリクトが発生する可能性があります。

複数のcommitをrevertする

commitの範囲をrevertするには:

git revert HEAD~3..HEAD

これは範囲内の各commitに対して個別のrevert commitを作成します。単一のrevert commitを好む場合は、--no-commitフラグを追加して手動でcommitしてください:

git revert --no-commit HEAD~3..HEAD
git commit -m "revert last 3 commits"

適切なアプローチの選択

状況 方法 変更に何が起こるか
早すぎるcommit、再stagingしたい git reset --soft HEAD~1 変更はstagingに残る
早すぎるcommit、作り直したい git reset --mixed HEAD~1 変更は作業ディレクトリに残る
破棄したいものをcommitした git reset --hard HEAD~1 変更は削除される
commitメッセージの修正やファイルの追加が必要 git commit --amend 前のcommitが置き換えられる
pushされたcommitを安全に取り消す必要がある git revert HEAD 新しいcommitが変更を取り消す

ミスからの復旧

git reset --hardを使ってしまい、使うべきではなかったと気づいた場合でも、復旧する方法があることが多いです。Gitは、HEADが指していた場所のログを保持しており、これをreflogと呼びます:

git reflog

これは最近のHEADの位置のリストを表示します。失ったと思ったcommitを見つけて、そこに戻ることができます:

git reset --hard HEAD@{2}

reflogはあなたのセーフティネットです。エントリはデフォルトで90日間保持されるので、ミスから復旧するための十分な期間があります。

GitSquidでcommitを取り消す

GitSquidは最も一般的な取り消し操作を簡素化します。commitした直後にCmd+Z(WindowsとLinuxではCtrl+Z)を押すと、最後のcommitを取り消し、変更をstagingに保持して作り直す準備ができます。より具体的な操作については、グラフ内の任意のcommitを右クリックすると、コンテキストメニューからresetとrevertのオプションにアクセスでき、フラグを暗記しなくても取り消したいcommitを正確に指定できます。

Gitのcommitを取り消すことはストレスである必要はありません。reset、amend、revertの違いを理解し、状況に合ったものを知れば、自信を持ってミスを修正できます。最も重要な区別はシンプルです:ローカルならresetまたはamend。pushされているならrevert。