Skip to content

Commit

Permalink
Make --ignore-vcs _always_ respect gitignore files
Browse files Browse the repository at this point in the history
Summary: Currently, `--ignore-vcs` only serves to unset `--no-ignore-vcs`.
There is currently no way to tell fd to respect gitignore files when not in a
git repository.  This commit makes `--ignore-vcs` always respect all gitignore
files.

This behaves similarly to the `--no-require-git` option in [ripgrep](https://github.com/BurntSushi/ripgrep/blob/3bb71b0cb8727ac43237af78ba5c707298de0128/crates/core/app.rs#L2214-L2226)

Test Plan: `tests/tests.rs`

Background: I am using [Sapling](https://sapling-scm.com/docs/introduction/)
for working with git repositories (including this commit ☺️).  Since Sapling
uses `.sl` instead of `.git`, tools using the `ignore` crate (rg and fd) would show gitignored files.
I made a patch (vegerot/ripgrep@ebf17ee)
to `ignore` to respect gitignores with _either_ `.git` or `.sl`.  However,
@BurntSushi said he did not want to merge that patch and instead suggested I
use `--no-require-git` (BurntSushi/ripgrep#2374).
This works fine, but I couldn't use this workaround for my other favorite tool!
That's what this patch is for 😁
  • Loading branch information
vegerot committed Dec 29, 2022
1 parent 7c86c7d commit 9a59a79
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ pub struct Opts {
#[arg(long, overrides_with = "no_ignore_vcs", hide = true, action = ArgAction::SetTrue)]
ignore_vcs: (),

/// Do not require a git repository to respect gitignores.
#[arg(
long,
overrides_with = "require_git",
hide_short_help = true,
help = "Do not require a git repository to use gitignores.",
// same description as ripgrep's flag
long_help = "By default, fd will only respect global gitignore rules, .gitignore rules,\
and local exclude rules if fd detects that you are searching inside a\
git repository. This flag allows you to relax this restriction such that\
fd will respect all git related ignore rules regardless of whether you're\
searching in a git repository or not.\n\n\
This flag behaves the same as --no-require-git flag from ripgrep.\n\n\
This flag can be disabled with --require-git."
)]
pub no_require_git: bool,

/// Overrides --no-require-git
#[arg(long, overrides_with = "no_require_git", hide = true, action = ArgAction::SetTrue)]
require_git: (),

/// Show search results from files and directories that would otherwise be
/// ignored by '.gitignore', '.ignore', or '.fdignore' files in parent directories.
#[arg(
Expand Down
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub struct Config {
/// Whether to respect VCS ignore files (`.gitignore`, ..) or not.
pub read_vcsignore: bool,

/// Whether to require a `.git` directory to respect gitignore files.
pub require_git_to_read_vcsignore: bool,

/// Whether to respect the global ignore file or not.
pub read_global_ignore: bool,

Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
ignore_hidden: !(opts.hidden || opts.rg_alias_ignore()),
read_fdignore: !(opts.no_ignore || opts.rg_alias_ignore()),
read_vcsignore: !(opts.no_ignore || opts.rg_alias_ignore() || opts.no_ignore_vcs),
require_git_to_read_vcsignore: !opts.no_require_git,
read_parent_ignore: !opts.no_ignore_parent,
read_global_ignore: !(opts.no_ignore
|| opts.rg_alias_ignore()
Expand Down
1 change: 1 addition & 0 deletions src/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub fn scan(paths: &[PathBuf], patterns: Arc<Vec<Regex>>, config: Arc<Config>) -
.git_ignore(config.read_vcsignore)
.git_global(config.read_vcsignore)
.git_exclude(config.read_vcsignore)
.require_git(config.require_git_to_read_vcsignore)
.overrides(overrides)
.follow_links(config.follow_links)
// No need to check for supported platforms, option is unavailable on unsupported ones
Expand Down
56 changes: 56 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,62 @@ fn test_custom_ignore_precedence() {
te.assert_output(&["--no-ignore", "foo"], "inner/foo");
}

/// Don't require git to respect gitignore (--no-require-git)
#[test]
fn test_respect_ignore_files() {
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);

// Not in a git repo anymore
fs::remove_dir(te.test_root().join(".git")).unwrap();

// don't respect gitignore because we're not in a git repo
te.assert_output(
&["foo"],
"a.foo
gitignored.foo
one/b.foo
one/two/c.foo
one/two/C.Foo2
one/two/three/d.foo
one/two/three/directory_foo/",
);

// respect gitignore because we set `--ignore-vcs`
te.assert_output(
&["--no-require-git", "foo"],
"a.foo
one/b.foo
one/two/c.foo
one/two/C.Foo2
one/two/three/d.foo
one/two/three/directory_foo/",
);

// make sure overriding works
te.assert_output(
&["--no-require-git", "--require-git", "foo"],
"a.foo
gitignored.foo
one/b.foo
one/two/c.foo
one/two/C.Foo2
one/two/three/d.foo
one/two/three/directory_foo/",
);

te.assert_output(
&["--no-require-git", "--no-ignore", "foo"],
"a.foo
gitignored.foo
fdignored.foo
one/b.foo
one/two/c.foo
one/two/C.Foo2
one/two/three/d.foo
one/two/three/directory_foo/",
);
}

/// VCS ignored files (--no-ignore-vcs)
#[test]
fn test_no_ignore_vcs() {
Expand Down

0 comments on commit 9a59a79

Please sign in to comment.