Blog

git reset –hard について説明

git reset --hard は、リポジトリを過去の特定の状態に 完全に戻す ための強力なコマンドです。しかし、その強力さゆえに作業内容を失う危険性も伴います。

このコマンドの目的、一般的なエラーとその対処法、そして万が一の場合の復旧方法について詳しく解説します。


git reset --hard の目的と動作

git reset --hard は、以下の3つの要素を すべて 指定したコミットの状態に書き換えます。

  1. HEAD: 現在のブランチが指し示すコミット
  2. インデックス (Staging Area): git add で追加された、次のコミットの候補となるファイルの状態
  3. ワーキングディレクトリ: 現在作業しているファイル群

つまり、指定したコミット以降に行ったすべての変更(コミット、ステージング、ファイル編集)がきれいさっぱり消え去ります

主な使用例

  • 直前のコミットを取り消す: ちょっとした修正漏れや、コミットメッセージの間違いをやり直したいときに使います。Bash# 直前のコミットを取り消し、その変更内容も破棄する git reset --hard HEAD~1
  • 特定の状態に戻って作業をやり直す: 実験的な変更がうまくいかなかった場合など、クリーンな状態から再開したいときに使います。Bash# <commit-hash> の状態に完全に戻る git reset --hard <commit-hash>
  • リモートリポジトリの状態にローカルを強制的に合わせる: ローカルでの変更をすべて破棄し、リモートリポジトリの最新状態にしたいときに使います。Bash# リモートの最新情報を取得 git fetch origin # ローカルのmainブランチをリモートのmainブランチと完全に一致させる git reset --hard origin/main

⚠️ 注意

git reset –hard は、コミットしていない変更(ワーキングディレクトリ内の変更)を復元不可能な形で削除します。実行する前に git status で状態を確認し、必要な変更は git stash などで一時退避させることを強く推奨します。


一般的なエラーと対処法

1. fatal: ambiguous argument '<commit>': unknown revision or path not in the working tree.

  • 原因: 指定したコミットハッシュやブランチ名が存在しない場合に発生します。タイプミスや、存在しない履歴(例: 最初のコミットで HEAD~1 を指定)が原因です。
  • 対処法:
    1. git loggit branch -a を実行し、正しいコミットハッシュやブランチ名を確認します。
    2. 入力した文字列が正しいか再確認して、コマンドを再実行してください。

2. error: you have unmerged paths.

  • 原因: git mergegit rebase の途中でコンフリクト(競合)が発生し、未解決のファイルが残っている状態です。Gitは、データの損失を防ぐため、コンフリクトが解決されるまで reset のような危険な操作を許可しません。
  • 対処法:
    • コンフリクトを解決してマージを完了させる場合:
      1. git status でコンフリクトしているファイルを確認します。
      2. 該当ファイルを開き、<<<<<<<, =======, >>>>>>> のマーカーを参考にコードを修正します。
      3. git add <解決したファイル> で解決したことをGitに伝えます。
      4. git commit を実行してマージを完了させます。
      5. その後、必要であれば git reset --hard を実行します。
    • マージ自体を取りやめる場合:
      • 以下のコマンドで、マージを開始する前の状態に戻ることができます。Bashgit merge --abort

【重要】 間違えてリセットした場合の復元方法

git reset --hard でコミットを消してしまっても、すぐに諦める必要はありません。Gitは、コミットされた変更の履歴を一定期間、内部的に保持しています。この履歴(reflog)を利用して、失われたコミットを復元できます。

git reflog を使った復元の手順

git reflog (reference log) は、HEAD が過去にどのコミットを指していたかの移動履歴を表示するコマンドです。

例:間違えて reset --hard してしまった状況

  1. 間違えてリセットを実行Bash# 現在のコミット履歴 $ git log --oneline a1b2c3d (HEAD -> main) feat: すごい機能を追加 d4e5f6g docs: ドキュメントを更新 h7i8j9k fix: 軽微なバグを修正 # あ、間違えてコミットを1つ消してしまった! $ git reset --hard HEAD~1 HEAD is now at d4e5f6g docs: ドキュメントを更新
  2. git reflog で履歴を確認reset する前の状態を探します。Bash$ git reflog d4e5f6g (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1 a1b2c3d HEAD@{1}: commit: feat: すごい機能を追加 <-- これが復元したいコミット! d4e5f6g HEAD@{2}: commit: docs: ドキュメントを更新 ... reflog の出力を見ると、reset を実行する直前(HEAD@{1})に a1b2c3d というコミットがあったことがわかります。
  3. git reset –hard で元に戻すreflog で見つけたコミットハッシュ(または HEAD@{n} というエイリアス)を指定して、再度 reset を実行します。Bash# コミットハッシュを指定して復元 $ git reset --hard a1b2c3d # または、reflogのエイリアスを指定して復元 # $ git reset --hard HEAD@{1}
  4. 復元されたことを確認git log を実行して、”すごい機能を追加” のコミットが元に戻っていることを確認します。Bash$ git log --oneline a1b2c3d (HEAD -> main) feat: すごい機能を追加 d4e5f6g docs: ドキュメントを更新 h7i8j9k fix: 軽微なバグを修正

最も重要な注意点: この方法で復元できるのは、一度でもコミットされた変更のみです。コミットされていない(ワーキングディレクトリ上やステージングエリアにしかなかった)変更は git reflog にも記録されないため、復元できません。


一つ前の記事 simデータとは 測量