Rollup merge of #130161 - onur-ozkan:fmt-changed-files, r=Kobzol,RalfJung

refactor merge base logic and fix `x fmt`

When remote upstream is not configured, using [get_git_modified_files](38e3a5771c/src/tools/build_helper/src/git.rs (L114)) to find modified files fails because [get_rust_lang_rust_remote](38e3a5771c/src/tools/build_helper/src/git.rs (L46-L48)) can not resolve "rust-lang/rust" from the git output. The changes in this PR makes bootstrap to find the latest bors commit, treating it as the "closest upstream commit" so that the change tracker logic can use it to find the diffs.

In addition, [skips formatting](e392454483) if there are no modified files.

Fixes #130147
This commit is contained in:
Matthias Krüger 2024-09-11 20:04:24 +02:00 committed by GitHub
commit ff4b3d4780
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 79 additions and 58 deletions

View File

@ -15,6 +15,7 @@
use std::process::Stdio; use std::process::Stdio;
use std::{env, fs, str}; use std::{env, fs, str};
use build_helper::git::get_closest_merge_commit;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use crate::core::build_steps::tool::SourceType; use crate::core::build_steps::tool::SourceType;
@ -26,8 +27,7 @@
use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection};
use crate::utils::exec::command; use crate::utils::exec::command;
use crate::utils::helpers::{ use crate::utils::helpers::{
self, exe, get_clang_cl_resource_dir, get_closest_merge_base_commit, is_debug_info, is_dylib, self, exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date,
symlink_dir, t, up_to_date,
}; };
use crate::{CLang, Compiler, DependencyType, GitRepo, Mode, LLVM_TOOLS}; use crate::{CLang, Compiler, DependencyType, GitRepo, Mode, LLVM_TOOLS};
@ -127,12 +127,8 @@ fn make_run(run: RunConfig<'_>) {
// the `rust.download-rustc=true` option. // the `rust.download-rustc=true` option.
let force_recompile = let force_recompile =
if builder.rust_info().is_managed_git_subrepository() && builder.download_rustc() { if builder.rust_info().is_managed_git_subrepository() && builder.download_rustc() {
let closest_merge_commit = get_closest_merge_base_commit( let closest_merge_commit =
Some(&builder.src), get_closest_merge_commit(Some(&builder.src), &builder.config.git_config(), &[])
&builder.config.git_config(),
&builder.config.stage0_metadata.config.git_merge_commit_email,
&[],
)
.unwrap(); .unwrap();
// Check if `library` has changes (returns false otherwise) // Check if `library` has changes (returns false otherwise)

View File

@ -200,6 +200,11 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
adjective = Some("modified"); adjective = Some("modified");
match get_modified_rs_files(build) { match get_modified_rs_files(build) {
Ok(Some(files)) => { Ok(Some(files)) => {
if files.is_empty() {
println!("fmt info: No modified files detected for formatting.");
return;
}
for file in files { for file in files {
override_builder.add(&format!("/{file}")).expect(&file); override_builder.add(&format!("/{file}")).expect(&file);
} }

View File

@ -16,6 +16,7 @@
use std::{env, io}; use std::{env, io};
use build_helper::ci::CiEnv; use build_helper::ci::CiEnv;
use build_helper::git::get_closest_merge_commit;
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::core::config::{Config, TargetSelection}; use crate::core::config::{Config, TargetSelection};
@ -153,10 +154,9 @@ pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> L
/// This retrieves the LLVM sha we *want* to use, according to git history. /// This retrieves the LLVM sha we *want* to use, according to git history.
pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
let llvm_sha = if is_git { let llvm_sha = if is_git {
helpers::get_closest_merge_base_commit( get_closest_merge_commit(
Some(&config.src), Some(&config.src),
&config.git_config(), &config.git_config(),
&config.stage0_metadata.config.git_merge_commit_email,
&[ &[
config.src.join("src/llvm-project"), config.src.join("src/llvm-project"),
config.src.join("src/bootstrap/download-ci-llvm-stamp"), config.src.join("src/bootstrap/download-ci-llvm-stamp"),

View File

@ -17,6 +17,7 @@ pub fn suggest(builder: &Builder<'_>, run: bool) {
.tool_cmd(Tool::SuggestTests) .tool_cmd(Tool::SuggestTests)
.env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository) .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository)
.env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch) .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch)
.env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL", git_config.git_merge_commit_email)
.run_capture_stdout(builder) .run_capture_stdout(builder)
.stdout(); .stdout();

View File

@ -2098,6 +2098,7 @@ fn run(self, builder: &Builder<'_>) {
let git_config = builder.config.git_config(); let git_config = builder.config.git_config();
cmd.arg("--git-repository").arg(git_config.git_repository); cmd.arg("--git-repository").arg(git_config.git_repository);
cmd.arg("--nightly-branch").arg(git_config.nightly_branch); cmd.arg("--nightly-branch").arg(git_config.nightly_branch);
cmd.arg("--git-merge-commit-email").arg(git_config.git_merge_commit_email);
cmd.force_coloring_in_ci(); cmd.force_coloring_in_ci();
#[cfg(feature = "build-metrics")] #[cfg(feature = "build-metrics")]

View File

@ -1,6 +1,8 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::{env, fs}; use std::{env, fs};
use build_helper::git::get_closest_merge_commit;
use crate::core::build_steps::compile; use crate::core::build_steps::compile;
use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::toolstate::ToolState;
use crate::core::builder; use crate::core::builder;
@ -8,7 +10,7 @@
use crate::core::config::TargetSelection; use crate::core::config::TargetSelection;
use crate::utils::channel::GitInfo; use crate::utils::channel::GitInfo;
use crate::utils::exec::{command, BootstrapCommand}; use crate::utils::exec::{command, BootstrapCommand};
use crate::utils::helpers::{add_dylib_path, exe, get_closest_merge_base_commit, git, t}; use crate::utils::helpers::{add_dylib_path, exe, git, t};
use crate::{gha, Compiler, Kind, Mode}; use crate::{gha, Compiler, Kind, Mode};
#[derive(Debug, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@ -576,10 +578,9 @@ fn run(self, builder: &Builder<'_>) -> PathBuf {
&& target_compiler.stage > 0 && target_compiler.stage > 0
&& builder.rust_info().is_managed_git_subrepository() && builder.rust_info().is_managed_git_subrepository()
{ {
let commit = get_closest_merge_base_commit( let commit = get_closest_merge_commit(
Some(&builder.config.src), Some(&builder.config.src),
&builder.config.git_config(), &builder.config.git_config(),
&builder.config.stage0_metadata.config.git_merge_commit_email,
&[], &[],
) )
.unwrap(); .unwrap();

View File

@ -14,7 +14,7 @@
use std::{cmp, env, fs}; use std::{cmp, env, fs};
use build_helper::exit; use build_helper::exit;
use build_helper::git::{output_result, GitConfig}; use build_helper::git::{get_closest_merge_commit, output_result, GitConfig};
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize; use serde_derive::Deserialize;
@ -24,7 +24,7 @@
use crate::core::config::flags::{Color, Flags, Warnings}; use crate::core::config::flags::{Color, Flags, Warnings};
use crate::utils::cache::{Interned, INTERNER}; use crate::utils::cache::{Interned, INTERNER};
use crate::utils::channel::{self, GitInfo}; use crate::utils::channel::{self, GitInfo};
use crate::utils::helpers::{self, exe, get_closest_merge_base_commit, output, t}; use crate::utils::helpers::{self, exe, output, t};
macro_rules! check_ci_llvm { macro_rules! check_ci_llvm {
($name:expr) => { ($name:expr) => {
@ -2512,6 +2512,7 @@ pub fn git_config(&self) -> GitConfig<'_> {
GitConfig { GitConfig {
git_repository: &self.stage0_metadata.config.git_repository, git_repository: &self.stage0_metadata.config.git_repository,
nightly_branch: &self.stage0_metadata.config.nightly_branch, nightly_branch: &self.stage0_metadata.config.nightly_branch,
git_merge_commit_email: &self.stage0_metadata.config.git_merge_commit_email,
} }
} }
@ -2688,13 +2689,7 @@ fn download_ci_rustc_commit(&self, download_rustc: Option<StringOrBool>) -> Opti
// Look for a version to compare to based on the current commit. // Look for a version to compare to based on the current commit.
// Only commits merged by bors will have CI artifacts. // Only commits merged by bors will have CI artifacts.
let commit = get_closest_merge_base_commit( let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[]).unwrap();
Some(&self.src),
&self.git_config(),
&self.stage0_metadata.config.git_merge_commit_email,
&[],
)
.unwrap();
if commit.is_empty() { if commit.is_empty() {
println!("ERROR: could not find commit hash for downloading rustc"); println!("ERROR: could not find commit hash for downloading rustc");
println!("HELP: maybe your repository history is too shallow?"); println!("HELP: maybe your repository history is too shallow?");
@ -2786,13 +2781,7 @@ pub fn last_modified_commit(
) -> Option<String> { ) -> Option<String> {
// Look for a version to compare to based on the current commit. // Look for a version to compare to based on the current commit.
// Only commits merged by bors will have CI artifacts. // Only commits merged by bors will have CI artifacts.
let commit = get_closest_merge_base_commit( let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[]).unwrap();
Some(&self.src),
&self.git_config(),
&self.stage0_metadata.config.git_merge_commit_email,
&[],
)
.unwrap();
if commit.is_empty() { if commit.is_empty() {
println!("error: could not find commit hash for downloading components from CI"); println!("error: could not find commit hash for downloading components from CI");
println!("help: maybe your repository history is too shallow?"); println!("help: maybe your repository history is too shallow?");

View File

@ -10,7 +10,6 @@
use std::time::{Instant, SystemTime, UNIX_EPOCH}; use std::time::{Instant, SystemTime, UNIX_EPOCH};
use std::{env, fs, io, str}; use std::{env, fs, io, str};
use build_helper::git::{get_git_merge_base, output_result, GitConfig};
use build_helper::util::fail; use build_helper::util::fail;
use crate::core::builder::Builder; use crate::core::builder::Builder;
@ -523,28 +522,6 @@ pub fn git(source_dir: Option<&Path>) -> BootstrapCommand {
git git
} }
/// Returns the closest commit available from upstream for the given `author` and `target_paths`.
///
/// If it fails to find the commit from upstream using `git merge-base`, fallbacks to HEAD.
pub fn get_closest_merge_base_commit(
source_dir: Option<&Path>,
config: &GitConfig<'_>,
author: &str,
target_paths: &[PathBuf],
) -> Result<String, String> {
let mut git = git(source_dir);
let merge_base = get_git_merge_base(config, source_dir).unwrap_or_else(|_| "HEAD".into());
git.args(["rev-list", &format!("--author={author}"), "-n1", "--first-parent", &merge_base]);
if !target_paths.is_empty() {
git.arg("--").args(target_paths);
}
Ok(output_result(git.as_command_mut())?.trim().to_owned())
}
/// Sets the file times for a given file at `path`. /// Sets the file times for a given file at `path`.
pub fn set_file_times<P: AsRef<Path>>(path: P, times: fs::FileTimes) -> io::Result<()> { pub fn set_file_times<P: AsRef<Path>>(path: P, times: fs::FileTimes) -> io::Result<()> {
// Windows requires file to be writable to modify file times. But on Linux CI the file does not // Windows requires file to be writable to modify file times. But on Linux CI the file does not

View File

@ -1,9 +1,10 @@
use std::path::Path; use std::path::{Path, PathBuf};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
pub struct GitConfig<'a> { pub struct GitConfig<'a> {
pub git_repository: &'a str, pub git_repository: &'a str,
pub nightly_branch: &'a str, pub nightly_branch: &'a str,
pub git_merge_commit_email: &'a str,
} }
/// Runs a command and returns the output /// Runs a command and returns the output
@ -95,7 +96,11 @@ pub fn updated_master_branch(
Err("Cannot find any suitable upstream master branch".to_owned()) Err("Cannot find any suitable upstream master branch".to_owned())
} }
pub fn get_git_merge_base( /// Finds the nearest merge commit by comparing the local `HEAD` with the upstream branch's state.
/// To work correctly, the upstream remote must be properly configured using `git remote add <name> <url>`.
/// In most cases `get_closest_merge_commit` is the function you are looking for as it doesn't require remote
/// to be configured.
fn git_upstream_merge_base(
config: &GitConfig<'_>, config: &GitConfig<'_>,
git_dir: Option<&Path>, git_dir: Option<&Path>,
) -> Result<String, String> { ) -> Result<String, String> {
@ -107,6 +112,38 @@ pub fn get_git_merge_base(
Ok(output_result(git.arg("merge-base").arg(&updated_master).arg("HEAD"))?.trim().to_owned()) Ok(output_result(git.arg("merge-base").arg(&updated_master).arg("HEAD"))?.trim().to_owned())
} }
/// Searches for the nearest merge commit in the repository that also exists upstream.
///
/// If it fails to find the upstream remote, it then looks for the most recent commit made
/// by the merge bot by matching the author's email address with the merge bot's email.
pub fn get_closest_merge_commit(
git_dir: Option<&Path>,
config: &GitConfig<'_>,
target_paths: &[PathBuf],
) -> Result<String, String> {
let mut git = Command::new("git");
if let Some(git_dir) = git_dir {
git.current_dir(git_dir);
}
let merge_base = git_upstream_merge_base(config, git_dir).unwrap_or_else(|_| "HEAD".into());
git.args([
"rev-list",
&format!("--author={}", config.git_merge_commit_email),
"-n1",
"--first-parent",
&merge_base,
]);
if !target_paths.is_empty() {
git.arg("--").args(target_paths);
}
Ok(output_result(&mut git)?.trim().to_owned())
}
/// Returns the files that have been modified in the current branch compared to the master branch. /// Returns the files that have been modified in the current branch compared to the master branch.
/// The `extensions` parameter can be used to filter the files by their extension. /// The `extensions` parameter can be used to filter the files by their extension.
/// Does not include removed files. /// Does not include removed files.
@ -116,7 +153,7 @@ pub fn get_git_modified_files(
git_dir: Option<&Path>, git_dir: Option<&Path>,
extensions: &[&str], extensions: &[&str],
) -> Result<Option<Vec<String>>, String> { ) -> Result<Option<Vec<String>>, String> {
let merge_base = get_git_merge_base(config, git_dir)?; let merge_base = get_closest_merge_commit(git_dir, config, &[])?;
let mut git = Command::new("git"); let mut git = Command::new("git");
if let Some(git_dir) = git_dir { if let Some(git_dir) = git_dir {

View File

@ -384,6 +384,7 @@ pub struct Config {
// Needed both to construct build_helper::git::GitConfig // Needed both to construct build_helper::git::GitConfig
pub git_repository: String, pub git_repository: String,
pub nightly_branch: String, pub nightly_branch: String,
pub git_merge_commit_email: String,
/// True if the profiler runtime is enabled for this target. /// True if the profiler runtime is enabled for this target.
/// Used by the "needs-profiler-support" header in test files. /// Used by the "needs-profiler-support" header in test files.
@ -461,7 +462,11 @@ pub fn has_asm_support(&self) -> bool {
} }
pub fn git_config(&self) -> GitConfig<'_> { pub fn git_config(&self) -> GitConfig<'_> {
GitConfig { git_repository: &self.git_repository, nightly_branch: &self.nightly_branch } GitConfig {
git_repository: &self.git_repository,
nightly_branch: &self.nightly_branch,
git_merge_commit_email: &self.git_merge_commit_email,
}
} }
} }

View File

@ -148,6 +148,7 @@ fn build(&mut self) -> Config {
self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"),
"--git-repository=", "--git-repository=",
"--nightly-branch=", "--nightly-branch=",
"--git-merge-commit-email=",
]; ];
let mut args: Vec<String> = args.iter().map(ToString::to_string).collect(); let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();

View File

@ -163,7 +163,13 @@ pub fn parse_config(args: Vec<String>) -> Config {
) )
.optopt("", "edition", "default Rust edition", "EDITION") .optopt("", "edition", "default Rust edition", "EDITION")
.reqopt("", "git-repository", "name of the git repository", "ORG/REPO") .reqopt("", "git-repository", "name of the git repository", "ORG/REPO")
.reqopt("", "nightly-branch", "name of the git branch for nightly", "BRANCH"); .reqopt("", "nightly-branch", "name of the git branch for nightly", "BRANCH")
.reqopt(
"",
"git-merge-commit-email",
"email address used for finding merge commits",
"EMAIL",
);
let (argv0, args_) = args.split_first().unwrap(); let (argv0, args_) = args.split_first().unwrap();
if args.len() == 1 || args[1] == "-h" || args[1] == "--help" { if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
@ -346,6 +352,7 @@ fn make_absolute(path: PathBuf) -> PathBuf {
git_repository: matches.opt_str("git-repository").unwrap(), git_repository: matches.opt_str("git-repository").unwrap(),
nightly_branch: matches.opt_str("nightly-branch").unwrap(), nightly_branch: matches.opt_str("nightly-branch").unwrap(),
git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
profiler_support: matches.opt_present("profiler-support"), profiler_support: matches.opt_present("profiler-support"),
} }

View File

@ -8,6 +8,7 @@ fn main() -> ExitCode {
&GitConfig { &GitConfig {
git_repository: &env("SUGGEST_TESTS_GIT_REPOSITORY"), git_repository: &env("SUGGEST_TESTS_GIT_REPOSITORY"),
nightly_branch: &env("SUGGEST_TESTS_NIGHTLY_BRANCH"), nightly_branch: &env("SUGGEST_TESTS_NIGHTLY_BRANCH"),
git_merge_commit_email: &env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL"),
}, },
None, None,
&Vec::new(), &Vec::new(),