`git clean -fdx` で絶対クリーンされると思ってないか❓

「 `git clean -fdx` したら、untrackなファイルは全部消えるんでしょ❓」って思っているそこのあなた。
実はリポジトリをネストしたときには、消えないことがあるのだ❗

## TL;DR

`git clean -ffdx` を使え。
事前確認(ドライラン)は `git clean -nffdx` を使え。

## 入れ子になったGitリポジトリ(nested repository)は `-fdx` ではクリーンされない

例えば、空っぽの `repo` フォルダから始めて、
`git init` した後、適当にテキストファイル( `akane.md` )を作ってみる。
ディレクトリ構成は↓のようになる。

```txt
repo
├── .git
└── akane.md
```

この状態で `git clean -nfdx` すると、クリーンされるファイルとフォルダが事前確認できる。

```shell
$ git clean -nfdx
Would remove akane.md
```

`akane.md` がクリーン対象になってることがわかる。
これは新規追加したuntrackなファイルなので、クリーン対象になっているのは期待動作だね。

ここから、 `untracked-nested-repo` ディレクトリを作成し、
`git init` してみる。リポジトリが入れ子になるね。
ついでに、適当なテキストファイル( `aoi.md` )を配置すると、↓の構成になる。

```txt
repo
├── .git
├── akane.md
└── untracked-nested-repo
    ├── .git
    └── aoi.md
```

同じように、 `git clean -nfdx` を実行してみると...

```shell
$ git clean -nfdx
Would remove akane.md
```

**あれ❓変わってなくね❓**

そう、実はuntrackでnestedなrepositoryは `git clean -fdx` の対象にならないのである❗
`git clean -fdx` に絶対の信頼を寄せていた自分はここで膝から崩れ落ちた。

あわやパニック状態になる自分。
Google先生に聞いても、「 `git clean` には `-f` をつけないと消えないぜ❗」的な素人アドバイスしかくれない。ｿｳｼﾞｬﾈｰﾖ。

## Docをよく読んで見る

こういうときは落ち着いて、 [公式Doc](https://git-scm.com/docs/git-clean) を読む。

> ## `-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` を叩いてみると...

```shell
$ git clean -nffdx
Would remove akane.md
Would remove untracked-nested-repo/
```

おぉ❗untrackでnestedなrepoもクリーン対象になってる❗

そこで、 `-n` をやめて `git clean -ffdx` してみると...

```shell
$ git clean -ffdx
Removing akane.md
Removing untracked-nested-repo/
```

消えた❗消えた❗これが期待動作だよ❗

というわけで、ようやく真にクリーンな状態になりましたとさ。

## 余談: 「どこでハマるんだこんなケース❓」

[Stable Diffusion web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) でハマったんだなこれが。
こいつはStable Diffusionをブラウザから使えるようにする界隈じゃ定番のツール。

問題になるのは拡張機能を保存しておくディレクトリで、↓のようになってる。

```txt
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` の結果は変わらなかった😭