0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ripgrep の除外(.gitignore・.ignore)は、どの順でどこまで効くか — 自作 CLI に同じ規則を入れ、rg --files と43項目を3つのOSで突き合わせた

0
Posted at

結論

ripgrep は、.gitignore などに書かれたファイルを黙って探しません。自作の検索 CLI に同じ規則を入れるため、rg --files の一覧と自分の CLI の一覧が一致するかを、43項目のテストで Mac・Linux・Windows の3つとも確かめました(不一致 0 件)。

その途中で分かった、ripgrep の除外の「効き方」を先にまとめます。どれも下の再現手順で確かめられます。

# 決まり 確かめ方
1 強さは .ignore > .gitignore > .git/info/exclude > git の全体設定 > --ignore-file。同じ種類なら深いフォルダーが勝つ .ignore の !a.log が .gitignore の a.log に勝つ
2 .gitignore は git のリポジトリの中だけで効く(.ignore はどこでも効く) .git の無いフォルダーでは無視される。--no-require-git で効く
3 名前を書いたファイル・フォルダーは除外しない rg --files build/a.log は build/ が除外でも出る
4 --no-ignore は --ignore-file を止めない。止めるのは --no-ignore-files --no-ignore だけでは --ignore-file の規則が残る
5 除外したフォルダーの中は ! で戻せない(git と同じ) e/ と !e/keep/z.log を並べても z.log は出ない
6 --ignore-file はいちばん弱い。そこに書いた ! は .gitignore の除外を覆せない --ignore-file の !b.log は効かない
7 探し始めより上のフォルダーの除外も効く(.gitignore はリポジトリの根まで) d/sub で動かしても、根の .gitignore が効く

なぜそうなっているのかは、rg --files --debug で1件ずつ確かめられます(後述)。

以下、それぞれの中身と、「同じ規則にした」をどう確かめたかを書きます。

前提:除外は「速さ」ではなく「件数」の問題

最初は、除外を性能の機能だと思っていました。node_modules を読まなければ速い、と。

作ってみると、性質が違いました。除外が違うと、件数が違います。 件数が違えば、どちらが正しいのか誰にも分かりません。速さを比べる前に、「同じものを探している」ことを保証しないといけない。

そこで完了条件を、速さではなく rg --files と自分の CLI の --files の一覧が全パターンで一致することに置きました。

1. 読むファイルと、強さの順番

ripgrep が読むのは .rgignore・.ignore・.gitignore・.git/info/exclude・git の全体設定(core.excludesFile、無ければ ~/.config/git/ignore)と、--ignore-file で渡したファイルです。

強さは二段構えです。種類で順番が決まり、同じ種類なら深いフォルダーが勝つ。 弱いほうから並べるとこうなります。

順 読むもの 備考
弱 --ignore-file <path> 何回でも書ける。いちばん弱い
↓ git の全体設定 ~/.gitconfig の core.excludesFile
↓ .git/info/exclude
↓ .gitignore git のリポジトリの中だけ
↓ .ignore リポジトリの外でも効く
強 .rgignore ripgrep 専用の名前

書式は gitignore と同じです。*.log(どの深さでも)・/build(その除外ファイルの直下だけ)・build/(フォルダーだけ)・**・[a-z]・!pattern(取り消し)・\#・行末の空白。

--ignore-file が「いちばん弱い」というのは、そこに !b.log と書いても、.gitignore の b.log を覆せないということです。逆に .ignore の !a.log は .gitignore の a.log に勝ちます。

2. .gitignore は git のリポジトリの中だけで効く

ここは実際にテストで引っかかりました。.git フォルダーごと複製していない写しに .gitignore を置いても、ripgrep は読みません。

mkdir norepo && cd norepo
mkdir build && touch a.log build/b.log
echo 'build/' > .gitignore

rg --files                    # a.log と build/b.log の両方が出る(git ではないので読まない)
rg --files --no-require-git   # a.log だけ

「どこでも効かせたい」なら .ignore に書きます。こちらはリポジトリの外でも効きます。

3. 名前を書いたファイル・フォルダーは除外しない

いちばん間違えやすいところです。除外が効くのは、たどって見つけたファイルだけで、引数で名指ししたものは外しません。

# .gitignore に build/ と *.tmp があっても
rg --files build/a.log x.tmp   # build/a.log と x.tmp が出る
rg --files build               # build/a.log が出る(名指ししたフォルダーの中は規則で見る)

ファイル名まで打った人は「そのファイルを探せ」と言っています。設定ファイルを理由に黙って無視されたら、0 件の理由が分かりません。

自分の CLI ではワイルドカードを自前で広げるので、「広げた結果には除外を効かせる・名指しのファイルには効かせない・先頭に書いたフォルダー('build/**' の build)は外さない」と決めて、rg に build/ を渡したときと同じ一覧になるようにしました。

4. --no-ignore は --ignore-file を止めない

ここはこちらが一度間違えました。 仕様書に「--no-ignore は --ignore-file で足した規則も止める。ripgrep と同じ」と書いて実装し、外部のレビューで誤りだと指摘されました。

echo extra.log > ../extra.ignore

rg --files --no-ignore --ignore-file ../extra.ignore
#   → .gitignore・.ignore の除外は外れるが、extra.log は出ない(--ignore-file は効いたまま)

rg --files --no-ignore --no-ignore-files --ignore-file ../extra.ignore
#   → extra.log も出る

--no-ignore が止めるのは .ignore・.gitignore・.git/info/exclude・全体設定の類で、--ignore-file を止めるのは --no-ignore-files です。こちらも ripgrep に合わせて --no-ignore-files を足し、突き合わせのテストに組み合わせ3通り(既定・--no-ignore・--ignore-file)を入れました。

「〜と同じ」と書いた行は、突き合わせるまで仮説です。 man ページを読んだつもりでも、自分の理解のほうが間違っていることがあります。

5. 除外したフォルダーの中は ! で戻せない

e/
!e/keep/z.log

と書いても、e/keep/z.log は出ません。git と同じで、親フォルダーが除外されていると、その中は見に行かないからです。戻したいなら、フォルダーではなく中身を除外する書き方(e/* + !e/keep/ …)にします。

自分で照合を書くと、ここを「! は後勝ち」の一般則で処理してしまい、戻ってしまいがちです。除外したフォルダーは中を見ない、という順番で作るのが確実でした(大きな node_modules を読まずに済むので速くもなります)。

6. 探し始めより上の除外も効く

cd d/sub
rg --files     # 根の .gitignore に y.log があれば、d/sub/y.log は出ない

.gitignore はリポジトリの根まで、.ignore はさらに上までさかのぼって読みます。「今のフォルダーの除外ファイルだけ見る」実装だと、ここで一覧がずれます。

理由を1件ずつ見る:rg --files --debug

一覧が合わないとき、いちばん役に立ったのがこれです。

$ rg --files --debug 2>&1 | grep -E 'ignoring|whitelisting'
... ignoring ./b.log: Ignore(IgnoreMatch(Gitignore(Glob { from: Some("./.gitignore"), original: "b.log", ...
... whitelisting ./a.log: Whitelist(IgnoreMatch(Gitignore(Glob { from: Some("./.ignore"), original: "!a.log", ...

どのファイルの、どの行で外れた(戻った)かが出ます。自分の実装とずれた項目は、まずここで ripgrep の判断を確かめてから直しました。

「同じ規則にした」をどう確かめたか

やり方

小さなフォルダーの木をテストごとに作り、rg --files と自分の CLI の --files の一覧を並べ替えて比べるだけです。

#!/usr/bin/env bash
# 使い方: ./cmp-files.sh 比べるフォルダー  (MYCLI に自分の CLI を入れておく)
set -u
D=${1:?フォルダーを指定}; MYCLI=${MYCLI:-mycli}
norm() { tr '\\' '/' | sed 's#^\./##' | sort; }   # 区切りをそろえてから比べる

for opt in "" "--no-ignore" "--ignore-file ../extra.ignore"; do
  a=$(cd "$D" && rg --files $opt | norm)
  b=$(cd "$D" && $MYCLI --files '**' $opt | norm)
  if [ "$a" = "$b" ]; then echo "PASS [$opt]"
  else echo "FAIL [$opt]"; diff <(echo "$a") <(echo "$b"); fi
done

作った木は次のようなものです。.git あり/なし、入れ子の .gitignore、.ignore、!、除外フォルダーの中の !、/ で固定、フォルダーだけ、**、[a-z]、\#、行末の空白、.git/info/exclude、CRLF と BOM の除外ファイル、探し始めより上の .ignore。これに3通りのオプションを掛けて、43項目になりました(項目数は 36 → 42 → 43 と、間違いを見つけるたびに増えています)。

結果

Mac Linux Windows
rg --files との突き合わせ PASS 43/FAIL 0 PASS 43/FAIL 0 PASS 43/FAIL 0

書式の1行ずつを見る単体テストは別に 44 件あります。

Windows だけ 15 項目落ちた

一度、Windows だけ 15 項目が FAIL になりました。原因はテストスクリプトの側でした。

Windows の ripgrep は、C:/W11Work/…\1net10-code\… のように、/ と \ が混じったパスを返すことがありました。スクリプトが区切りをそろえる前に対象フォルダー分のパスを削ろうとしていたので、削れずに、全部が「ripgrep にだけ出るファイル」に見えていました。件数そのものは一致していました。

上のスクリプトの norm で、区切りをそろえてから比べるのはそのためです。もう1つの教訓は、以前の Windows のテスト機にはこのフォルダーが無く、この区間を飛ばしていたことです。飛ばしたテストは、合格したテストではありません。

合わせなかったところ

  • .rgignore は読みません。 ripgrep 専用の名前なので、自分の CLI が読むと「ripgrep 用の設定が別の道具にも効く」という分かりにくさが残ります。同じ内容が要るなら .ignore に書いてもらう形にしました。意図的な非互換です。
  • --no-ignore-vcs・--no-ignore-parent などの細かい止め方は作っていません。
  • git の全体設定は ~/.gitconfig の [core] の excludesFile だけを読み、include は追いません。

違うところを数え上げられる状態にしておくほうが、「だいたい同じです」と言うより使う側は困りません。

確認の条件

  • ripgrep 15.2.0(Mac・Windows)/15.1.0(Linux)。この記事の各コマンドの出力は、ripgrep 15.1.0(Linux x86_64)で 2026-10-01 に確かめ直したもの
  • 自分の CLI は C#(.NET・NativeAOT)。照合は git や外部ライブラリを呼ばずに自前で書いた
  • 突き合わせは 2026-09-29 に3つの OS で実施(Mac は Apple M4、Linux は aarch64 の VM、Windows は別の機械)
  • この記事に速さの数字はありません。除外の判定そのものの速さは、切り出して測っていません

まとめ

  • ripgrep の除外は「種類の強さ」と「深さ」の二段構え。--ignore-file がいちばん弱く、.gitignore はリポジトリの中だけ、名指しは除外しない、除外したフォルダーの中は戻せない。
  • --no-ignore で --ignore-file は止まらない。--no-ignore-files が要る。
  • 互換性は「対応した」ではなく、相手の出力と一致したで定義する。ずれたら rg --files --debug で相手の判断を見る。
  • パスの区切りは、比べる前にそろえる。飛ばしたテストを合格と数えない。

題材にした CLI(UwView の uvf・uvp)で同じ除外を使う例と、束ねた索引との関係はこちらに書きました: https://uvp.y42u.net/blog/uwview-wf07-ignore-rules/

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?