From 08c0d427bff145c719fba8f25cc82084ad1aa9d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20=F0=9F=91=A8=F0=9F=8F=BD=E2=80=8D=F0=9F=92=BB=20Copl?= =?UTF-8?q?an?= Date: Fri, 13 Jan 2023 13:52:05 -0800 Subject: [PATCH] Add flag --no-require-git to always respect gitignore files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 adds the flag `--no-require-git` to make fd always respect all gitignore files. This behaves the same as the `--no-require-git` option in [ripgrep](https://github.com/BurntSushi/ripgrep/blob/3bb71b0cb8727ac43237af78ba5c707298de0128/crates/core/app.rs#L2214-L2226) This commit also contains an unrelated wording fix to CONTRIBUTING.md 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 (https://github.com/vegerot/ripgrep/commit/ebf17eef22a04450ca86e9dc790db7cb00e11e52) 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` (https://github.com/BurntSushi/ripgrep/issues/2374). This works fine, but I couldn't use this workaround for my other favorite tool! That's what this patch is for 😁 (a follow-up patch will add a similar `FD_CONFIG_PATH` environment variable like `RG_CONFIG_PATH`) --- CHANGELOG.md | 2 ++ CONTRIBUTING.md | 4 ++-- src/cli.rs | 22 +++++++++++++++++++ src/config.rs | 3 +++ src/main.rs | 1 + src/walk.rs | 1 + tests/tests.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 88 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be1efc2..c3bef47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Features +- Add flag --no-require-git to always respect gitignore files, see #1216 (@vegerot) + ## Bugfixes - Fix logic for when to use global ignore file. There was a bug where the only case where the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a2313e..b3a7480 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,11 +13,11 @@ give us the chance to discuss any potential changes first. ## Add an entry to the changelog If your contribution changes the behavior of `fd` (as opposed to a typo-fix -in the documentation), please update the [`CHANGELOG.md`](CHANGELOG.md) file +in the documentation), please update the [`CHANGELOG.md`](CHANGELOG.md#upcoming-release) file and describe your changes. This makes the release process much easier and therefore helps to get your changes into a new `fd` release faster. -The top of the `CHANGELOG` contains a *"unreleased"* section with a few +The top of the `CHANGELOG` contains an *"Upcoming release"* section with a few subsections (Features, Bugfixes, …). Please add your entry to the subsection that best describes your change. diff --git a/src/cli.rs b/src/cli.rs index 8e97f1c..55fbca8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -74,6 +74,28 @@ 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. + /// 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. + /// + /// + /// This flag can be disabled with --require-git. + #[arg( + long, + overrides_with = "require_git", + hide_short_help = true, + // same description as ripgrep's flag: ripgrep/crates/core/app.rs + long_help + )] + 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( diff --git a/src/config.rs b/src/config.rs index 7003a01..75b4c2b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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, diff --git a/src/main.rs b/src/main.rs index 001ba5a..567058c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -239,6 +239,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result>, config: Arc) - .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 diff --git a/tests/tests.rs b/tests/tests.rs index 5036afd..a2bc4d9 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -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 `--no-require-git` + 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() { @@ -2365,6 +2421,7 @@ fn test_number_parsing_errors() { #[test_case("--hidden", &["--no-hidden"] ; "hidden")] #[test_case("--no-ignore", &["--ignore"] ; "no-ignore")] #[test_case("--no-ignore-vcs", &["--ignore-vcs"] ; "no-ignore-vcs")] +#[test_case("--no-require-git", &["--require-git"] ; "no-require-git")] #[test_case("--follow", &["--no-follow"] ; "follow")] #[test_case("--absolute-path", &["--relative-path"] ; "absolute-path")] #[test_case("-u", &["--ignore", "--no-hidden"] ; "u")]