From eb78ae975825f5cf2ecb9cf58280d4d056b20901 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Tue, 17 Oct 2023 04:25:52 +0900 Subject: [PATCH] cli: show hints for branches rejected to push --- cli/src/commands/git.rs | 58 +++++++++++++++++++++++++++++--------- cli/tests/test_git_push.rs | 6 ++++ 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/cli/src/commands/git.rs b/cli/src/commands/git.rs index 6eaf53b1b..b56b29388 100644 --- a/cli/src/commands/git.rs +++ b/cli/src/commands/git.rs @@ -708,7 +708,7 @@ fn cmd_git_push( match classify_branch_update(branch_name, &remote, targets) { Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)), Ok(None) => {} - Err(message) => writeln!(ui.warning(), "{message}")?, + Err(reason) => reason.print(ui)?, } } tx_description = format!("push all branches to git remote {remote}"); @@ -720,7 +720,7 @@ fn cmd_git_push( match classify_branch_update(branch_name, &remote, targets) { Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)), Ok(None) => {} - Err(message) => writeln!(ui.warning(), "{message}")?, + Err(reason) => reason.print(ui)?, } } tx_description = format!("push all deleted branches to git remote {remote}"); @@ -743,7 +743,7 @@ fn cmd_git_push( ui.stderr(), "Branch {branch_name}@{remote} already matches {branch_name}", )?, - Err(message) => return Err(user_error(message)), + Err(reason) => return Err(reason.into()), } } @@ -794,7 +794,7 @@ fn cmd_git_push( ui.stderr(), "Branch {branch_name}@{remote} already matches {branch_name}", )?, - Err(message) => return Err(user_error(message)), + Err(reason) => return Err(reason.into()), } } @@ -837,7 +837,7 @@ fn cmd_git_push( match classify_branch_update(branch_name, &remote, targets) { Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)), Ok(None) => {} - Err(message) => writeln!(ui.warning(), "{message}")?, + Err(reason) => reason.print(ui)?, } } if (!args.revisions.is_empty() || use_default_revset) && branches_targeted.is_empty() { @@ -1006,21 +1006,53 @@ fn get_default_push_remote( } } +#[derive(Clone, Debug)] +struct RejectedBranchUpdateReason { + message: String, + hint: Option, +} + +impl RejectedBranchUpdateReason { + fn print(&self, ui: &Ui) -> io::Result<()> { + writeln!(ui.warning(), "{}", self.message)?; + if let Some(hint) = &self.hint { + writeln!(ui.hint(), "Hint: {hint}")?; + } + Ok(()) + } +} + +impl From for CommandError { + fn from(reason: RejectedBranchUpdateReason) -> Self { + let RejectedBranchUpdateReason { message, hint } = reason; + CommandError::UserError { message, hint } + } +} + fn classify_branch_update( branch_name: &str, remote_name: &str, targets: TrackingRefPair, -) -> Result, String> { +) -> Result, RejectedBranchUpdateReason> { let push_action = classify_branch_push_action(targets); match push_action { BranchPushAction::AlreadyMatches => Ok(None), - BranchPushAction::LocalConflicted => Err(format!("Branch {branch_name} is conflicted")), - BranchPushAction::RemoteConflicted => { - Err(format!("Branch {branch_name}@{remote_name} is conflicted")) - } - BranchPushAction::RemoteUntracked => Err(format!( - "Non-tracking remote branch {branch_name}@{remote_name} exists" - )), + BranchPushAction::LocalConflicted => Err(RejectedBranchUpdateReason { + message: format!("Branch {branch_name} is conflicted"), + hint: Some( + "Run `jj branch list` to inspect, and use `jj branch set` to fix it up.".to_owned(), + ), + }), + BranchPushAction::RemoteConflicted => Err(RejectedBranchUpdateReason { + message: format!("Branch {branch_name}@{remote_name} is conflicted"), + hint: Some("Run `jj git fetch` to update the conflicted remote branch.".to_owned()), + }), + BranchPushAction::RemoteUntracked => Err(RejectedBranchUpdateReason { + message: format!("Non-tracking remote branch {branch_name}@{remote_name} exists"), + hint: Some(format!( + "Run `jj branch track {branch_name}@{remote_name}` to import the remote branch." + )), + }), BranchPushAction::Update(update) => Ok(Some(update)), } } diff --git a/cli/tests/test_git_push.rs b/cli/tests/test_git_push.rs index 7deb4eb47..9ea026cdb 100644 --- a/cli/tests/test_git_push.rs +++ b/cli/tests/test_git_push.rs @@ -684,6 +684,7 @@ fn test_git_push_conflicting_branches() { insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" Branch branch2 is conflicted + Hint: Run `jj branch list` to inspect, and use `jj branch set` to fix it up. Nothing changed. "###); @@ -691,6 +692,7 @@ fn test_git_push_conflicting_branches() { let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push", "--branch", "branch2"]); insta::assert_snapshot!(stderr, @r###" Error: Branch branch2 is conflicted + Hint: Run `jj branch list` to inspect, and use `jj branch set` to fix it up. "###); // --all shouldn't be blocked by conflicting branch @@ -699,6 +701,7 @@ fn test_git_push_conflicting_branches() { insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" Branch branch2 is conflicted + Hint: Run `jj branch list` to inspect, and use `jj branch set` to fix it up. Branch changes to push to origin: Move branch branch1 from 45a3aa29e907 to fd1d63e031ea "###); @@ -709,6 +712,7 @@ fn test_git_push_conflicting_branches() { insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" Branch branch2 is conflicted + Hint: Run `jj branch list` to inspect, and use `jj branch set` to fix it up. Branch changes to push to origin: Move branch branch1 from fd1d63e031ea to 8263cf992d33 "###); @@ -742,6 +746,7 @@ fn test_git_push_moved_forward_untracked() { let (_stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push"]); insta::assert_snapshot!(stderr, @r###" Non-tracking remote branch branch1@origin exists + Hint: Run `jj branch track branch1@origin` to import the remote branch. Nothing changed. "###); } @@ -759,6 +764,7 @@ fn test_git_push_moved_sideways_untracked() { let (_stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push"]); insta::assert_snapshot!(stderr, @r###" Non-tracking remote branch branch1@origin exists + Hint: Run `jj branch track branch1@origin` to import the remote branch. Nothing changed. "###); }