メインコンテンツへスキップ
Git の仕組み・リポジトリ運用
Git
バージョン管理
信頼性
セキュリティ
テスト

Gitで消えた作業を取り戻す:reflog・fsck・cruft packの仕組みから導く復旧手順

git reset --hard、commit --amend、branch -D、force pushで消えた作業の復旧手順を、到達可能性・reflogの寿命・cruft packという内部の仕組みから導きます。ステージ済みの未コミット変更まで拾えるgit fsckの使い方、bareリポジトリでreflogが効かない理由、そして『本当に消す』ときの正しい手順まで、実測ログ付きで解説します。

公開日
読了時間
20分
著者
友田 陽大
シェア
目次

git reset --hard を打ったら、3時間分のコミットが消えました」

このとき本当に起きたことは、41バイトのファイルの中身が書き換わった、それだけです。コミットオブジェクトは1つも削除されていません。

Gitで「消えた」と感じる事故のほとんどは、削除ではなく到達不能化です。そして到達不能なオブジェクトには猶予期間があり、既定で2週間残ります。この構造を知っていれば、事故のときに焦って事態を悪化させる —— git gc を打つ、cloneし直す —— ということがなくなります。

この記事は、Gitの内部構造を前提にした復旧のオペレーション手順書です。「このコマンドを打つ」だけでなく「なぜそれで戻るのか」「戻らないのはどういうときか」まで含めて、公式ドキュメントを一次情報として整理します。実行ログはすべて git version 2.50.1 で実際に取得したものです。

Gitの内部構造そのもの(オブジェクト・参照・インデックス)を先に押さえたい方は、対になる記事を先に読んでください。ここではその知識を前提に、壊れた状態からの戻し方に集中します。


1. 大原則:Gitは「消す」より「見えなくする」ほうが得意

到達可能性(reachability)という一本の軸

Gitのオブジェクトが生きているか死んでいるかは、たった1つの基準で決まります。

どこかの参照から辿れるか。

参照とは、ブランチ(refs/heads/*)、タグ(refs/tags/*)、リモート追跡ブランチ(refs/remotes/*)、HEAD、stash(refs/stash)、そしてreflogのエントリです。ここから辿れるコミット、そのコミットが指すtree、treeが指すblob —— これらが「到達可能(reachable)」。それ以外が「到達不能(unreachable)」です。

git reset --hardgit branch -Dgit commit --amend、force push。これらはすべて参照の付け替えです。オブジェクトは一切消していません。

実際に消えるのは gc のときだけ、しかも猶予つき

到達不能オブジェクトが物理削除されるのは、git gc がprune段階まで進んだときです。git-gc(1) はこう定義しています。

--prune=<date> —— date より古いloose objectをpruneする(既定は2週間前、設定変数 gc.pruneExpire で上書き可能)。--prune=now は年齢に関わらずloose objectをpruneするが、他のプロセスが同時にリポジトリへ書き込んでいる場合に破損のリスクを高める。--prune は既定で有効。

さらに現在のGitは、到達不能オブジェクトをloose objectのまま置かず cruft pack にまとめます。

--cruft —— 到達不能オブジェクトを期限切れにするとき、loose objectとして保存する代わりに、それらを別のcruft packへまとめる。--cruft既定で有効

cruft packは、到達不能オブジェクトとその「いつから到達不能か」のタイムスタンプを保持する仕組みです。従来のloose object方式ではファイルの mtime が猶予判定に使われ、repackのたびにmtimeが更新されていつまでも消えないという問題がありましたが、cruft packはこれを解消しています。復旧の観点では「到達不能になったものは、packの中にまとまって2週間残っている」と理解しておけば十分です。

事故直後にやってはいけない3つ

やってはいけないこと何が失われるか
git gc / git gc --prune=now到達不能オブジェクトそのもの。復旧不能になる
リポジトリを消して git clone し直すreflog・到達不能オブジェクト・stash・ローカルブランチ。ローカルにしかない情報は全部
作業ツリーで操作を続ける上書きされていない未コミット変更

最初にやるべきことは1つです。

# 調査の前に、.git ごとバックアップを取る(数秒で終わる)
$ cp -a .git /tmp/git-backup-$(date +%s)

復旧作業はほぼすべて読み取りで完了します。バックアップさえあれば、途中で判断を誤ってもやり直せます。


2. 道具1:reflog —— 参照が動いた履歴

何が記録されるか

reflogは「参照がいつ、どの値から、どの値へ動いたか」の追記ログです。.git/logs/refs/heads/main のようなテキストファイルとして、参照ごとに存在します。

$ git reflog
b673550 HEAD@{0}: commit (amend): oops overwritten
c3d491e HEAD@{1}: commit: second
f9dc22c HEAD@{2}: commit (initial): important work

HEAD@{1}c3d491e が、amendで潰される前のコミットです。潰されていません。ログに残り、オブジェクトも残っています。

git reflog showgit log -g --abbrev-commit --pretty=oneline のエイリアスなので、git log のオプションがそのまま使えます。時刻付きで見るのが実務的です。

$ git reflog --date=iso
faf9142 HEAD@{2026-08-16 14:30:28 +0900}: reset: moving to HEAD~2
6aa734d HEAD@{2026-08-16 14:30:28 +0900}: commit: commit 3
49c9b67 HEAD@{2026-08-16 14:30:28 +0900}: commit: commit 2
faf9142 HEAD@{2026-08-16 14:30:28 +0900}: commit (initial): commit 1

reset: moving to HEAD~2」という操作の理由まで記録されているのが reflog の強みです。何が起きたかをログから再構成できます。

HEAD@{n}main@{n} は別物

これは混同しやすい点です。

  • HEAD@{n} —— HEADの移動履歴。ブランチの切り替えも含む
  • main@{n} —— mainブランチの移動履歴。他のブランチにいた間の出来事は含まれない

「ブランチを切り替えたあと元のブランチで何かが消えた」ケースでは、git reflog show main のほうが早く見つかります。

そして main@{one.week.ago} のような時刻指定も使えます。「先週の金曜の状態」に戻りたいときの最短経路です。

$ git diff main@{2.days.ago} main   # 2日前からの差分
$ git switch -c rescue main@{yesterday}

reflogの寿命 —— ここが落とし穴

reflogは永遠には残りません。git-reflog(1) の記載は明確です。

--expire=<time> —— […] 既定は90日--expire-unreachable=<time> —— […] 現在の先端から到達できないエントリについて […] 既定は30日

2つの期限がある点が重要です。

種類既定設定
到達可能な履歴のエントリ90日gc.reflogExpire
到達不能なエントリ30日gc.reflogExpireUnreachable

事故で捨てたコミットは「到達不能」側なので、猶予は30日です。90日ではありません。

期限を延ばしたければ、設定は1行です。個人の開発マシンなら十分に妥当な判断です。

# 到達不能なreflogエントリも1年残す(ディスクコストはほぼ無視できる)
$ git config --global gc.reflogExpireUnreachable "1.year"

最大の落とし穴:bareリポジトリにreflogはない

core.logAllRefUpdates の既定値について、公式はこう書いています。

この値は、作業ディレクトリを持つリポジトリでは既定で truebareリポジトリでは既定で false である。

つまり、

  • 手元の開発リポジトリ → reflogあり ✅
  • 素のGitサーバ、ミラー、git clone --bare したもの → reflogなし
  • CIのワークスペース → 作業ツリーがあるので有効だが、ジョブ終了で消える ⚠️

「サーバ側でforce pushされて履歴が消えた」ケースは、素のGitでは救えません。 GitHubなどのホスティングは独自にイベントを記録していることがあり、Events APIやサポート経由で失われたコミットのSHAが特定できる場合があります。ただしこれはGitの機能ではなくベンダーの機能なので、当てにした設計にしてはいけません。

サーバ側の備えは、reflogではなくポリシーとバックアップです(→ セクション8)。


3. 道具2:fsck —— reflogに載らないものを拾う

reflogは「参照が動いた」記録です。裏を返すと、参照を経由しなかったものは載りません

  • git add はしたがコミットしていない内容
  • git stash drop したstash
  • 中断したマージやrebaseの中間状態
  • 配管コマンドで作ったが参照を付けなかったオブジェクト

これらを見つける道具が git fsck です。

$ git fsck --unreachable
unreachable blob dbbfe51483ff05f84e7187a835d023be8e496473

$ git cat-file -p dbbfe51483ff05f84e7187a835d023be8e496473
precious uncommitted content

上の実行例は、git add した直後に git reset --hard したという状況を再現したものです。ファイルは作業ツリーから消えていますが、git add の時点でblobがオブジェクトDBに書かれているため、内容はまだそこにあります。reflogには一切出てきません。

--unreachable / --dangling / --lost-found

オプション意味
--unreachableどの参照からも到達できないオブジェクトを列挙
--dangling(既定で有効)「存在するが直接使われていない」オブジェクトを表示。--no-dangling で抑制
--lost-founddangling なオブジェクトを .git/lost-found/commit/.git/lost-found/other/ に書き出す。blobは中身をファイルとして書き出す
--no-reflogsreflogを参照点として扱わない。「reflogにも載っていないもの」だけを見たいときに使う
--connectivity-onlyblobの中身を読まず接続性だけ検査。巨大リポジトリで高速

--lost-found は実務で強力です。

$ git fsck --lost-found
dangling commit 6aa734d3e7b4fa54de72f05f9fad0eb6f12949b9

$ ls .git/lost-found/commit/
6aa734d3e7b4fa54de72f05f9fad0eb6f12949b9

--no-reflogs は逆に、reflogがまだ生きているうちは何も出ないことに注意してください。「fsckで何も出ないから諦める」のではなく、先に git reflog を見るのが正しい順序です。

候補が大量に出たときの絞り込み

古いリポジトリでは到達不能オブジェクトが数千件出ることがあります。日付とメッセージで絞ります。

# 到達不能なコミットだけを、日付・メッセージ付きで新しい順に
$ git fsck --unreachable --no-progress 2>/dev/null \
  | awk '$2 == "commit" { print $3 }' \
  | git log --stdin --no-walk --date=iso \
        --pretty='%h %ad %an %s' \
  | sort -k2 -r \
  | head -20

git log --stdin --no-walk は「標準入力で渡したコミットだけを、履歴を辿らずに1行ずつ表示する」という組み合わせです。数千件の候補から目的のものを見つけるのに、これ以上速い方法はありません。


4. ケース別・復旧レシピ

各ケースで「何が起きたか(どの参照が動いたか)」を先に示します。仕組みが分かればコマンドは導出できます。

A. git reset --hard でコミット済みの履歴を飛ばした

起きたこと:ブランチの参照が過去の値に書き換わった。コミットオブジェクトは無傷。

$ git reflog                    # 直前の値を探す
6aa734d HEAD@{1}: commit: commit 3   ← これが飛ばした先端

$ git branch rescue 6aa734d     # まず参照を付けて安全確保
$ git log --oneline rescue      # 中身を確認
6aa734d commit 3
49c9b67 commit 2
faf9142 commit 1

$ git switch main && git reset --hard rescue   # 納得できたら戻す

ポイント:いきなり reset --hard で戻さず、先に git branch で参照を付ける。参照が付いた瞬間に到達可能になり、gcの対象から外れます。これが最も安全な最初の一手です。

B. git commit --amend で前のコミットを潰した

起きたことHEAD が新しいコミットを指すよう更新された。元のコミットは到達不能。

$ git reflog
b673550 HEAD@{0}: commit (amend): oops overwritten
c3d491e HEAD@{1}: commit: second        ← amend前のコミット

$ git switch -c before-amend c3d491e    # 元の内容をブランチとして取り出す

両方のコミットを見比べたいなら差分が取れます。

$ git diff c3d491e b673550

C. ブランチを git branch -D で消した

起きたことrefs/heads/<name> というファイルが消えた。それだけ。

# 削除時の値はHEAD reflogに残る
$ git reflog --date=iso | grep -i "checkout\|feature"

# もしくは削除メッセージから拾う(Gitは削除時にSHAを表示する)
$ git branch feature-restored <sha>

削除時に表示される Deleted branch feature (was 6aa734d). の SHA が最短経路です。ターミナルのスクロールバックを先に見てください。

D. rebase が壊れた/途中で迷子になった

起きたこと:rebaseは元の各コミットを新しいコミットとして作り直す。中断状態では HEAD が detached になっている。

# 進行中なら、まず中止(rebase開始前の状態に完全に戻る)
$ git rebase --abort

# すでに完了してしまった後なら、開始前の位置を使う
$ git reset --hard ORIG_HEAD

ORIG_HEAD は、reset / merge / rebase / pull といった「HEADを大きく動かす操作」の直前の値を記録する参照です。直前の1回分だけなので、次の危険な操作をする前に使ってください。

$ git rev-parse ORIG_HEAD
b6735506b23643a633056af01af9afa194ce358e

E. 未コミットの編集を git restore / git checkout . で消した

ここは運命が分かれます

状態復旧可否方法
git add 済み(ステージされていた)可能blobがオブジェクトDBにある。git fsck --unreachable で拾う
一度もaddしていない不可能Gitはそのバイト列を一度も見ていない
# add済みだった内容を探す
$ git fsck --unreachable | grep blob
unreachable blob dbbfe51483ff05f84e7187a835d023be8e496473

$ git cat-file -p dbbfe514 > recovered.txt

教訓が明確です:作業が惜しくなったら、とりあえず git add する。 コミットしなくても、addした瞬間に内容はオブジェクトDBに入り、復旧の射程内に入ります。これは「こまめにコミットする」より習慣にしやすく、効果はほぼ同じです。

エディタのローカル履歴(VS Codeの Timeline、JetBrainsの Local History)はGitの外側にある別の保険です。addしていなかった場合、実質そこが唯一の望みになります。

F. git stash drop してしまった

起きたことrefs/stash のエントリが1つ消えた。stashコミット自体は残っている。

stashの実体はコミットです。しかもマージコミットです。

$ git log --oneline --graph refs/stash
*   719d02e WIP on main: faf9142 commit 1     ← 作業ツリーの状態
|\
| * 766195b index on main: faf9142 commit 1   ← インデックスの状態
|/
* faf9142 commit 1                            ← 元のHEAD

第1親が元のHEAD、第2親がインデックスの状態、そしてstashコミット自身が作業ツリーの状態。3つの木がそのまま3つのコミットに写し取られています

drop後の復旧は、オブジェクト名さえ分かれば1コマンドです。

$ git fsck --unreachable | grep commit
unreachable commit d0a3490836dcafc8846416cd1bbbae32f83a4421

$ git stash apply d0a3490836dcafc8846416cd1bbbae32f83a4421
On branch main
Changes not staged for commit:
  ...

git stash applystashスタックのエントリでなくても、生のコミットSHAを受け付けます。これを知らずに諦めるケースが非常に多い。

G. force pushでリモートの履歴を消した

起きたこと:リモートの参照が書き換わった。そして前述のとおり、bareリポジトリにreflogはない

順に試します。

# 1. 自分または誰かのローカルに、古い値が残っていないか
$ git reflog show refs/remotes/origin/main

# 2. 手元に古いコミットが残っていれば、それを押し戻す
$ git push --force-with-lease=main:$(git rev-parse refs/remotes/origin/main) \
      origin <old-sha>:main

手元にも残っていない場合、Gitの機能では戻せません。ホスティング側の記録(GitHubのEvents API、PRのコミット一覧、CIのログに残るSHA)を当たることになります。CIのビルドログにチェックアウトしたSHAが出力されていることは多く、実務では有力な手掛かりです。

そして根本対策は復旧手順ではなく、サーバ側でforce pushを禁止することです(→ セクション8)。

H. コンフリクトの解決をやり直したい

# マージ全体をやり直す
$ git merge --abort

# 特定ファイルだけ、コンフリクト状態に戻す(3世代が再びstageに載る)
$ git checkout --merge -- path/to/file

# rerereが誤った解決を記憶してしまった場合、その記憶を消す
$ git rerere forget path/to/file

git checkout --merge-m)は、解決済みのファイルをコンフリクトマーカー付きの状態に戻します。--conflict=diff3 を付けると共通祖先も一緒に表示され、「相手が何を変えたのか」が判断しやすくなります。


5. 「本当に消したい」とき —— 秘密情報の混入

復旧の裏返しとして、消えないことが問題になるケースがあります。APIキーやパスワードをコミットしてしまった場合です。

まず理解すべきこと:後続コミットでの削除は無意味

「次のコミットで消したから大丈夫」は誤りです。オブジェクトDBには元のblobが残り、git log -p でも git show <old-sha> でも読めます。

正しい順序

1. ただちに鍵をローテーション(無効化)する。

これが唯一の確実な対策です。履歴の書き換えは、以下すべてに届きません。

  • すでにcloneした全員のローカルリポジトリ
  • ホスティング上のフォーク
  • ホスティングのキャッシュ(GitHubは書き換え後も一定期間、直リンクでコミットを返しうる)
  • CI・ミラー・バックアップ
  • クローラや検索インデックス

漏れた鍵は「漏れた」のであって、「たぶん見られていない鍵」に戻ることはありません。

2. そのうえで履歴から除去する。

公式の git-filter-branch(1) は、自分自身の使用を推奨していません。

git filter-branch には、意図した履歴書き換えに対して非自明な破壊を生む数多くの落とし穴がある(しかも性能が絶望的に悪いため、そうした問題を調査する時間もほとんど残らない)。これらの安全性と性能の問題は後方互換を保ったままでは修正できず、そのためその使用は推奨されないgit-filter-repo のような代替の履歴フィルタリングツールを使ってほしい。

したがって使うのは git filter-repo(別途インストールが必要な公式推奨ツール)です。

$ pip install git-filter-repo

# 特定のパスを全履歴から除去
$ git filter-repo --invert-paths --path config/secrets.yml

# 文字列を全履歴で置換(値そのものを消す)
$ git filter-repo --replace-text <(echo 'AKIAEXAMPLE0000000000==>REDACTED')

同ドキュメントの警告も併せて読むべきです。

警告! 書き換えられた履歴はすべてのオブジェクトについて異なるオブジェクト名を持ち、元のブランチと収束することはない。書き換えたブランチを元のブランチの上に簡単にpushしたり配布したりすることはできない。

チーム全員に再cloneを依頼する必要がある、破壊的な操作です。だからこそ**順序が「ローテーション → 掃除」**であって、逆ではありません。

予防:受け入れ側で止める

# 壊れた/怪しいオブジェクトの送受信を検出する
$ git config --global transfer.fsckObjects true
$ git config --global fetch.fsckObjects true
$ git config --global receive.fsckObjects true

そのうえで、秘密情報の検出はpre-commitフックやCIのシークレットスキャンでコミット前に止めるのが本筋です。事後の履歴書き換えは、常に高くつきます。


6. 復旧の判断フロー

事故が起きたときに上から順に読むための表です。

症状最初に見る場所主なコマンド
コミットが消えた(reset / amend / rebase)git refloggit branch rescue <sha>
ブランチを消したgit reflog/削除時の出力git branch <name> <sha>
rebase・mergeの直前に戻りたいORIG_HEADgit reset --hard ORIG_HEAD
進行中のrebase・mergeを取り消したいgit rebase --abort / git merge --abort
add済みの未コミット変更が消えたgit fsck --unreachablegit cat-file -p <blob>
addしていない編集が消えたエディタのローカル履歴(Gitでは不可)
stashを消したgit fsck --unreachablegit stash apply <sha>
リモートの履歴がforce pushで消えたローカルの refs/remotes/* refloggit push --force-with-lease で押し戻す
リポジトリが壊れた(オブジェクト破損)git fsck --full別クローンから該当オブジェクトを補完

7. 復旧できる状態を維持する設定

事故は起きます。問題は「起きたときに戻せるか」です。

# 到達不能なreflogエントリの保持を30日→1年に(個人マシンなら妥当)
git config --global gc.reflogExpireUnreachable "1.year"

# 到達可能な履歴のreflogも延長
git config --global gc.reflogExpire "1.year"

# 到達不能オブジェクトのprune猶予を2週間→90日に
git config --global gc.pruneExpire "90.days.ago"

# 壊れたオブジェクトを受け取らない・送らない
git config --global transfer.fsckObjects true
git config --global fetch.fsckObjects true
git config --global receive.fsckObjects true

これらのコストはディスク容量だけで、しかもcruft packにまとまるためオーバーヘッドは小さい。復旧可能期間を延ばす投資として、費用対効果が極めて高い設定群です。

git maintenance を使っている場合も、上の設定は有効です。incremental 戦略では gc タスクが既定で無効なので、そもそも全体prune自体が日常的には走りません。


8. サーバ側:復旧に頼らない設計

ここまでの手段はすべてローカルに情報が残っていることが前提でした。組織として守るなら、サーバ側で事故そのものを起こさせないほうが確実です。

ポリシーで止める

対策効果
保護ブランチでforce pushを禁止セクション4-Gの事故が発生しなくなる
ブランチ削除の禁止誤削除の防止
直接pushの禁止(PR必須)レビューされない履歴改変を排除
署名コミットの要求誰が書いたかの証明

クライアント設定(--force-with-lease を使う等)は規律に依存するため、組織の防御としては弱いです。サーバ側で拒否させてください。

ミラーバックアップ

# 全参照を含む完全なミラーを作る
$ git clone --mirror https://github.com/org/repo.git repo.git

# 定期更新(削除された参照も同期する。バックアップ用途では慎重に)
$ cd repo.git && git remote update --prune

--prune を付けるとリモートで消された参照がバックアップ側でも消えるので、「誤削除からの復旧」目的なら付けないという判断もあります。バックアップの目的(障害復旧か、誤操作復旧か)で挙動を選んでください。

git bundle:単一ファイルのオフラインバックアップ

# 全参照を1ファイルに固める
$ git bundle create backup-$(date +%Y%m%d).bundle --all

# 検証(壊れていないか、前提コミットが揃っているか)
$ git bundle verify backup-20260816.bundle

# 復元(bundleは通常のリモートとして扱える)
$ git clone backup-20260816.bundle restored-repo

bundleはネットワークもサーバも要らない単一ファイルなので、オブジェクトストレージに置くだけでオフサイトバックアップになります。git bundle verify で整合性を検証できる点も、tarでリポジトリを固めるより優れています。


9. まとめ

Gitの復旧は、記憶したコマンドの多さではなく1つのモデルの理解で決まります。

  1. 参照を消してもオブジェクトは残る。 既定で2週間(gc.pruneExpire)、cruft packの中に。
  2. reflogは参照が動いた履歴。 到達不能エントリの寿命は30日で、bareリポジトリには存在しない
  3. fsckはreflogに載らないものを拾う。 git add 済みの内容、dropしたstash、中断した操作。
  4. 復旧の第一手は git branch rescue <sha> 参照を付ければ到達可能になり、gcの対象から外れる。
  5. 消えないことが問題になるとき(秘密情報)は、履歴の掃除より先に鍵のローテーション。

そして最も大事なのは、事故直後に何もしないことです。cp -a .git /tmp/backup を打ってから調査を始めれば、ほぼすべてのケースで取り戻せます。

Gitがなぜこう振る舞うのか —— オブジェクト・参照・インデックスという3つのデータ構造がどう組み合わさっているのか —— は、対になる内部構造の記事で扱っています。復旧手順を「暗記」から「導出」に変えたい方は、そちらも併せてどうぞ。

よくある質問

git reset --hard で消えたコミットは本当に取り戻せますか?
コミット済みなら、ほぼ確実に取り戻せます。resetが動かしたのは参照(ブランチが指す41バイトの値)だけで、コミットオブジェクト自体は削除されていません。git reflog で直前の値を探し、git branch rescue <sha> で新しい参照を付け直すだけです。オブジェクトが実際に消えるのは git gc がprune段階まで進んだときで、その猶予期間は既定で2週間(gc.pruneExpire)あります。焦って git gc --prune=now や clone のやり直しをしないことが最重要です。
reflogに出てこないものは諦めるしかないですか?
いいえ、git fsck があります。reflogは『参照が動いた履歴』なので、参照を経由しなかったもの(git add しただけの内容、drop済みのstash、中断したマージ)は載りません。git fsck --unreachable は到達できないオブジェクトを全種類(commit / tree / blob)列挙するので、そこから git cat-file -p で中身を確認できます。ステージ済みの未コミット変更が dangling blob として残っているのは、この方法でしか見つかりません。
CIサーバやGitHub側で消えた履歴も同じ方法で戻せますか?
手元と同じようにはいきません。reflogは既定では『作業ツリーを持つリポジトリ』でのみ有効で、bareリポジトリでは無効です(core.logAllRefUpdates)。つまり素のGitサーバやミラーには履歴の痕跡が残りません。GitHubなど一部のホスティングは独自にイベントを記録しており、Events APIやサポート経由で失われたコミットのSHAを特定できる場合があります。確実な備えは、サーバ側でforce pushを禁止し、別途 git clone --mirror のバックアップを回すことです。
コミットしてしまったAPIキーは、履歴を書き換えれば安全になりますか?
なりません。書き換えは新しいコミット列を作るだけで、元のオブジェクトはローカルにも、すでにcloneした全員の手元にも、ホスティング側のフォークやキャッシュにも残りえます。公式もfilter-branchについて『書き換えた履歴はすべてのオブジェクト名が変わり、元のブランチとは収束しない』と警告しています。順序は、(1)ただちに鍵をローテーション(無効化)する、(2)そのうえで履歴からも除去する、です。ローテーションだけが確実な対策で、履歴の掃除はその後始末です。
復旧作業の前にやってはいけないことは何ですか?
3つあります。(1)git gc / git gc --prune=now を実行しない——到達不能オブジェクトを本当に消してしまいます。(2)リポジトリを消して clone し直さない——ローカルにしかない情報(reflog、到達不能オブジェクト、stash)がすべて失われます。(3)作業ツリーで上書き操作を続けない。まず .git ディレクトリごとバックアップ(cp -a .git /tmp/git-backup)を取ってから調査を始めてください。復旧はほぼ常に『読み取り』で完了します。

参考文献

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

履歴が消えた/秘密情報をコミットしてしまった、という状況ですか?

Git の履歴事故からの復旧と、再発しない運用設計

force push で消えた共有ブランチ、rebase で壊れた履歴、コミットしてしまった認証情報。まず何を止めて何を保全するかの初動を含め、復旧可能性の判断と復旧作業を承ります。復旧後は、保護ルール・バックアップ(mirror / bundle)・秘密情報の検出を含む再発防止まで設計します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

あわせて読みたい