From 8e52082021624d9d2df91232db09ec6f5596d13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 21 Dec 2025 21:21:45 +0000 Subject: [PATCH 1/9] Tweak "conflict markers" diagnostic by detecting git state When detecting "diff markers", inspect the current `git` repo's state to see if there is an active rebase. If that is the case, we try to identify whether the rebase is caused by `git rebase` or implicitly by `git pull`. In order to do that we query `git` to ask for the path of `.git/rebase-merge` and then inspect the files `orig-head` (the commit we are rebasing from), `onto` (the commit we are rebasing onto) and `head-name` (the branch being rebased). We modify the labels pointing at the markers to be clear on where each section of code is coming from. `git rebase` case: ``` error: encountered diff marker --> src/main.rs:2:1 | 2 | <<<<<<< HEAD | ^^^^^^^ between this marker and `=======` is the code from branch `master` we're rebasing onto 3 | println!("Hello, main!"); 4 | ======= | ------- between this marker and `>>>>>>>` is the code you had in branch `branch-2` that you are rebasing 5 | println!("Hello, branch!"); 6 | >>>>>>> e644375 (branch) | ^^^^^^^ this marker concludes the conflict region | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = note: for an explanation on these markers from the `git` documentation: visit ``` `git pull` case: ``` error: encountered diff marker --> src/main.rs:2:1 | 2 | <<<<<<< HEAD | ^^^^^^^ between this marker and `=======` is the code from the remote branch `origin/main` we're rebasing onto 3 | println!("Hello, 1!"); 4 | ======= | ------- between this marker and `>>>>>>>` is the code you had in branch `main` that you are rebasing 5 | println!("Hello, 2!"); 6 | >>>>>>> ebbeec7 (second) | ^^^^^^^ this marker concludes the conflict region | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = note: for an explanation on these markers from the `git` documentation: visit ``` Also, the generic `help` description had the `git pull` case flipped from what it should have been. --- .../rustc_parse/src/parser/diagnostics.rs | 141 ++++++++++++++++-- tests/ui/parser/diff-markers/enum-2.stderr | 2 +- tests/ui/parser/diff-markers/enum.stderr | 2 +- tests/ui/parser/diff-markers/fn-arg.stderr | 2 +- .../parser/diff-markers/item-with-attr.stderr | 2 +- tests/ui/parser/diff-markers/item.stderr | 2 +- tests/ui/parser/diff-markers/statement.stderr | 2 +- .../ui/parser/diff-markers/struct-expr.stderr | 2 +- tests/ui/parser/diff-markers/struct.stderr | 2 +- .../ui/parser/diff-markers/trait-item.stderr | 2 +- .../parser/diff-markers/tuple-struct.stderr | 2 +- .../parser/diff-markers/use-statement.stderr | 2 +- 12 files changed, 139 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index d7d343ac16b49..b08353813ab0d 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,5 +1,6 @@ use std::mem::take; use std::ops::{Deref, DerefMut}; +use std::process::Command; use ast::token::IdentIsRaw; use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; @@ -3052,6 +3053,107 @@ impl<'a> Parser<'a> { None } + /// Try to identify if a `git rebase` is in progress to provide more accurate labels. + fn vcs_conflict_marker_labels(&self, is_middlediff3: bool) -> ConflictMarkerLabels { + let mut msg_start: String = "that we're merging into".into(); + let mut msg_middle: String = "incoming code".into(); + let mut show_help = true; + + // `git rev-parse --git-path rebase-merge`, in order to get the git metadata for the + // rebase currently happening. If this directory doesn't exist, then the merge conflicts + // were not caused by a rebase. + if let Ok(output) = + Command::new("git").args(["rev-parse", "--git-path", "rebase-merge"]).output() + && let Ok(relative_path) = String::from_utf8(output.stdout) + && let relative_path = relative_path.trim() + && let Ok(orig_head_sha) = std::fs::read_to_string(format!("{relative_path}/orig-head")) + && let Ok(onto_sha) = std::fs::read_to_string(format!("{relative_path}/onto")) + { + // git is installed, we're currently in a git repo, and that repo is currently in a + // rebase. + show_help = false; + debug!(?relative_path, ?orig_head_sha, ?onto_sha); + if let Ok(head_name) = std::fs::read_to_string(format!("{relative_path}/head-name")) + && let head_name = head_name.trim() + && let Some(branch_name) = head_name.strip_prefix("refs/heads/") + { + debug!(?head_name); + msg_middle = + format!("code you had in branch `{branch_name}` that you are rebasing"); + } else { + msg_middle = + format!("code you had in commit `{orig_head_sha}` that you are rebasing"); + } + if let Ok(output) = + Command::new("git").args(["branch", "--points-at", onto_sha.trim()]).output() + && let Ok(onto_branch) = String::from_utf8(output.stdout) + { + debug!(?onto_branch); + let onto_branches: Vec<_> = onto_branch + .lines() + .filter(|line| !line.starts_with("*")) + .map(|line| format!("`{}`", line.trim())) + .collect(); + match &onto_branches[..] { + branches if branches.len() > 4 => { + msg_start = format!("from the branch we're rebasing onto"); + } + [] => { + // `git pull` caused a conflict and we're rebasing, instead of it being + // caused by the more common `git rebase branch-name`. + if let Ok(output) = Command::new("git") + .args(["branch", "-r", "--points-at", onto_sha.trim()]) + .output() + && let Ok(onto_branch) = String::from_utf8(output.stdout) + && let onto_branches = onto_branch + .lines() + .filter_map(|line| { + if line.split(" -> ").count() == 1 { + Some(format!("`{}`", line.trim())) + } else { + None + } + }) + .collect::>() + && !onto_branches.is_empty() + { + if let [branch] = &onto_branches[..] { + msg_start = + format!("from the remote branch {branch} we're rebasing onto"); + } else { + msg_start = format!("from the remote branch we're rebasing onto"); + } + } + } + [branch] => { + msg_start = format!("from branch {branch} we're rebasing onto"); + } + [branches @ .., last] => { + msg_start = format!( + "from branches {} and {last} we're rebasing onto", + branches.join(", ") + ); + } + } + } else { + msg_start = format!("from commit `{}` that we're rebasing onto", onto_sha.trim()); + } + } + + ConflictMarkerLabels { + start: if is_middlediff3 { + "between this marker and `|||||||` is the code that we're merging into".to_string() + } else { + format!("between this marker and `=======` is the code {msg_start}") + }, + middle: format!("between this marker and `>>>>>>>` is the {msg_middle}"), + middlediff3: "between this marker and `=======` is the base code (what the two refs \ + diverged from)", + end: "this marker concludes the conflict region", + show_help, + } + } + pub(super) fn recover_vcs_conflict_marker(&mut self) { // <<<<<<< let Some(start) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt) else { @@ -3083,29 +3185,25 @@ impl<'a> Parser<'a> { self.bump(); } + let labels = self.vcs_conflict_marker_labels(middlediff3.is_some()); + let mut err = self.dcx().struct_span_fatal(spans, "encountered diff marker"); match middlediff3 { // We're using diff3 Some(middlediff3) => { - err.span_label( - start, - "between this marker and `|||||||` is the code that we're merging into", - ); - err.span_label(middlediff3, "between this marker and `=======` is the base code (what the two refs diverged from)"); + err.span_label(start, labels.start); + err.span_label(middlediff3, labels.middlediff3); } None => { - err.span_label( - start, - "between this marker and `=======` is the code that we're merging into", - ); + err.span_label(start, labels.start); } }; if let Some(middle) = middle { - err.span_label(middle, "between this marker and `>>>>>>>` is the incoming code"); + err.span_label(middle, labels.middle); } if let Some(end) = end { - err.span_label(end, "this marker concludes the conflict region"); + err.span_label(end, labels.end); } err.note( "conflict markers indicate that a merge was started but could not be completed due \ @@ -3113,13 +3211,17 @@ impl<'a> Parser<'a> { to resolve a conflict, keep only the code you want and then delete the lines \ containing conflict markers", ); - err.help( + if labels.show_help { + // We couldn't identify that we're in the middle of a `git rebase` (either direct or + // caused by `git pull`), so we provide more generic information. + err.help( "if you're having merge conflicts after pulling new code:\n\ - the top section is the code you already had and the bottom section is the remote code\n\ + the top section is the remote code and the bottom section is the code you already had\n\ if you're in the middle of a rebase:\n\ the top section is the code being rebased onto and the bottom section is the code \ coming from the current commit being rebased", ); + } err.note( "for an explanation on these markers from the `git` documentation:\n\ @@ -3141,3 +3243,16 @@ impl<'a> Parser<'a> { Ok(()) } } + +struct ConflictMarkerLabels { + /// The label for the `<<<<<<<` marker. + start: String, + /// The label for the `|||||||` or `=======` marker. + middle: String, + /// The label for the `=======` marker. + middlediff3: &'static str, + /// The label for the `>>>>>>>` marker. + end: &'static str, + /// Whether we display a generic help on where the code might be coming from in each section. + show_help: bool, +} diff --git a/tests/ui/parser/diff-markers/enum-2.stderr b/tests/ui/parser/diff-markers/enum-2.stderr index b76cf5d5a01ee..f131f13ba6d51 100644 --- a/tests/ui/parser/diff-markers/enum-2.stderr +++ b/tests/ui/parser/diff-markers/enum-2.stderr @@ -16,7 +16,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/enum.stderr b/tests/ui/parser/diff-markers/enum.stderr index 0ce473bc70232..570b221291e12 100644 --- a/tests/ui/parser/diff-markers/enum.stderr +++ b/tests/ui/parser/diff-markers/enum.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/fn-arg.stderr b/tests/ui/parser/diff-markers/fn-arg.stderr index 24521ffa62623..48b1ed080f26b 100644 --- a/tests/ui/parser/diff-markers/fn-arg.stderr +++ b/tests/ui/parser/diff-markers/fn-arg.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/item-with-attr.stderr b/tests/ui/parser/diff-markers/item-with-attr.stderr index 432673cd5518b..c990a68867974 100644 --- a/tests/ui/parser/diff-markers/item-with-attr.stderr +++ b/tests/ui/parser/diff-markers/item-with-attr.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/item.stderr b/tests/ui/parser/diff-markers/item.stderr index 180c74e5d6967..eaa0e6aa1c308 100644 --- a/tests/ui/parser/diff-markers/item.stderr +++ b/tests/ui/parser/diff-markers/item.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/statement.stderr b/tests/ui/parser/diff-markers/statement.stderr index 6dccce4a48eee..77c9820e8b4fa 100644 --- a/tests/ui/parser/diff-markers/statement.stderr +++ b/tests/ui/parser/diff-markers/statement.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/struct-expr.stderr b/tests/ui/parser/diff-markers/struct-expr.stderr index 3733cdd349644..e8cb25169ac34 100644 --- a/tests/ui/parser/diff-markers/struct-expr.stderr +++ b/tests/ui/parser/diff-markers/struct-expr.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/struct.stderr b/tests/ui/parser/diff-markers/struct.stderr index 44f8346613e68..4a9f3771ae57b 100644 --- a/tests/ui/parser/diff-markers/struct.stderr +++ b/tests/ui/parser/diff-markers/struct.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/trait-item.stderr b/tests/ui/parser/diff-markers/trait-item.stderr index 4361542c7743b..76e48bd7081da 100644 --- a/tests/ui/parser/diff-markers/trait-item.stderr +++ b/tests/ui/parser/diff-markers/trait-item.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/tuple-struct.stderr b/tests/ui/parser/diff-markers/tuple-struct.stderr index 7fda24ba48532..c42c96c993dfa 100644 --- a/tests/ui/parser/diff-markers/tuple-struct.stderr +++ b/tests/ui/parser/diff-markers/tuple-struct.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: diff --git a/tests/ui/parser/diff-markers/use-statement.stderr b/tests/ui/parser/diff-markers/use-statement.stderr index 3eac7bebb5af1..0bfaca244ae69 100644 --- a/tests/ui/parser/diff-markers/use-statement.stderr +++ b/tests/ui/parser/diff-markers/use-statement.stderr @@ -13,7 +13,7 @@ LL | >>>>>>> branch = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers = help: if you're having merge conflicts after pulling new code: - the top section is the code you already had and the bottom section is the remote code + the top section is the remote code and the bottom section is the code you already had if you're in the middle of a rebase: the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: From 208aac77a1147f8f0a84e7391ed9aba419cdeaca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 15:52:29 +0000 Subject: [PATCH 2/9] Do not invoke `git` and trigger in fewer cases The logic now only works if `cargo` is called from the git root, otherwise it won't find where the git metadata is. We rely purely on inspecting the files with rebase metadata and don't attempt to resolve a rebase target SHA to a branch name. Reword slightly the labels. --- .../rustc_parse/src/parser/diagnostics.rs | 151 +++++++++--------- .../git-pull-rebase-conflict/file.stderr | 19 +++ .../run-make/git-pull-rebase-conflict/main.rs | 7 + .../git-pull-rebase-conflict/rmake.rs | 13 ++ .../run-make/git-rebase-conflict/file.stderr | 19 +++ tests/run-make/git-rebase-conflict/main.rs | 7 + tests/run-make/git-rebase-conflict/rmake.rs | 13 ++ 7 files changed, 152 insertions(+), 77 deletions(-) create mode 100644 tests/run-make/git-pull-rebase-conflict/file.stderr create mode 100644 tests/run-make/git-pull-rebase-conflict/main.rs create mode 100644 tests/run-make/git-pull-rebase-conflict/rmake.rs create mode 100644 tests/run-make/git-rebase-conflict/file.stderr create mode 100644 tests/run-make/git-rebase-conflict/main.rs create mode 100644 tests/run-make/git-rebase-conflict/rmake.rs diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index b08353813ab0d..0970cf973a5b3 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,6 +1,5 @@ use std::mem::take; use std::ops::{Deref, DerefMut}; -use std::process::Command; use ast::token::IdentIsRaw; use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; @@ -3059,84 +3058,82 @@ impl<'a> Parser<'a> { let mut msg_middle: String = "incoming code".into(); let mut show_help = true; - // `git rev-parse --git-path rebase-merge`, in order to get the git metadata for the - // rebase currently happening. If this directory doesn't exist, then the merge conflicts - // were not caused by a rebase. - if let Ok(output) = - Command::new("git").args(["rev-parse", "--git-path", "rebase-merge"]).output() - && let Ok(relative_path) = String::from_utf8(output.stdout) - && let relative_path = relative_path.trim() - && let Ok(orig_head_sha) = std::fs::read_to_string(format!("{relative_path}/orig-head")) - && let Ok(onto_sha) = std::fs::read_to_string(format!("{relative_path}/onto")) - { - // git is installed, we're currently in a git repo, and that repo is currently in a - // rebase. - show_help = false; - debug!(?relative_path, ?orig_head_sha, ?onto_sha); - if let Ok(head_name) = std::fs::read_to_string(format!("{relative_path}/head-name")) - && let head_name = head_name.trim() - && let Some(branch_name) = head_name.strip_prefix("refs/heads/") + // Ideally, we'd execute `git rev-parse --git-path rebase-merge`, in order to get the git + // metadata for the rebase currently happening, but instead we only customize the output if + // invoking cargo from the project root. If this directory doesn't exist, then the merge + // conflict might not have been caused by a git rebase, and we'll fall-back on the default + // diagnostic. + let git = |path: &str| { + std::fs::read_to_string(format!(".git/{path}")) + .map(|content| content.trim().to_string()) + }; + let mut local_branch = None; + let mut local_sha = None; + let mut onto_descr = None; + let mut onto_sha = None; + let mut is_git_pull = false; + if let Ok(onto) = git("rebase-merge/onto") { + let onto = onto.trim(); + onto_sha = Some(onto.to_string()); + if let Ok(fetch) = git("FETCH_HEAD") + && let Some(parts) = fetch + .lines() + .map(|line| line.split('\t').collect::>()) + .filter(|parts| parts[1] == "") + .next() + && let [fetch_head, "", branch_descr] = &parts[..] + && &onto == fetch_head { - debug!(?head_name); - msg_middle = - format!("code you had in branch `{branch_name}` that you are rebasing"); - } else { - msg_middle = - format!("code you had in commit `{orig_head_sha}` that you are rebasing"); + is_git_pull = true; + onto_descr = Some(branch_descr.trim().to_string()); } - if let Ok(output) = - Command::new("git").args(["branch", "--points-at", onto_sha.trim()]).output() - && let Ok(onto_branch) = String::from_utf8(output.stdout) - { - debug!(?onto_branch); - let onto_branches: Vec<_> = onto_branch - .lines() - .filter(|line| !line.starts_with("*")) - .map(|line| format!("`{}`", line.trim())) - .collect(); - match &onto_branches[..] { - branches if branches.len() > 4 => { - msg_start = format!("from the branch we're rebasing onto"); - } - [] => { - // `git pull` caused a conflict and we're rebasing, instead of it being - // caused by the more common `git rebase branch-name`. - if let Ok(output) = Command::new("git") - .args(["branch", "-r", "--points-at", onto_sha.trim()]) - .output() - && let Ok(onto_branch) = String::from_utf8(output.stdout) - && let onto_branches = onto_branch - .lines() - .filter_map(|line| { - if line.split(" -> ").count() == 1 { - Some(format!("`{}`", line.trim())) - } else { - None - } - }) - .collect::>() - && !onto_branches.is_empty() - { - if let [branch] = &onto_branches[..] { - msg_start = - format!("from the remote branch {branch} we're rebasing onto"); - } else { - msg_start = format!("from the remote branch we're rebasing onto"); - } - } - } - [branch] => { - msg_start = format!("from branch {branch} we're rebasing onto"); - } - [branches @ .., last] => { - msg_start = format!( - "from branches {} and {last} we're rebasing onto", - branches.join(", ") - ); - } - } - } else { - msg_start = format!("from commit `{}` that we're rebasing onto", onto_sha.trim()); + } + if let Ok(head_name) = git("rebase-merge/head-name") + && let head_name = head_name.trim() + && let Some(branch_name) = head_name.strip_prefix("refs/heads/") + { + local_branch = Some(branch_name.to_string()); + } + if let Ok(local) = git("rebase-merge/stopped-sha") { + local_sha = Some(local.trim().to_string()); + } + match (local_branch, local_sha, onto_descr, onto_sha) { + (Some(branch_name), _, Some(onto_descr), _) => { + // `git pull`, branch_descr (from `.git/FETCH_HEAD`) has "branch 'name' of " + msg_start = format!("from {}", onto_descr.trim()); + msg_middle = if is_git_pull { + format!("code from local branch `{branch_name}` before `git pull`") + } else { + format!("code from branch `{branch_name}`") + }; + show_help = false; + } + (None, Some(from_sha), Some(onto_descr), _) => { + // `git pull`, branch_descr (from `.git/FETCH_HEAD`) has "branch 'name' of " + msg_start = format!("from {}", onto_descr.trim()); + msg_middle = format!("code you had in local commit `{from_sha}` before `git pull`"); + show_help = false; + } + (Some(branch_name), _, None, Some(onto_sha)) => { + // `git rebase`, but we don't have the branch name for the target. + // We could do `git branch --points-at onto_sha` to get a list of branch names, but + // that would necessitate to call into `git` *and* would be a linear scan of every + // branch, which can be expensive in repos with lots of branches. + msg_start = format!("you had in commit `{onto_sha}` that you're rebasing onto"); + msg_middle = format!("code from branch `{branch_name}` that you are rebasing"); + show_help = false; + } + (_, Some(from_sha), None, Some(onto_sha)) => { + // `git rebase`, but we don't have the branch name for the source nor the target. + msg_start = format!("you had in commit `{onto_sha}` that you're rebasing onto"); + msg_middle = format!("code from commit `{from_sha}` that you are rebasing"); + show_help = false; + } + (_, None, _, _) => { + // We're not in a `git rebase` or `git pull`. + } + _ => { + // Invalid git repo states, we're not in a rebase. } } diff --git a/tests/run-make/git-pull-rebase-conflict/file.stderr b/tests/run-make/git-pull-rebase-conflict/file.stderr new file mode 100644 index 0000000000000..302faabae40ff --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/file.stderr @@ -0,0 +1,19 @@ +error: encountered diff marker + --> main.rs:2:1 + | +2 | <<<<<<< HEAD //~ ERROR encountered diff marker + | ^^^^^^^ between this marker and `=======` is the code from branch 'main' of github.com:user/repo +3 | Foo(u8), +4 | ======= + | ------- between this marker and `>>>>>>>` is the code from local branch `branch-name` before `git pull` +5 | Bar(i8), +6 | >>>>>>> branch + | ^^^^^^^ this marker concludes the conflict region + | + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = note: for an explanation on these markers from the `git` documentation: + visit + +error: aborting due to 1 previous error + diff --git a/tests/run-make/git-pull-rebase-conflict/main.rs b/tests/run-make/git-pull-rebase-conflict/main.rs new file mode 100644 index 0000000000000..45df6e3251d76 --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/main.rs @@ -0,0 +1,7 @@ +enum E { +<<<<<<< HEAD //~ ERROR encountered diff marker + Foo(u8), +======= + Bar(i8), +>>>>>>> branch +} diff --git a/tests/run-make/git-pull-rebase-conflict/rmake.rs b/tests/run-make/git-pull-rebase-conflict/rmake.rs new file mode 100644 index 0000000000000..1c46ec6992a0d --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/rmake.rs @@ -0,0 +1,13 @@ +//@ needs-target-std +// +// An attempt to set the output `-o` into a directory or a file we cannot write into should indeed +// be an error; but not an ICE (Internal Compiler Error). This test attempts both and checks +// that the standard error matches what is expected. +// See https://github.com/rust-lang/rust/issues/66530 + +use run_make_support::{diff, rustc}; + +fn main() { + let file_out = rustc().input("main.rs").run_fail().stderr_utf8(); + diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); +} diff --git a/tests/run-make/git-rebase-conflict/file.stderr b/tests/run-make/git-rebase-conflict/file.stderr new file mode 100644 index 0000000000000..1cbfa5d046f6e --- /dev/null +++ b/tests/run-make/git-rebase-conflict/file.stderr @@ -0,0 +1,19 @@ +error: encountered diff marker + --> main.rs:2:1 + | +2 | <<<<<<< HEAD //~ ERROR encountered diff marker + | ^^^^^^^ between this marker and `=======` is the code you had in commit `REBASING_ON_TOP_OF_THIS_COMMIT_SHA_VALUE` that you're rebasing onto +3 | Foo(u8), +4 | ======= + | ------- between this marker and `>>>>>>>` is the code from branch `branch-name` that you are rebasing +5 | Bar(i8), +6 | >>>>>>> branch + | ^^^^^^^ this marker concludes the conflict region + | + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = note: for an explanation on these markers from the `git` documentation: + visit + +error: aborting due to 1 previous error + diff --git a/tests/run-make/git-rebase-conflict/main.rs b/tests/run-make/git-rebase-conflict/main.rs new file mode 100644 index 0000000000000..45df6e3251d76 --- /dev/null +++ b/tests/run-make/git-rebase-conflict/main.rs @@ -0,0 +1,7 @@ +enum E { +<<<<<<< HEAD //~ ERROR encountered diff marker + Foo(u8), +======= + Bar(i8), +>>>>>>> branch +} diff --git a/tests/run-make/git-rebase-conflict/rmake.rs b/tests/run-make/git-rebase-conflict/rmake.rs new file mode 100644 index 0000000000000..1c46ec6992a0d --- /dev/null +++ b/tests/run-make/git-rebase-conflict/rmake.rs @@ -0,0 +1,13 @@ +//@ needs-target-std +// +// An attempt to set the output `-o` into a directory or a file we cannot write into should indeed +// be an error; but not an ICE (Internal Compiler Error). This test attempts both and checks +// that the standard error matches what is expected. +// See https://github.com/rust-lang/rust/issues/66530 + +use run_make_support::{diff, rustc}; + +fn main() { + let file_out = rustc().input("main.rs").run_fail().stderr_utf8(); + diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); +} From 6a2f9ba426e36d3f06d0986b9beb35072742aa74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 16:18:51 +0000 Subject: [PATCH 3/9] Fix tidy check --- tests/run-make/git-pull-rebase-conflict/file.stderr | 2 +- tests/run-make/git-pull-rebase-conflict/{main.rs => main.rust} | 0 tests/run-make/git-pull-rebase-conflict/rmake.rs | 3 ++- tests/run-make/git-rebase-conflict/file.stderr | 2 +- tests/run-make/git-rebase-conflict/{main.rs => main.rust} | 0 tests/run-make/git-rebase-conflict/rmake.rs | 3 ++- 6 files changed, 6 insertions(+), 4 deletions(-) rename tests/run-make/git-pull-rebase-conflict/{main.rs => main.rust} (100%) rename tests/run-make/git-rebase-conflict/{main.rs => main.rust} (100%) diff --git a/tests/run-make/git-pull-rebase-conflict/file.stderr b/tests/run-make/git-pull-rebase-conflict/file.stderr index 302faabae40ff..bb9444bcd4ce7 100644 --- a/tests/run-make/git-pull-rebase-conflict/file.stderr +++ b/tests/run-make/git-pull-rebase-conflict/file.stderr @@ -1,5 +1,5 @@ error: encountered diff marker - --> main.rs:2:1 + --> main.rust:2:1 | 2 | <<<<<<< HEAD //~ ERROR encountered diff marker | ^^^^^^^ between this marker and `=======` is the code from branch 'main' of github.com:user/repo diff --git a/tests/run-make/git-pull-rebase-conflict/main.rs b/tests/run-make/git-pull-rebase-conflict/main.rust similarity index 100% rename from tests/run-make/git-pull-rebase-conflict/main.rs rename to tests/run-make/git-pull-rebase-conflict/main.rust diff --git a/tests/run-make/git-pull-rebase-conflict/rmake.rs b/tests/run-make/git-pull-rebase-conflict/rmake.rs index 1c46ec6992a0d..a95a39fab2c37 100644 --- a/tests/run-make/git-pull-rebase-conflict/rmake.rs +++ b/tests/run-make/git-pull-rebase-conflict/rmake.rs @@ -8,6 +8,7 @@ use run_make_support::{diff, rustc}; fn main() { - let file_out = rustc().input("main.rs").run_fail().stderr_utf8(); + // We use `.rust` purely to avoid `fmt` check on a file with conflict markers + let file_out = rustc().input("main.rust").run_fail().stderr_utf8(); diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); } diff --git a/tests/run-make/git-rebase-conflict/file.stderr b/tests/run-make/git-rebase-conflict/file.stderr index 1cbfa5d046f6e..3db11f5bcd261 100644 --- a/tests/run-make/git-rebase-conflict/file.stderr +++ b/tests/run-make/git-rebase-conflict/file.stderr @@ -1,5 +1,5 @@ error: encountered diff marker - --> main.rs:2:1 + --> main.rust:2:1 | 2 | <<<<<<< HEAD //~ ERROR encountered diff marker | ^^^^^^^ between this marker and `=======` is the code you had in commit `REBASING_ON_TOP_OF_THIS_COMMIT_SHA_VALUE` that you're rebasing onto diff --git a/tests/run-make/git-rebase-conflict/main.rs b/tests/run-make/git-rebase-conflict/main.rust similarity index 100% rename from tests/run-make/git-rebase-conflict/main.rs rename to tests/run-make/git-rebase-conflict/main.rust diff --git a/tests/run-make/git-rebase-conflict/rmake.rs b/tests/run-make/git-rebase-conflict/rmake.rs index 1c46ec6992a0d..a95a39fab2c37 100644 --- a/tests/run-make/git-rebase-conflict/rmake.rs +++ b/tests/run-make/git-rebase-conflict/rmake.rs @@ -8,6 +8,7 @@ use run_make_support::{diff, rustc}; fn main() { - let file_out = rustc().input("main.rs").run_fail().stderr_utf8(); + // We use `.rust` purely to avoid `fmt` check on a file with conflict markers + let file_out = rustc().input("main.rust").run_fail().stderr_utf8(); diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); } From 7059ba1724abeb9425968333e2183262947f8567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 16:19:40 +0000 Subject: [PATCH 4/9] Do not mention target commit when we already mention source branch --- compiler/rustc_parse/src/parser/diagnostics.rs | 13 +++++-------- tests/run-make/git-rebase-conflict/file.stderr | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 0970cf973a5b3..6585fedfebf37 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -3114,26 +3114,23 @@ impl<'a> Parser<'a> { msg_middle = format!("code you had in local commit `{from_sha}` before `git pull`"); show_help = false; } - (Some(branch_name), _, None, Some(onto_sha)) => { + (Some(branch_name), _, None, Some(_)) => { // `git rebase`, but we don't have the branch name for the target. // We could do `git branch --points-at onto_sha` to get a list of branch names, but // that would necessitate to call into `git` *and* would be a linear scan of every // branch, which can be expensive in repos with lots of branches. - msg_start = format!("you had in commit `{onto_sha}` that you're rebasing onto"); + msg_start = "that you're rebasing onto".to_string(); msg_middle = format!("code from branch `{branch_name}` that you are rebasing"); show_help = false; } - (_, Some(from_sha), None, Some(onto_sha)) => { + (None, Some(from_sha), None, Some(onto_sha)) => { // `git rebase`, but we don't have the branch name for the source nor the target. - msg_start = format!("you had in commit `{onto_sha}` that you're rebasing onto"); + msg_start = format!("you had in commit `{onto_sha}` that you are rebasing onto"); msg_middle = format!("code from commit `{from_sha}` that you are rebasing"); show_help = false; } - (_, None, _, _) => { - // We're not in a `git rebase` or `git pull`. - } _ => { - // Invalid git repo states, we're not in a rebase. + // We're not in a `git rebase` or `git pull`. } } diff --git a/tests/run-make/git-rebase-conflict/file.stderr b/tests/run-make/git-rebase-conflict/file.stderr index 3db11f5bcd261..943b0850a5b11 100644 --- a/tests/run-make/git-rebase-conflict/file.stderr +++ b/tests/run-make/git-rebase-conflict/file.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> main.rust:2:1 | 2 | <<<<<<< HEAD //~ ERROR encountered diff marker - | ^^^^^^^ between this marker and `=======` is the code you had in commit `REBASING_ON_TOP_OF_THIS_COMMIT_SHA_VALUE` that you're rebasing onto + | ^^^^^^^ between this marker and `=======` is the code that you're rebasing onto 3 | Foo(u8), 4 | ======= | ------- between this marker and `>>>>>>>` is the code from branch `branch-name` that you are rebasing From 2b76d0a366cd74db7381a15821f653da60a4799c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 16:47:30 +0000 Subject: [PATCH 5/9] Account for conflict markers caused by `git merge` --- .../rustc_parse/src/parser/diagnostics.rs | 25 ++++++++++++++----- tests/run-make/git-merge-conflict/file.stderr | 19 ++++++++++++++ tests/run-make/git-merge-conflict/main.rust | 7 ++++++ tests/run-make/git-merge-conflict/rmake.rs | 14 +++++++++++ 4 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 tests/run-make/git-merge-conflict/file.stderr create mode 100644 tests/run-make/git-merge-conflict/main.rust create mode 100644 tests/run-make/git-merge-conflict/rmake.rs diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 6585fedfebf37..52d5c74033e91 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -3070,6 +3070,7 @@ impl<'a> Parser<'a> { let mut local_branch = None; let mut local_sha = None; let mut onto_descr = None; + let mut merge_to = None; let mut onto_sha = None; let mut is_git_pull = false; if let Ok(onto) = git("rebase-merge/onto") { @@ -3097,8 +3098,14 @@ impl<'a> Parser<'a> { if let Ok(local) = git("rebase-merge/stopped-sha") { local_sha = Some(local.trim().to_string()); } - match (local_branch, local_sha, onto_descr, onto_sha) { - (Some(branch_name), _, Some(onto_descr), _) => { + if let Ok(head) = git("HEAD") + && let Some(merging_to) = head.trim().strip_prefix("ref: refs/heads/") + && let Ok(_) = git("MERGE_HEAD") + { + merge_to = Some(merging_to.to_string()); + } + match (local_branch, local_sha, onto_descr, onto_sha, merge_to) { + (Some(branch_name), _, Some(onto_descr), _, None) => { // `git pull`, branch_descr (from `.git/FETCH_HEAD`) has "branch 'name' of " msg_start = format!("from {}", onto_descr.trim()); msg_middle = if is_git_pull { @@ -3108,13 +3115,13 @@ impl<'a> Parser<'a> { }; show_help = false; } - (None, Some(from_sha), Some(onto_descr), _) => { + (None, Some(from_sha), Some(onto_descr), _, None) => { // `git pull`, branch_descr (from `.git/FETCH_HEAD`) has "branch 'name' of " msg_start = format!("from {}", onto_descr.trim()); msg_middle = format!("code you had in local commit `{from_sha}` before `git pull`"); show_help = false; } - (Some(branch_name), _, None, Some(_)) => { + (Some(branch_name), _, None, Some(_), None) => { // `git rebase`, but we don't have the branch name for the target. // We could do `git branch --points-at onto_sha` to get a list of branch names, but // that would necessitate to call into `git` *and* would be a linear scan of every @@ -3123,14 +3130,20 @@ impl<'a> Parser<'a> { msg_middle = format!("code from branch `{branch_name}` that you are rebasing"); show_help = false; } - (None, Some(from_sha), None, Some(onto_sha)) => { + (None, Some(from_sha), None, Some(onto_sha), None) => { // `git rebase`, but we don't have the branch name for the source nor the target. msg_start = format!("you had in commit `{onto_sha}` that you are rebasing onto"); msg_middle = format!("code from commit `{from_sha}` that you are rebasing"); show_help = false; } + (None, None, None, None, Some(merge_to)) => { + // `git merge from-branch` + msg_start = format!("from branch `{merge_to}`"); + msg_middle = format!("code that you're merging"); + show_help = false; + } _ => { - // We're not in a `git rebase` or `git pull`. + // We're not in a `git merge`, `git rebase` or `git pull`. } } diff --git a/tests/run-make/git-merge-conflict/file.stderr b/tests/run-make/git-merge-conflict/file.stderr new file mode 100644 index 0000000000000..0fa291ea38c75 --- /dev/null +++ b/tests/run-make/git-merge-conflict/file.stderr @@ -0,0 +1,19 @@ +error: encountered diff marker + --> main.rust:2:1 + | +2 | <<<<<<< HEAD //~ ERROR encountered diff marker + | ^^^^^^^ between this marker and `=======` is the code from branch `current-branch` +3 | Foo(u8), +4 | ======= + | ------- between this marker and `>>>>>>>` is the code that you're merging +5 | Bar(i8), +6 | >>>>>>> branch + | ^^^^^^^ this marker concludes the conflict region + | + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = note: for an explanation on these markers from the `git` documentation: + visit + +error: aborting due to 1 previous error + diff --git a/tests/run-make/git-merge-conflict/main.rust b/tests/run-make/git-merge-conflict/main.rust new file mode 100644 index 0000000000000..45df6e3251d76 --- /dev/null +++ b/tests/run-make/git-merge-conflict/main.rust @@ -0,0 +1,7 @@ +enum E { +<<<<<<< HEAD //~ ERROR encountered diff marker + Foo(u8), +======= + Bar(i8), +>>>>>>> branch +} diff --git a/tests/run-make/git-merge-conflict/rmake.rs b/tests/run-make/git-merge-conflict/rmake.rs new file mode 100644 index 0000000000000..a95a39fab2c37 --- /dev/null +++ b/tests/run-make/git-merge-conflict/rmake.rs @@ -0,0 +1,14 @@ +//@ needs-target-std +// +// An attempt to set the output `-o` into a directory or a file we cannot write into should indeed +// be an error; but not an ICE (Internal Compiler Error). This test attempts both and checks +// that the standard error matches what is expected. +// See https://github.com/rust-lang/rust/issues/66530 + +use run_make_support::{diff, rustc}; + +fn main() { + // We use `.rust` purely to avoid `fmt` check on a file with conflict markers + let file_out = rustc().input("main.rust").run_fail().stderr_utf8(); + diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); +} From 3699a66c9ef7bbcaae3463c49329737ebb153f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 16:53:57 +0000 Subject: [PATCH 6/9] Add the necessary `.git` contents to the tests --- tests/run-make/git-merge-conflict/git/HEAD | 1 + tests/run-make/git-merge-conflict/git/MERGE_HEAD | 1 + tests/run-make/git-merge-conflict/rmake.rs | 3 ++- tests/run-make/git-pull-rebase-conflict/git/FETCH_HEAD | 1 + .../git-pull-rebase-conflict/git/rebase-merge/head-name | 1 + .../git-pull-rebase-conflict/git/rebase-merge/interactive | 0 tests/run-make/git-pull-rebase-conflict/git/rebase-merge/onto | 1 + .../git-pull-rebase-conflict/git/rebase-merge/orig-head | 1 + .../git-pull-rebase-conflict/git/rebase-merge/stopped-sha | 1 + tests/run-make/git-pull-rebase-conflict/rmake.rs | 3 ++- tests/run-make/git-rebase-conflict/git/rebase-merge/head-name | 1 + .../run-make/git-rebase-conflict/git/rebase-merge/interactive | 0 tests/run-make/git-rebase-conflict/git/rebase-merge/onto | 1 + tests/run-make/git-rebase-conflict/git/rebase-merge/orig-head | 1 + .../run-make/git-rebase-conflict/git/rebase-merge/stopped-sha | 1 + tests/run-make/git-rebase-conflict/rmake.rs | 3 ++- 16 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 tests/run-make/git-merge-conflict/git/HEAD create mode 100644 tests/run-make/git-merge-conflict/git/MERGE_HEAD create mode 100644 tests/run-make/git-pull-rebase-conflict/git/FETCH_HEAD create mode 100644 tests/run-make/git-pull-rebase-conflict/git/rebase-merge/head-name create mode 100644 tests/run-make/git-pull-rebase-conflict/git/rebase-merge/interactive create mode 100644 tests/run-make/git-pull-rebase-conflict/git/rebase-merge/onto create mode 100644 tests/run-make/git-pull-rebase-conflict/git/rebase-merge/orig-head create mode 100644 tests/run-make/git-pull-rebase-conflict/git/rebase-merge/stopped-sha create mode 100644 tests/run-make/git-rebase-conflict/git/rebase-merge/head-name create mode 100644 tests/run-make/git-rebase-conflict/git/rebase-merge/interactive create mode 100644 tests/run-make/git-rebase-conflict/git/rebase-merge/onto create mode 100644 tests/run-make/git-rebase-conflict/git/rebase-merge/orig-head create mode 100644 tests/run-make/git-rebase-conflict/git/rebase-merge/stopped-sha diff --git a/tests/run-make/git-merge-conflict/git/HEAD b/tests/run-make/git-merge-conflict/git/HEAD new file mode 100644 index 0000000000000..f381a9ddad020 --- /dev/null +++ b/tests/run-make/git-merge-conflict/git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/current-branch diff --git a/tests/run-make/git-merge-conflict/git/MERGE_HEAD b/tests/run-make/git-merge-conflict/git/MERGE_HEAD new file mode 100644 index 0000000000000..527830201beb8 --- /dev/null +++ b/tests/run-make/git-merge-conflict/git/MERGE_HEAD @@ -0,0 +1 @@ +SHA_FOR_THE_COMMIT_WE_ARE_MERGING diff --git a/tests/run-make/git-merge-conflict/rmake.rs b/tests/run-make/git-merge-conflict/rmake.rs index a95a39fab2c37..721fbdf762e5c 100644 --- a/tests/run-make/git-merge-conflict/rmake.rs +++ b/tests/run-make/git-merge-conflict/rmake.rs @@ -5,10 +5,11 @@ // that the standard error matches what is expected. // See https://github.com/rust-lang/rust/issues/66530 -use run_make_support::{diff, rustc}; +use run_make_support::{diff, rfs, rustc}; fn main() { // We use `.rust` purely to avoid `fmt` check on a file with conflict markers + rfs::copy_dir_all("git", ".git"); let file_out = rustc().input("main.rust").run_fail().stderr_utf8(); diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); } diff --git a/tests/run-make/git-pull-rebase-conflict/git/FETCH_HEAD b/tests/run-make/git-pull-rebase-conflict/git/FETCH_HEAD new file mode 100644 index 0000000000000..4e0adfbe4cb8e --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/git/FETCH_HEAD @@ -0,0 +1 @@ +REBASING_ON_TOP_OF_THIS_COMMIT_SHA_VALUE branch 'main' of github.com:user/repo diff --git a/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/head-name b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/head-name new file mode 100644 index 0000000000000..687a037c2919a --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/head-name @@ -0,0 +1 @@ +refs/heads/branch-name diff --git a/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/interactive b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/interactive new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/onto b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/onto new file mode 100644 index 0000000000000..b178957b41b24 --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/onto @@ -0,0 +1 @@ +REBASING_ON_TOP_OF_THIS_COMMIT_SHA_VALUE diff --git a/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/orig-head b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/orig-head new file mode 100644 index 0000000000000..715f6674d2268 --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/orig-head @@ -0,0 +1 @@ +COMMIT_SHA_OF_THE_BRANCH_WE_ARE_REBASING diff --git a/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/stopped-sha b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/stopped-sha new file mode 100644 index 0000000000000..b3c08f7cd47bb --- /dev/null +++ b/tests/run-make/git-pull-rebase-conflict/git/rebase-merge/stopped-sha @@ -0,0 +1 @@ +A_COMMIT_SHA_VALUE_GIT_REBASE_STOPPED_AT diff --git a/tests/run-make/git-pull-rebase-conflict/rmake.rs b/tests/run-make/git-pull-rebase-conflict/rmake.rs index a95a39fab2c37..721fbdf762e5c 100644 --- a/tests/run-make/git-pull-rebase-conflict/rmake.rs +++ b/tests/run-make/git-pull-rebase-conflict/rmake.rs @@ -5,10 +5,11 @@ // that the standard error matches what is expected. // See https://github.com/rust-lang/rust/issues/66530 -use run_make_support::{diff, rustc}; +use run_make_support::{diff, rfs, rustc}; fn main() { // We use `.rust` purely to avoid `fmt` check on a file with conflict markers + rfs::copy_dir_all("git", ".git"); let file_out = rustc().input("main.rust").run_fail().stderr_utf8(); diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); } diff --git a/tests/run-make/git-rebase-conflict/git/rebase-merge/head-name b/tests/run-make/git-rebase-conflict/git/rebase-merge/head-name new file mode 100644 index 0000000000000..687a037c2919a --- /dev/null +++ b/tests/run-make/git-rebase-conflict/git/rebase-merge/head-name @@ -0,0 +1 @@ +refs/heads/branch-name diff --git a/tests/run-make/git-rebase-conflict/git/rebase-merge/interactive b/tests/run-make/git-rebase-conflict/git/rebase-merge/interactive new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/run-make/git-rebase-conflict/git/rebase-merge/onto b/tests/run-make/git-rebase-conflict/git/rebase-merge/onto new file mode 100644 index 0000000000000..b178957b41b24 --- /dev/null +++ b/tests/run-make/git-rebase-conflict/git/rebase-merge/onto @@ -0,0 +1 @@ +REBASING_ON_TOP_OF_THIS_COMMIT_SHA_VALUE diff --git a/tests/run-make/git-rebase-conflict/git/rebase-merge/orig-head b/tests/run-make/git-rebase-conflict/git/rebase-merge/orig-head new file mode 100644 index 0000000000000..715f6674d2268 --- /dev/null +++ b/tests/run-make/git-rebase-conflict/git/rebase-merge/orig-head @@ -0,0 +1 @@ +COMMIT_SHA_OF_THE_BRANCH_WE_ARE_REBASING diff --git a/tests/run-make/git-rebase-conflict/git/rebase-merge/stopped-sha b/tests/run-make/git-rebase-conflict/git/rebase-merge/stopped-sha new file mode 100644 index 0000000000000..b3c08f7cd47bb --- /dev/null +++ b/tests/run-make/git-rebase-conflict/git/rebase-merge/stopped-sha @@ -0,0 +1 @@ +A_COMMIT_SHA_VALUE_GIT_REBASE_STOPPED_AT diff --git a/tests/run-make/git-rebase-conflict/rmake.rs b/tests/run-make/git-rebase-conflict/rmake.rs index a95a39fab2c37..721fbdf762e5c 100644 --- a/tests/run-make/git-rebase-conflict/rmake.rs +++ b/tests/run-make/git-rebase-conflict/rmake.rs @@ -5,10 +5,11 @@ // that the standard error matches what is expected. // See https://github.com/rust-lang/rust/issues/66530 -use run_make_support::{diff, rustc}; +use run_make_support::{diff, rfs, rustc}; fn main() { // We use `.rust` purely to avoid `fmt` check on a file with conflict markers + rfs::copy_dir_all("git", ".git"); let file_out = rustc().input("main.rust").run_fail().stderr_utf8(); diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); } From 8adbe852e376a0fe45baba5fbbf2b494dda41a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 16:59:31 +0000 Subject: [PATCH 7/9] update test descriptions --- tests/run-make/git-merge-conflict/rmake.rs | 7 +------ tests/run-make/git-pull-rebase-conflict/rmake.rs | 7 +------ tests/run-make/git-rebase-conflict/rmake.rs | 7 +------ 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/tests/run-make/git-merge-conflict/rmake.rs b/tests/run-make/git-merge-conflict/rmake.rs index 721fbdf762e5c..0c6dc702bd4ac 100644 --- a/tests/run-make/git-merge-conflict/rmake.rs +++ b/tests/run-make/git-merge-conflict/rmake.rs @@ -1,9 +1,4 @@ -//@ needs-target-std -// -// An attempt to set the output `-o` into a directory or a file we cannot write into should indeed -// be an error; but not an ICE (Internal Compiler Error). This test attempts both and checks -// that the standard error matches what is expected. -// See https://github.com/rust-lang/rust/issues/66530 +// Checks the output in cases where a manual `git merge` had conflicts. use run_make_support::{diff, rfs, rustc}; diff --git a/tests/run-make/git-pull-rebase-conflict/rmake.rs b/tests/run-make/git-pull-rebase-conflict/rmake.rs index 721fbdf762e5c..ca6dfd3826e8f 100644 --- a/tests/run-make/git-pull-rebase-conflict/rmake.rs +++ b/tests/run-make/git-pull-rebase-conflict/rmake.rs @@ -1,9 +1,4 @@ -//@ needs-target-std -// -// An attempt to set the output `-o` into a directory or a file we cannot write into should indeed -// be an error; but not an ICE (Internal Compiler Error). This test attempts both and checks -// that the standard error matches what is expected. -// See https://github.com/rust-lang/rust/issues/66530 +// Checks the output in cases where a `git pull` configured to rebase had conflicts. use run_make_support::{diff, rfs, rustc}; diff --git a/tests/run-make/git-rebase-conflict/rmake.rs b/tests/run-make/git-rebase-conflict/rmake.rs index 721fbdf762e5c..4635a6c8b853b 100644 --- a/tests/run-make/git-rebase-conflict/rmake.rs +++ b/tests/run-make/git-rebase-conflict/rmake.rs @@ -1,9 +1,4 @@ -//@ needs-target-std -// -// An attempt to set the output `-o` into a directory or a file we cannot write into should indeed -// be an error; but not an ICE (Internal Compiler Error). This test attempts both and checks -// that the standard error matches what is expected. -// See https://github.com/rust-lang/rust/issues/66530 +// Checks the output in cases where a manual `git rebase` was stopped due to conflicts. use run_make_support::{diff, rfs, rustc}; From 3cf97becec2a6fee0fdc83059af363e9dc441857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 17:19:22 +0000 Subject: [PATCH 8/9] Tweak output and wording --- .../rustc_parse/src/parser/diagnostics.rs | 40 +++++-------------- tests/run-make/git-merge-conflict/file.stderr | 2 +- .../git-pull-rebase-conflict/file.stderr | 2 +- .../run-make/git-rebase-conflict/file.stderr | 2 +- tests/ui/parser/diff-markers/enum-2.stderr | 6 +-- tests/ui/parser/diff-markers/enum.stderr | 6 +-- tests/ui/parser/diff-markers/fn-arg.stderr | 6 +-- .../parser/diff-markers/item-with-attr.stderr | 6 +-- tests/ui/parser/diff-markers/item.stderr | 6 +-- tests/ui/parser/diff-markers/statement.stderr | 6 +-- .../ui/parser/diff-markers/struct-expr.stderr | 6 +-- tests/ui/parser/diff-markers/struct.stderr | 6 +-- .../ui/parser/diff-markers/trait-item.stderr | 6 +-- .../parser/diff-markers/tuple-struct.stderr | 6 +-- .../parser/diff-markers/use-statement.stderr | 6 +-- 15 files changed, 23 insertions(+), 89 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 52d5c74033e91..eaac92b96a227 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -3054,9 +3054,8 @@ impl<'a> Parser<'a> { /// Try to identify if a `git rebase` is in progress to provide more accurate labels. fn vcs_conflict_marker_labels(&self, is_middlediff3: bool) -> ConflictMarkerLabels { - let mut msg_start: String = "that we're merging into".into(); + let mut msg_start: String = "being merged into".into(); let mut msg_middle: String = "incoming code".into(); - let mut show_help = true; // Ideally, we'd execute `git rev-parse --git-path rebase-merge`, in order to get the git // metadata for the rebase currently happening, but instead we only customize the output if @@ -3109,17 +3108,15 @@ impl<'a> Parser<'a> { // `git pull`, branch_descr (from `.git/FETCH_HEAD`) has "branch 'name' of " msg_start = format!("from {}", onto_descr.trim()); msg_middle = if is_git_pull { - format!("code from local branch `{branch_name}` before `git pull`") + format!("code from local branch '{branch_name}' before `git pull`") } else { - format!("code from branch `{branch_name}`") + format!("code from branch '{branch_name}'") }; - show_help = false; } (None, Some(from_sha), Some(onto_descr), _, None) => { // `git pull`, branch_descr (from `.git/FETCH_HEAD`) has "branch 'name' of " msg_start = format!("from {}", onto_descr.trim()); msg_middle = format!("code you had in local commit `{from_sha}` before `git pull`"); - show_help = false; } (Some(branch_name), _, None, Some(_), None) => { // `git rebase`, but we don't have the branch name for the target. @@ -3127,20 +3124,17 @@ impl<'a> Parser<'a> { // that would necessitate to call into `git` *and* would be a linear scan of every // branch, which can be expensive in repos with lots of branches. msg_start = "that you're rebasing onto".to_string(); - msg_middle = format!("code from branch `{branch_name}` that you are rebasing"); - show_help = false; + msg_middle = format!("code from branch '{branch_name}' that you are rebasing"); } (None, Some(from_sha), None, Some(onto_sha), None) => { // `git rebase`, but we don't have the branch name for the source nor the target. msg_start = format!("you had in commit `{onto_sha}` that you are rebasing onto"); msg_middle = format!("code from commit `{from_sha}` that you are rebasing"); - show_help = false; } (None, None, None, None, Some(merge_to)) => { // `git merge from-branch` - msg_start = format!("from branch `{merge_to}`"); + msg_start = format!("from branch '{merge_to}'"); msg_middle = format!("code that you're merging"); - show_help = false; } _ => { // We're not in a `git merge`, `git rebase` or `git pull`. @@ -3148,16 +3142,14 @@ impl<'a> Parser<'a> { } ConflictMarkerLabels { - start: if is_middlediff3 { - "between this marker and `|||||||` is the code that we're merging into".to_string() - } else { - format!("between this marker and `=======` is the code {msg_start}") - }, + start: format!( + "between this marker and `{}` is the code {msg_start}", + if is_middlediff3 { "|||||||" } else { "=======" } + ), middle: format!("between this marker and `>>>>>>>` is the {msg_middle}"), middlediff3: "between this marker and `=======` is the base code (what the two refs \ diverged from)", end: "this marker concludes the conflict region", - show_help, } } @@ -3218,18 +3210,6 @@ impl<'a> Parser<'a> { to resolve a conflict, keep only the code you want and then delete the lines \ containing conflict markers", ); - if labels.show_help { - // We couldn't identify that we're in the middle of a `git rebase` (either direct or - // caused by `git pull`), so we provide more generic information. - err.help( - "if you're having merge conflicts after pulling new code:\n\ - the top section is the remote code and the bottom section is the code you already had\n\ - if you're in the middle of a rebase:\n\ - the top section is the code being rebased onto and the bottom section is the code \ - coming from the current commit being rebased", - ); - } - err.note( "for an explanation on these markers from the `git` documentation:\n\ visit ", @@ -3260,6 +3240,4 @@ struct ConflictMarkerLabels { middlediff3: &'static str, /// The label for the `>>>>>>>` marker. end: &'static str, - /// Whether we display a generic help on where the code might be coming from in each section. - show_help: bool, } diff --git a/tests/run-make/git-merge-conflict/file.stderr b/tests/run-make/git-merge-conflict/file.stderr index 0fa291ea38c75..e906c926c7e90 100644 --- a/tests/run-make/git-merge-conflict/file.stderr +++ b/tests/run-make/git-merge-conflict/file.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> main.rust:2:1 | 2 | <<<<<<< HEAD //~ ERROR encountered diff marker - | ^^^^^^^ between this marker and `=======` is the code from branch `current-branch` + | ^^^^^^^ between this marker and `=======` is the code from branch 'current-branch' 3 | Foo(u8), 4 | ======= | ------- between this marker and `>>>>>>>` is the code that you're merging diff --git a/tests/run-make/git-pull-rebase-conflict/file.stderr b/tests/run-make/git-pull-rebase-conflict/file.stderr index bb9444bcd4ce7..cbab58690a995 100644 --- a/tests/run-make/git-pull-rebase-conflict/file.stderr +++ b/tests/run-make/git-pull-rebase-conflict/file.stderr @@ -5,7 +5,7 @@ error: encountered diff marker | ^^^^^^^ between this marker and `=======` is the code from branch 'main' of github.com:user/repo 3 | Foo(u8), 4 | ======= - | ------- between this marker and `>>>>>>>` is the code from local branch `branch-name` before `git pull` + | ------- between this marker and `>>>>>>>` is the code from local branch 'branch-name' before `git pull` 5 | Bar(i8), 6 | >>>>>>> branch | ^^^^^^^ this marker concludes the conflict region diff --git a/tests/run-make/git-rebase-conflict/file.stderr b/tests/run-make/git-rebase-conflict/file.stderr index 943b0850a5b11..7cf0314dce3f5 100644 --- a/tests/run-make/git-rebase-conflict/file.stderr +++ b/tests/run-make/git-rebase-conflict/file.stderr @@ -5,7 +5,7 @@ error: encountered diff marker | ^^^^^^^ between this marker and `=======` is the code that you're rebasing onto 3 | Foo(u8), 4 | ======= - | ------- between this marker and `>>>>>>>` is the code from branch `branch-name` that you are rebasing + | ------- between this marker and `>>>>>>>` is the code from branch 'branch-name' that you are rebasing 5 | Bar(i8), 6 | >>>>>>> branch | ^^^^^^^ this marker concludes the conflict region diff --git a/tests/ui/parser/diff-markers/enum-2.stderr b/tests/ui/parser/diff-markers/enum-2.stderr index f131f13ba6d51..418baaf338ea5 100644 --- a/tests/ui/parser/diff-markers/enum-2.stderr +++ b/tests/ui/parser/diff-markers/enum-2.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/enum-2.rs:3:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `|||||||` is the code that we're merging into + | ^^^^^^^ between this marker and `|||||||` is the code being merged into LL | x: u8, LL | ||||||| | ------- between this marker and `=======` is the base code (what the two refs diverged from) @@ -15,10 +15,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/enum.stderr b/tests/ui/parser/diff-markers/enum.stderr index 570b221291e12..4557987f2b2d7 100644 --- a/tests/ui/parser/diff-markers/enum.stderr +++ b/tests/ui/parser/diff-markers/enum.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/enum.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | Foo(u8), LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/fn-arg.stderr b/tests/ui/parser/diff-markers/fn-arg.stderr index 48b1ed080f26b..2d0122eb673d6 100644 --- a/tests/ui/parser/diff-markers/fn-arg.stderr +++ b/tests/ui/parser/diff-markers/fn-arg.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/fn-arg.rs:3:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | x: u8, LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/item-with-attr.stderr b/tests/ui/parser/diff-markers/item-with-attr.stderr index c990a68867974..6e0b1f16328f5 100644 --- a/tests/ui/parser/diff-markers/item-with-attr.stderr +++ b/tests/ui/parser/diff-markers/item-with-attr.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/item-with-attr.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | fn foo() {} LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/item.stderr b/tests/ui/parser/diff-markers/item.stderr index eaa0e6aa1c308..ab3b9d8a6119c 100644 --- a/tests/ui/parser/diff-markers/item.stderr +++ b/tests/ui/parser/diff-markers/item.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/item.rs:1:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | fn foo() {} LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/statement.stderr b/tests/ui/parser/diff-markers/statement.stderr index 77c9820e8b4fa..310d3762e1fc7 100644 --- a/tests/ui/parser/diff-markers/statement.stderr +++ b/tests/ui/parser/diff-markers/statement.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/statement.rs:10:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | S::foo(); LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/struct-expr.stderr b/tests/ui/parser/diff-markers/struct-expr.stderr index e8cb25169ac34..54bb74d0ffd08 100644 --- a/tests/ui/parser/diff-markers/struct-expr.stderr +++ b/tests/ui/parser/diff-markers/struct-expr.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/struct-expr.rs:6:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | x: 42, LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/struct.stderr b/tests/ui/parser/diff-markers/struct.stderr index 4a9f3771ae57b..b8fd039a73054 100644 --- a/tests/ui/parser/diff-markers/struct.stderr +++ b/tests/ui/parser/diff-markers/struct.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/struct.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | x: u8, LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/trait-item.stderr b/tests/ui/parser/diff-markers/trait-item.stderr index 76e48bd7081da..9e82157300965 100644 --- a/tests/ui/parser/diff-markers/trait-item.stderr +++ b/tests/ui/parser/diff-markers/trait-item.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/trait-item.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | fn foo() {} LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/tuple-struct.stderr b/tests/ui/parser/diff-markers/tuple-struct.stderr index c42c96c993dfa..976dcb877a472 100644 --- a/tests/ui/parser/diff-markers/tuple-struct.stderr +++ b/tests/ui/parser/diff-markers/tuple-struct.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/tuple-struct.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | u8, LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit diff --git a/tests/ui/parser/diff-markers/use-statement.stderr b/tests/ui/parser/diff-markers/use-statement.stderr index 0bfaca244ae69..5cb92e171c884 100644 --- a/tests/ui/parser/diff-markers/use-statement.stderr +++ b/tests/ui/parser/diff-markers/use-statement.stderr @@ -2,7 +2,7 @@ error: encountered diff marker --> $DIR/use-statement.rs:2:1 | LL | <<<<<<< HEAD - | ^^^^^^^ between this marker and `=======` is the code that we're merging into + | ^^^^^^^ between this marker and `=======` is the code being merged into LL | bar, LL | ======= | ------- between this marker and `>>>>>>>` is the incoming code @@ -12,10 +12,6 @@ LL | >>>>>>> branch | = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers - = help: if you're having merge conflicts after pulling new code: - the top section is the remote code and the bottom section is the code you already had - if you're in the middle of a rebase: - the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased = note: for an explanation on these markers from the `git` documentation: visit From d84c2d67ddaa34df227453ab7fbea385b80fe1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 23 Dec 2025 19:29:02 +0000 Subject: [PATCH 9/9] More information for `git merge` and `git pull --no-rebase` --- .../rustc_parse/src/parser/diagnostics.rs | 21 ++++++++++++++++++- tests/run-make/git-merge-conflict/file.stderr | 2 +- .../run-make/git-merge-conflict/git/MERGE_MSG | 4 ++++ .../git-pull-merge-conflict/file.stderr | 19 +++++++++++++++++ .../run-make/git-pull-merge-conflict/git/HEAD | 1 + .../git-pull-merge-conflict/git/MERGE_HEAD | 1 + .../git-pull-merge-conflict/git/MERGE_MSG | 4 ++++ .../git-pull-merge-conflict/main.rust | 7 +++++++ .../run-make/git-pull-merge-conflict/rmake.rs | 10 +++++++++ 9 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/run-make/git-merge-conflict/git/MERGE_MSG create mode 100644 tests/run-make/git-pull-merge-conflict/file.stderr create mode 100644 tests/run-make/git-pull-merge-conflict/git/HEAD create mode 100644 tests/run-make/git-pull-merge-conflict/git/MERGE_HEAD create mode 100644 tests/run-make/git-pull-merge-conflict/git/MERGE_MSG create mode 100644 tests/run-make/git-pull-merge-conflict/main.rust create mode 100644 tests/run-make/git-pull-merge-conflict/rmake.rs diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index eaac92b96a227..757a14d756168 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -3099,9 +3099,23 @@ impl<'a> Parser<'a> { } if let Ok(head) = git("HEAD") && let Some(merging_to) = head.trim().strip_prefix("ref: refs/heads/") - && let Ok(_) = git("MERGE_HEAD") + && let Ok(msg) = git("MERGE_MSG") { merge_to = Some(merging_to.to_string()); + if let Some(line) = msg.lines().next() + && let Some(msg) = line.strip_prefix("Merge branch ") + { + let split: Vec<_> = msg.split(" into ").collect(); + onto_descr = Some(if let [msg, _branch] = &split[..] { + // `git merge` + // Merge branch 'main' into branch-name + format!("branch {msg}") + } else { + // `git pull --no-rebase` + // Merge branch `main` of github.com:user/repo + format!("remote branch {msg}") + }); + } } match (local_branch, local_sha, onto_descr, onto_sha, merge_to) { (Some(branch_name), _, Some(onto_descr), _, None) => { @@ -3136,6 +3150,11 @@ impl<'a> Parser<'a> { msg_start = format!("from branch '{merge_to}'"); msg_middle = format!("code that you're merging"); } + (None, None, Some(onto_descr), None, Some(merge_to)) => { + // `git merge from-branch` + msg_start = format!("from branch '{merge_to}'"); + msg_middle = format!("code from {onto_descr}"); + } _ => { // We're not in a `git merge`, `git rebase` or `git pull`. } diff --git a/tests/run-make/git-merge-conflict/file.stderr b/tests/run-make/git-merge-conflict/file.stderr index e906c926c7e90..c996c90cfd244 100644 --- a/tests/run-make/git-merge-conflict/file.stderr +++ b/tests/run-make/git-merge-conflict/file.stderr @@ -5,7 +5,7 @@ error: encountered diff marker | ^^^^^^^ between this marker and `=======` is the code from branch 'current-branch' 3 | Foo(u8), 4 | ======= - | ------- between this marker and `>>>>>>>` is the code that you're merging + | ------- between this marker and `>>>>>>>` is the code from remote branch 'main' of github.com:user/repo 5 | Bar(i8), 6 | >>>>>>> branch | ^^^^^^^ this marker concludes the conflict region diff --git a/tests/run-make/git-merge-conflict/git/MERGE_MSG b/tests/run-make/git-merge-conflict/git/MERGE_MSG new file mode 100644 index 0000000000000..d46c7ea0560b0 --- /dev/null +++ b/tests/run-make/git-merge-conflict/git/MERGE_MSG @@ -0,0 +1,4 @@ +Merge branch 'main' of github.com:user/repo + +# Conflicts: +# src/main.rs \ No newline at end of file diff --git a/tests/run-make/git-pull-merge-conflict/file.stderr b/tests/run-make/git-pull-merge-conflict/file.stderr new file mode 100644 index 0000000000000..f7f537611f952 --- /dev/null +++ b/tests/run-make/git-pull-merge-conflict/file.stderr @@ -0,0 +1,19 @@ +error: encountered diff marker + --> main.rust:2:1 + | +2 | <<<<<<< HEAD //~ ERROR encountered diff marker + | ^^^^^^^ between this marker and `=======` is the code from branch 'current-branch' +3 | Foo(u8), +4 | ======= + | ------- between this marker and `>>>>>>>` is the code from branch 'main' +5 | Bar(i8), +6 | >>>>>>> branch + | ^^^^^^^ this marker concludes the conflict region + | + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = note: for an explanation on these markers from the `git` documentation: + visit + +error: aborting due to 1 previous error + diff --git a/tests/run-make/git-pull-merge-conflict/git/HEAD b/tests/run-make/git-pull-merge-conflict/git/HEAD new file mode 100644 index 0000000000000..f381a9ddad020 --- /dev/null +++ b/tests/run-make/git-pull-merge-conflict/git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/current-branch diff --git a/tests/run-make/git-pull-merge-conflict/git/MERGE_HEAD b/tests/run-make/git-pull-merge-conflict/git/MERGE_HEAD new file mode 100644 index 0000000000000..527830201beb8 --- /dev/null +++ b/tests/run-make/git-pull-merge-conflict/git/MERGE_HEAD @@ -0,0 +1 @@ +SHA_FOR_THE_COMMIT_WE_ARE_MERGING diff --git a/tests/run-make/git-pull-merge-conflict/git/MERGE_MSG b/tests/run-make/git-pull-merge-conflict/git/MERGE_MSG new file mode 100644 index 0000000000000..cb478bcb8dfd3 --- /dev/null +++ b/tests/run-make/git-pull-merge-conflict/git/MERGE_MSG @@ -0,0 +1,4 @@ +Merge branch 'main' into branch-name + +# Conflicts: +# src/main.rs \ No newline at end of file diff --git a/tests/run-make/git-pull-merge-conflict/main.rust b/tests/run-make/git-pull-merge-conflict/main.rust new file mode 100644 index 0000000000000..45df6e3251d76 --- /dev/null +++ b/tests/run-make/git-pull-merge-conflict/main.rust @@ -0,0 +1,7 @@ +enum E { +<<<<<<< HEAD //~ ERROR encountered diff marker + Foo(u8), +======= + Bar(i8), +>>>>>>> branch +} diff --git a/tests/run-make/git-pull-merge-conflict/rmake.rs b/tests/run-make/git-pull-merge-conflict/rmake.rs new file mode 100644 index 0000000000000..0c6dc702bd4ac --- /dev/null +++ b/tests/run-make/git-pull-merge-conflict/rmake.rs @@ -0,0 +1,10 @@ +// Checks the output in cases where a manual `git merge` had conflicts. + +use run_make_support::{diff, rfs, rustc}; + +fn main() { + // We use `.rust` purely to avoid `fmt` check on a file with conflict markers + rfs::copy_dir_all("git", ".git"); + let file_out = rustc().input("main.rust").run_fail().stderr_utf8(); + diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); +}