Skip to content

Commit d5e5762

Browse files
committed
Handle non-existant upstream master branches in x fmt
1 parent 25c1531 commit d5e5762

File tree

4 files changed

+99
-20
lines changed

4 files changed

+99
-20
lines changed

Cargo.lock

-1
Original file line numberDiff line numberDiff line change
@@ -5308,7 +5308,6 @@ dependencies = [
53085308
name = "tidy"
53095309
version = "0.1.0"
53105310
dependencies = [
5311-
"build_helper",
53125311
"cargo_metadata 0.14.0",
53135312
"ignore",
53145313
"lazy_static",

src/bootstrap/format.rs

+35-17
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Runs rustfmt on the repository.
22
33
use crate::builder::Builder;
4-
use crate::util::{output, program_out_of_date, t};
5-
use build_helper::git::get_rust_lang_rust_remote;
4+
use crate::util::{output, output_result, program_out_of_date, t};
5+
use build_helper::git::updated_master_branch;
66
use ignore::WalkBuilder;
77
use std::collections::VecDeque;
88
use std::path::{Path, PathBuf};
@@ -79,21 +79,24 @@ fn update_rustfmt_version(build: &Builder<'_>) {
7979
/// rust-lang/master and what is now on the disk.
8080
///
8181
/// Returns `None` if all files should be formatted.
82-
fn get_modified_rs_files(build: &Builder<'_>) -> Option<Vec<String>> {
83-
let Ok(remote) = get_rust_lang_rust_remote() else { return None; };
82+
fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, String> {
83+
let Ok(updated_master) = updated_master_branch(Some(&build.config.src)) else { return Ok(None); };
84+
8485
if !verify_rustfmt_version(build) {
85-
return None;
86+
return Ok(None);
8687
}
8788

8889
let merge_base =
89-
output(build.config.git().arg("merge-base").arg(&format!("{remote}/master")).arg("HEAD"));
90-
Some(
91-
output(build.config.git().arg("diff-index").arg("--name-only").arg(merge_base.trim()))
92-
.lines()
93-
.map(|s| s.trim().to_owned())
94-
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))
95-
.collect(),
96-
)
90+
output_result(build.config.git().arg("merge-base").arg(&updated_master).arg("HEAD"))?;
91+
Ok(Some(
92+
output_result(
93+
build.config.git().arg("diff-index").arg("--name-only").arg(merge_base.trim()),
94+
)?
95+
.lines()
96+
.map(|s| s.trim().to_owned())
97+
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))
98+
.collect(),
99+
))
97100
}
98101

99102
#[derive(serde::Deserialize)]
@@ -130,6 +133,9 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
130133
Ok(status) => status.success(),
131134
Err(_) => false,
132135
};
136+
137+
let mut paths = paths.to_vec();
138+
133139
if git_available {
134140
let in_working_tree = match build
135141
.config
@@ -163,10 +169,21 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
163169
ignore_fmt.add(&format!("!/{}", untracked_path)).expect(&untracked_path);
164170
}
165171
if !check && paths.is_empty() {
166-
if let Some(files) = get_modified_rs_files(build) {
167-
for file in files {
168-
println!("formatting modified file {file}");
169-
ignore_fmt.add(&format!("/{file}")).expect(&file);
172+
match get_modified_rs_files(build) {
173+
Ok(Some(files)) => {
174+
for file in files {
175+
println!("formatting modified file {file}");
176+
ignore_fmt.add(&format!("/{file}")).expect(&file);
177+
}
178+
}
179+
Ok(None) => {}
180+
Err(err) => {
181+
println!(
182+
"WARN: Something went wrong when running git commands:\n{err}\n\
183+
Falling back to formatting all files."
184+
);
185+
// Something went wrong when getting the version. Just format all the files.
186+
paths.push(".".into());
170187
}
171188
}
172189
}
@@ -176,6 +193,7 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
176193
} else {
177194
println!("Could not find usable git. Skipping git-aware format checks");
178195
}
196+
179197
let ignore_fmt = ignore_fmt.build().unwrap();
180198

181199
let rustfmt_path = build.initial_rustfmt().unwrap_or_else(|| {

src/bootstrap/util.rs

+17
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,23 @@ pub fn output(cmd: &mut Command) -> String {
412412
String::from_utf8(output.stdout).unwrap()
413413
}
414414

415+
pub fn output_result(cmd: &mut Command) -> Result<String, String> {
416+
let output = match cmd.stderr(Stdio::inherit()).output() {
417+
Ok(status) => status,
418+
Err(e) => return Err(format!("failed to run command: {:?}: {}", cmd, e)),
419+
};
420+
if !output.status.success() {
421+
return Err(format!(
422+
"command did not execute successfully: {:?}\n\
423+
expected success, got: {}\n{}",
424+
cmd,
425+
output.status,
426+
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
427+
));
428+
}
429+
Ok(String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?)
430+
}
431+
415432
/// Returns the last-modified time for `path`, or zero if it doesn't exist.
416433
pub fn mtime(path: &Path) -> SystemTime {
417434
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)

src/tools/build_helper/src/git.rs

+47-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::process::Command;
1+
use std::{path::Path, process::Command};
22

33
/// Finds the remote for rust-lang/rust.
44
/// For example for these remotes it will return `upstream`.
@@ -8,8 +8,11 @@ use std::process::Command;
88
/// upstream https://github.com/rust-lang/rust (fetch)
99
/// upstream https://github.com/rust-lang/rust (push)
1010
/// ```
11-
pub fn get_rust_lang_rust_remote() -> Result<String, String> {
11+
pub fn get_rust_lang_rust_remote(git_dir: Option<&Path>) -> Result<String, String> {
1212
let mut git = Command::new("git");
13+
if let Some(git_dir) = git_dir {
14+
git.current_dir(git_dir);
15+
}
1316
git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]);
1417

1518
let output = git.output().map_err(|err| format!("{err:?}"))?;
@@ -28,3 +31,45 @@ pub fn get_rust_lang_rust_remote() -> Result<String, String> {
2831
rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?;
2932
Ok(remote_name.into())
3033
}
34+
35+
pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result<bool, String> {
36+
let mut git = Command::new("git");
37+
if let Some(git_dir) = git_dir {
38+
git.current_dir(git_dir);
39+
}
40+
git.args(["rev-parse", rev]);
41+
let output = git.output().map_err(|err| format!("{err:?}"))?;
42+
43+
match output.status.code() {
44+
Some(0) => Ok(true),
45+
Some(128) => Ok(false),
46+
None => {
47+
return Err(format!(
48+
"git didn't exit properly: {}",
49+
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
50+
));
51+
}
52+
Some(code) => {
53+
return Err(format!(
54+
"git command exited with status code: {code}: {}",
55+
String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
56+
));
57+
}
58+
}
59+
}
60+
61+
/// Returns the master branch from which we can take diffs to see changes.
62+
/// This will usually be rust-lang/rust master, but sometimes this might not exist.
63+
/// This could be because the user is updating their forked master branch using the GitHub UI
64+
/// and therefore doesn't need an upstream master branch checked out.
65+
/// We will then fall back to origin/master in the hope that at least this exists.
66+
pub fn updated_master_branch(git_dir: Option<&Path>) -> Result<String, String> {
67+
let upstream_remote = get_rust_lang_rust_remote(git_dir)?;
68+
let upstream_master = format!("{upstream_remote}/master");
69+
if rev_exists(&upstream_master, git_dir)? {
70+
return Ok(upstream_master);
71+
}
72+
73+
// We could implement smarter logic here in the future.
74+
Ok("origin/master".into())
75+
}

0 commit comments

Comments
 (0)