「さっきpullしたらファイルが消えた」——朝一でpullした直後にエディタで開いていたファイルが突然行方不明、プロジェクトのビルドが通らない、git statusが想定外に大量の「deleted:」を出している。そんな瞬間は焦りますが、pull直後ならORIG_HEADを使った90秒復旧がほぼ確実に効きます。Gitは一度取り込んだ情報を1世代前まで自動保存しているからです。
$ git status On branch main Your branch is up to date with origin/main. Changes not staged for commit: deleted: src/utils.ts ← 消えてる deleted: config/local.env ← これも消えてる deleted: docs/notes.md ← さらに消えてる
この記事では、pull後の意図しないファイル削除の7つの典型原因と最速90秒復旧フローを解説します。ORIG_HEAD・HEAD@{1}の活用、コンフリクト誤決定の巻き戻し、上流の削除コミットをrevertで取り戻す方法、force push起因の巻き戻し救出、他クローンやCIログからの最終救出、そして事前に削除を検知するpull戦略まで、現場で即使える形で網羅しました。
この記事で学べること
- 90秒最速復旧(
ORIG_HEADで丸ごと巻き戻し) - pull後の削除が起きる7つの典型パターン
ORIG_HEAD・HEAD@{1}の仕組みと使い分け- 特定ファイルだけ復元する
git restore --source - コンフリクト中の「Deleted by them/us」誤決定のやり直し
- 上流の削除コミットを
revertで打ち消す手順 - rebase中・force push後の削除救出
- 予防:pullする前に差分プレビューする運用設定+自動化スクリプト
【最速90秒】まずこの3手で多くが救える
pullの直後で追加操作をしていなければ、この3ステップで完全に戻せます。追加のcommit/rebase/reset等を挟むと成功率が下がるため、「削除が発覚したら一旦手を止める」が鉄則です。
# ■ 0〜30秒:退避ブランチで保険確保 git switch -c safety/pull-rollback-$(date +%H%M) git switch - # 元のブランチに戻る # ■ 30〜60秒:削除されたファイルを確認 git diff --name-status ORIG_HEAD..HEAD # D path/to/file ← 削除されたファイル # A path/to/new ← 追加されたファイル # ■ 60〜90秒:pull前に完全巻き戻し git reset --hard ORIG_HEAD # もしくは作業ツリー変更を残しつつ履歴だけ戻す git reset --merge ORIG_HEAD # 完了。いつでもyolo safetyブランチから再復元できる
ポイント:ORIG_HEADはmerge・pull・rebase・reset等の「大きな操作の直前のHEAD」をGitが自動保存する特殊参照。pull直後ならORIG_HEAD=pull前の先端なので、git reset --hard ORIG_HEADで完全に巻き戻せます。次の操作でORIG_HEADが上書きされる前に動くのがコツ。
注意:ORIG_HEADは次のmerge/pull/rebase等で上書きされます。pull直後でも別の操作を挟むと失われるので、削除発覚後は即座にreset実行か、safety branchに退避してから他の作業を進めてください。
pull後の削除が起きる7つの典型原因
原因別に対処方法が大きく変わります。git statusの表示・git log --merges・git reflogを見てどのパターンか特定しましょう。
診断の出発点:削除されたファイル一覧
git diff --name-status ORIG_HEAD..HEAD | grep "^D":削除のみ抽出git log --diff-filter=D --name-only ORIG_HEAD..HEAD:削除コミット特定git log --merges --oneline:マージコミット確認git reflog:pull/merge/rebaseの痕跡確認
救出の鍵:ORIG_HEADとHEAD@{1}
復旧作業で最も頻繁に使うのがこの2つの参照です。それぞれ役割が違うので押さえておきましょう。
# ORIG_HEADの中身を確認
cat .git/ORIG_HEAD
git log --oneline -1 ORIG_HEAD
# HEAD@{1} の位置
git log --oneline -1 "HEAD@{1}"
# reflog全体
git reflog
# HEAD@{0}: pull: Fast-forward
# HEAD@{1}: commit: ... ← pull前の状態
# HEAD@{2}: ...
ポイント:pull直後ならORIG_HEAD=HEAD@{1}とほぼ同じ位置を指します。どちらでも同じ結果が得られますが、ORIG_HEADは「pull/merge/rebase専用」、HEAD@{1}は「どの操作でも1つ前」というニュアンスの違いがあります。時間が経ってpull以外の操作を重ねたなら、HEAD@{1}よりgit reflogでさらに古い位置を探す必要があります。
解決①:pull自体をまるごと巻き戻す
「pull前の状態に完全に戻してやり直したい」が最もシンプルで確実な解決策です。まだ追加の作業を重ねていないなら最短ルートです。
# 作業ツリーも含めて完全に戻す(推奨) git reset --hard ORIG_HEAD # 作業ツリーの変更は残しつつ履歴だけ戻す(分離版) git reset --merge ORIG_HEAD # 状態確認 git status git log --oneline -5 # 改めて慎重にpull(diffを先に見てから) git fetch --prune git diff --name-status HEAD..origin/main # 想定外の削除がないか確認してから git merge --ff-only origin/main
注意:reset --hardは未コミットの変更を破棄します。作業中の変更があれば先にgit stash push -u -m "before rollback"で退避、あるいはreset --mergeで作業ツリーを残すほうが安全です。
解決②:特定ファイルだけpull前の状態へ戻す
pullで取り込まれた変更の大半は正しく、一部ファイルだけ復元したいケース。git restore --sourceで対象ファイルだけ過去の状態から持ってこられます。
# ORIG_HEADから特定ファイルを取り出す
git restore --source=ORIG_HEAD -- path/to/fileA path/to/fileB
# 旧コマンド(Git 2.23未満)
git checkout ORIG_HEAD -- path/to/fileA path/to/fileB
# ディレクトリ単位
git restore --source=ORIG_HEAD -- config/ docs/
# 削除された全ファイルを一気に復元
git diff --name-only --diff-filter=D ORIG_HEAD..HEAD | \
xargs -I {} git restore --source=ORIG_HEAD -- {}
# 確認してコミット
git status
git add .
git commit -m "restore: pullで誤削除されたファイルを復元"
git push
ポイント:git restore --sourceは指定したコミット時点のファイル内容をそのまま復元します。履歴は書き換えず、「復元commit」を1つ追加する形なので共有ブランチでも安全。大量ファイルならxargsでパイプ処理すれば一括復活できます。
解決③:コンフリクト解消中に「削除を採用」してしまった
pullやrebaseで「CONFLICT (modify/delete)」や「Deleted by them」「Deleted by us」が出たとき、誤って削除側を採用して確定してしまうとファイルが失われます。解消直後ならORIG_HEADから復活できます。
# merge進行中の状態 git status # deleted by them: path/to/file # 残す側を明示 git checkout --theirs path/to/file # 相手側の変更を採用 git checkout --ours path/to/file # 自分側を採用 git add path/to/file # もしmergeを先に完了してしまった場合 git restore --source=ORIG_HEAD -- path/to/file git add . git commit -m "restore: conflict解消時に誤って削除したファイルを復元" # rebase中なら git add path/to/file git rebase --continue
conflict誤決定を防ぐ
Deleted by them:相手側が削除した→残すなら--ours側の自分版を使うDeleted by us:自分側が削除した→相手側が残したいなら--theirs- 削除を採用したい:ファイルを明示的に
git rmしてからcommit - 不明なら
git log -- path/to/fileで削除commitの文脈確認
解決④:上流の削除コミットをrevertで打ち消す
「誰かがgit rmでcommitして、それがpullで取り込まれた」ケース。履歴を保ちつつファイルを戻したいならgit revertで削除コミットを打ち消します。取り消しcommitが新規に追加される形なので共有ブランチでも安全。
# 削除コミットを探す(削除のみ含むコミットを抽出) git log --diff-filter=D --summary # ファイル名で削除履歴を辿る git log --all --full-history --diff-filter=D -- path/to/file # ハッシュを特定したらrevert git revert <削除SHA> # 複数の削除コミットをまとめて打ち消す git revert --no-commit <開始SHA>..<終了SHA> git commit -m "revert: 上流で誤削除されたファイル群を復元" git push
ポイント:revertは履歴を残しながら打ち消すため、チーム全体へ影響せず誤削除を元に戻せます。削除コミットがマージコミット経由で取り込まれた場合はgit revert -m 1 <マージSHA>になります(詳細はmergeコミットを取り消して履歴を元に戻す方法)。
解決⑤:pull --rebase後にファイルが消えた
rebaseは自分のcommit列を付け替える操作。分岐点付近の削除がそのまま適用されるため、意図せず削除が反映されることがあります。HEAD@{1}やreflogから救出しましょう。
# HEAD@{1} は rebase 前のHEAD
git log --oneline "HEAD@{1}" -1
# reflogで詳細な履歴確認
git reflog | head -20
# abc1234 HEAD@{0}: rebase finished
# def5678 HEAD@{1}: rebase onto origin/main
# ghi9012 HEAD@{2}: ... ← rebase前のcommit
# rebase前の位置からファイル復元
git restore --source="HEAD@{1}" -- path/to/file
# 全部やり直したい場合
git reset --hard "HEAD@{2}" # 適切なreflog位置を指定
rebase中なら途中でも戻せる
rebase進行中にファイル削除を気付いたら、git rebase --abortでrebase開始前に戻れます。完了してしまった場合はHEAD@{N}のNを調整して適切な位置を探すのがコツ。rebase全般のトラブルはリベース途中でエラーになったときの復旧方法も参照。
解決⑥:他メンバーのforce pushで削除が混入した
リモートのforce push(履歴書き換え)で削除されたcommitがある状態でpullすると、手元のファイルが消える事態が起きます。自分のローカルreflogに古い履歴が残っていれば救出可能です。
# reflogで「forced-update」痕跡を探す
git reflog
# abc1234 HEAD@{0}: pull: ... forced-update
# def5678 HEAD@{1}: ... ← force push前の位置
# 古いHEAD位置から復元
git restore --source="HEAD@{1}" -- path/to/file
# 広範囲の削除なら他メンバーのクローンから救出
# メンバーのローカルで:
git bundle create /tmp/recovery.bundle --all
# 自分のマシンで
git fetch /tmp/recovery.bundle "refs/heads/*:refs/remotes/backup/*"
git restore --source=backup/main -- path/to/file
警告:force push起因の削除は、時間差で他メンバーのローカルでも削除が広がります。チーム全員が再同期する前に、誰かのローカルから救出するか、CIキャッシュ/デプロイ先サーバーからgit bundleで回収するのが確実。詳細は誤ってmaster/mainを削除したときの復旧方法のケースFも参考に。
最終手段:他クローン・CI・バックアップから救出
自分のreflogから辿れない、gcが走った、という状況でもあきらめる必要はありません。チームメンバー・CIサーバー・デプロイ先・定期バックアップに同じ履歴が残っている可能性が高いです。
# 【他メンバーのマシンで】 cd /path/to/repo git bundle create /tmp/recovery.bundle --all # USBやSlackでbundle転送 # 【自分のマシンで】 git fetch /tmp/recovery.bundle "refs/heads/*:refs/remotes/backup/*" git restore --source=backup/main -- path/to/lost_file # CIサーバーのcheckout先から ssh ci-server "cd /workspace && cat path/to/file" > ./recovered_file # デプロイ先サーバーから scp prod-server:/app/path/to/file ./recovered_file
IDE の Local History も救済源
- VS Code / Cursor:左サイドバーのTimelineビューで過去履歴を確認
- JetBrains(IntelliJ/WebStorm):右クリック → Local History → Show History
- macOS:Time Machineが有効なら過去版を復元
- Windows:ファイル履歴/シャドウコピー
予防:pullする前に差分プレビューする
「取り込んでから削除発覚」ではなく「取り込む前に削除がないか確認」する運用にすれば、事故は大幅に減ります。Gitの設定とスクリプトで半自動化できます。
# ff可能な時だけpull(不要なマージコミット抑止) git config --global pull.ff only # FFできない時はrebase git config --global pull.rebase true # rebase時の自動stash git config --global rebase.autoStash true # 追跡情報のprune git config --global fetch.prune true
pullプレビュー用のエイリアス
# git preview コマンドを作る
git config --global alias.preview '!f() { \
git fetch --prune && \
echo "=== Deletions ===" && \
git diff --name-status HEAD..@{u} | grep "^D" || echo "(none)"; \
echo "=== Additions ===" && \
git diff --name-status HEAD..@{u} | grep "^A" || echo "(none)"; \
echo "=== Modifications ===" && \
git diff --name-status HEAD..@{u} | grep "^M" || echo "(none)"; \
}; f'
# 使用例
git preview
# 削除ファイルがあれば一覧される → 確認してからpull
#!/bin/bash
# ~/bin/git-pull-safe.sh
git fetch --prune
DELETIONS=$(git diff --name-status HEAD..@{u} | grep "^D" | wc -l)
if [ "$DELETIONS" -gt 0 ]; then
echo "⚠️ Pull で $DELETIONS 個のファイルが削除されます:"
git diff --name-status HEAD..@{u} | grep "^D"
read -p "続行しますか? (y/N) " yn
[ "$yn" != "y" ] && exit 1
fi
git pull "$@"
ポイント:このpull-safeスクリプトは削除があると確認プロンプトを出す設計。チームに配布すれば誤削除事故が激減します。~/.bashrcでalias pull="git-pull-safe.sh"のようにエイリアス化してもいいでしょう。
実践シナリオ
シナリオ① 朝一pullでビルドエラー、必要ファイル消失
git diff --name-status ORIG_HEAD..HEAD | grep "^D" # 削除ファイルを確認 # 一旦戻す git reset --hard ORIG_HEAD # 原因確認→慎重にpull git fetch --prune git preview git pull
シナリオ② 特定ファイルだけ戻したい(他の変更は取り込みたい)
git restore --source=ORIG_HEAD -- src/utils.ts git add . git commit -m "restore: src/utils.ts を pull 前の状態に戻す" git push
シナリオ③ pull –rebase後に気づいた削除
git log --oneline "HEAD@{1}" -1
git restore --source="HEAD@{1}" -- path/to/file
git add .
git commit -m "restore: rebase前の状態を復元"
シナリオ④ 上流の削除コミット自体を取り消したい
git log --diff-filter=D --summary # 削除コミットのSHAを特定 git revert <削除SHA> git push
やってはいけない落とし穴
追加操作を重ねてから気付く
削除発覚後にさらにpullやmergeを重ねるとORIG_HEADが上書きされます。発覚した瞬間に手を止めてORIG_HEADを確保するのが最優先。
conflict解消の「Deleted by them」で条件反射的にgit rm
「削除すべきか?」の判断をせずにgit rm+git commitしてしまうとファイルが失われます。迷ったらgit log -- path/to/fileで削除commitのメッセージ・author・日時を確認してから判断を。
reflogに残っていないか確認せず諦める
「消えた」と思ってもgit reflogには数週間前の痕跡が残っています。git fsck --lost-foundまで試してから、他クローンやバックアップへ移るのが順序。
共有mainでreset --hard+force push
「pullを無かったことにしたい」で共有mainをforce pushすると他メンバーのローカルを破壊します。個人のローカル/作業ブランチだけresetし、共有履歴はrevertで対処するのが鉄則。
削除発覚後にgc実行
慌ててgit gcを手動実行するとreflogや dangling commit が削除されます。救出作業中はgc.auto=0で自動gcも停止し、救出完了後に整理するのが安全。
pull前diffをしない習慣
毎回のpullで削除混入リスクがあります。git previewのようなエイリアスで差分プレビューをデフォルト化しておけば、意図しない削除を事前に検知できます。
よくある質問
ORIG_HEAD(明示的にpull/merge/rebase前を示す)が推奨。他の操作を挟んでいればHEAD@{1}は別の操作の前を指すので注意。git reflogで古いHEAD位置を探せます。git log -- path/to/fileでファイルが最後に存在したコミットを特定し、git restore --source=<SHA> -- path/to/fileで復元。git checkout --ours path/to/file+git addで確定。削除を採用するならgit rm path/to/file。削除commitの意図を履歴で確認してから判断を。git revert <削除SHA>で削除コミット全体を打ち消せます。範囲指定で複数まとめるならgit revert --no-commit <開始>..<終了>→git commitで1件のcommitにまとめられます。git reflogでrebase前の位置を探し、git restore --source=<SHA> -- path/to/fileで復元。作業中のcommitが重なっていれば個別にcherry-pickで取り戻します。git bundle経由で回収を。詳細は本記事の「解決⑥」章を参照。git fetch後にgit diff --name-status HEAD..@{u} | grep "^D"で削除予定ファイルをリストアップ。本記事の「pull-safe」スクリプトをエイリアス化すれば自動で警告を出してくれます。関連記事
- 【Git】消したファイルを戻す方法 — ファイル復元の総合ガイド
- 【Git】mergeコミットを取り消して履歴を元に戻す方法 — マージ由来の削除対処
- 【Git】マージの取り消し方法 — マージの全体像
- 【Git】コンフリクトを解消したのにpullできないときの原因と対策 — pull conflict関連
- 【Git】リモートとローカルの履歴が食い違ったときの同期方法 — pull戦略全般
- 【Git】pull後にマージコミットが大量発生する原因と履歴整理方法 — 関連するpull後問題
- 【Git】誤ってmaster/mainを削除したときの復旧方法 — 大規模な削除復旧
- 【Git】よく使うgitコマンドまとめ — 日常コマンドの早見表
まとめ
- pull直後なら
git reset --hard ORIG_HEADで90秒復旧 - 発覚後は追加操作を止めるのが最重要(
ORIG_HEAD上書き防止) - 7原因(上流削除/merge大量削除/conflict誤決定/rebase伝搬/force push/.gitignore/submodule)
- 特定ファイルだけなら
git restore --source=ORIG_HEAD -- path - conflict誤決定は
--ours/--theirsで採用側を明示 - 上流の削除コミットは
git revertで履歴を保ったまま取り戻す - 最終手段は他クローン/CI/
git bundle/IDE Local History - 予防:
pull.ff=only+git previewエイリアスで事前diff
pull後の意図しないファイル削除は、ORIG_HEADとHEAD@{1}を味方に付ければほぼ確実に復旧できます。原因を7パターンに分類し、診断→対処の順序で動けば、慌てずに数分で元通り。運用面ではgit previewのような事前diff確認エイリアスをチーム標準化すれば、削除混入事故そのものが予防できます。復旧手段を知っているというだけで、日々のpullに対する心理的ハードルが大きく下がります。

