Gitの10大トラブル対処法:Detached HEADから削除したコミットの復元まで

Git tutorial - IT technology blog
Git tutorial - IT technology blog

ターミナルのエラーに慌てないで

デプロイ直前に真っ赤なエラーメッセージが表示されるのは、すべての開発者にとって悪夢です。同僚が git reset --hard を誤って入力し、1週間分のコードを失いかけて泣きそうになっていたのを目の当たりにしたことがあります。

Gitは非常に柔軟ですが、ポインタ(pointers)の仕組みを正しく理解していないと罠にハマりやすいです。ディレクトリを削除して git clone し直すのではなく、Gitをコントロールする方法を学びましょう。ここでは、技術的な思考でスマートに解決できる10のトラブル事例を紹介します。

Gitは実質的なタイムマシンである

Gitでは、一度 commit したものなら、ほとんどの場合失われることはありません。Gitはすべての変更履歴を Reference Logs (reflog) という隠れたログに記録しています。.gitディレクトリの構造を理解し、この鍵を握れば、Gitは決して扱いにくいシステムではなくなります。

1. 「Detached HEAD」エラー — 宙ぶらりんの状態

このエラーは、ブランチ名ではなく特定のコミットIDに対して git checkout したときに発生します。この状態では、HEADはどのブランチも指していません。コードを書くことはできますが、新しいコミットはどのブランチにも属しません。

# よくあるメッセージ:
# You are in 'detached HEAD' state.

対処法: もしこの状態でいくつか機能を実装してしまった場合は、すぐに新しいブランチを作成して作業内容を保存しましょう。

# 現在の場所で新しいブランチを作成
git checkout -b recovery-branch

# メインブランチに戻ってマージする
git checkout main
git merge recovery-branch

2. Push Rejected (Non-fast-forward)

コードをpushしようとして [rejected] main -> main (fetch first) というメッセージが出ることがあります。これは、同僚が先にサーバーへpushしたため、手元のリポジトリがサーバーより古くなっている場合に起こります。

警告: 共同プロジェクトで git push --force を使うのは絶対にやめましょう。このコマンドはサーバー上のチーム全員のコミット履歴を上書きして消し去ってしまいます。安全のために Branch Protection Rules を活用してブランチを保護しましょう。

安全な対処法:

# 方法1: Rebase (コミット履歴を一本に保つ)
git pull --rebase origin main
git push origin main

# 方法2: 通常のマージ
git pull origin main
# コンフリクトがあれば解消してからpush

3. 誤ってMainブランチにコミットしてしまった

feature-login ブランチで作業すべきところを、急いでいて main に直接コミットしてしまうのはよくある話です。

対処法: それらのコミットを正しいブランチに移動し、mainを元の状態に戻します。

# 1. 直近のコミットを保持するために新しいブランチを作成
git branch feature-login

# 2. mainブランチを戻す (例: 3つ前のコミットまで戻す)
git reset --hard HEAD~3

# 3. featureブランチに切り替えて作業を続ける
git checkout feature-login

4. 誤って `git reset –hard` したデータを救出する

ここで git reflog が真価を発揮します。もしresetコマンドで重要な5つのコミットを消してしまったとしても、安心してください。Gitにはまだその痕跡が残っています。

git reflog

# 失ったコミットの内容が含まれる行(例: ab12345)を探す
# その時点の状態に正確に復元する
git reset --hard ab12345

5. 直前のコミットメッセージを修正する

コミットした直後にタイポに気づいたり、設定ファイルを git add し忘れたりすることがあります。履歴を汚す新しいコミットを作る代わりに、--amendgit commit –fixup を使いましょう。

# 忘れていたファイルを追加
git add config.json

# 直前のコミットを上書き
git commit --amend --no-edit

# メッセージ内容だけを変更したい場合
git commit --amend -m "より適切な新しいメッセージ"

6. 失敗したMerge/Rebaseを中止する

コンフリクト(衝突)の解消が複雑すぎてコードがめちゃくちゃになってしまうことがあります。マージ前の状態に戻して、やり直したい場合に有効です。

# 即座に中止して元の状態に戻す
git merge --abort

# rebaseの途中の場合も同様
git rebase --abort

7. .gitignoreにあるのに追跡され続けるファイル

.env.gitignore に追加したのに、ファイルを修正するたびにGitが変更を検知してしまうことがあります。これは、そのファイルが以前にGitのキャッシュに保存されていることが原因です。.gitignoreのグローバル設定やキャッシュの仕組みを理解して正しく除外しましょう。

対処法: ファイルをキャッシュから削除します(ローカルのファイルは削除されません)。

git rm --cached .env
git add .gitignore
git commit -m "環境変数の追跡を停止"

8. 大容量ファイルを誤ってコミットしてしまった

500MBのログファイルや node_modules ディレクトリをうっかりコミットしてしまった場合、後のコミットで削除しても、そのファイルは履歴に残り続け、.git ディレクトリを肥大化させます。

対処法: git-filter-repobfg といったツールを使用します。filter-branch は非常に低速で、現在はGit公式でも非推奨とされているため避けましょう。

# BFGを使用して大きなファイルを素早く削除する例
bfg --strip-blobs-with-ids-larger-than 50M

9. ブランチ名を素早く変更する

feature-ui とすべきところを feture-ui と打ち間違えるのはよくあることです。ローカルで名前を修正してから、サーバーを更新しましょう。

# ローカルで名前を変更
git branch -m feture-ui feature-ui

# サーバー上の間違ったブランチを削除し、正しいブランチをpush
git push origin --delete feture-ui
git push origin feature-ui

10. 大文字・小文字の区別(Case-Sensitivity)によるエラー

Windowsは User.jsuser.js を区別しませんが、Linux(サーバー)は区別します。これがデプロイ時のエラーの原因になることがよくあります。Gitは、小文字から大文字へのファイル名変更を認識しない場合があります。

対処法: git mv コマンドを使って、Gitに名前の変更を強制的に認識させます。

git mv user.js User.js

# または、Gitが常に大文字小文字を区別するように設定
git config core.ignorecase false

プロジェクトでの実体験から

Gitを扱うのはシステムを運用するのと同じで、ミスを完全に避けるのは難しいです。しかし、安易に --force を使うのではなく、安全なコマンドを優先しましょう。

私からのちょっとしたコツ:rebaseやfilter-branchなど、履歴を深く操作するコマンドを実行する前には、バックアップ用のブランチを作っておきましょう。もし失敗しても、git restore や git switch を活用すれば、わずか5秒で元の状態に戻せます。クリーンで美しいコミット履歴を目指しましょう。Gitのエラーで徹夜することがなくなりますように!

Share: