【Git】pull後に意図しないファイル削除が発生した時の復元方法|ORIG_HEAD徹底活用&7パターン完全ガイド

【Git】pull後に意図しないファイル削除が発生したときの復元方法 Git

「さっき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_HEADHEAD@{1}の活用、コンフリクト誤決定の巻き戻し、上流の削除コミットをrevertで取り戻す方法、force push起因の巻き戻し救出、他クローンやCIログからの最終救出、そして事前に削除を検知するpull戦略まで、現場で即使える形で網羅しました。

この記事で学べること

  • 90秒最速復旧ORIG_HEADで丸ごと巻き戻し)
  • pull後の削除が起きる7つの典型パターン
  • ORIG_HEADHEAD@{1}の仕組みと使い分け
  • 特定ファイルだけ復元するgit restore --source
  • コンフリクト中の「Deleted by them/us」誤決定のやり直し
  • 上流の削除コミットをrevertで打ち消す手順
  • rebase中・force push後の削除救出
  • 予防:pullする前に差分プレビューする運用設定+自動化スクリプト
スポンサーリンク

【最速90秒】まずこの3手で多くが救える

pullの直後で追加操作をしていなければ、この3ステップで完全に戻せます。追加のcommit/rebase/reset等を挟むと成功率が下がるため、「削除が発覚したら一旦手を止める」が鉄則です。

90秒復旧テンプレート
# ■ 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 --mergesgit reflogを見てどのパターンか特定しましょう。

原因 診断ポイント 推奨対処
① 上流で削除コミットが入った git log --diff-filter=Dで削除commitあり git revert <削除SHA>
② mergeコミット内で大量削除 git log --mergesに大量D git revert -m 1 <マージSHA>
③ conflict解消で削除側を採用 Deleted by them/us表示後 ORIG_HEADから復元
④ rebaseで削除が伝搬 pull –rebase使用後 HEAD@{1}から復元
⑤ 他メンバーのforce push reflogに「forced-update」 他クローンから救出
.gitignore変更で追跡解除 git rm --cachedcommitが存在 ignore戻しまたは別途保存
⑦ submodule指し先変更で配下が空 submodule内が空っぽ submodule update

診断の出発点:削除されたファイル一覧

  • 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 merge/pull/rebase等の直前HEAD 次の大きな操作時
HEAD@{1} reflogの1つ前エントリ 次のHEAD移動時
参照の確認コマンド
# 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_HEADHEAD@{1}とほぼ同じ位置を指します。どちらでも同じ結果が得られますが、ORIG_HEADは「pull/merge/rebase専用」、HEAD@{1}は「どの操作でも1つ前」というニュアンスの違いがあります。時間が経ってpull以外の操作を重ねたなら、HEAD@{1}よりgit reflogでさらに古い位置を探す必要があります。

解決①:pull自体をまるごと巻き戻す

「pull前の状態に完全に戻してやり直したい」が最もシンプルで確実な解決策です。まだ追加の作業を重ねていないなら最短ルートです。

reset –hard で巻き戻し
# 作業ツリーも含めて完全に戻す(推奨)
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から復活できます。

conflict誤決定のリカバリ
# 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が新規に追加される形なので共有ブランチでも安全。

削除コミットをrevert
# 削除コミットを探す(削除のみ含むコミットを抽出)
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から救出しましょう。

rebase後の削除救出
# 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 --abortrebase開始前に戻れます。完了してしまった場合はHEAD@{N}のNを調整して適切な位置を探すのがコツ。rebase全般のトラブルはリベース途中でエラーになったときの復旧方法も参照。

解決⑥:他メンバーのforce pushで削除が混入した

リモートのforce push(履歴書き換え)で削除されたcommitがある状態でpullすると、手元のファイルが消える事態が起きます。自分のローカルreflogに古い履歴が残っていれば救出可能です。

force push後の救出
# 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サーバー・デプロイ先・定期バックアップに同じ履歴が残っている可能性が高いです。

他クローンからbundle救出
# 【他メンバーのマシンで】
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の設定とスクリプトで半自動化できます。

推奨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プレビュー用のエイリアス

pull前にdiffを確認する仕組み
# 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
pull-safe スクリプト(削除があれば警告)
#!/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スクリプトは削除があると確認プロンプトを出す設計。チームに配布すれば誤削除事故が激減します。~/.bashrcalias pull="git-pull-safe.sh"のようにエイリアス化してもいいでしょう。

実践シナリオ

シナリオ① 朝一pullでビルドエラー、必要ファイル消失

90秒復旧
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後に気づいた削除

HEAD@{1} から救出
git log --oneline "HEAD@{1}" -1
git restore --source="HEAD@{1}" -- path/to/file
git add .
git commit -m "restore: rebase前の状態を復元"

シナリオ④ 上流の削除コミット自体を取り消したい

revert で履歴に残す
git log --diff-filter=D --summary
# 削除コミットのSHAを特定
git revert <削除SHA>
git push

やってはいけない落とし穴

追加操作を重ねてから気付く

削除発覚後にさらにpullmergeを重ねるとORIG_HEADが上書きされます。発覚した瞬間に手を止めてORIG_HEADを確保するのが最優先。

conflict解消の「Deleted by them」で条件反射的にgit rm

「削除すべきか?」の判断をせずにgit rmgit 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のようなエイリアスで差分プレビューをデフォルト化しておけば、意図しない削除を事前に検知できます。

よくある質問

QORIG_HEADとHEAD@{1}どっち使う?
Apull直後ならどちらも同じ位置を指します。迷ったらORIG_HEAD(明示的にpull/merge/rebase前を示す)が推奨。他の操作を挟んでいればHEAD@{1}は別の操作の前を指すので注意。
Qpull後に数時間経ってから削除に気付いた
AORIG_HEADは上書きされている可能性がありますが、git reflogで古いHEAD位置を探せます。git log -- path/to/fileでファイルが最後に存在したコミットを特定し、git restore --source=<SHA> -- path/to/fileで復元。
Qconflict解消で「Deleted by them」、どっちを選ぶ?
A「them(相手)が削除した」なので、残したいなら自分側git checkout --ours path/to/filegit addで確定。削除を採用するならgit rm path/to/file。削除commitの意図を履歴で確認してから判断を。
Q上流の削除を打ち消したいが複数ファイル
Agit revert <削除SHA>で削除コミット全体を打ち消せます。範囲指定で複数まとめるならgit revert --no-commit <開始>..<終了>git commitで1件のcommitにまとめられます。
Qrebaseでファイルが消えたのに気付かず作業続行してしまった
Agit reflogでrebase前の位置を探し、git restore --source=<SHA> -- path/to/fileで復元。作業中のcommitが重なっていれば個別にcherry-pickで取り戻します。
Qforce push後にpullしたら消えた
A自分のローカルreflogに古い位置が残っていれば救出可能。残っていなければ他メンバーのクローンからgit bundle経由で回収を。詳細は本記事の「解決⑥」章を参照。
Q事前に削除を検知する方法は?
Agit fetch後にgit diff --name-status HEAD..@{u} | grep "^D"で削除予定ファイルをリストアップ。本記事の「pull-safe」スクリプトをエイリアス化すれば自動で警告を出してくれます。

関連記事

まとめ

  • 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=onlygit previewエイリアスで事前diff

pull後の意図しないファイル削除は、ORIG_HEADHEAD@{1}を味方に付ければほぼ確実に復旧できます。原因を7パターンに分類し、診断→対処の順序で動けば、慌てずに数分で元通り。運用面ではgit previewのような事前diff確認エイリアスをチーム標準化すれば、削除混入事故そのものが予防できます。復旧手段を知っているというだけで、日々のpullに対する心理的ハードルが大きく下がります。