git-safety-net について
ブランチ、スタッシュ、ワークツリーにまたがる作業の消失を防ぎ、問題発生後の調査と復旧を支援する Git スキルです。監査、復旧、マージ検証、安全な整理を選び、許可された変更の前に作業を保全します。
主な機能
- 01ブランチ、ワークツリー、スタッシュ、未参照コミットの監査
- 02reflog と fsck によるコミットの探索
- 03整理前の危険な作業の保全
- 04試行マージによる内容のマージ確認
- 05許可された対象に限定した整理
活用シーン
- 01ブランチ、コミット、スタッシュの消失調査
- 02古いブランチやワークツリー削除前のリスク確認
- 03ブランチの内容が main に反映済みか確認
- 04並行開発後の未コミット作業の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ daymade/ claude-code-skills --skill git-safety-netインストール後、Claude Code で/git-safety-netを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlevals/evals.jsonreferences/merge_verification.mdprevention_practices.mdrecovery_playbook.mdscripts/git_align_checkout.shgit_classify_refs.shgit_export_before_drop.shgit_find_all_checkouts.shgit_hosted_vs_cached.shgit_loss_audit.shgit_prepare_clone_retirement.shgit_preserve_danglers.shgit_verify_branch_merged.shtests/test_git_align_checkout.pytest_git_classify_refs.pytest_git_export_before_drop.pytest_git_find_all_checkouts.pytest_git_hosted_vs_cached.pytest_git_loss_audit.pytest_git_prepare_clone_retirement.pytest_git_preserve_danglers.pytest_git_verify_branch_merged_options.pytest_git_verify_branch_merged.py詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
Git セーフティネット
ブランチ、スタッシュ、リベースが入り組んで作業を失うのを防ぎ、すでに問題が起きている場合はフォレンジックに復旧します。ここに記載されたコマンドは、破壊的と明示された手順を除き、すべて非破壊的または追加的です。復旧によって損失を悪化させてはなりません。
成果の取り決め — セーフティネットをユーザーの作業に従属させる
Mode B/E または ref やバックアップに書き込むコマンドを実行する前に、会話内で次の内容を伝えてください (別のファイルは作成しないでください)。
- **成果:**ユーザーの言葉で示した、ユーザーから見える最終状態。
- **現在のフェーズ:**現時点で許可されている作業。「後で」行う作業は、このフェーズでは許可されていません。
- **許可された対象:**このフェーズで調査できる名前付きオブジェクトと、そのうち変更できるもの。
- **停止条件:**タスクを終了する客観的に確認可能な事実。
そのうえで、次の境界を守ってください。
- **証拠の範囲と操作の範囲は別です。**読み取り専用の監査で別のクローン、ref、リポジトリ、または dangling object が見つかることがあります。その発見によって報告範囲を広げることはできますが、新たに見つかったオブジェクトを保全、アップロード、マージ、削除、またはその他の方法で変更する権限は得られません。
- 権限はオブジェクトごとに適用され、リポジトリ全体に暗黙に及ぶものではありません。「引き継ぐ」「続ける」「クリーンアップを完了する」は、ユーザーが指定したチェックアウト、ref、または PR にのみ適用されます。後から見つかった共同作業者所有のワークツリー、ブランチ、または PR は、ユーザーが変更対象として指定するまで報告のみの対象です。ユーザーがそのままにしておくよう指示した場合は、その対象を除外として記録し、ワーキングツリーの内容を調べたり、バックアップしたり、マージしたり、ロック解除や削除をしたり、ref を変更したりしてはなりません。
- **次に許可された破壊的操作によって脅かされる最小限の集合を保全してください。**現在のフェーズがコミット、プッシュ、検証のみで、削除、リセット、gc、履歴の書き換え、ワークツリーの削除が許可されていない場合、全 ref の bundle を作成したり、すべての dangling object を pin したりしてはなりません。
- **転送方法を選ぶ前に、成果物を分類してください。**永続的なプロジェクトソースは、リポジトリの通常の Git/LFS ポリシーに従います。一時的な復旧成果物(bundle、ワーキングツリーの diff、スナップショット、転送用チャンク)は、リポジトリ外のバックアップディレクトリに置きます。バックアップをリモートに置くためだけに、それらをステージング、コミット、プッシュしたり、Git LFS 経由にしたりしてはなりません。Git LFS は永続的にバージョン管理するプロジェクトのバイナリ用であり、一時的な復旧資材を転送するための代替手段ではありません。
- **保全と廃棄は別々のフェーズとして扱ってください。**復旧成果物の検証によって証明できるのは復旧可能性であり、業務上のコードが取り込まれたことではありません。破壊的操作と、内容レベルの事後条件が満たされるまで、その成果物を保持してください。タスク専用の一時成果物を廃棄できるのは、すべてのペイロードが取り込まれたか、不要になったことが証明され、かつ成果物自体のクリーンアップが明示的に許可されている場合に限ります。バックアップのみの成果物、未解決の成果物、複数の内容が混在する成果物は保持してください。
- **新しい保存先や実行環境は、スコープの変更として扱ってください。**2つ目のリポジトリ、新しいリモート、クラウドへのアップロード、Git LFS、または全履歴のエクスポートが必要な場合は、明示された成果が実際にそれを必要とするときに、計画を立て直し、明示的な権限を得てください。ユーザーが求めていない転送上の問題を解決しようとしてはなりません。
- **ユーザーが望む状態で完了を証明してください。**目的のコミットを含むリモート、保全された WIP、要求されたブランチやワークツリーの状態が成果です。bundle の数、チェックサム、アップロードの受領記録、監査の広さは補助的な証拠であり、成果の代わりにはなりません。取り決めが満たされたら停止し、無関係な発見事項は別途記録し、手を加えないでください。
引き継ぎに「アップロードしない」とあることを理由に、許可されているコミットやプッシュを保留する前に、元の制約の対象オブジェクト、操作、宛先を確認してください。 収束権限の確認を使用してください。アシスタントが作成した制約は参照の手がかりであり、新たなユーザー判断ではありません。
エントリールーター — ユーザーが何を懸念しているかに応じてモードを選びます
| ユーザーの発言/要望… | 移動先 |
|---|---|
| 「コミット/ブランチ/スタッシュを失ったと思う」「削除した X を復元して」「git reflog」 | モード A — 復元 |
| 混乱したセッションの後に「何か失った?」「どのワークツリー/スタッシュ/ブランチが残っている?」 | モード B — 監査と保全 |
| 古いブランチを削除する前に「すべてマージ済み?」「まだ main にないものは?」 | モード C — マージ済みか確認 |
| 並行作業や複数ブランチでの作業を始める際に「今後、二度と起きないようにしたい」 | モード D — 予防 |
| 「ワークツリー/スタッシュ/ブランチをクリーンアップして」「すべてを main に集約して」「main ブランチを 1 つだけ残して」 | モード E — 安全に整理 |
| 「監査では問題なしとされたけど、ほかに失われたものはない?」「もう一度確認して」 | モード B、ステップ 0 から開始 — 再確認の依頼がある場合、最初の確認が不注意だったというより、対象範囲が適切でなかった可能性が高いです |
判断に迷ったら、モードを選択できる最小限の読み取り専用の調査を実行します。網羅的な損失監査を行う場合、または対象のチェックアウトが不明な場合に限り、モード B のステップ 0 でマシン全体を調査します。リポジトリ/ブランチ/ワークツリーが指定されたタスクでは、その対象を維持します。対象外で見つかった情報は、ユーザーが許可対象を拡大するまで報告のみにとどめます。
重要なルール(これらを理解したうえで、各モードを適用してください)
- 判定を信頼する前に、証拠の範囲を正しく把握し、作業範囲を黙って広げないでください。ここにある各手段が確認できるのは、実行先のリポジトリだけです。
git worktree list、git branch -a、git fsck、git stash list、git log --not --remotesは、どれも同じリポジトリの独立したクローンがマシン上の別の場所にあっても、構造上それを検出できません。リンクされたワークツリー(git worktree add)には、元のリポジトリを指す gitlink ファイル があるため検出されます。一方、2 つ目のgit cloneは完全な.gitを独自に持ち、参照元とのつながりもないため、どの手段でも検出されません。網羅的な監査を行う場合や対象が不明な場合に限り、最初にgit_find_all_checkouts.shを実行してください。それ以外の場合は、指定された対象を監査します。実際に起きた事例:リポジトリの監査結果は問題なしで、全ブランチがプッシュ済みだったにもかかわらず、作業中の機能の 440 行分が、rm -rfひとつで消えていたはずの隣接クローンに未追跡ファイルとして残っていました。 範囲には、2 つ目の軸として時間もあります。 すべてのorigin/*ref は最後にフェッチした時点のキャッシュされたスナップショットであり、リモートそのものではありません。そのため、リモートの状態に依存する判定を信頼する前にgit fetch --all --pruneを実行してください。古いキャッシュは、判定の方向に応じて適切に解釈します。*「何が失われるか」を確認する場合は安全側に誤るため(プッシュされていない作業を過剰に報告することはあっても、隠すことはありません)、ここにあるスクリプトはオフラインでも実行できます。一方、「これはすでに上流にあるか」*を確認する場合は逆に誤ります。リモートにすでにある作業が独自の変更として判定されて再送され、さらにその間にリモート側で更新があった場合、「復元」に見せかけてそれらの更新を黙って取り消すことになります。実際に起きた事例:1 日前の比較ベースによって、すでにマージ済みの変更が未送信に見えました。救済用 PR は、その後のレビューで追加された 3 件の修正を上書きするところでした。そのうち 1 件はセキュリティ修正でした。 範囲には、3 つ目の軸として ref セットの変化もあります。 ブランチ一覧と検証済みのバンドルで証明できるのは、ある時点で存在したものです。それを 5 分後に削除してよいという許可にはなりません。削除の直前に、ローカル ref とホスティングサービス上のブランチを再列挙し、対象の各 ref がバンドルに記録されたオブジェクトと今も一致していることを確認してください。新しいブランチ、先端の移動、新たな並行 PR が見つかった場合は、分類をやり直し、新しいバンドルが必要です。古い一覧に基づいて削除しないでください。 範囲には、4 つ目の軸として所有権もあります。 リポジトリが見えるからといって、見えるすべてのオブジェクトがこのタスクの対象とは限りません。作業を始める前に、見つかった ref/ワークツリー/PR を、変更が許可されているもの、調査のみのもの、明示的に除外するものに分類してください。クリーンアップの成功は、許可された対象の範囲で判定します。 - チェックアウト内で「何が失われるか」を権威ある形で確認するには、
git_loss_audit.shを実行してください。 現在の HEAD、リンクされたすべてのワークツリーの HEAD、ローカルブランチ、タグをすべてのリモートと比較し、各ワークツリーの追跡対象/未追跡の変更に加えて、stash と宙ぶらりんのコミットを調べます。より簡易なgit log HEAD --branches --tags --not --remotesでは、別のワークツリーの detached HEAD と、コミットされていないすべてのファイルを見落とします。先行/遅延コミット数ではこの問いに答えられません。指定されたチェックアウトで実行し、Step 0 で見つかった追加のチェックアウトでは、変更が明示的に許可されている場合に限って実行してください。このリポジトリ全体を対象とするスクリプトは、列挙するすべての対象(リンクされたワークツリー、ローカル ref/タグ、stash、宙ぶらりんのコミット)が、宣言した証拠の範囲内にある場合にのみ実行してください。除外フラグはありません。それ以外の場合は、許可されたチェックアウト/ref に主張の範囲を限定し、その対象自身のstatus、HEAD、upstream/リモートの識別情報、git log HEAD --not --remotesを範囲内の証拠として使用してください。他の対象は未監査であると報告します。 - 「コミットを失くした」とき、最初に実行するのは
git fsckではなくgit reflogです。 reflog には、HEAD のすべての位置(コミット、チェックアウト、リセット、リベース)が約 90 日間記録され、失くしたコミットはたいてい先頭の数行にあります。git fsckは、reflog からたどれないコミットを探すための、より広範な手段です。 - クリーンアップの前に保全し、どのバックアップ手段で作業を実際に取り出せるのか把握してください。 ブランチの削除、
gcの実行、強制プッシュの前に、失われるおそれのあるコミットや宙ぶらりんのコミットを、ガベージコレクションの対象にならない場所に固定してください。クリーンアップを元に戻せるのは、作業を指す ref(または reflog の保持期間)が残っている間だけです。重要な非対称性:bundle、archive、format-patchで取り出せるのは、Git がすでに認識しているオブジェクトだけです。 一度もgit addされず、stash -uもされていない未追跡ファイルは、これらの形式では見えません。ディスク上のコピーが唯一のコピーなので、保全するにはファイルを文字どおりコピーして退避する必要があります。「リポジトリ」をバックアップしたから未追跡の作業も含まれていると思い込むと、問題がないように見えるバックアップから、危険にさらされている唯一のものが黙って抜け落ちます。バックアップ先がこのリポジトリ自身のリモートではなく、すでに存在する別のリポジトリにプッシュしたブランチである場合は、プッシュ前に共有履歴を確認してください(Mode B Step 2)。名前が似ていても、何の証明にもなりません。 - 「マージ済み」はコミット数ではなく内容で検証してください。ただし、ほとんどの内容チェックも信頼できません。 squash マージ後、
main..branchは、内容が main に含まれていても、ブランチの元のコミットを「未マージ」として表示します。見せかけのコミットが 100 件を超えることもよくあります。しかし、最も近そうな内容チェックに置き換えるだけでは不十分です。ある監査では、「今度こそ内容レベルのチェックだろう」という手段を 3 つ続けて試し、どれも誤った結果を返しました。git cherry(squash によるパッチ ID の書き換えで、誤って「未マージ」と判定)、diff base...refの3 点ドット形式(「base に何がないか」を調べるために使われましたが、3 点ドットは別の問いに答えるため、欠落ファイルを5 倍過少に報告)、ファイル単位の存在確認(base にファイルがあっても、ref の変更行が欠けている場合があります)です。毎回正しい結果を返したのは、試行マージ(git_verify_branch_merged.shが実行するgit merge-tree)だけでした。diff 形式と段階ごとの信頼性については、references/merge_verification.md を参照してください。 - 削除を許可するような、重大な影響を伴う網羅的な「すべてマージ済みか」という判定では、敵対的な観点から検証してください。 通常は独立したレビュー担当者 1 名を使います。複数のリポジトリや証拠の軸を 1 回の調査で網羅できず、ユーザーがその並行調査を許可している場合に限り、複数のレビュー担当者を使ってください。少なくとも 1 回は、宣言した証拠の範囲を反証するよう試みてください(ルール 1)。ただし、新たに見つかった対象は Outcome 契約に従い、報告のみとします。
モード A — 失われた作業を復元する
「消えた」コミット/ブランチ/スタッシュは、ほとんどの場合、約 90 日間はオブジェクトストアに残っています —
それがなぜ正しいのか、そして何がその状態を終わらせるのかについては、references/recovery_playbook.md § メンタルモデル:gc が実行されるまでは何も消えていない、を参照してください。そこにある手順は症状別に整理されているので、該当する段階に直接進んでください —
§ 手順 1 — git reflog(ほとんどの復元はここで完了します)、
§ 手順 2 — 削除されたスタッシュ、
§ 手順 3 — detached HEAD での作業、
§ 手順 4 — 真の孤立オブジェクトに対する git fsck。30 秒版:
git reflog --date=iso | head -40 # find the lost HEAD position (most recoveries are here)
git show <sha> # CONFIRM it's the right commit before acting
git switch -c rescue/<name> <sha> # recover onto a NEW branch — never reset onto live work
reflog に表示されない場合は、まず fsck を試すのではなく、症状に応じて次の手順に進んでください。削除されたスタッシュには専用の復元方法があり(references/recovery_playbook.md § 手順 2)、detached HEAD で放棄された作業にも別の方法があります(§ 手順 3)。git fsck --dangling が必要なのは、たとえば rebase によって生じた真の孤立オブジェクトだけです(§ 手順 4)。
モード B — リスクのあるものを監査してから保全する
手順 0 — 証拠の範囲を確定する(ルール 1)。 「リスクのあるもの」の対象と、それを見えなくするチェックアウトの種類については、references/recovery_playbook.md § 「リスクのあるものがあるか」を確認する信頼できる方法、および § リンクされたワークツリーと detached worktree HEADs を参照してください。 網羅的な監査が Outcome contract で求められている場合、または対象のチェックアウトが不明な場合に限り、マシン全体のチェックアウト検索を実行してください。対象が指定されている場合は、そのチェックアウトを記録して手順 1 に進み、無関係なクローンまで作業対象にしないでください。 網羅的な検索が必要な場合は、リポジトリのすべてのチェックアウトをマシン上で探してください。リポジトリ内のコマンドからは確認できない独立したクローンも含めます。
scripts/git_find_all_checkouts.sh # defaults to this repo's parent + grandparent
DEPTH=6 scripts/git_find_all_checkouts.sh ~ # widen when clones live far from each other
正規化されたリモート URL で隣接するチェックアウトを照合するため(同じリポジトリの SSH 形式と HTTPS 形式は同一として比較されます)、現在のチェックアウトまたは候補チェックアウトのいずれかに origin がない場合は、共通するコミット履歴があるかどうかに切り替えて確認します。この履歴チェックは、リポジトリの真のルートを確認できない shallow clone に対しても機能します。ディレクトリ名による照合は行いません。独立したクローンは通常、元のクローンとは異なる名前(repo と repo-hotfix など)であり、名前による照合が失敗するのはまさにそのような場合だからです。現在のチェックアウトを特定する前にパスの別名を正規化し、候補の調査中はリポジトリが提供する fsmonitor コマンドを無効にします。また、ブランチに upstream がない場合でも、ローカルで認識しているリモート追跡 ref から到達できるコミットはプッシュ済みとして扱います。ほかのチェックアウトに、未コミット、未追跡、未プッシュ、または調査不能な作業がある場合、終了コードは 1 です。調査のみを許可されたチェックアウトでは、検索で止めてください。手順 1 はリモート追跡 ref を取得して変更します。手順 1 は、そのチェックアウトの変更が許可された後にのみ実行し、手順 2 は許可された項目にのみ適用してください。「リスクのあるものはない」という主張の対象は、実際に監査したチェックアウトだけです。
メンテナーによる検証
チェックアウト検索を変更した後、分離された回帰テストスイートを実行してください。
uv run python -m unittest discover -s tests -p 'test_*.py'
手順 1 — 監査(非破壊)。 現時点で、失われるリスクがあるものがあるかどうかを確認します。
スクリプトが列挙するすべてのワークツリー/ref/タグ/スタッシュ/ぶら下がりオブジェクトが、宣言された証拠の範囲内にある場合:
scripts/git_loss_audit.sh # defaults to remote "origin"; pass a remote name to override
上記の対象のいずれかが範囲外の場合、そのスクリプトは実行せず、チェックアウト/ref の範囲に限定した証拠だけを収集してください。
git status --porcelain=v1 --untracked-files=all
git rev-parse HEAD
git log --oneline HEAD --not --remotes
git ls-remote --exit-code <remote> refs/heads/<branch> # 0 present · 2 absent · 128 probe failed
ここでは --exit-code と完全修飾された refs/heads/ プレフィックスの両方が重要であり、単なる書式上の問題ではありません。git ls-remote <remote> <branch> だけでは、到達不能なリモートと存在しない ref が同じように報告されます(どちらも何も出力されません)。また、ブランチ名だけを指定すると、同名のタグにも一致します。この落とし穴は、トラブルシューティング § 「『このブランチは削除されたのか』という確認で、まだ存在すると表示される」で両方向から検証されています。
完全な監査では、ブランチまたは detached 状態とクリーンかどうかを含むすべてのワークツリーに加え、
ローカルにのみ存在するコミット、ダーティまたは利用不可のワークツリー、スタッシュ、宙ぶらりんのコミットの件数を出力する必要があります。
リモートに存在しないコミットがある場合、またはワークツリーがダーティか検査不能な場合、終了コードは 1 です。スタッシュと宙ぶらりんのコミットは表示されますが、それだけで監査が失敗することはありません。したがって、終了コード 0 でも、表示されたスタッシュや宙ぶらりんのコミットを削除してよいということにはなりません。報告された項目はすべて、トリアージするか保全してください。名前が挙がったワークツリーがクリーンで、HEAD がリモートに含まれているか、意図的に保全されていることが確認できるまで、クリーンアップが安全だと主張しないでください。
スコープ指定のパスで確認できるのは、許可されたチェックアウト/ref のみです。除外されたワークツリー、その他のローカル ref、スタッシュ、宙ぶらりんのコミットについては何も確認できないため、監査対象外として一覧に残す必要があります。
ステップ 2 — 次に許可された破壊的操作で危険にさらされるものだけを保全する(追加型、gc で失われない方法)。 検出事項があるだけでは、バックアップは必要ありません。削除、gc、または履歴の書き換えによって、報告されたコミットが到達不能になる可能性がある場合は、操作の前にそのコミットを保全してください。報告された宙ぶらりんのコミットがすべて許可された対象セットに実際に含まれている場合に限り、セット全体用のヘルパーを使用します。
scripts/git_preserve_danglers.sh --patch-dir ~/git-danglers # pin + export patches
ピン留めが gc 後も残る理由と、セット全体がスコープに含まれない場合の対象を絞った単一 ref の方法については、次を参照してください。
references/recovery_playbook.md § 保全:許可された宙ぶらりんのコミットをピン留めし、gc で失われないようにする。
これにより、すべての宙ぶらりんのコミットが refs/dangling-backup/<sha> の下にピン留めされます(参照先のコミットがガベージコレクションで失われることはありません)。git branch を煩雑にすることなく、スタッシュ以外の各コミットについて .patch を任意で書き出します。特定の重要なコミットについては、ローカルブランチ、プッシュ済みのリモートブランチ、git format-patch ファイルをすべて用意し、完全に保全してください。これにより、ディスク障害や単一の git gc で失われることを防げます。詳細と三重バックアップの理由については、references/recovery_playbook.md
§ 重要なコミットを三重バックアップする、を参照してください。
すでに存在するリポジトリに保全用ブランチをプッシュする前に、共有履歴を確認してください。名前が似ているだけでは、正しいリポジトリだという証拠にはなりません。 候補のリポジトリのデフォルトブランチを取得し(git fetch <candidate-url> <default-branch>)、git merge-base <that-default-branch> <ref> を実行してください。ローカルの merge-base が終了コード 1 を返す場合、またはホスティングサービスの比較画面で共通の祖先がないと表示される場合は、両者に共有履歴はなく、対象は別のプロジェクトです。まず、この作業自体のリモートにブランチをプッシュしてください。そのリモートがない場合は、新しいリポジトリを作成してください。どちらにも該当しない場合、または適切な保存先が不明な場合は、ユーザーに確認してください。名前が一致したからといって、決してそのままプッシュしてはいけません。適切な保存先が明らかでない場合は、常に merge-base の確認をテストとして実施してください。この作業の履歴をすでに共有しているリポジトリだけが、別のプロジェクトにすることなくブランチを保存できます。実際に、デプロイ元のバックアップブランチが、名前が似ているという理由だけで選ばれた無関係なプライベートリポジトリにプッシュされた事例があります。すでにプッシュしてしまい、後で読み戻した際に共通の祖先がないと判明した場合は、ブランチの保存先が間違っていたものとして扱ってください。コンテンツを正しい保存先に移してから、誤ったリポジトリから削除してください。「どこかにすでにバックアップされている」という理由で、そこに残してはいけません。
追跡されていないファイルには別のツールが必要です — 単純にコピーしてください(ルール 4)。 <backup> は対象リポジトリの外、かつ廃止するすべてのチェックアウトの外に置いてください。上記の方法で移動するのは git オブジェクト です。git に一度も登録されていないファイルは含まれません。これらは明示的に保全し、後から読む人がそれぞれの復元対象を把握できるよう、手段を分けてください。
git -C <checkout> status --porcelain | grep '^??' # what is untracked
cp <each-untracked-path> <backup>/ # the ONLY copy — plain cp
git -C <checkout> diff > <backup>/uncommitted.diff # tracked-but-uncommitted
git -C <checkout> bundle create <backup>/history.bundle origin/main..HEAD # unpushed commits
git bundle verify <backup>/history.bundle # prove it restores
そのファイルの隣に、どこから取得したのか、どのブランチのものか、セッションがいつ終了したのかを記した、1 段落の README を作成してください。6 週間後に誰も内容を解釈できないバックアップは、ないよりほんの少しましなだけです。そして、それを読む人は作成した本人ではありません。
モード C — 件数に惑わされず、すべてマージ済みか確認する
落とし穴:古いブランチに「mainより173コミット先行」と表示されても、すべての行がすでにmainに含まれていることがあります (スカッシュマージによるものです)。その仕組みについては references/merge_verification.md § コミット数が当てにならない理由を参照してください。 件数から「未マージ」と判断してはいけません。ブランチごとの内容チェック(手順と出力の読み方: § ブランチごとの判定手順):
scripts/git_verify_branch_merged.sh <branch> [<base>] # base defaults to origin/main
古いベースが安全でない方向に作用するのは、このモードだけです(ルール1)。昨日の
origin/mainを基準に判定すると、数時間前に内容が取り込まれたブランチでも未マージと表示され、
それを「救済」しようとすると、その上に構築された内容に古いバージョンを再適用してしまいます。
まさにそのため、スクリプトは最初にフェッチします。フェッチするとリモート追跡参照が更新されるため、
既存の調整作業ですべてのチェックアウトへの書き込みが停止され、排他的な所有権が移譲された後に限って実行してください。
それができない場合は、読み取り専用のままにして、マージ判定ができないことを報告してください。
所有権の移譲後にフェッチ自体が失敗した場合、スクリプトはキャッシュ済みの参照にフォールバックし、
その旨を標準エラー出力にのみ表示します。
この表示を注記ではなくブロッカーとして扱ってください。判定に基づいて行動する前に、ネットワークが復旧してから再実行してください。
手動での比較(git diff origin/main <branch>、git log origin/main..<branch>)には、そのような安全策は一切ありません。
唯一の書き込み担当者が先に参照を更新する必要があり、2点と3点では答える問いが異なります
(references/merge_verification.md § 問いに応じたdiff形式の選択)。
人間の判断材料にはなり得ても、自動判定に使ってはならないシグナルは、
§ 手動調査専用のヒントに分けてあります。
判定結果として、別途承認されたモードEの削除ゲートで内容上安全に削除できる
MERGED (ancestor) または MERGED (content contained) と、
ブランチがまだ変更するファイルを列挙する UNMERGED / NEEDS REVIEW を報告します。
この判定はヒューリスティックではなく、根拠のあるものです。git merge-treeを使い、
ブランチをベースに取り込む3方向マージを試行します(メモリ上で行い、チェックアウトはしません)。
そのマージで変更が発生しなかった場合にのみ、内容が含まれていると判定します。そのため、
コミット数がゼロでなくてもスカッシュマージ済みのブランチはMERGEDと判定されます。一方、
ベースにない取り消し・編集・新規ファイルがあればUNMERGEDと判定されます。
判定は安全側に倒します。内容が含まれていると証明できないものはすべてレビュー対象として報告します。
「マージ済み」という誤判定は作業の喪失につながりますが、「未マージ」という誤判定は確認の手間が増えるだけだからです
(references/merge_verification.md
§ 安全側に倒す理由)。試行マージに根拠があり、ヒューリスティックではない理由、および
--find-objectやblob比較では不十分な理由については、§ 妥当性のある内容チェックを参照してください。
スカッシュマージ後にベースで同じ行が再び変更されており(変更が後からあっただけではありません。無関係なファイルや無関係な行への変更なら、現在のベースに対する試行マージの妥当性は保たれます)、ベースのツリーを再現する代わりに競合が発生する場合は、--merge-commit <sha>を使って、
その時点でのマージコミットで内容が含まれていることを証明します。これは「ベースに今含まれているか」とは異なる、より限定的な問いです
(§ 履歴上のマージコミットの段階)。
ひとつの固定されたベースに対してリポジトリ全体のブランチを確認する場合は、
scripts/git_classify_refs.sh --base <sha> [--pr-map <json>]を実行すると、--pr-mapを使ったマージコミットの段階も含め、同じ判定手順をオフラインで一括実行できます。ブランチごとに個別に実行する必要はありません。
リポジトリ全体のブランチに対する読み取り専用の並列処理パターン(バッチごとにエージェントを1つ割り当て、
「すべてマージ済み」という見立てに対して反証を試みるよう指示し、各指摘を個別に再確認する方法)については、
§ 複数エージェントによる敵対的検証を参照してください。エージェントに与えるべき制約については、
§ 検証エージェントのルールを参照してください。
モード D — 破綻を防ぐ
ブランチが絡まり、作業が取り残される事態を未然に防ぐ習慣については、
references/prevention_practices.md を参照してください。以下の各項目には、詳細な説明がある節の名前が§付きで記載されています。ファイル全体を読むのではなく、状況に合った項目を参照してください。
特に重要なもの:
- トポロジーを提案する前に、現在の協業契約を確認してください。 共有チェックアウト、ワークツリー、ブランチ、コントリビューションの流れに関する明示的なユーザーまたはプロジェクトの決定は、この一般的なガイダンスより優先されます。雑然とした監査を一度行っただけで、「セッションごとにワークツリーを1つ」という恒久的なルールにしないでください。 (§ 現在の権限に基づいてトポロジーを選ぶ。)
- 物理チェックアウト1つにつき書き込み担当者は1人です。並行エージェントとセッションは読み取り専用のままにしてください。 同じチェックアウト内のトピックブランチでは、共有作業ファイル、現在のブランチ、インデックスは分離されません。書き込み担当者は、推測したファイル一覧ではなく、リポジトリのタスク/調整契約によって決まります。担当が不明確な場合や、別の書き込み担当者が作業中の場合は、チェックアウトを変更しないでください。 (§ 共有チェックアウトと並行セッション:書き込み担当者は1人 — 以下の「並行セッション」に関する2つの箇条書きにも適用され、推測した名前ではなくプロセスの証拠から未特定の書き込み担当者を見つける場合にも適用されます。)
- 切り替える前にコミットし、WIP は早めにプッシュしてください。 stash のやりくりよりもリモートにバックアップされたコミットを優先しますが、より強い権限を持つ狭い範囲の stash の例外は維持してください。汚れたチェックアウトを準備完了に見せるために、スコープを指定しない stash を使わないでください。 (§ 並行作業/複数ブランチの作業、§ 作業中のブランチを早めにプッシュする。)
- ワークツリーは明示的に承認された例外であり、通常の既定ではありません。 ワークツリーは作業ファイル、
HEAD、インデックスを分離しますが、refs、stash、オブジェクトストレージ、設定、フックは共有し、無視対象の依存関係もコピーしません。承認されている場合、リンクされたワークツリーは見えない独立クローンより安全ですが、廃止時には個別に監査する対象です。 (§ 承認された各ワークツリーを廃止前に監査する。) - 正確なコミットを基準に引き継ぎとマージを行い、最後に AND 条件で確認してください。 ブランチ、ローカルの
HEAD、最新のリモート先端を記録し、それらが引き継ぎ時の SHA と一致することを必須とします。直接マージではブランチではなくその SHA を指定します。ホスト型のマージでは、利用できる場合は期待する head SHA の事前条件を使い、そうでない場合は直前にホスト上で head を読み戻し、引き継ぎ時の SHA と一致することを確認します。セッションが所有するすべてのバイトがそのリモートにバックアップされたコミットに含まれている必要があります。また、残存する各パスを列挙し、帰属先を明記してください。 - プロセスのスナップショットはロックではなく、マージもクリーンアップの権限にはなりません。 このチェックアウトに書き込めるスケジューラーは、対象パスが重ならなくても書き込み担当者とみなします。Git を変更する前に、プロジェクト既存の調整機構によって静止状態が証明され、排他的な所有権が移譲されている必要があります。その仕組みがなければ読み取り専用のままにして、問題を報告してください。この一般的な Skill に基づいて、自動化を停止、再設定したり、リースを考案したりしないでください。refs やチェックアウトの廃止は、別途承認された Mode E の証拠に基づいてのみ行ってください。 (§ 既知の自動書き込み元はセッション所有の WIP ではない。)
- コミットする前に現在のブランチを確認してください(
git branch --show-current)。誤ったフィーチャーブランチにコミットされた修正は、本来の PR から見えず、クリーンアップ時に失われやすくなります。 (§ 各コミット前にブランチを確認する — 誤ったブランチからの削除が Mode E まで待たれる理由も含む。) - チェックアウトを基準にした変更操作で、別の書き込み担当者と競合しないでください。 別の書き込み担当者が作業中の場合は、リポジトリの調整システムから排他的な書き込み権限が移譲されるまで停止してください。移譲後に状態を修復または前進させるときは、正確な ref とオブジェクトを指定し、たまたまチェックアウトされているブランチに依存しないでください。
reset --hard、merge、rebaseはいずれも、実行時点でチェックアウトされているものを対象にします。承認された結果に合致する場合は、チェックアウトに依存しない ref 修復方法を使ってください。
実際のインシデント:別のセッションがまだチェックアウトを所有している間にgit branch -f <branch> <target> # instead of: switch <branch> && reset --hard <target> git fetch origin <branch>:<branch> # fast-forward a branch you are not on git push origin <sha>:refs/heads/<branch>reset --hard origin/mainを実行したところ、そのセッションのフィーチャーブランチが対象になり、2つのコミット分巻き戻りました。最初にすべきことは停止して所有権を移譲することです。移譲後は、対象を明示した ref 修復によって、チェックアウト位置が操作の一部になるのを避けられます。 - 並行セッションが以前に共有ツリーを切り替え、未コミットの作業が取り残された場合、 そのセッションが静止し、排他的な所有権が移譲されるまで変更しないでください。その後、§ 並行セッションが共有ツリーを切り替えた後に取り残された作業を復旧する にあるインシデント専用の移動手順に従ってください。各ベースに対してファイルが一致することを証明し、明示したパスのみをコミットしてから、所有権を返す前に元のブランチへ戻してください。ブランチの削除は、別途承認された Mode E の操作です。
- 逆のケース:別のセッションのコミットが自分のブランチに入り、実行済みのチェックがすべて成功し続ける場合。 共有チェックアウトでは、
HEADが自分のブランチを指している間に兄弟セッションが作ったコミットは、自分のコミットの親となり、PR に含まれます。git branch --show-currentは自分のブランチを示し、ツリーはクリーンで、git diff --cached --name-statusには自分のパスだけが表示されます。これらはすべて事実ですが、コミットされた瞬間に相手の作業がインデックスからなくなるため、いずれも検知には役立ちません。ブランチを作成したベースに対する累積範囲を確認したときにのみ現れます。検出は読み取り専用なので、すべてのプッシュ前と PR の作成前に実行してください。
検証行は不可欠です。base=<the base SHA you recorded when you created the branch> git rev-parse --verify "$base^{commit}" # must print a SHA — see below before trusting the rest git log --oneline "$base"..HEAD # every commit here must be yours git diff --name-only "$base" HEAD # every path here must be yours$baseが空だと、"$base"..HEADはHEAD..HEADとなります。終了コード 0 で何も出力されず、「他者のコミットがない」場合と区別できません。§ 自分のブランチに取り込まれた他者のコミット があるのに、より厳密なチェックが行われていないのは、この見落としやすいケースが理由です。また、Git から「自分のコミット」を判定することはできません。共有チェックアウトでは、両セッションが同じ author と committer を記録するため、両者を分けるフラグはありません。どのコミットが自分のものかは、コミット時に記録した SHA に基づいて判断します。どのコミットが自分のものか特定できない場合は、停止して確認を求めてください。修復ではコミットを削除するため、ここでの推測はこのスキルが防ごうとしている損失につながります。 ブランチ作成時にベース SHA を記録してください。後からの導出ではキャッシュ済みのリモート ref を読み取り、それを更新するための fetch 自体にも所有権の確認が必要になるためです。§ 自分のブランチに取り込まれた他者のコミット。 その範囲に他者のコミットがあることは、別の書き込み担当者がこのチェックアウトを使用していた証拠なので、修復を自分の判断で始めてはいけません。 停止してください。上記の所有権ルールがそのまま適用されます。所有権が移譲された後の修復は、自分のブランチの履歴を書き換える操作です。git rebase --ontoは書き換えるブランチをチェックアウトするため、近道を使わず、既定の手順に従います。該当する Mode B の証拠取得手順を実施し、続いてgit branch backup/pre-rewrite <your-branch>を実行します(履歴を書き換える前にスナップショットを取得する — これにより rebase を取り消せます)。次にgit branch rescue/foreign-<short-sha> <foreign-sha>を実行します(これは相手の作業を保全するもので、別の義務であり、異なる ref です)。その後、git rebase --onto "<foreign-sha>^" "<foreign-sha>" <your-branch>を実行します。移動先は他者のコミットの親であり、ベースではありません。--onto "$base"を使うと、他者のコミットより前のものがすべて破棄され、自分のそれ以前のコミットも含まれますが、終了コード 0 になります。その後、上記の検出をもう一度実行してください。rebase の終了コードから、自分のコミットが一緒に取り込まれたかどうかは分かりません。どちらの ref も後から廃止するには、推測ではなく、削除の根拠となる Mode C/E の証拠が必要です。完全な手順と、「相手の作業が残ったか」を確認する一見明白な2つの方法が誤った答えを返す理由は、references/prevention_practices.md の 自分のブランチに取り込まれた他者のコミット を参照してください。実際のインシデント:HEADが新しく作成されたブランチを指している間に兄弟セッションがコミットし、PR にそのセッションの進行中の作業が含まれました。唯一の兆候は、作成者が1つのコンポーネントにしか触れていないのに、リポジトリのバリデーターが変更されたコンポーネントを2つ報告したことでした。 - 並行セッションが共有ツリーに書き込み中の場合、リポジトリへの変更はすべて停止してください。
switch、add、reset、一時インデックスを使ったコミットの作成、refs の更新、プッシュを行わないでください。リポジトリの調整システムを使って書き込み担当者を静止させ、排他的な所有権を移譲してください。その仕組みがなければ、問題を報告し、現在の証拠を保全してください。唯一の書き込み担当者になった後であれば、オブジェクトストアのみを使ったコミットにより、帰属先を特定できる他者の WIP を共有インデックスと作業ツリーから隔離できます。候補ごとに Git エントリーの正確なタプル(mode, object ID, path)を固定してください。バイト列だけでは不十分です。100755、120000、160000はそれぞれ実行属性、シンボリックリンク、gitlink の挙動を伴います。最も安全な取得元は、不変の候補コミットです。
不変のコミットにまだ含まれていない、所有権のある一時的な通常ファイルでは、意図する mode を明示的に導出します(実行可能ならcandidate_ref=<immutable-candidate-commit-oid> candidate_path=path/to/file candidate_entry=$(git ls-tree "$candidate_ref" -- "$candidate_path") candidate_mode=$(printf '%s\n' "$candidate_entry" | awk 'NR == 1 { print $1 }') candidate_oid=$(printf '%s\n' "$candidate_entry" | awk 'NR == 1 { print $3 }') test -n "$candidate_mode" && test -n "$candidate_oid" || exit 1 candidate_index=$(mktemp /tmp/git_safety_candidate_index.XXXXXX) export GIT_INDEX_FILE="$candidate_index" # the tree's real index is untouched git read-tree origin/main # start from the pushed base, not the dirty tree git update-index --add --cacheinfo "$candidate_mode,$candidate_oid,$candidate_path" tree=$(git write-tree) commit=$(git commit-tree "$tree" -p origin/main -m "…") # HEAD does not move unset GIT_INDEX_FILE rm "$candidate_index" git push origin "$commit":refs/heads/<branch> # open the PR from here100755、それ以外なら100644)。バイト列をハッシュ化し、シンボリックリンクやサブモジュールにこの方法を適用しようとした場合は失敗させてください。これらのエントリー型では、まず不変の候補コミットを固定し、上記のように mode とオブジェクトのタプルをコピーします。別のセッションが編集中の共有パスからエントリーを取得しないでください。この手順ではオブジェクトストアと使い捨てのインデックスのみを読み書きするため、共有ツリーでのgit statusはバイト単位で変化しません。これは唯一の書き込み担当者が行う保全手法であり、別の誰かがリポジトリを所有している間に変更してよいという許可ではありません。commit-treeは通常のgit commitのフック経路を実行しません。プッシュ前に、リポジトリで指定されている正確な pre-commit/セキュリティゲートを候補に対して実行し、pre-push も引き続き実行してください。所有権が移譲された後、保全対象の他者の WIP があるためにチェックアウトの切り替えや共有インデックスへのステージングが適さない場合にのみ使用してください。 - 何も指定しない
git commitは、ステージしたものだけではなくインデックス全体をスナップショットします。また、インデックスを経由しないコミットは、インデックスに罠を残します。 インデックスを更新せずに現在のブランチを移動すると、commit-treeとそのブランチへのupdate-ref、または一時的なGIT_INDEX_FILEを使ったgit commitによって、HEADが前進する一方でインデックスは古い基準のままになります。(上記の、唯一の書き込み担当者が別のブランチへプッシュする方法ではローカルの ref は移動しないため、このずれは残りません。)新しいコミットで追加されたファイルはすべて、ステージ済みの削除(git statusではDの行と、それに対応する未追跡の??エントリー)として表示されます。git commit -- <path>では、このずれは作成も修復もされません。そのコマンドが更新するのは指定したパスだけです。次に誰かが何も指定しないgit commitを実行すると、このずれが爆発し、インデックス全体がコミットされて、見かけ上の削除が実際の削除になります。配布済みのファイルがHEADから消える一方、作業ツリーは変わっていないように見えます。実際のインシデント:一時インデックスを使ったコミットの後、配布済みのディレクトリ24ファイル分がこの状態に置かれました。並行セッションが何も指定しないコミットを1回実行していれば、ブランチ先端から削除されていたでしょう。その兆候はgit statusのD行だけでした。ここから2つの義務が生じます。インデックスと同期せずにブランチを前進させた担当者は、直ちに再同期してください。git diff --cached --name-statusを確認し、コミットで変更したパスが差分に表示されなくなるまでgit restore --staged -- <the paths the commit touched>を実行します(並行セッションが独自にステージしたエントリーはそのセッションのものであり、自分が解除してはいけません)。また、共有ツリーで何も指定しないコミットを行う前に、同じ差分を自分の変更の影響範囲として確認してください。Dの行を含むすべてのエントリーが意図したものになっている必要があります。見覚えのないエントリーがあれば、停止してください。コミットしてはいけません。 (§ コミット範囲の衛生管理。) - rebase またはブランチ削除の前に、該当する Mode B の証拠取得手順を実施してください。 網羅的な損失監査は、列挙されるすべてのワークツリー/ref/タグ/stash/dangling オブジェクトが証拠の対象範囲に含まれる場合にのみ使ってください。それ以外の場合は、承認されたチェックアウト/ref に範囲を限定した確認を行い、安全性に関する主張もその範囲に限定してください。
- 共有バージョン/ロックファイルを更新する前に、ベースの現在値を確認してください。 これにより、並行する2つのブランチが同じ更新をそれぞれ主張し、後の変更のリリースを妨げる衝突を見逃さずに済みます。 (§ 並行ブランチ間でのバージョン/ロックファイルの衝突。)
モード E — ワークツリー、スタッシュ、ブランチを安全に整理する
モード A とは逆の懸念です。「何かを失った」ではなく、「残骸がたまってきたけれど、
どれを削除できるだろう?」というものです。削除自体は簡単ですが、各項目がすでに置き換えられていると証明することが作業です。
Outcome の契約から始めてください。網羅的な監査や対象が不明な場合は、チェックアウトの検出を実行してください。
名前が指定されたワークツリーやブランチを対象とする場合は、その所有リポジトリ内にとどまってください。git_loss_audit.sh は、列挙されるすべてのワークツリー/参照/タグ/ぶら下がりオブジェクトが証拠の対象範囲内にある場合に限り実行してください。確認のみのオブジェクトは報告のみとして扱い、明示的に除外された共同作業者のリソースは、整理計画と最終的な件数の両方から除外して、名前が指定された対象のみを整理してください。列挙される対象領域に除外対象がある場合は、全損失監査や --all-refs エクスポートを実行せず、チェックアウト/参照の範囲を限定した確認と、対象を絞ったエクスポートを使用してください:
ステップ 1 — 各残骸を分類する:作業中の WIP か、置き換え済みのドラフトか? 証拠の強さ順:
- 最新の根拠と試行マージ — ベースと正確なブランチ先端を更新してから、
scripts/git_verify_branch_merged.shを実行します。祖先関係/内容包含という判定は、削除を正当化する証拠になります。NEEDS REVIEW が返された場合は、この手順を下に進んでください。不確かな状態を、より弱いヒューリスティックで MERGED に変えてはいけません。同じ固定されたベースに対して複数の残骸を確認する場合は、scripts/git_classify_refs.shが、オフラインの一括処理で全ブランチにこの段階を正確に実行します。 git cherry <base> <branch>はヒントであり、判定ではありません。-は、そのパッチ ID が上流にあることを証明します。+は、スカッシュマージによって意図的に新しいパッチ ID が作成されるため、作業が欠けていることを証明しません。この出力だけを根拠に、ブランチ全体を復旧または削除してはいけません。- 同一ファイルの置き換え確認 — ベース上で後から手が加えられたファイルに触れるスタッシュや
+コミットの場合:そのファイルのバージョンを抽出し、ベース上の現在のバージョンと比較します(git show <ref>:<path> | wc -lとgit show <base>:<path> | wc -lを比較してから、差分を目視確認します)。 ベース上のバージョンが上位互換(残骸の内容をすべて含み、それに加えて後からの作業も含む)であれば、残骸は置き換え済みのドラフトです。実例:「unfinished dev」とラベルが付いたスタッシュには 1128 行のレンダラーが含まれていましたが、main のバージョンは 1151 行で、同じ関数に後から追加された機能パラメーターも含まれていました。このスタッシュを復元していたら、復旧ではなくリグレッションになっていたでしょう。 - 関数/マーカーレベルの確認 — 残骸に固有の追加内容(
def new_helper、定数、エラー文字列など)をベース内で grep します。すべてベース上に存在するなら、置き換え済みです。これは、ファイルの構成が大きく変わり、手順 2 では確認できない「リファクタリングに取り込まれた」ケースを検出できます。
置き換え済みだと証明できないものは、すべて存続させてください(モード C と同じ安全重視の判断です。誤って「置き換え済み」とすると作業を失いますが、誤って「まだ作業中」とするとブランチ名を余分に残すだけです)。判定を変える警告が 1 つあります。残骸のラベルは証拠ではありません。「unfinished development」という名前のスタッシュが、すでに完全に取り込まれた初期ドラフトであることもあります。名前ではなく、内容を現在のベースと比較して判断してください。各手順の実例(スカッシュの副産物やリファクタリングへの取り込み例を含む)については、 references/merge_verification.md の「置き換えのトリアージ」を参照してください。
ステップ 2 — 削除の権限を得た後、削除の直前に、その削除によって失われるものだけを正確に保存します:
# Targeted branch cleanup: prefer the narrow export.
scripts/git_export_before_drop.sh --branch <branch> --out <external-backup-dir>
# Pin only an authorized dangling SHA; leave unrelated danglers report-only.
git update-ref refs/dangling-backup/<sha> <sha>
# Full ref topology: only when every captured ref is explicitly authorized.
scripts/git_export_before_drop.sh --all-refs --out <external-backup-dir>
scripts/git_export_before_drop.sh --verify-current <external-backup-dir>/all-refs.bundle
対象となる update-ref は、許可されたダングリングコミットにのみ到達します。報告されたすべてのダングラーが
スコープ内にある場合は、セット全体を対象とする git_preserve_danglers.sh で置き換えてもかまいません。名前付きブランチ/ワークツリーの廃止には、
--branch オプションを繰り返し指定することを優先してください。--all-refs はブランチ、タグ、スタッシュ、隠しバックアップ
ref、およびリンクされたワークツリーの HEAD ref を取得するため、取得されたセット全体が許可されている場合にのみ有効です。
--all-stashes は、スタッシュも削除対象である場合にのみ追加してください。--verify-current は最終的な
compare-and-swap ゲートです。記録された ref が移動または消失した場合、終了コード 1 で終了します。その前にリモートの権限を
更新し、不一致があればバンドルを再構築してください。バックアップはリポジトリの外に保管し、決して
1 つのブランチをリポジトリのエクスポートにしないでください。
他のセッションがまだコミットや PR のオープンを行う可能性がある状況で、複数ブランチの「メインは 1 つだけ」クリーンアップを行う場合は、 ステップ 3 の前に references/merge_verification.md の § 単一ライターウィンドウを通じて多数のブランチを 1 つのメインに収束させる を読んでください。別のライターがアクティブな間、このルートは読み取り専用です。フェッチ、オブジェクト/ref の作成、バンドルのエクスポート、プッシュ/PR、および削除は、 既存のコーディネーションが静止状態を証明し、最終リードバックを通じて排他的な所有権を移譲するまで待機します。 このリファレンスには、単一ブランチの廃止には不要な、移動する ref のインベントリ、ダーティな WIP の保全、不変候補、重複 PR、 および最終的なブランチ数のゲートが追加されています。
ステップ 3 — 安全な順序で破棄します:
- スタッシュ: インデックスの大きい方から順に 削除します(
stash@{1}より先にdrop stash@{2})— 削除するとインデックスはずれていくため、上から順に処理することで、すべての番号がバックアップファイル名が示す内容と一致し続けます。 - リンクされたワークツリー: 空の
git -C <path> status --porcelain=v1 --untracked-files=allを要求し、その後、無視されたパスを--ignoredで別途インベントリします。通常のクリーンステータスでは!!ファイルが隠れてしまい、どのバンドルもそれらを保存できません。再現可能であることが証明されていないものはすべてコピーし、その相対パスを保持し、記録された削除前のコンテンツハッシュと照合して検証してください。 正確な HEAD を記録し、新たにフェッチしたベースに対してそれが含まれている/置き換えられていることを証明し、そのブランチまたは衝突チェック済みのリカバリ ref を検証済みのターゲットバンドルにエクスポートし、現在のセッションでの削除権限を取得します。削除前の最終ゲートとして、空のステータスと完全な無視対象インベントリを再実行します。すべての無視対象パス、エントリ種別、ファイルハッシュ、シンボリックリンクのターゲットについて、フリーズした削除前マニフェストとの完全な一致を要求し、保存された各ソースとバックアップコピーを再検証します。少しでも差異があれば中止します。削除は次の操作でなければなりません。削除はgit worktree remove <absolute-path>を--forceなしで のみ実行します。その後、パスと登録が消えている一方で、記録された HEAD が保持されたブランチ/ベースまたは検証済みバンドルを通じて依然として解決でき、コピーされたすべての無視対象項目が記録されたハッシュと依然として一致することを証明します。プライマリ/現在のチェックアウトを決して削除しないでください。そのブランチの廃止は、別個に、別途承認された操作としてのみ行います。references/merge_verification.md の § Worktree retirement に従ってください。 - ローカルブランチ:
git branch -dを優先します(未マージを拒否します)。-Dは、ステップ 1 で置き換え済みかつバックアップ済みであることが証明され、ユーザーが削除を承認した項目にのみ使用します。-dが、内容が完全にマージされているブランチを拒否する通常の理由はスクワッシュマージです:-dはコミットの祖先関係で判断しますが、スクワッシュはそのブランチのコミットを 1 つの新しい SHA のコミットに置き換えたため、すべての行が取り込まれていても祖先関係が断たれます。だからといって反射的に-Dに手を伸ばす口実にはなりません — ステップ 1 の 内容 チェック(git cherry、スーパーセット差分)にフォールバックし、それで包含が証明できて初めて-Dを使用するという意味です。-dはまた、「マージ済み」をチェックアウトの現在のHEAD(またはそのブランチに構成された上流)を基準に判断し、あなたが想定しているベースを基準にはしません — 古いブランチ上にあるチェックアウトでは、意図したベースの祖先に本当になっているブランチを-dが拒否し、これは上記のスクワッシュのケースとそっくりに見えますが、修正方法は異なります。-Dに手を伸ばすのではなく、git merge-base --is-ancestor <tip> <base>で意図したベースに対して直接祖先関係を確認し、代わりに正しく切り替える/対象を定めてください。リモートブランチは、正確なリモートとリポジトリの可視性/所有権を再検証した後にのみ削除します。 -Dの前に、包含も祖先関係もカバーしない一点を確認してください: このブランチを head とするオープンな PR が存在してはなりません。 独自の内容がゼロのブランチでも、オープンな PR の head である可能性があり、それを削除するとその PR の head も失われます —refs/pull/<N>/headは通常依然としてオブジェクトを提供しますが、それは運による復旧であり、設計によるものではありません(ラダーは references/recovery_playbook.md の § Ladder step 4 にあります)。GitHub では、ブランチ名ではなく PR 番号で 確認してください。gh pr list --head <branch>は、プルリクエストがすでに存在するブランチに対して空の結果を返したことがあり、「空の結果に基づいて構築されたブランチ削除の判断は誤っていたでしょう」(実測 — その記録は references/merge_verification.md の § The historical-merge-commit rung、「On GitHub, do that confirmation by PR number」の下にあります)。gh pr view <number> --json headRefOid,state,mergeCommit、または REST の同等物gh api repos/<owner>/<repo>/pulls/<number>を使用してください。番号がまだ不明な場合は、REST 検索が依然としてpr listに勝ります:gh api "repos/<owner>/<repo>/pulls?state=all&head=<owner>:<branch>"。GitHub 以外のリモートでは、そのプラットフォーム自身の PR 検索を使用してください — ブランチ名の grep は代用になりません。これは削除の直前に行い、以前に取得したインベントリから行っては決してなりません。ref は動くため、「インベントリ取得後に開かれた PR」こそがこれが捉えるケースです。実測 2026-09-22 — あるピアが「そのブランチはクローズ済みの PR に対応しているので削除して安全」とディスパッチプロンプトに伝えましたが、実行エージェント自身の削除前再チェックにより、それが 新たに開かれた PR の head であることが判明しました。- リモートブランチを削除した後は、出力ではなく終了コードを通じて結果を読み戻してください。 ここでは 2 つの形が逆方向に誤解を招きます。マージ時にブランチを削除するように構成されたリポジトリはマージ時にすでにそれを削除しているため、後からの
git push <remote> --delete <branch>はremote ref does not existとともに 1 で終了します — これは望んでいた最終状態ですが、エラーとして報告されます。そして、明らかな読み戻しこそが壊れています。何も出力しないgit ls-remote <remote> <branch>はブランチが消えた証明にはなりません。到達不能なリモートも何も出力しないからです。git ls-remote --exit-code <remote> refs/heads/<branch>を使用してコードを読んでください — 0 は存在、2 は不在、128 はプローブ自体の失敗であり、128 はブランチの運命が 不明 であることを意味し、その読み取りに基づいて何も廃止してはなりません。どちらの罠も両方向で実測されており、Troubleshooting の § 「A 'did that branch get deleted?' probe says it still exists」で扱われており、そこでは ref を完全修飾しなければならない理由も説明されています。 - 独立したクローンは、廃止前に ref 完全な準備が必要です。 クリーンなワークツリーは、クローン専用の ref、reflog 履歴、無視されたバイト、スタッシュ、フック/設定、または
git clone --sharedによって作成されたobjects/info/alternates依存関係について何も語りません。scripts/git_prepare_clone_retirement.sh --clone <absolute-clone> --survivor <absolute-kept-checkout> --out <new-external-backup-dir>を実行してください。これは、それらの隠れた損失状態、すべてのクローン専用の到達不能な Git オブジェクト、パーシャル/プロミサー クローン、アタッチされたリンク済みワークツリー、ローカルサブモジュールリポジトリ、既知の Git LFS/annex オブジェクトストア、追跡対象のコンテンツフィルター、およびリカバリアーカイブが安全に解決できないリポジトリローカルの設定/フックの間接参照を拒否します。リポジトリの fsmonitor 実行を無効化し、ref の tip とシンボリック ref のトポロジーおよびメタデータファイルの種別/モードをフリーズし、その後、自己完結型の全 ref バンドルとコンテンツに紐づいたレシートを作成します。すべての予備的な Git プローブを完了し、存在しない隔離ターゲットをフリーズし、プロセスの占有状況を単独で実行し、その後、承認された上書き禁止の隔離移動を 次の操作 として、--verify-current <backup-dir>を最終プローブとして実行します。この順序により、lsofが兄弟 Git プロセスを観察することを防ぎつつ、検証後のより大きなギャップを開きません。旧パスの不在 + 新パスの存在を証明してください。恒久的な削除は別個の明示的な決定です。完全な READ-DO シーケンスと--sharedの境界: references/merge_verification.md の § Independent clone retirement。この廃止時の占有チェックは、Skill の 3 つのlsof使用のうち 2 番目です —references/prevention_practices.mdの共有ファイル書き込み者プローブとreferences/merge_verification.mdのクローン占有プローブは、同じ読み取り専用で、真の書き込み者がいれば中止するというルールを共有しています。一方の基準を変更したら、他方でも変更してください。 ステップ 4 — 削除後は、ファイル名ではなく内容で再確認します。 クリーンアップ(または一連のスカッシュマージ)がすでに完了していて、「その結果、作業内容が失われていないか」が問題になった場合、十分だと感じられたファイル名ベースの確認――git ls-treeのファイル名に対するcommや、「すべてのファイルがまだ main にある」という確認――では不十分です。同じファイル名でも、内容が同じとは限りません。削除されたブランチと残すブランチの両方に存在するファイルでも、行ごとに内容が異なる場合があります。blob レベルで再確認し、差分を正しい方向で読み取ってください。
git diff <survivor-ref> <deleted-or-merged-tip> # survivor first, the gone thing second
- の行は残すブランチには存在し、先端には存在しません → 残すブランチは上位互換です(安全です。先端にあったものをすべて含み、さらに内容があります)。+ の行は先端には存在し、残すブランチには存在しません → 損失の可能性があります。各項目について、ステップ 1 の手順に沿って確認してください。そのシンボルは、形を変えて(削除ではなく、名前変更やリファクタリングによって)残すブランチに存在していないでしょうか。- が大半で + が少しだけの差分は、「残すブランチでは作業が進み、削除されたブランチは古いバージョンだった」ことを示す特徴です。これはマージが成功したということであり、作業が失われたということではありません。同じ確認を保存されたバックアップにも適用してください。バイト単位で同一、または残すブランチが上位互換であれば安全です。残すブランチのどこにも実際に存在しない行があれば、それをエスカレーションしてください。
復旧(削除を取り消したい場合): パッチは git apply で再適用できます。未追跡の tar はその場に展開されます。バンドルは git fetch <file>.bundle <branch>:restored/<branch> で完全な履歴を復元します。
ステップ 5 — 一時的な復旧用アーティファクトのライフサイクルを終了するのは、個別に許可された場合に限ります。 まずステップ 4 を完了し、維持するブランチがすべてのペイロードを含んでいるか、意図的に置き換えていることを証明します。その後、references/merge_verification.md の「タスク所有の一時的な復旧用アーティファクトを廃棄する」に従ってください。単一の main ブランチに統合するという一般的な依頼は、外部バックアップの削除を許可するものではありません。作業の唯一のコピーである、未解決のペイロードを含む、解決済みと未解決のペイロードが混在している、またはこのタスクで作成も明示的な引き継ぎもされていないアーティファクトは、すべて保持してください。許可を得て廃棄した後は、対象となる各アーティファクトの正確なパスが存在しないことを個別に証明してください。削除コマンドの実行結果は、完了後の状態を証明するものではありません。
スクリプト(以下を実行してください。特記がない限り、破壊的な操作は行いません)
以下の scripts/... パスはすべて、この Skill のバンドルルートを基準とした相対パスです。PATH 上または対象リポジトリ内で実行できるコマンドがあるとは限りません。読み込まれた Skill のディレクトリを解決し、バンドル内のパスを呼び出してください。command -v で実際に見つからない限り、ユーザーに裸の git_verify_branch_merged.sh を実行するよう指示しないでください。
| スクリプト | 機能 | 変更内容 |
|---|---|---|
scripts/git_find_all_checkouts.sh [root ...] | git worktree list では見つからない独立したクローンも含め、このリポジトリのすべてのチェックアウトをマシン上で検索し、未コミット・未追跡・未プッシュの作業、リモートキャッシュの経過時間、借用された alternates オブジェクトストアにフラグを立てます | 何も変更しません(読み取り専用、fetch なし) |
scripts/git_hosted_vs_cached.sh [remote] | ホスティングサービスに直接問い合わせ(git ls-remote --heads)、その結果と、このチェックアウトのキャッシュ済み refs/remotes/<remote>/* を比較します。一致/SHA の相違/ホスティング側のみ/キャッシュ側のみ(prune 候補)を示します。ls-remote が失敗した場合は終了コード 3 で終了し、キャッシュをホスティング側の確定情報として表示することはありません | 何も変更しません(読み取り専用、fetch なし。確定情報を得る唯一の問い合わせは fetch ではなく ls-remote です) |
scripts/git_loss_audit.sh [remote] | 1 つのリモートを更新してから、すべてのワークツリー、ローカル参照/タグ、stash、dangling オブジェクトを報告します。除外は行わないため、証拠となり得る範囲全体を対象にする必要があります | リモート追跡参照のみ |
scripts/git_preserve_danglers.sh [--patch-dir DIR] | すべての dangling commit を refs/dangling-backup/ に固定し、必要に応じてパッチを作成します。対象全体を処理します | 参照の追加のみ(削除や gc は決して行いません) |
scripts/git_verify_branch_merged.sh <branch> [base] [--no-fetch] [--base REF] [--merge-commit SHA] | 1 つのブランチについて、内容に基づく MERGED/UNMERGED の判定を行います。--merge-commit を指定すると、base が後から同じ行を変更し、CURRENT base では証明できなくなった場合に、過去のマージコミット時点で取り込み済みであることを証明する判定基準が追加されます。また、判定結果では current-base の状態も示します(競合=通常、競合なしだが変更あり=回帰候補)。--no-fetch/--base はオフラインでの使用や、フラグ指定による一括処理に対応します | リモート追跡参照のみ(--no-fetch の場合は完全にスキップ) |
scripts/git_classify_refs.sh --base SHA [--pr-map JSON] [--all-namespaces] | 上記の一括処理版です。固定した 1 つの base に対して、オフラインの 1 回の処理ですべてのローカル/リモート追跡ブランチを分類します(祖先/内容に含まれる/要確認。ファイル数の変更も表示)。--pr-map を指定すると、ホスティングプラットフォームからエクスポートした PR 情報を使って --merge-commit の判定基準を追加します | 何も変更しません(読み取り専用、fetch なし。事前に git rev-parse で base を固定してください) |
scripts/git_align_checkout.sh --preview --target SHA / --apply --target SHA --source-worktree PATH --backup-manifest FILE | 一時的なスナップショットブランチを作成せず、HEAD も移動させずに、チェックアウトの内容を対象コミットまでプレビューまたは反映します。--apply がパスを上書きするのは、その現在の内容を再現できると証明された場合のみです(対象の履歴、またはマニフェストの sha256 に基づきます) | --preview:何も変更しません。--apply:安全性を証明できたパスの作業ツリーファイルのみ。実際のインデックス、HEAD、削除対象は決して変更しません |
scripts/git_export_before_drop.sh [export options] | stash に加え、選択したブランチまたは現在のすべての参照を、検証済みのバンドルにエクスポートします | バックアップファイルのみを書き込みます(削除や破棄は決して行いません) |
scripts/git_export_before_drop.sh --verify-current BUNDLE | エクスポート後にバンドル内の参照が移動または消失していた場合に失敗します | 何も変更しません(読み取り専用) |
scripts/git_prepare_clone_retirement.sh --clone PATH --survivor PATH --out DIR | 非表示または未処理のクローン状態があれば処理を拒否し、すべての参照先端、symbolic-ref の参照先、reflog の識別情報、対象範囲の config/hooks/info メタデータを自己完結型の復旧セットに凍結します。既存データを上書きしない保存先が存在しないことと、プロセスによる使用状況を確認して凍結した後、--verify-current DIR による最終確認を行い、次の操作として移動を実行する必要があります | 新しい外部バックアップディレクトリへの書き込みのみ。lazy fetch/fsmonitor を無効化し、追跡対象のコンテンツフィルターがある場合は処理を拒否します。移動、削除、参照の変更は決して行いません |
これらのヘルパーはリポジトリのルートから実行します。find、config、symbolic-ref、submodule status、status、cat-file、rev-list、rev-parse、fsck、for-each-ref、remote get-url などの読み取り専用の列挙・設定コマンドを使用します。さらに、スコープを限定した fetch、archive、bundle create/verify、メタデータのハッシュ化・アーカイブ、そして(preserve のみ)各スクリプトの表の行に記載されている場合の update-ref を使用します。git_find_all_checkouts.sh だけはリポジトリ読み取り専用であり、読み取り専用エージェントと併用しても安全です。これは fetch を一切行わないため、オフラインやプロキシの背後でも動作します。fetch を行ったり、バックアップ状態を書き込んだり、ref を追加したりするヘルパーは、既存のコーディネーションが排他的な書き込み所有権を移譲した後にのみ実行されます。どのヘルパーも checkout、reset、push、stash drop、branch -d、gc を許可しません。 |
トラブルシューティング
- 監査は問題なしという結果だったのに、ユーザーはまだ何かが足りないと考えている — その言葉を信じ、徹底度ではなく対象範囲を疑ってください。リポジトリ内のツールは、おそらく見えていた唯一のディレクトリについてはすべて正しかったのでしょう。すでに実行したものを再実行する前に、ステップ 0(
git_find_all_checkouts.sh)を実行してください。対象範囲を誤って正しく実行されたチェックを繰り返しても、同じ問題なしという答えに確信が加わるだけで、初回より悪い結果になります。 git_find_all_checkouts.shは何も見つけないが、別のコピーがあるはずだとかなり確信している — 可能性が高い原因は 3 つです。順に、(1)コピーがデフォルトのルート外にある(~などの明示的なルートを渡し、DEPTHを増やしてください)、(2)除外対象のパスの下にある — スイープではnode_modules、.venv、vendor、.terraformをスキップします、(3)originがまったく別の場所を指している(フォーク、またはパスリモート)ため、リモートの照合で除外される —git -C <suspect> remote -vで確認し、ルートコミットを手作業で比較してください:git rev-list --max-parents=0 HEAD。リポジトリにリモートがまだなかったときにcp -rで作成されたコピーは、ルートコミットだけで照合されます。git_loss_audit.shが古いスタッシュのように見えるぶら下がりコミットを報告する — スタッシュを多用した作業の後なら想定内です。現在は reflog から到達可能です。対象を承認済みの SHA ひとつに絞ってupdate-refで固定するか、報告されたぶら下がりコミットがすべて対象範囲内にある場合に限りgit_preserve_danglers.shを使い、その後、時間があるときにgit show <sha>で調べてください。- ブランチに非常に多くの「先行コミット」が表示されるが、マージ済みだと思われる — コミット数ではなく、
git_verify_branch_merged.sh(内容)を信頼してください。モード C を参照してください。 - 復旧アーティファクトが予想外に大きくなった、またはアップロード/LFS 転送が停止した — 再試行をやめ、Outcome contract を再実行してください。これはスコープ/配置の兆候であり、転送の問題を解くパズルではありません。承認済みで差し迫った削除によってデータが脅かされていないなら、バックアップは時期尚早でした。バックアップが必要なら、外部バックアップディレクトリに保管して検証してください。リモート転送は別個の判断事項であり、自動的な代替手段ではありません。
- スクリプト内の
git fetchがプロキシ経由/オフラインで停止する — 古いキャッシュは未プッシュの作業を過剰に報告することしかできないため、キャッシュ済みのリモート参照を使った消失検出は引き続き機能します。マージと置き換えの判定(モード C、モード E)は例外で、実際に fetch が必要です。fetch できない場合は、判定が確定したかのように示すのではなく、レポートにその旨を記載してください。 - 作業が未マージに見えるが、作業中にリポジトリが更新された — 何かを救出する前に、更新時刻を確認してください:
git_find_all_checkouts.shは各チェックアウトの最終 fetch 時刻を表示し、新たに fetch した後のgit log --oneline <cached-base>..origin/mainで、その間に届いた変更を確認できます。長時間の作業セッションがリスク期間です — 開始時に比較したベースは、終了時には何時間も前のものになっていることがあります。見分けるべき兆候:コミットしたとわかっている変更が上流にないように見え、再度送ろうとしている。まず fetch してから内容で比較してください。すでに取り込まれていた場合は、自分の変更を再適用して上書きする前に、誰かが改善していないか確認してください。 - プッシュまたはマージコマンドの応答が失われ(タイムアウト、TLS エラー、EOF)、リモート参照が移動した — 参照が移動したことは、自分の書き込みが反映された証拠ではありません。並行セッションがあるリポジトリでは、新しい tip は別の人のマージかもしれません。その前提で再試行すると、変更を二重適用したり、出荷されていない作業を成功したと報告したりすることになります。内容で決着をつけてください — ただし、その前に 2 つ注意点があります。リモート追跡参照の更新は fetch であり、モード C では排他的な所有権が条件となります — これは逆方向にも当てはまります。唯一の書き込み者になったら、読み取る前に fetch してください:上記のルール 1 はこの問いにも当てはまります。古い
origin/<branch>を使うと、リモートにすでにある作業を固有のものと誤認し、この項目の冒頭で述べた二重適用につながります。競合状態にあるチェックアウトでは fetch できません。キャッシュ済み参照から得た判定は判定とは言えないため、利用不可として報告してください。また、git merge-base --is-ancestor <your-sha> origin/<branch>が答えるのは、マージによってコミットが保持された場合だけです — squash マージまたは rebase マージではコミットが書き換えられるため、そこでの終了コード 1 は「このオブジェクトではない」という意味であり、「取り込まれていない」ではありません。それに基づいて動くと、この項目が警告している二重適用がまさに発生します(ある日に、あるリポジトリで確認した例:ある PR のコミットは祖先で、別の PR のコミットはそうではありませんでしたが、どちらも取り込まれていました)。また、3 つ目の終了コードもあります。<your-sha>がこのリポジトリに存在するコミットでない場合は 128 です — 独自のオブジェクトを作成したホスト側のマージで発生する値であり、1 とは異なる結果です。どちらのマージ方式でも使える確認方法は内容と、その対照行です:
実際のインシデント:ネットワーク層で 3 回のマージ試行が失敗する間に、git show origin/<branch>:<path> | grep -cF '<a string only your version contains>' git show origin/<branch>:<path> | grep -cF 'string-that-cannot-exist' # must be 0, or the probe is brokenorigin/mainが 2 回進みました — 1 回はこの文書の著者のマージ、もう 1 回は並行セッションによるものでした。 - 「そのブランチは削除されたか」という確認で、まだ存在すると表示される — 信じる前に確認方法を調べてください。
git ls-remote <remote> <ref> > fの後に[ -s f ]を実行する方法は、どちらの方向にも誤りであり、どちらの結果になるかはリダイレクトの詳細に依存します。到達不能なリモートで検証したところ、> fだけの場合、Git はエラーを stderr に書き込むため、ファイルは 0 バイトのままです。そのため、行方が不明なブランチを「すでに削除済み」と判定し、保護をやめてしまいます。ストリームを統合(> f 2>&1、&> f)すると、同じエラーで 155 バイトが書き込まれ、すでに削除されたブランチを「まだ存在する」と判定します。まさにこの用途のために用意されているコマンドの終了コードを使ってください:git ls-remote --exit-code <remote> refs/heads/<branch>は、参照が一致すると 0、一致しないと 2 を返します。それ以外(リモートに到達できない場合の 128)は、確認自体が失敗したことを意味します — ファイルサイズによるテストでは、これらの結果が毎回異なる形で誤ってひとまとめにされます。128 の場合、ブランチの行方は不明です。削除済みとは異なります。この判断に基づいて、保護するものは何も外さず、何も廃止せず、リモートに接続できるようになってから再試行するか、人に判断を仰いでください。リモートに到達できないことは待つ理由であり、クリーンアップする理由ではありません。 完全修飾された参照を渡してください:ls-remoteはすべての名前空間を対象に名前の末尾で照合するため、ブランチ名だけを指定すると、同じ名前のタグにも一致します。ブランチが削除済みでも 0 が返されます(実際に確認済み — リリースでブランチ名と同じタグが付けられた後によくある状況です)。作業ツリーにも同じ落とし穴があります:[ -e <path> ]では、追跡済みでクリーンなファイルと、追跡されていない同名ファイルを区別できません。代わりに Git に問い合わせてください —git cat-file -e <branch>:<path>で、0 はブランチにそのファイルがあることを意味します。0 以外を「存在しない」と解釈してはいけません:128 は、ブランチ名の誤入力や、追跡済みシンボリックリンクをたどるパスも対象とします。Git は stderr のテキストによってのみこれらを区別するため、コードだけでなく、そのテキストを読んでください。そうしなければ、この項目の冒頭で述べた欠陥をそのまま作り直すことになります。 - コミットをチェックアウトした後、detached HEAD になっている — そのコミットは
git switch -c <branch> HEADを実行すれば安全です(または reflog が約 90 日間記憶しています)。重要な新しい作業を、gcをまたいで detached HEAD に残さないでください。そこですでに放棄された作業の復旧については、references/recovery_playbook.md § Ladder step 3 — detached-HEAD work を参照してください。約 90 日ではこのリポジトリの使い方に対して短すぎる場合は、1 度だけ期間を延長してください:同リファレンスの § Widen the safety window (config)、および references/prevention_practices.md § Set a wider reflog safety window once。 - クリーンアップ後に残る worktree が 1 つだけ —
git worktree listには、常にプライマリリポジトリのチェックアウトが含まれます。件数をゼロにするためだけに削除してはいけません。目標はチェックアウトをゼロにすることではなく、維持されているチェックアウトを 1 つにすることです。 refs/dangling-backup/*参照が後になって邪魔になっている — 内容がリモートにあることを確認した後(モード C)、git for-each-ref --format='%(refname)' refs/dangling-backup/ | xargs -n1 git update-ref -dで削除してください。検証してからにしてください。- ブランチが上流から分岐しているが、ローカル固有のコミットに見覚えがある —
git cherry <upstream> <branch>を実行してください。すべて-の行なら、ローカルにしかない各コミットとパッチが同一のコミットが、すでに上流にあります(git cherryはパッチ ID を比較するため、SHA は一致しない場合があります)。典型的な原因は、リモート追跡参照が古いブランチでコミットし、その後同じ変更が再度作成されてプッシュされたことです。すべての行が-なら、ブランチを上流まで早送りしてください。重複オブジェクトはオブジェクトストア内で引き続き復旧可能です。+の行が 1 つでもあれば、実際のローカル固有の作業です。通常の分岐として扱ってください(ステップ 1 の注意点は逆方向にも当てはまります。squash マージの後では、+があっても作業が未反映である証拠にはなりません。そのためgit cherryは確定判定ではなく手掛かりとして読み取ってください — 早送りが許されるのは、すべて-の場合だけです)。
次のステップ
復旧/監査の後、リポジトリに通常のセットアップ、安全なコミット/プッシュ、競合処理、または引き継ぎの整理も必要な場合は、それが auto-repo-setup スキルの役割です(/auto-repo-setup を実行してください)。このスキルはフォレンジック/復旧レイヤーで、そちらは通常のワークフローレイヤーです。