「 git clean -fdx
したら、untrackなファイルは全部消えるんでしょ❓」って思っているそこのあなた。
実はリポジトリをネストしたときには、消えないことがあるのだ❗
TL;DR
git clean -ffdx
を使え。
事前確認(ドライラン)は git clean -nffdx
を使え。
入れ子になったGitリポジトリ(nested repository)は -fdx
ではクリーンされない
例えば、空っぽの repo
フォルダから始めて、
git init
した後、適当にテキストファイル( akane.md
)を作ってみる。
ディレクトリ構成は↓のようになる。
repo
├── .git
└── akane.md
この状態で git clean -nfdx
すると、クリーンされるファイルとフォルダが事前確認できる。
$ git clean -nfdx
Would remove akane.md
akane.md
がクリーン対象になってることがわかる。
これは新規追加したuntrackなファイルなので、クリーン対象になっているのは期待動作だね。
ここから、 untracked-nested-repo
ディレクトリを作成し、
git init
してみる。リポジトリが入れ子になるね。
ついでに、適当なテキストファイル( aoi.md
)を配置すると、↓の構成になる。
repo
├── .git
├── akane.md
└── untracked-nested-repo
├── .git
└── aoi.md
同じように、 git clean -nfdx
を実行してみると...
$ git clean -nfdx
Would remove akane.md
あれ❓変わってなくね❓
そう、実はuntrackでnestedなrepositoryは git clean -fdx
の対象にならないのである❗
git clean -fdx
に絶対の信頼を寄せていた自分はここで膝から崩れ落ちた。
あわやパニック状態になる自分。
Google先生に聞いても、「 git clean
には -f
をつけないと消えないぜ❗」的な素人アドバイスしかくれない。ソウジャネーヨ。
Docをよく読んで見る
こういうときは落ち着いて、 公式Doc を読む。
-f
--force
If the Git configuration variable clean.requireForce is not set to false,
git clean
will refuse to delete files or directories unless given -f or -i. Git will refuse to modify untracked nested git repositories (directories with a .git subdirectory) unless a second -f is given.
ん❓
Git will refuse to modify untracked nested git repositories (directories with a .git subdirectory) unless a second -f is given .
意訳: 「untracked nested repositoryは f
が 2個 ないと無視しちゃうんだよね。」
なんじゃそりゃ〜〜〜〜❗
何年もGit使ってるけど初めて知ったぞそんなの...。
っていうかコマンドサンプルもなしかよ。非英語ネイティブにはつらいぜ❗
-ff
にしてみる
おそるおそる git clean -nffdx
を叩いてみると...
$ git clean -nffdx
Would remove akane.md
Would remove untracked-nested-repo/
おぉ❗untrackでnestedなrepoもクリーン対象になってる❗
そこで、 -n
をやめて git clean -ffdx
してみると...
$ git clean -ffdx
Removing akane.md
Removing untracked-nested-repo/
消えた❗消えた❗これが期待動作だよ❗
というわけで、ようやく真にクリーンな状態になりましたとさ。
余談: 「どこでハマるんだこんなケース❓」
Stable Diffusion web UI でハマったんだなこれが。
こいつはStable Diffusionをブラウザから使えるようにする界隈じゃ定番のツール。
問題になるのは拡張機能を保存しておくディレクトリで、↓のようになってる。
stable-diffusion-webui
├── .git
├── ...
└── extensions
├── kakucho1
│ ├── .git
│ └── ...
├── kakucho2
│ ├── .git
│ └── ...
└── ...
つまり、拡張機能は、各拡張機能のGitリポジトリをそのまま /extensions
の直下に clone
するような構造になってる。
ここで、ルートリポジトリの .gitignore
には /extensions
が指定されている。
以上により『untrackでnestedなリポジトリ』がユースケースとして存在することになる。
で、「web UIのバージョンあげたいけど、せっかくだからクリーンな状態からあげるか〜」と思って、 git clean -fdx
したら、拡張機能が消えないもんだから、慌てだしたってことね。
まぁ、ぶっちゃけ、web UIの場合は拡張機能が残ってても実害はないので、本記事の罠は問題にはならないけど。
しかし、例えば『フォルダの中身を自動で認識して動くシステム』みたいなものを評価する場合を考えると、やばいかも。
テンポラリデータを使って開発していて、いざ本番用に評価しようとしたとき、 git clean -fdx
しても、データが残ったままで評価することになりうる。
そうなると、本来はNGになるべき評価がOKになったりするので、影響がでかい。
余談: 「そもそも -n
と -f
がそれぞれ反対の意味を持つ1セットじゃないの❓ -nfdx
ってなんだよ...。」
そう思ったそこのあなた❗私も全く同じ意見だった。
確か、どのwebサイトも -ndx
が『実際には消さない』、 -fdx
を『実際に消す』、として紹介していた気がする。
ただ、公式Docには -n
は『実際には消さない』と書いてあって、 -f
は『これがないと実際に消すのはやめる』って書いてあるので、微妙にニュアンスが違う。
ぴったり正反対のオプションではないらしい...多分。英語力に自信がない❗
-nf
にしたときの挙動は書いてないんだよね...。分かりづらいなぁ。
ともかく、 -nf
を指定すると -n
の『実際には消さない』を優先するのが実動作のようだ。
また、 -n
と -f
を正反対の意味を持つ1セットとして考えると、
-ff
の対になるものが存在しないので矛盾が生じる。
そういう意味もあって、 -f
と -ff
がベースで、そこに -n
をつけるって考え方のほうが良いようだ。
ちなみに、「 -f
の反対が -n
なら、 -ff
の反対は -nn
じゃろ❗」って思って試したけど、 -n
と -nn
の結果は変わらなかった😭