Repository: sharkdp/fd (language: rust)

Issue:
Supporting "--no-ignore-parent" like ripgrep, or support ignoring specifit .gitignore?
My problem is that I use my home directory as a git repo to manage my dotfiles, so there is a `~/.gitignore` that ignores everything except some files. The problem is `fd` won't work by default at this situation. Although there is a `--no-ignore-vcs`, but I still want `.gitignore` to be respected when I am in a project directory.

Ripgrep has a flag `--no-ignore-parent` that can ignore `.gitignore` in parent directory, which works well enough. A even better solution could introduce a new flag to exclude certain `.gitignore` only, such as `fd --no-ignore-file ~/.gitignore`.

Similar ripgrep issue: https://github.com/BurntSushi/ripgrep/issues/372



Discussion / hints:
I'm okay with adding a new option, analogous to `ripgrep`.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08e4e60fe..16d6e206a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Features
 
+- Add new `--no-ignore-parent` flag, see #787 (@will459)
+
 ## Bugfixes
 
 - Set default path separator to `/` in MSYS, see #537 and #730 (@aswild)
diff --git a/src/app.rs b/src/app.rs
index 2d6ec9484..fcfbebe8a 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -49,6 +49,16 @@ pub fn build_app() -> App<'static, 'static> {
                          ignored by '.gitignore' files.",
                 ),
         )
+        .arg(
+            Arg::with_name("no-ignore-parent")
+                .long("no-ignore-parent")
+                .overrides_with("no-ignore-parent")
+                .hidden_short_help(true)
+                .long_help(
+                    "Show search results from files and directories that would otherwise be \
+                        ignored by '.gitignore', '.ignore', or '.fdignore' files in parent directories.",
+                ),
+        )
         .arg(
             Arg::with_name("no-global-ignore-file")
                 .long("no-global-ignore-file")
diff --git a/src/main.rs b/src/main.rs
index 8654688ad..f3cda2daa 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -338,6 +338,10 @@ fn run() -> Result<ExitCode> {
         read_vcsignore: !(matches.is_present("no-ignore")
             || matches.is_present("rg-alias-hidden-ignore")
             || matches.is_present("no-ignore-vcs")),
+        read_parent_ignore: !(matches.is_present("no-ignore")
+            || matches.is_present("rg-alias-hidden-ignore")
+            || matches.is_present("no-ignore-vcs")
+            || matches.is_present("no-ignore-parent")),
         read_global_ignore: !(matches.is_present("no-ignore")
             || matches.is_present("rg-alias-hidden-ignore")
             || matches.is_present("no-global-ignore-file")),
diff --git a/src/options.rs b/src/options.rs
index aa9b5ea90..cf7d28db4 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -24,6 +24,9 @@ pub struct Options {
     /// Whether to respect `.fdignore` files or not.
     pub read_fdignore: bool,
 
+    /// Whether to respect ignore files in parent directories or not.
+    pub read_parent_ignore: bool,
+
     /// Whether to respect VCS ignore files (`.gitignore`, ..) or not.
     pub read_vcsignore: bool,
 
diff --git a/src/walk.rs b/src/walk.rs
index 052919172..d970d2c22 100644
--- a/src/walk.rs
+++ b/src/walk.rs
@@ -68,7 +68,7 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Options>) ->
     walker
         .hidden(config.ignore_hidden)
         .ignore(config.read_fdignore)
-        .parents(config.read_fdignore || config.read_vcsignore)
+        .parents(config.read_parent_ignore)
         .git_ignore(config.read_vcsignore)
         .git_global(config.read_vcsignore)
         .git_exclude(config.read_vcsignore)


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 7fe2c7068..4642f2862 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -497,6 +497,78 @@ fn test_gitignore_and_fdignore() {
     );
 }
 
+/// Ignore parent ignore files (--no-ignore-parent)
+#[test]
+fn test_no_ignore_parent() {
+    let dirs = &["inner"];
+    let files = &[
+        "inner/parent-ignored",
+        "inner/child-ignored",
+        "inner/not-ignored",
+    ];
+    let te = TestEnv::new(dirs, files);
+
+    // Ignore 'parent-ignored' in root
+    fs::File::create(te.test_root().join(".gitignore"))
+        .unwrap()
+        .write_all(b"parent-ignored")
+        .unwrap();
+    // Ignore 'child-ignored' in inner
+    fs::File::create(te.test_root().join("inner/.gitignore"))
+        .unwrap()
+        .write_all(b"child-ignored")
+        .unwrap();
+
+    te.assert_output_subdirectory("inner", &[], "not-ignored");
+
+    te.assert_output_subdirectory(
+        "inner",
+        &["--no-ignore-parent"],
+        "parent-ignored
+        not-ignored",
+    );
+}
+
+/// Ignore parent ignore files (--no-ignore-parent) with an inner git repo
+#[test]
+fn test_no_ignore_parent_inner_git() {
+    let dirs = &["inner"];
+    let files = &[
+        "inner/parent-ignored",
+        "inner/child-ignored",
+        "inner/not-ignored",
+    ];
+    let te = TestEnv::new(dirs, files);
+
+    // Make the inner folder also appear as a git repo
+    fs::create_dir_all(te.test_root().join("inner/.git")).unwrap();
+
+    // Ignore 'parent-ignored' in root
+    fs::File::create(te.test_root().join(".gitignore"))
+        .unwrap()
+        .write_all(b"parent-ignored")
+        .unwrap();
+    // Ignore 'child-ignored' in inner
+    fs::File::create(te.test_root().join("inner/.gitignore"))
+        .unwrap()
+        .write_all(b"child-ignored")
+        .unwrap();
+
+    te.assert_output_subdirectory(
+        "inner",
+        &[],
+        "not-ignored
+        parent-ignored",
+    );
+
+    te.assert_output_subdirectory(
+        "inner",
+        &["--no-ignore-parent"],
+        "not-ignored
+        parent-ignored",
+    );
+}
+
 /// Precedence of .fdignore files
 #[test]
 fn test_custom_ignore_precedence() {