diff --git a/src/bin/cargo/commands/fix.rs b/src/bin/cargo/commands/fix.rs index 4cbf9b9565e..ed582661bf2 100644 --- a/src/bin/cargo/commands/fix.rs +++ b/src/bin/cargo/commands/fix.rs @@ -66,6 +66,11 @@ pub fn cli() -> App { .long("allow-dirty") .help("Fix code even if the working directory is dirty"), ) + .arg( + Arg::with_name("allow-staged") + .long("allow-staged") + .help("Fix code even if the working directory has staged changes"), + ) .after_help( "\ This Cargo subcommmand will automatically take rustc's suggestions from @@ -135,6 +140,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { compile_opts: opts, allow_dirty: args.is_present("allow-dirty"), allow_no_vcs: args.is_present("allow-no-vcs"), + allow_staged: args.is_present("allow-staged"), broken_code: args.is_present("broken-code"), })?; Ok(()) diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index 275a471bd17..f26fc872e91 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -33,6 +33,7 @@ pub struct FixOptions<'a> { pub compile_opts: CompileOptions<'a>, pub allow_dirty: bool, pub allow_no_vcs: bool, + pub allow_staged: bool, pub broken_code: bool, } @@ -87,25 +88,38 @@ fn check_version_control(opts: &FixOptions) -> CargoResult<()> { error pass `--allow-no-vcs`") } - if opts.allow_dirty { + if opts.allow_dirty && opts.allow_staged { return Ok(()) } let mut dirty_files = Vec::new(); + let mut staged_files = Vec::new(); if let Ok(repo) = git2::Repository::discover(config.cwd()) { - let mut opts = git2::StatusOptions::new(); - opts.include_ignored(false); - for status in repo.statuses(Some(&mut opts))?.iter() { - if status.status() != git2::Status::CURRENT { - if let Some(path) = status.path() { - dirty_files.push(path.to_string()); - } + let mut repo_opts = git2::StatusOptions::new(); + repo_opts.include_ignored(false); + for status in repo.statuses(Some(&mut repo_opts))?.iter() { + if let Some(path) = status.path() { + match status.status() { + git2::Status::CURRENT => (), + git2::Status::INDEX_NEW | + git2::Status::INDEX_MODIFIED | + git2::Status::INDEX_DELETED | + git2::Status::INDEX_RENAMED | + git2::Status::INDEX_TYPECHANGE => + if !opts.allow_staged { + staged_files.push(path.to_string()) + }, + _ => + if !opts.allow_dirty { + dirty_files.push(path.to_string()) + }, + }; } } } - if dirty_files.is_empty() { + if dirty_files.is_empty() && staged_files.is_empty() { return Ok(()) } @@ -113,13 +127,18 @@ fn check_version_control(opts: &FixOptions) -> CargoResult<()> { for file in dirty_files { files_list.push_str(" * "); files_list.push_str(&file); - files_list.push_str("\n"); + files_list.push_str(" (dirty)\n"); + } + for file in staged_files { + files_list.push_str(" * "); + files_list.push_str(&file); + files_list.push_str(" (staged)\n"); } - bail!("the working directory of this project is detected as dirty, and \ + bail!("the working directory of this project has uncommitted changes, and \ `cargo fix` can potentially perform destructive changes; if you'd \ - like to suppress this error pass `--allow-dirty`, or commit the \ - changes to these files:\n\ + like to suppress this error pass `--allow-dirty`, `--allow-staged`, \ + or commit the changes to these files:\n\ \n\ {}\n\ ", files_list); diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index 0a5f9f2a06f..cfc5c7a007e 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -6,6 +6,8 @@ use support::git; use support::is_nightly; use support::{basic_manifest, project}; +use std::io::Write; + #[test] fn do_not_fix_broken_builds() { let p = project() @@ -725,12 +727,12 @@ fn warns_about_dirty_working_directory() { .with_status(101) .with_stderr( "\ -error: the working directory of this project is detected as dirty, and `cargo \ -fix` can potentially perform destructive changes; if you'd like to \ -suppress this error pass `--allow-dirty`, or commit the changes to \ -these files: +error: the working directory of this project has uncommitted changes, \ +and `cargo fix` can potentially perform destructive changes; if you'd \ +like to suppress this error pass `--allow-dirty`, `--allow-staged`, or \ +commit the changes to these files: - * src/lib.rs + * src/lib.rs (dirty) ", @@ -738,6 +740,40 @@ these files: p.cargo("fix --allow-dirty").run(); } +#[test] +fn warns_about_staged_working_directory() { + let p = project().file("src/lib.rs", "pub fn foo() {}").build(); + + let repo = git2::Repository::init(&p.root()).unwrap(); + let mut cfg = t!(repo.config()); + t!(cfg.set_str("user.email", "foo@bar.com")); + t!(cfg.set_str("user.name", "Foo Bar")); + drop(cfg); + git::add(&repo); + git::commit(&repo); + File::create(&p.root().join("src/lib.rs")) + .unwrap() + .write_all("pub fn bar() {}".to_string().as_bytes()) + .unwrap(); + git::add(&repo); + + p.cargo("fix") + .with_status(101) + .with_stderr( + "\ +error: the working directory of this project has uncommitted changes, \ +and `cargo fix` can potentially perform destructive changes; if you'd \ +like to suppress this error pass `--allow-dirty`, `--allow-staged`, or \ +commit the changes to these files: + + * src/lib.rs (staged) + + +", + ).run(); + p.cargo("fix --allow-staged").run(); +} + #[test] fn does_not_warn_about_clean_working_directory() { let p = project().file("src/lib.rs", "pub fn foo() {}").build();