diff --git a/CHANGELOG.md b/CHANGELOG.md index 203bd1c6..5a3e9f1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ Feature enhancements: Add `--include-zero` flag that shows files searched without matches. * [FEATURE #1390](https://github.com/BurntSushi/ripgrep/pull/1390): Add `--no-context-separator` flag that always hides context separators. +* [FEATURE #1420](https://github.com/BurntSushi/ripgrep/pull/1420): + Add `--no-ignore-exclude` to disregard rules in `.git/info/exclude` files. Bug fixes: diff --git a/complete/_rg b/complete/_rg index 5fa6e9c2..b90d1801 100644 --- a/complete/_rg +++ b/complete/_rg @@ -129,6 +129,10 @@ _rg() { '--ignore-file-case-insensitive[process ignore files case insensitively]' $no'--no-ignore-file-case-insensitive[process ignore files case sensitively]' + + '(ignore-exclude)' # Local exclude (ignore)-file options + "--no-ignore-exclude[don't respect local exclude (ignore) files]" + $no'--ignore-exclude[respect local exclude (ignore) files]' + + '(ignore-global)' # Global ignore-file options "--no-ignore-global[don't respect global ignore files]" $no'--ignore-global[respect global ignore files]' diff --git a/doc/rg.1.txt.tpl b/doc/rg.1.txt.tpl index 3ce0881d..2d3a391f 100644 --- a/doc/rg.1.txt.tpl +++ b/doc/rg.1.txt.tpl @@ -104,6 +104,76 @@ was found. In summary: unable to read a file). +AUTOMATIC FILTERING +------------------- +TL;DR - To disable automatic filtering, use 'rg -uuu'. + +One of ripgrep's most important features is its automatic smart filtering. +It is the most apparent differentiating feature between ripgrep and other tools +like 'grep'. As such, its behavior may be surprising to users that aren't +expecting it. + +ripgrep does four types of filtering automatically: + + 1. Files and directories that match ignore rules are not searched. + 2. Hidden files and directories are not searched. + 3. Binary files (files with a 'NUL' byte) are not searched. + 4. Symbolic links are not followed. + +The first type of filtering is the most sophisticated. ripgrep will attempt to +respect your gitignore rules as faithfully as possible. In particular, this +includes the following: + + * Any global rules, e.g., in '$HOME/.config/git/ignore'. + * Any rules in '.gitignore'. + * Any local rules, e.g., in '.git/info/exclude'. + +In some cases, ripgrep and git will not always be in sync in terms of which +files are ignored. For example, a file that is ignored via '.gitignore' but is +tracked by git would not be searched by ripgrep even though git tracks it. This +is unlikely to ever be fixed. Instead, you should either make sure your exclude +rules match the files you track precisely, or otherwise use 'git grep' for +search. + +Additional ignore rules can be provided outside of a git context: + + * Any rules in '.ignore'. + * Any rules in '.rgignore'. + * Any rules in files specified with the '--ignore-file' flag. + +The precedence of ignore rules is as follows, with later items overriding +earlier items: + + * Files given by '--ignore-file'. + * Global gitignore rules, e.g., from '$HOME/.config/git/ignore'. + * Local rules from '.git/info/exclude'. + * Rules from '.gitignore'. + * Rules from '.ignore'. + * Rules from '.rgignore'. + +So for example, if 'foo' were in a '.gitignore' and '!foo' were in an +'.rgignore', then 'foo' would not be ignored since '.rgignore' takes precedence +over '.gitignore'. + +Each of the types of filtering can be configured via command line flags: + + * There are several flags starting with '--no-ignore' that toggle which, + if any, ignore rules are respected. '--no-ignore' by itself will disable + all of them. + * '--hidden' will force ripgrep to search hidden files and directories. + * '--binary' will force ripgrep to search binary files. + * '-L/--follow' will force ripgrep to follow symlinks. + +As a special short hand, the `-u` flag can be specified up to three times. Each +additional time incrementally decreases filtering: + + * '-u' is equivalent to '--no-ignore'. + * '-uu' is equivalent to '--no-ignore --hidden'. + * '-uuu' is equivalent to '--no-ignore --hidden --binary'. + +In particular, 'rg -uuu' should search the same exact content as 'grep -r'. + + CONFIGURATION FILES ------------------- ripgrep supports reading configuration files that change ripgrep's default diff --git a/src/app.rs b/src/app.rs index b72baf09..94e8a421 100644 --- a/src/app.rs +++ b/src/app.rs @@ -595,6 +595,7 @@ pub fn all_args_and_flags() -> Vec { flag_no_config(&mut args); flag_no_ignore(&mut args); flag_no_ignore_dot(&mut args); + flag_no_ignore_exclude(&mut args); flag_no_ignore_global(&mut args); flag_no_ignore_messages(&mut args); flag_no_ignore_parent(&mut args); @@ -1769,6 +1770,26 @@ This flag can be disabled with the --ignore-dot flag. args.push(arg); } +fn flag_no_ignore_exclude(args: &mut Vec) { + const SHORT: &str = "Don't respect local exclusion files."; + const LONG: &str = long!("\ +Don't respect ignore files that are manually configured for the repository +such as git's '.git/info/exclude'. + +This flag can be disabled with the --ignore-exclude flag. +"); + let arg = RGArg::switch("no-ignore-exclude") + .help(SHORT).long_help(LONG) + .overrides("ignore-exclude"); + args.push(arg); + + let arg = RGArg::switch("ignore-exclude") + .hidden() + .overrides("no-ignore-exclude"); + args.push(arg); +} + + fn flag_no_ignore_global(args: &mut Vec) { const SHORT: &str = "Don't respect global ignore files."; const LONG: &str = long!("\ diff --git a/src/args.rs b/src/args.rs index 32ea1b11..3dc4b16f 100644 --- a/src/args.rs +++ b/src/args.rs @@ -881,7 +881,7 @@ impl ArgMatches { .ignore(!self.no_ignore_dot()) .git_global(!self.no_ignore_vcs() && !self.no_ignore_global()) .git_ignore(!self.no_ignore_vcs()) - .git_exclude(!self.no_ignore_vcs()) + .git_exclude(!self.no_ignore_vcs() && !self.no_ignore_exclude()) .ignore_case_insensitive(self.ignore_file_case_insensitive()); if !self.no_ignore() { builder.add_custom_ignore_filename(".rgignore"); @@ -1231,6 +1231,11 @@ impl ArgMatches { self.is_present("no-ignore-dot") || self.no_ignore() } + /// Returns true if local exclude (ignore) files should be ignored. + fn no_ignore_exclude(&self) -> bool { + self.is_present("no-ignore-exclude") || self.no_ignore() + } + /// Returns true if global ignore files should be ignored. fn no_ignore_global(&self) -> bool { self.is_present("no-ignore-global") || self.no_ignore() diff --git a/tests/feature.rs b/tests/feature.rs index 4d918163..33ab4e17 100644 --- a/tests/feature.rs +++ b/tests/feature.rs @@ -728,6 +728,18 @@ rgtest!(f1207_ignore_encoding, |dir: Dir, mut cmd: TestCommand| { eqnice!("\u{FFFD}\u{FFFD}\x00b\n", cmd.stdout()); }); +// See: https://github.com/BurntSushi/ripgrep/pull/1420 +rgtest!(f1420_no_ignore_dot, |dir: Dir, mut cmd: TestCommand| { + dir.create_dir(".git/info"); + dir.create(".git/info/exclude", "foo"); + dir.create("bar", ""); + dir.create("foo", ""); + + cmd.arg("--sort").arg("path").arg("--files"); + eqnice!("bar\n", cmd.stdout()); + eqnice!("bar\nfoo\n", cmd.arg("--no-ignore-exclude").stdout()); +}); + rgtest!(no_context_sep, |dir: Dir, mut cmd: TestCommand| { dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx"); cmd.args(&[