Gitのミスを安全に元に戻す方法:Revert、Reset、Checkoutの解説

Gitのミスも自信を持って対処しましょう!このガイドでは、コミットを安全に取り消し、ファイルを復元し、リポジトリの履歴を管理するための`git revert`、`git reset`、`git checkout`について解説します。貴重な作業を失うことなくエラーを修正するために、各コマンドをいつ、どのように使うべきかを学び、あらゆるGitユーザーにとって必読の書となるでしょう。

Gitのミスを安全に取り消す方法:Revert、Reset、Checkoutの解説

Gitのミスは、実際よりも深刻に感じられることがよくあります。間違ったファイルをコミットしたり、ステージングしすぎたり、共有ブランチに悪い変更をプッシュしてしまったりするかもしれません。安全な修正方法は、変更がすでに共有されているかどうかという1つの質問にかかっています。

このガイドでは、git revertgit resetgit checkoutをいつ使うべきかを、慎重にコピーできる例とともに説明します。簡単に言うと、共有コミットにはgit revert、ローカルでのクリーンアップにはgit reset、ブランチの切り替えや古いファイル状態の復元を意図する場合にのみgit checkoutを使用します。

まず、取り消そうとしているものを確認する

取り消しコマンドを実行する前に、現在の状態を確認しましょう。

git status
git log --oneline --decorate -5

git statusは、ステージングされていない、ステージングされている、または追跡されていない作業があるかどうかを教えてくれます。git logは、最近のコミットとブランチがどこを指しているかを表示します。

失いたくないローカル作業がある場合は、まず簡単な安全用ブランチを作成しましょう。

git branch backup-before-undo

このブランチがあれば、間違ったコマンドを選択した場合でも簡単に戻れます。

すでにプッシュされたコミットにはgit revertを使用する

git revertは、以前のコミットの逆を適用する新しいコミットを作成します。履歴を削除しないため、maindevelop、または他の人がプルしている可能性のある共有ブランチにとって最も安全な選択肢です。

履歴が次のようになっているとします。

A -- B -- C -- D  main

コミットCにバグが混入しましたが、Dは問題がなく、残す必要があります。コミットハッシュを確認します。

git log --oneline

次に、そのコミットだけを元に戻します。

git revert abcdef1

Gitがエディタを開き、元に戻すコミットメッセージを入力します。保存すると、履歴は次のようになります。

A -- B -- C -- D -- E  main

コミットEは、コミットCからの変更を取り消します。誰でも通常どおりにその新しいコミットをプルできます。

マージコミットを元に戻す

マージコミットでは、Gitがメインラインとなる親を認識する必要があるため、追加のフラグが1つ必要です。

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

mainへの通常のマージの場合、-m 1は通常「最初の親を保持する」、つまりマージ先のブランチを意味します。ここで推測しないでください。最初にこれを実行して親を確認します。

git show --summary <merge-commit-hash>

どの親を保持すべきかわからない場合は、チームメイトに確認するか、一時的なブランチでrevertをテストしてください。

ローカルコミットにはgit resetを使用する

git resetは、現在のブランチポインタを移動します。モードによっては、ステージングエリアと作業ファイルも変更できます。主にプッシュしていないコミットに使用します。

ファイルのステージングを解除する

間違ったファイルをステージングした場合は、ファイルの内容を変更せずにステージングを解除します。

git restore --staged unwanted_file.txt

古いGitの例では、次の同等のコマンドがよく使われます。

git reset HEAD unwanted_file.txt

どちらもステージングエリアからファイルを削除します。編集内容はワーキングディレクトリに残ります。

最後のコミットを取り消すが、変更はステージングしたままにする

コミットメッセージが間違っていた場合や、再コミットする前にファイルをもう1つ追加したい場合に--softを使用します。

git reset --soft HEAD~1

ブランチは1つ前のコミットに戻りますが、そのコミットの変更はステージングされたままです。

最後のコミットを取り消し、変更をステージングされていない状態にする

再度コミットする前にファイルをやり直したい場合は、デフォルトのmixedリセットを使用します。

git reset HEAD~1

これにより、ブランチが1つ前のコミットに戻り、変更はワーキングディレクトリに残ります。

ローカルコミットとファイル変更を破棄する

--hardは、ブランチ、ステージングエリア、およびワーキングディレクトリ内の追跡ファイルをリセットします。

git reset --hard HEAD~1

これは、破棄する変更が不要であると確信している場合にのみ使用してください。追跡されていないファイルは削除されませんが、追跡ファイルの編集内容は再確認なしで破棄されます。

git checkoutは注意して使用する

git checkoutは、古いGitワークフローでは2つの一般的な役割があります。ブランチの切り替えとファイルの復元です。新しいバージョンのGitでは、これらの役割はより明確なコマンドに分割されています。ブランチにはgit switch、ファイルにはgit restoreです。

ブランチを切り替えるには:

git switch main

古い同等のコマンド:

git checkout main

追跡されている1つのファイルのコミットされていない変更を破棄するには:

git restore my_file.txt

古い同等のコマンド:

git checkout -- my_file.txt

このファイルの復元は、選択したファイルのコミットされていない編集に対して破壊的です。後で編集が必要になる可能性がある場合は、最初にスタッシュします。

git stash push -m "save work before restore"

やりすぎた場合はgit reflogを使用する

git reset --hardを実行したり、誤ってブランチを移動したりした場合でも、git reflogを使用すると、HEADが以前どこを指していたかが表示されることがあります。

git reflog

HEAD@{2}のような以前のエントリや、リセット前のコミットハッシュが表示される場合があります。そこからリカバリブランチを作成できます。

git branch recovery <commit-hash>

Reflogはクローンにローカルであり、バックアップシステムではありませんが、最近のミスから救ってくれることがよくあります。

どのコマンドを選ぶべきか?

悪いコミットがすでにプッシュされている場合、または他の人がそのコミットに基づいて作業している可能性がある場合は、git revertを使用します。

最後のローカルコミットをやり直す必要があるが、変更はまだ正しい場合は、git reset --softを使用します。

最後のローカルコミットをステージングされていないファイル変更として戻したい場合は、git resetを使用します。

ローカルの追跡された変更を削除できる場合にのみ、git reset --hardを使用します。

特定のファイルのコミットされていない変更を破棄したい場合は、git restoreまたはgit checkout -- <file>を使用します。

最も安全な習慣は、git statusを確認し、リスクが高い場合は一時的なバックアップブランチを作成し、共有履歴を書き換えないことです。コミットが共有リモートに到達した場合、git revertが通常最もクリーンな修正方法です。