From 51864c13fc329332b9ebcf2a7e45e7b6aadeedbd Mon Sep 17 00:00:00 2001 From: dana Date: Mon, 29 Jan 2018 13:10:59 -0600 Subject: [PATCH] ignore: fix handling of / in patterns This commit makes handling of patterns containing a `/` match actual git behaviour and the specification written in `man gitignore`. Fixes #761 --- ignore/src/gitignore.rs | 21 ++++++++++--------- ignore/src/overrides.rs | 3 ++- ...gnore_matched_path_or_any_parents_tests.rs | 16 +++++++------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/ignore/src/gitignore.rs b/ignore/src/gitignore.rs index e7b0007e..a21afa55 100644 --- a/ignore/src/gitignore.rs +++ b/ignore/src/gitignore.rs @@ -416,7 +416,6 @@ impl GitignoreBuilder { is_only_dir: false, }; let mut literal_separator = false; - let has_slash = line.chars().any(|c| c == '/'); let mut is_absolute = false; if line.starts_with("\\!") || line.starts_with("\\#") { line = &line[1..]; @@ -447,13 +446,13 @@ impl GitignoreBuilder { // If there is a literal slash, then we note that so that globbing // doesn't let wildcards match slashes. glob.actual = line.to_string(); - if has_slash { + if is_absolute || line.chars().any(|c| c == '/') { literal_separator = true; } - // If there was a leading slash, then this is a glob that must - // match the entire path name. Otherwise, we should let it match - // anywhere, so use a **/ prefix. - if !is_absolute { + // If there was a slash, then this is a glob that must match the entire + // path name. Otherwise, we should let it match anywhere, so use a **/ + // prefix. + if !literal_separator { // ... but only if we don't already have a **/ prefix. if !(glob.actual.starts_with("**/") || (glob.actual == "**" && glob.is_only_dir)) { glob.actual = format!("**/{}", glob.actual); @@ -617,10 +616,10 @@ mod tests { ignored!(ig25, ROOT, "Cargo.lock", "./tabwriter-bin/Cargo.lock"); ignored!(ig26, ROOT, "/foo/bar/baz", "./foo/bar/baz"); ignored!(ig27, ROOT, "foo/", "xyz/foo", true); - ignored!(ig28, ROOT, "src/*.rs", "src/grep/src/main.rs"); - ignored!(ig29, "./src", "/llvm/", "./src/llvm", true); - ignored!(ig30, ROOT, "node_modules/ ", "node_modules", true); - ignored!(ig31, ROOT, "**/", "foo/bar", true); + ignored!(ig28, "./src", "/llvm/", "./src/llvm", true); + ignored!(ig29, ROOT, "node_modules/ ", "node_modules", true); + ignored!(ig30, ROOT, "**/", "foo/bar", true); + ignored!(ig31, ROOT, "path1/*", "path1/foo"); not_ignored!(ignot1, ROOT, "amonths", "months"); not_ignored!(ignot2, ROOT, "monthsa", "months"); @@ -640,6 +639,8 @@ mod tests { "./third_party/protobuf/csharp/src/packages/repositories.config"); not_ignored!(ignot15, ROOT, "!/bar", "foo/bar"); not_ignored!(ignot16, ROOT, "*\n!**/", "foo", true); + not_ignored!(ignot17, ROOT, "src/*.rs", "src/grep/src/main.rs"); + not_ignored!(ignot18, ROOT, "path1/*", "path2/path1/foo"); fn bytes(s: &str) -> Vec { s.to_string().into_bytes() diff --git a/ignore/src/overrides.rs b/ignore/src/overrides.rs index 03fd39c4..955dc5ad 100644 --- a/ignore/src/overrides.rs +++ b/ignore/src/overrides.rs @@ -202,8 +202,9 @@ mod tests { #[test] fn gitignore() { let ov = ov(&["/foo", "bar/*.rs", "baz/**"]); + assert!(ov.matched("bar/lib.rs", false).is_whitelist()); assert!(ov.matched("bar/wat/lib.rs", false).is_ignore()); - assert!(ov.matched("wat/bar/lib.rs", false).is_whitelist()); + assert!(ov.matched("wat/bar/lib.rs", false).is_ignore()); assert!(ov.matched("foo", false).is_whitelist()); assert!(ov.matched("wat/foo", false).is_ignore()); assert!(ov.matched("baz", false).is_ignore()); diff --git a/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs b/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs index c76ee2d1..4de7cf3a 100644 --- a/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs +++ b/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs @@ -212,16 +212,16 @@ fn test_dirs_in_deep() { assert!(m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore()); // 02 - assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none()); // dir itself doesn't match - assert!(m("ROOT/parent_dir/dir_deep_02/file", false).is_ignore()); - assert!(m("ROOT/parent_dir/dir_deep_02/child_dir", true).is_ignore()); - assert!(m("ROOT/parent_dir/dir_deep_02/child_dir/file", false).is_ignore()); + assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none()); + assert!(m("ROOT/parent_dir/dir_deep_02/file", false).is_none()); + assert!(m("ROOT/parent_dir/dir_deep_02/child_dir", true).is_none()); + assert!(m("ROOT/parent_dir/dir_deep_02/child_dir/file", false).is_none()); // 03 - assert!(m("ROOT/parent_dir/dir_deep_03", true).is_none()); // dir itself doesn't match - assert!(m("ROOT/parent_dir/dir_deep_03/file", false).is_ignore()); - assert!(m("ROOT/parent_dir/dir_deep_03/child_dir", true).is_ignore()); - assert!(m("ROOT/parent_dir/dir_deep_03/child_dir/file", false).is_ignore()); + assert!(m("ROOT/parent_dir/dir_deep_03", true).is_none()); + assert!(m("ROOT/parent_dir/dir_deep_03/file", false).is_none()); + assert!(m("ROOT/parent_dir/dir_deep_03/child_dir", true).is_none()); + assert!(m("ROOT/parent_dir/dir_deep_03/child_dir/file", false).is_none()); // 10 assert!(m("ROOT/parent_dir/dir_deep_10", true).is_none());