Skip to content

Commit 3ca96e9

Browse files
committed
Add support for ! negate gitignore patterns.
1 parent d4b6e90 commit 3ca96e9

File tree

3 files changed

+84
-15
lines changed

3 files changed

+84
-15
lines changed

src/cargo/sources/path.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,16 @@ impl<'cfg> PathSource<'cfg> {
138138
.map(|p| glob_parse(p))
139139
.collect::<Result<Vec<_>, _>>();
140140

141+
// Don't warn if using a negate pattern, since those weren't ever
142+
// previously supported.
143+
let has_negate = pkg
144+
.manifest()
145+
.exclude()
146+
.iter()
147+
.chain(pkg.manifest().include().iter())
148+
.any(|p| p.starts_with("!"));
141149
// Don't warn about glob mismatch if it doesn't parse.
142-
let glob_is_valid = glob_exclude.is_ok() && glob_include.is_ok();
150+
let glob_is_valid = glob_exclude.is_ok() && glob_include.is_ok() && !has_negate;
143151
let glob_exclude = glob_exclude.unwrap_or_else(|_| Vec::new());
144152
let glob_include = glob_include.unwrap_or_else(|_| Vec::new());
145153

@@ -180,21 +188,15 @@ impl<'cfg> PathSource<'cfg> {
180188
{
181189
Match::None => Ok(true),
182190
Match::Ignore(_) => Ok(false),
183-
Match::Whitelist(pattern) => Err(failure::format_err!(
184-
"exclude rules cannot start with `!`: {}",
185-
pattern.original()
186-
)),
191+
Match::Whitelist(_) => Ok(true),
187192
}
188193
} else {
189194
match ignore_include
190195
.matched_path_or_any_parents(relative_path, /* is_dir */ false)
191196
{
192197
Match::None => Ok(false),
193198
Match::Ignore(_) => Ok(true),
194-
Match::Whitelist(pattern) => Err(failure::format_err!(
195-
"include rules cannot start with `!`: {}",
196-
pattern.original()
197-
)),
199+
Match::Whitelist(_) => Ok(false),
198200
}
199201
}
200202
};

src/doc/src/reference/manifest.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ The patterns should be [gitignore]-style patterns. Briefly:
148148
`foo`.
149149
- `/**/` matches zero or more directories. For example, `a/**/b` matches
150150
`a/b`, `a/x/b`, `a/x/y/b`, and so on.
151-
- `!` prefixed patterns are not supported.
151+
- `!` prefix negates a pattern. For example, a pattern of `src/**.rs` and
152+
`!foo.rs` would match all files with the `.rs` extension inside the `src`
153+
directory, except for any file named `foo.rs`.
152154

153155
If git is being used for a package, the `exclude` field will be seeded with
154156
the `gitignore` settings from the repository.

tests/testsuite/package.rs

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,13 @@ fn include_cargo_toml_implicit() {
11521152
.run();
11531153
}
11541154

1155-
fn include_exclude_test(include: &str, exclude: &str, files: &[&str], expected: &str) {
1155+
fn include_exclude_test(
1156+
include: &str,
1157+
exclude: &str,
1158+
files: &[&str],
1159+
expected: &str,
1160+
has_warnings: bool,
1161+
) {
11561162
let mut pb = project().file(
11571163
"Cargo.toml",
11581164
&format!(
@@ -1177,10 +1183,13 @@ fn include_exclude_test(include: &str, exclude: &str, files: &[&str], expected:
11771183
}
11781184
let p = pb.build();
11791185

1180-
p.cargo("package --list")
1181-
.with_stdout(expected)
1182-
// .with_stderr("") Add this back when warnings are removed.
1183-
.run();
1186+
let mut e = p.cargo("package --list");
1187+
if has_warnings {
1188+
e.with_stderr_contains("[..]");
1189+
} else {
1190+
e.with_stderr("");
1191+
}
1192+
e.with_stdout(expected).run();
11841193
p.root().rm_rf();
11851194
}
11861195

@@ -1201,6 +1210,7 @@ fn package_include_ignore_only() {
12011210
src/abc2.rs\n\
12021211
src/lib.rs\n\
12031212
",
1213+
false,
12041214
)
12051215
}
12061216

@@ -1216,6 +1226,7 @@ fn gitignore_patterns() {
12161226
foo\n\
12171227
x/foo/y\n\
12181228
",
1229+
true,
12191230
);
12201231

12211232
include_exclude_test(
@@ -1225,6 +1236,7 @@ fn gitignore_patterns() {
12251236
"Cargo.toml\n\
12261237
foo\n\
12271238
",
1239+
false,
12281240
);
12291241

12301242
include_exclude_test(
@@ -1237,6 +1249,7 @@ fn gitignore_patterns() {
12371249
foo\n\
12381250
src/lib.rs\n\
12391251
",
1252+
true,
12401253
);
12411254

12421255
include_exclude_test(
@@ -1259,6 +1272,7 @@ fn gitignore_patterns() {
12591272
other\n\
12601273
src/lib.rs\n\
12611274
",
1275+
false,
12621276
);
12631277

12641278
include_exclude_test(
@@ -1268,6 +1282,7 @@ fn gitignore_patterns() {
12681282
"Cargo.toml\n\
12691283
a/foo/bar\n\
12701284
",
1285+
false,
12711286
);
12721287

12731288
include_exclude_test(
@@ -1277,6 +1292,7 @@ fn gitignore_patterns() {
12771292
"Cargo.toml\n\
12781293
foo/x/y/z\n\
12791294
",
1295+
false,
12801296
);
12811297

12821298
include_exclude_test(
@@ -1288,5 +1304,54 @@ fn gitignore_patterns() {
12881304
a/x/b\n\
12891305
a/x/y/b\n\
12901306
",
1307+
false,
1308+
);
1309+
}
1310+
1311+
#[test]
1312+
fn gitignore_negate() {
1313+
include_exclude_test(
1314+
r#"["Cargo.toml", "*.rs", "!foo.rs", "\\!important"]"#, // include
1315+
"[]",
1316+
&["src/lib.rs", "foo.rs", "!important"],
1317+
"!important\n\
1318+
Cargo.toml\n\
1319+
src/lib.rs\n\
1320+
",
1321+
false,
1322+
);
1323+
1324+
// NOTE: This is unusual compared to git. Git treats `src/` as a
1325+
// short-circuit which means rules like `!src/foo.rs` would never run.
1326+
// However, because Cargo only works by iterating over *files*, it doesn't
1327+
// short-circuit.
1328+
include_exclude_test(
1329+
r#"["Cargo.toml", "src/", "!src/foo.rs"]"#, // include
1330+
"[]",
1331+
&["src/lib.rs", "src/foo.rs"],
1332+
"Cargo.toml\n\
1333+
src/lib.rs\n\
1334+
",
1335+
false,
1336+
);
1337+
1338+
include_exclude_test(
1339+
r#"["Cargo.toml", "src/**.rs", "!foo.rs"]"#, // include
1340+
"[]",
1341+
&["src/lib.rs", "foo.rs", "src/foo.rs", "src/bar/foo.rs"],
1342+
"Cargo.toml\n\
1343+
src/lib.rs\n\
1344+
",
1345+
false,
1346+
);
1347+
1348+
include_exclude_test(
1349+
"[]",
1350+
r#"["*.rs", "!foo.rs", "\\!important"]"#, // exclude
1351+
&["src/lib.rs", "foo.rs", "!important"],
1352+
"Cargo.toml\n\
1353+
foo.rs\n\
1354+
",
1355+
false,
12911356
);
12921357
}

0 commit comments

Comments
 (0)