個人開発をしています。コードはほとんど自分では書かず、AIと一緒に進めるスタイルです。複数のアプリ(sesebox という屋号でやっています)を1つのリポジトリにまとめて置いているので、作業ディレクトリにはいろんなプロジェクトのフォルダが並んでいます。
その環境で、「作業中にできたゴミファイルを消そう」としただけで、必要なフォルダを3つ消してしまいました。 しかも戻せませんでした。 初心者がいちばんやりがちな消し方だと思うので、何が起きたのかを順番に書いていきます。
何をしようとしていたか
あるコマンドの書き方をミスして、リポジトリの中に意味のないファイルが13個できてしまいました。 中身は空っぽだったり、コマンドの出力がそのままファイル名になっていたりするもので、完全にゴミです。
13個を1つずつ消すのは面倒だな、と思って、こう打ちました。
git clean -f -d
git clean は「ゴミ掃除コマンド」ではない
ここが今回のいちばんの勘違いでした。
git clean は、名前のとおり「掃除するコマンド」なのですが、何を掃除するかの基準が「ゴミかどうか」ではありません。
基準はこれです。
Git が管理していないファイル(untracked files)を消す
オプションの意味はこうです。
| オプション | 意味 |
|---|---|
-n |
消さずに「消す予定のもの」を表示するだけ(ドライラン) |
-f |
本当に消す(force。これが無いと安全装置で動きません) |
-d |
ファイルだけでなくディレクトリごと消す |
-x |
.gitignore で無視しているものまで消す |
つまり git clean -f -d は、「Gitに入っていないファイルとフォルダを、全部、確認なしで消す」 という意味です。
何が巻き込まれたか
私のリポジトリには、たまたま「まだ git add していないフォルダ」がありました。作りかけだったり、コミットするほどでもないと思って放置していたものです。
具体的には、nami という自作アプリの src/app/api/ 以下など、3つのフォルダがそれに当たりました。
私からすると「ゴミ13個」と「必要なフォルダ3つ」は見るからに別物なのですが、Gitから見ると全部同じ「untracked」でした。 区別する手がかりがありません。結果、13個と一緒に3つも消えました。
一番つらいのは「戻せない」こと
Gitを使っていると「まあ戻せるでしょ」と思いがちなのですが、今回は戻せませんでした。
理由はシンプルで、消えたのが「Gitに入っていないフォルダ」だったからです。
- Gitがコミット履歴として持っているファイル →
git checkoutやgit restoreで戻せる - 一度もコミットしていないファイル → Gitはそもそも中身を知らないので、復元しようがない
git clean が消すのは定義上いつも後者です。つまり git clean で消えたものは、原理的にGitでは戻せません。 ここが git reset などとまったく違うところで、初心者ほど「Gitのコマンドだから安全だろう」と思ってしまうポイントだと思います。
どうすればよかったか
❌ 除外指定で守ろうとする
最初に思いついたのはこれでした。
git clean -f -d -e "nami/src/app/api"
-e で「これは消さないで」と指定できます。でもこれは守りたいものを全部自分で挙げられている前提の書き方です。今回の私は、そもそも nami/src/app/api/ がuntrackedだと気づいていませんでした。気づいていないものは、除外リストにも書けません。
⭕ 消したいものを名指しする
結局いちばん安全なのはこれでした。
rm ゴミファイル1.txt
rm ゴミファイル2.txt
13回打つのは面倒ですが、「消すつもりだったもの」と「実際に消えたもの」が完全に一致します。 面倒くささと引き換えに、事故がゼロになります。
まとめて消したいときも、せめて対象をはっきりさせてから消します。
# 拡張子や名前のパターンで絞る
rm ./tmp-*.txt
⭕ どうしても git clean を使うなら、必ず -n を先に
git clean -n -d # 消す予定のものを表示するだけ(何も消えない)
これを打つと「これらを消します」というリストが出ます。そのリストを読んで、知らない名前が1つでもあったら止まる。 これだけで今回の事故は防げました。実際、リストには nami/src/app/api/ が出ていたはずです。
学んだこと
技術的な結論より、動き方の話のほうが大きかったです。
私はこの20分前に、本番のデータベースから行を2件消すという作業をしていました。そのときは、消す前に全カラムをメモして、復元用のSQLまで書いてから消しています。かなり慎重でした。
それなのに、「ゴミ掃除」だと思った瞬間に、確認を1つもしませんでした。
「本番だから丁寧に、ゴミだから雑に」と無意識に切り替えていたのですが、危ないのは対象の重要度ではなく、コマンドの作用範囲のほうでした。 git clean -f -d は、打った本人が思っているよりずっと広い範囲に効きます。
- 消すコマンドは、対象を名指しする
- 範囲がふわっとしているコマンドは、先にドライラン(
-n)で範囲を見る - 「ゴミだから」は、確認を省略する理由にならない
同じ環境の人(1つのリポジトリに複数プロジェクトを置いている人)は特に踏みやすいと思うので、参考になれば幸いです。