ripgrep と同じ除外規則にそろえた — Wide Field⑦:3つのOSで43項目が一致、ただし .rgignore は読みません

技術解説

件数が合いません。

同じフォルダーを rg --files と自作の CLI に渡したのに、対象になるファイルの一覧が違う。犯人は .gitignore でした——正確には、こちらがそれを読んでいなかったことです。

「ripgrep と同じ規則にする」とは、どこまで同じにすることなのでしょうか。

先に結論: .ignore・.gitignore に ripgrep 15 と同じ規則で従うようにし、rg --files との突き合わせ 43 項目が Mac・Linux・Windows の3つとも一致(不一致 0 件)。ただし .rgignore は読みません

uvf 'logs/**' ERROR のようにワイルドカードで広げたとき、.gitignore や .ignore に当たるファイルは探しません。読むファイル・強さの順番・! の取り消し・親フォルダーまでさかのぼる範囲まで ripgrep 15 に合わせ、rg --files と一覧が一致することを突き合わせで確かめています。合わせなかったのは .rgignore(ripgrep 専用の名前)だけです。Mac(Apple M4・10コア)・メモリ32GB・外付け USB SSD。

uvf・uvp は私が作っている UwView(v1.7.3.6 “Wide Field”・2026年9月28日公開)の検索コマンドです。uvf は無料版、uvp は有料版 UwView Pro のものです。除外の規則は v1.7.2.5 で入れ、v1.7.2.6 で直し、v1.7.3.6 の統合テスト(2026年9月29日)で3つの OS の結果を確認しています。


先に、合わせなかったところを書きます

  • .rgignore は読みません。 ripgrep 専用の名前なので、こちらが読むと「ripgrep 用の設定が UwView にも効く」という分かりにくさが残ります。同じ内容が要るなら .ignore に書いてもらう形にしました。ここは意図的な非互換です。
  • 細かい止め方は作っていません。 --no-ignore-vcs・--no-ignore-parent などは未実装です。要望が出たら足します。
  • --no-ignore の仕様を一度間違えました。 詳しくは後述しますが、「ripgrep と同じ」と書いた仕様書のほうが誤りで、外部レビューで指摘されて直しました。
  • 除外の処理そのものの速さは、単独では測っていません。 除外ファイルが1つも無いフォルダーでは判定を素通りさせる設計ですが、「素通りのときに何秒も変わらない」ことを切り出して計測した数字は持っていません。

除外は「対象を減らす機能」ではなく「相手と一致させる機能」

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

実際に作ってみると、性質がまるで違いました。除外は、同じコマンドを打ったときに ripgrep と同じ答えを返すための機能です。

理由は単純で、除外が違うと件数が違うからです。件数が違えば、どちらが正しいのか読者にもこちらにも分かりません。ベンチマークの土俵そのものが崩れます。速さを比べるには、先に「同じものを探している」ことを保証しなければならない。

だから完了条件も速さではなく、rg --files と uvf --files '**' の一覧が全パターンで一致することに置きました。

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

ripgrep 15 が読むのは .gitignore・.ignore・.rgignore・.git/info/exclude・git の全体設定(core.excludesFile)です。こちらは .rgignore を外した4種類を読みます。

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

順 読むもの 備考
弱 --ignore-file <path> で足したもの 何回でも書ける。いちばん弱い(ripgrep と同じ)
↓ git の全体設定(~/.gitconfig の core.excludesFile) 無ければ ~/.config/git/ignore
↓ .git/info/exclude
↓ .gitignore git のリポジトリの中だけで効く
強 .ignore リポジトリの外でも効く

.gitignore が「リポジトリの中だけ」なのも ripgrep に合わせた点です。.git フォルダーごと複製していない写しでは .gitignore は効きません。これは実際にテストで引っかかりました。.git の無い写しに .gitignore を置いても、git ではないので読まれない——ripgrep と同じ挙動です。

書き方は gitignore の書式そのままです。*.log(どの深さでも)・/build(直下だけ)・build/(フォルダーだけ)・**/x・!pattern(取り消し)・行末の空白の扱い・\# のエスケープ。親フォルダーが除外されているときは ! で取り消せないという git の癖まで合わせています。照合は自前で書きました(NativeAOT で動かすため、git も外部ライブラリも呼びません)。

名前を書いたファイルは、除外しない

いちばん間違えやすいのがここです。

# build/ が .gitignore に入っていても、これは探す
uvf 'build/app.log' ERROR

# これはワイルドカードなので、除外が効く
uvf 'build/*.log' ERROR

除外が効くのは、ワイルドカードで広げた結果だけ。名指ししたファイルは外しません。ripgrep も同じです。

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

同じ考えで、除外のせいで1件も当たらなくなった指定には、その旨を添えます。「(.gitignore などで除外。--no-ignore で含めます)」と出す。黙って 0 件にしない、というだけの話ですが、これが無いと利用者は自分のパターンを疑い続けることになります。

検索の出力そのものには何も足しません(grep・ripgrep と同じく黙って外す)。知らせるのは --files のときと、全部外れたときだけです。

--no-ignore を「ripgrep と同じ」と書いたのは、こちらの誤りでした

自社に都合のいい話ばかりになるので、間違えた箇所を書きます。

仕様書には「--no-ignore は --ignore-file で足したものも止める。ripgrep と同じ」と書いていました。これが誤りでした。 ripgrep 15.2.0 は --no-ignore を付けても --ignore-file の規則は効いたままで、それを止めるのは --no-ignore-files です。

外部レビューで指摘され、ripgrep のほうに合わせました。--no-ignore は .ignore・.gitignore 類だけを止め、--no-ignore-files を新しく作った。突き合わせのテストにも組み合わせ3通りを足して、ripgrep と一致することを確かめています。

「同じにする」と決めた以上、相手の実装が正解です。仕様書に書いた自分の理解ではなく。

束ねた索引との関係——外れた部品は「残すが、探さない」

uvp は複数ファイルを1本の .uwvz(uvp 用の圧縮形式+索引)に束ねます。ここに除外が加わると、同じコマンドでも対象のファイルが変わることになります。索引を作り直すべきでしょうか。

作り直しません。決め方はこうです。

状況 動き
除外で新しく外れた(ディスクには有る) 索引の部品は残すが、探さない
除外が外れて新しく入った 足す(増えたぶんだけ)
ディスクから消えた そのまま索引から探す(Keep)
--no-ignore で走らせた 除外されていたものも足す。次に既定で走らせると「残すが探さない」

つまり探す対象は「今回広げた一覧+ディスクから消えた部品」です。索引の中身は増えるだけで、設定を変えるたびに作り直しは起きません。

元ファイルが消えても索引を作り直さない(Keep)という決め方が先にあり、除外はその規則の上に一段乗っている形です。

数字——43 項目、3つの OS

正しさの数字です。速さではありません。

段 内容 Mac Linux Windows
itest .ignore・.gitignore の突き合わせ PASS 43/FAIL 0 PASS 43/FAIL 0 PASS 43/FAIL 0
  • 小さなフォルダーの木(.git あり/なし・入れ子の .gitignore・.ignore・!・除外フォルダーの中の !・/ 固定・フォルダーだけ・**・[a-z]・\#・行末の空白・.git/info/exclude・CRLF と BOM・探し始めより上の .ignore)を作り、rg --files と一覧が一致するかを見ています。既定・--no-ignore・--ignore-file の3通りで。
  • 単体テストは別にあり、書式の1行ずつを見る IgnoreRulesTests が 44 件、uvp の索引との関係が 6 件です。
  • 項目数は 36 → 42 → 43 と増えています。最初の 36 は開発版での合格で、そこに --no-ignore まわりの組み合わせと、実際の配布物での確認を足していった結果です。

Windows で 15 項目が落ちた話

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

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

件数そのものは、その15項目でも ripgrep と一致していました。区切りをそろえてから削る順に直して、3つの OS で 43 項目が通っています。

以前の Windows のテスト機には対象のフォルダーが無くてこの部分を飛ばしていたため、初めて表に出た不具合でした。飛ばしていたテストは、合格していたテストではありません。

確かめ方

  • 相手: ripgrep 15.2.0(Mac・Windows)/15.1.0(Linux)
  • 突き合わせ: rg --files と uvf --files '**'/uvp --files '**' の一覧を比較(既定・--no-ignore・--ignore-file)
  • 版: uvf・uvp とも v1.7.3.6 “Wide Field”。2026年9月29日の3 OS 統合テスト
  • 機械: Mac(Apple M4・10コア・メモリ32GB)/外付け USB SSD。Linux は aarch64 の VM、Windows は別の機械
  • 秒数は OS をまたいで比べていません。この記事に速さの数字はありません

持ち帰れる一般論

(1)互換性は、機能ではなく答えの一致で定義する。 「.gitignore に対応した」では何も保証できません。保証できるのは「rg --files と一覧が一致する」という形にしたときです。比較できる相手がいるなら、完了条件を相手の出力に合わせる。

(2)仕様書より、相手の実装が正しい。 --no-ignore の誤りは、相手の man ページを読んだつもりで書いた自分の理解が原因でした。「〜と同じ」と書いた行は、すべて実際に突き合わせるまで仮説です。

(3)意図的な非互換は、明示して1つに絞る。 .rgignore を読まないのは仕様です。ただし「だいたい同じです」で済ませると、利用者はどこが違うか永久に分かりません。違う点を数え上げられる状態にしておくほうが、全部同じだと言い張るより信用されます。

(4)飛ばしたテストを、合格と数えない。 Windows の 15 項目は、テスト機にフォルダーが無くて飛んでいた区間でした。緑色の表は、走った項目についてしか何も言っていません。

正直に書いておくこと

  • この記事に速さの数字はありません。除外は正しさの機能なので、PASS 43/FAIL 0 以外に主張できる数字を持っていません
  • 除外の判定が速さに影響しないことは、単独では測っていません。除外ファイルの無いフォルダーで素通りする設計ですが、切り出した計測はしていません
  • .rgignore は読みません。--no-ignore-vcs・--no-ignore-parent などの細かい止め方も未実装です
  • git の全体設定は ~/.gitconfig の [core] の1行だけを読みます。include は追いません(限界として書いておきます)
  • --no-ignore の仕様は一度間違えていました。外部レビューで指摘されて直したものです
  • Windows と Linux の結果は、正しさの確認としてだけ持ち込んでいます。秒数は OS をまたいで比べません
  • 私は比較の一方の開発者です。その分は割り引いてお読みください

使っている道具

UwView は、巨大なテキストファイル・巨大ログを調べるための CLI/GUI ツールです。v1.7.3.6 “Wide Field” では、複数のファイルと7つの圧縮形式を、外部のコマンドなしで1回で探せます。

uvp ならこう書く

# 除外を効かせて探す(既定。.ignore・.gitignore に従う)
uvf 'logs/**' ERROR

# 何が対象になるか、先に確かめる(除外した本数も出る・無料)
uvf 'logs/**' --files

# 除外をしない
uvf 'logs/**' ERROR --no-ignore

# 除外の規則を足す(何回でも書ける・いちばん弱い)
uvf 'logs/**' ERROR --ignore-file ./my-ignore

# --ignore-file で足したものも止める
uvf 'logs/**' ERROR --no-ignore --no-ignore-files

# 名指しは除外されない(.gitignore に build/ があっても探す)
uvf 'build/app.log' ERROR

# 束ねて探す。除外で外れた部品は索引に残るが探さない(UwView Pro)
uvp 'logs/**' ERROR

終了コードは grep と同じで、0=あり・1=なし・2=エラーまたは結果が不完全です。

無料版は GitHub Releases から。Mac は brew install --cask amru195704/uwview/uwview、Windows は Scoop(scoop bucket add uwview https://github.com/amru195704/scoop-uwview → scoop install uwview)でも入ります。

リンク

  • 外部コマンドを呼ばずに7形式を探す: https://uvp.y42u.net/blog/uwview-wf02-seven-formats-no-external/
  • 圧縮ログの検索を ugrep・ripgrep・zgrep と比べた: https://uvp.y42u.net/blog/uwview-ugrep-ripgrep-zgrep-compressed-logs/
  • .uwvz を作って、中を探して、取り出す: https://uvp.y42u.net/blog/uwvz-compressed-archive-search-extract/
  • ログは圧縮したほうが、速く探せます(形式の選び方): https://uvp.y42u.net/blog/log-compression-format-choice/
  • 読む回数だけを減らした13回のまとめ: https://uvp.y42u.net/blog/uwview-fl13-first-light-recap/
  • 実測の全データ(ベンチマーク一覧): https://uvp.y42u.net/benchmarks/
  • ソースコード(GitHub): https://github.com/amru195704/UwView

そして、ストレージを専有している巨大ログを圧縮して保管し、さらに高速に検索したいなら UwView Pro をどうぞ。永続索引・圧縮キャッシュ検索・約 1/9 保管で、開き直しも検索も一段速くなります(全 OS 対応・買い切り/月額プランあり)。


開発者より: アプリ・Kindle 本・公開プロジェクトの一覧は GitHub: amru195704 にまとめています。


タイトルとURLをコピーしました