Skip to content

Commit 7ac9572

Browse files
committed
Auto merge of #106415 - Nilstrieb:where-is-my-master-branch, r=jyn514
Handle non-existent upstream master branches in `x fmt` People who do have a remote for `rust-lang/rust` but don't have the master branch checked out there used to get this error when running `x fmt`: > fatal: ambiguous argument 'rust/master': unknown revision or path not in the working tree. > Use '--' to separate paths from revisions, like this: > 'git <command> [<revision>...] -- [<file>...]' > rust/master Which is not exactly helpful. Now, we fall back to `origin/master` (hoping that at least that remote exists) for that case. If there is still some error, we just fall back to `x fmt .` and print a warning. r? `@jyn514`
2 parents 0fb8b72 + d5e5762 commit 7ac9572

File tree

12 files changed

+192
-75
lines changed

12 files changed

+192
-75
lines changed

Cargo.lock

+4
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,10 @@ dependencies = [
271271
"toml",
272272
]
273273

274+
[[package]]
275+
name = "build_helper"
276+
version = "0.1.0"
277+
274278
[[package]]
275279
name = "bump-stage0"
276280
version = "0.1.0"

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"library/std",
55
"library/test",
66
"src/rustdoc-json-types",
7+
"src/tools/build_helper",
78
"src/tools/cargotest",
89
"src/tools/clippy",
910
"src/tools/clippy/clippy_dev",

src/bootstrap/Cargo.lock

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies = [
3636
name = "bootstrap"
3737
version = "0.0.0"
3838
dependencies = [
39+
"build_helper",
3940
"cc",
4041
"cmake",
4142
"fd-lock",
@@ -70,6 +71,10 @@ dependencies = [
7071
"regex-automata",
7172
]
7273

74+
[[package]]
75+
name = "build_helper"
76+
version = "0.1.0"
77+
7378
[[package]]
7479
name = "cc"
7580
version = "1.0.73"

src/bootstrap/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ path = "bin/sccache-plus-cl.rs"
3030
test = false
3131

3232
[dependencies]
33+
build_helper = { path = "../tools/build_helper" }
3334
cmake = "0.1.38"
3435
fd-lock = "3.0.8"
3536
filetime = "0.2"

src/bootstrap/format.rs

+34-44
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Runs rustfmt on the repository.
22
33
use crate::builder::Builder;
4-
use crate::util::{output, program_out_of_date, t};
4+
use crate::util::{output, output_result, program_out_of_date, t};
5+
use build_helper::git::updated_master_branch;
56
use ignore::WalkBuilder;
67
use std::collections::VecDeque;
78
use std::path::{Path, PathBuf};
@@ -78,50 +79,24 @@ fn update_rustfmt_version(build: &Builder<'_>) {
7879
/// rust-lang/master and what is now on the disk.
7980
///
8081
/// Returns `None` if all files should be formatted.
81-
fn get_modified_rs_files(build: &Builder<'_>) -> Option<Vec<String>> {
82-
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+
8385
if !verify_rustfmt_version(build) {
84-
return None;
86+
return Ok(None);
8587
}
8688

8789
let merge_base =
88-
output(build.config.git().arg("merge-base").arg(&format!("{remote}/master")).arg("HEAD"));
89-
Some(
90-
output(build.config.git().arg("diff-index").arg("--name-only").arg(merge_base.trim()))
91-
.lines()
92-
.map(|s| s.trim().to_owned())
93-
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))
94-
.collect(),
95-
)
96-
}
97-
98-
/// Finds the remote for rust-lang/rust.
99-
/// For example for these remotes it will return `upstream`.
100-
/// ```text
101-
/// origin https://github.com/Nilstrieb/rust.git (fetch)
102-
/// origin https://github.com/Nilstrieb/rust.git (push)
103-
/// upstream https://github.com/rust-lang/rust (fetch)
104-
/// upstream https://github.com/rust-lang/rust (push)
105-
/// ```
106-
fn get_rust_lang_rust_remote() -> Result<String, String> {
107-
let mut git = Command::new("git");
108-
git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]);
109-
110-
let output = git.output().map_err(|err| format!("{err:?}"))?;
111-
if !output.status.success() {
112-
return Err("failed to execute git config command".to_owned());
113-
}
114-
115-
let stdout = String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?;
116-
117-
let rust_lang_remote = stdout
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+
)?
11895
.lines()
119-
.find(|remote| remote.contains("rust-lang"))
120-
.ok_or_else(|| "rust-lang/rust remote not found".to_owned())?;
121-
122-
let remote_name =
123-
rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?;
124-
Ok(remote_name.into())
96+
.map(|s| s.trim().to_owned())
97+
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))
98+
.collect(),
99+
))
125100
}
126101

127102
#[derive(serde::Deserialize)]
@@ -158,6 +133,9 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
158133
Ok(status) => status.success(),
159134
Err(_) => false,
160135
};
136+
137+
let mut paths = paths.to_vec();
138+
161139
if git_available {
162140
let in_working_tree = match build
163141
.config
@@ -191,10 +169,21 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
191169
ignore_fmt.add(&format!("!/{}", untracked_path)).expect(&untracked_path);
192170
}
193171
if !check && paths.is_empty() {
194-
if let Some(files) = get_modified_rs_files(build) {
195-
for file in files {
196-
println!("formatting modified file {file}");
197-
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());
198187
}
199188
}
200189
}
@@ -204,6 +193,7 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
204193
} else {
205194
println!("Could not find usable git. Skipping git-aware format checks");
206195
}
196+
207197
let ignore_fmt = ignore_fmt.build().unwrap();
208198

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

src/bootstrap/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ use std::path::{Path, PathBuf};
113113
use std::process::Command;
114114
use std::str;
115115

116+
use build_helper::ci::CiEnv;
116117
use channel::GitInfo;
117118
use config::{DryRun, Target};
118119
use filetime::FileTime;
@@ -121,7 +122,7 @@ use once_cell::sync::OnceCell;
121122
use crate::builder::Kind;
122123
use crate::config::{LlvmLibunwind, TargetSelection};
123124
use crate::util::{
124-
exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed, CiEnv,
125+
exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed,
125126
};
126127

127128
mod bolt;

src/bootstrap/native.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ use crate::util::get_clang_cl_resource_dir;
2424
use crate::util::{self, exe, output, t, up_to_date};
2525
use crate::{CLang, GitRepo};
2626

27+
use build_helper::ci::CiEnv;
28+
2729
#[derive(Clone)]
2830
pub struct LlvmResult {
2931
/// Path to llvm-config binary.
@@ -217,7 +219,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
217219
return false;
218220
}
219221

220-
if crate::util::CiEnv::is_ci() {
222+
if CiEnv::is_ci() {
221223
// We assume we have access to git, so it's okay to unconditionally pass
222224
// `true` here.
223225
let llvm_sha = detect_llvm_sha(config, true);

src/bootstrap/util.rs

+17-29
Original file line numberDiff line numberDiff line change
@@ -255,35 +255,6 @@ pub enum CiEnv {
255255
GitHubActions,
256256
}
257257

258-
impl CiEnv {
259-
/// Obtains the current CI environment.
260-
pub fn current() -> CiEnv {
261-
if env::var("TF_BUILD").map_or(false, |e| e == "True") {
262-
CiEnv::AzurePipelines
263-
} else if env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
264-
CiEnv::GitHubActions
265-
} else {
266-
CiEnv::None
267-
}
268-
}
269-
270-
pub fn is_ci() -> bool {
271-
Self::current() != CiEnv::None
272-
}
273-
274-
/// If in a CI environment, forces the command to run with colors.
275-
pub fn force_coloring_in_ci(self, cmd: &mut Command) {
276-
if self != CiEnv::None {
277-
// Due to use of stamp/docker, the output stream of rustbuild is not
278-
// a TTY in CI, so coloring is by-default turned off.
279-
// The explicit `TERM=xterm` environment is needed for
280-
// `--color always` to actually work. This env var was lost when
281-
// compiling through the Makefile. Very strange.
282-
cmd.env("TERM", "xterm").args(&["--color", "always"]);
283-
}
284-
}
285-
}
286-
287258
pub fn forcing_clang_based_tests() -> bool {
288259
if let Some(var) = env::var_os("RUSTBUILD_FORCE_CLANG_BASED_TESTS") {
289260
match &var.to_string_lossy().to_lowercase()[..] {
@@ -441,6 +412,23 @@ pub fn output(cmd: &mut Command) -> String {
441412
String::from_utf8(output.stdout).unwrap()
442413
}
443414

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+
444432
/// Returns the last-modified time for `path`, or zero if it doesn't exist.
445433
pub fn mtime(path: &Path) -> SystemTime {
446434
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)

src/tools/build_helper/Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "build_helper"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]

src/tools/build_helper/src/ci.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::process::Command;
2+
3+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4+
pub enum CiEnv {
5+
/// Not a CI environment.
6+
None,
7+
/// The Azure Pipelines environment, for Linux (including Docker), Windows, and macOS builds.
8+
AzurePipelines,
9+
/// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds.
10+
GitHubActions,
11+
}
12+
13+
impl CiEnv {
14+
/// Obtains the current CI environment.
15+
pub fn current() -> CiEnv {
16+
if std::env::var("TF_BUILD").map_or(false, |e| e == "True") {
17+
CiEnv::AzurePipelines
18+
} else if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
19+
CiEnv::GitHubActions
20+
} else {
21+
CiEnv::None
22+
}
23+
}
24+
25+
pub fn is_ci() -> bool {
26+
Self::current() != CiEnv::None
27+
}
28+
29+
/// If in a CI environment, forces the command to run with colors.
30+
pub fn force_coloring_in_ci(self, cmd: &mut Command) {
31+
if self != CiEnv::None {
32+
// Due to use of stamp/docker, the output stream of rustbuild is not
33+
// a TTY in CI, so coloring is by-default turned off.
34+
// The explicit `TERM=xterm` environment is needed for
35+
// `--color always` to actually work. This env var was lost when
36+
// compiling through the Makefile. Very strange.
37+
cmd.env("TERM", "xterm").args(&["--color", "always"]);
38+
}
39+
}
40+
}

src/tools/build_helper/src/git.rs

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use std::{path::Path, process::Command};
2+
3+
/// Finds the remote for rust-lang/rust.
4+
/// For example for these remotes it will return `upstream`.
5+
/// ```text
6+
/// origin https://github.com/Nilstrieb/rust.git (fetch)
7+
/// origin https://github.com/Nilstrieb/rust.git (push)
8+
/// upstream https://github.com/rust-lang/rust (fetch)
9+
/// upstream https://github.com/rust-lang/rust (push)
10+
/// ```
11+
pub fn get_rust_lang_rust_remote(git_dir: Option<&Path>) -> Result<String, String> {
12+
let mut git = Command::new("git");
13+
if let Some(git_dir) = git_dir {
14+
git.current_dir(git_dir);
15+
}
16+
git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]);
17+
18+
let output = git.output().map_err(|err| format!("{err:?}"))?;
19+
if !output.status.success() {
20+
return Err("failed to execute git config command".to_owned());
21+
}
22+
23+
let stdout = String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?;
24+
25+
let rust_lang_remote = stdout
26+
.lines()
27+
.find(|remote| remote.contains("rust-lang"))
28+
.ok_or_else(|| "rust-lang/rust remote not found".to_owned())?;
29+
30+
let remote_name =
31+
rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?;
32+
Ok(remote_name.into())
33+
}
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+
}

src/tools/build_helper/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod ci;
2+
pub mod git;

0 commit comments

Comments
 (0)