about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2022-12-14 15:42:22 +0100
committerAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2022-12-27 14:54:37 +0100
commit633a6c8b66a8a2e54d9545071312d4406fa195e5 (patch)
treeae307ab8c447150c65b05ff27d409eadd8e4ca3e
parenta1fc71196a5554eafc5dd9a1b4e0c159717141e0 (diff)
downloadrust-633a6c8b66a8a2e54d9545071312d4406fa195e5.tar.gz
rust-633a6c8b66a8a2e54d9545071312d4406fa195e5.zip
Format only modified files
As discussed on #105688, this makes x fmt only format modified files
-rw-r--r--src/bootstrap/format.rs46
1 files changed, 46 insertions, 0 deletions
diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs
index b2f6afead79..b99bf33f482 100644
--- a/src/bootstrap/format.rs
+++ b/src/bootstrap/format.rs
@@ -44,6 +44,35 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F
     }
 }
 
+/// Finds the remote for rust-lang/rust.
+/// For example for these remotes it will return `upstream`.
+/// ```text
+/// origin  https://github.com/Nilstrieb/rust.git (fetch)
+/// origin  https://github.com/Nilstrieb/rust.git (push)
+/// upstream        https://github.com/rust-lang/rust (fetch)
+/// upstream        https://github.com/rust-lang/rust (push)
+/// ```
+fn get_rust_lang_rust_remote() -> Result<String, String> {
+    let mut git = Command::new("git");
+    git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]);
+
+    let output = git.output().map_err(|err| format!("{err:?}"))?;
+    if !output.status.success() {
+        return Err("failed to execute git config command".to_owned());
+    }
+
+    let stdout = String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?;
+
+    let rust_lang_remote = stdout
+        .lines()
+        .find(|remote| remote.contains("rust-lang"))
+        .ok_or_else(|| "rust-lang/rust remote not found".to_owned())?;
+
+    let remote_name =
+        rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?;
+    Ok(remote_name.into())
+}
+
 #[derive(serde::Deserialize)]
 struct RustfmtConfig {
     ignore: Vec<String>,
@@ -110,6 +139,23 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
                 // preventing the latter from being formatted.
                 ignore_fmt.add(&format!("!/{}", untracked_path)).expect(&untracked_path);
             }
+            if !check && paths.is_empty() {
+                let remote = t!(get_rust_lang_rust_remote());
+                let base = output(
+                    build
+                        .config
+                        .git()
+                        .arg("merge-base")
+                        .arg("HEAD")
+                        .arg(format!("{remote}/master")),
+                );
+                let files =
+                    output(build.config.git().arg("diff").arg("--name-only").arg(base.trim()));
+                for file in files.lines() {
+                    println!("formatting modified file {file}");
+                    ignore_fmt.add(&format!("/{file}")).expect(file);
+                }
+            }
         } else {
             println!("Not in git tree. Skipping git-aware format checks");
         }