diff --git a/cli/src/commands/git/push.rs b/cli/src/commands/git/push.rs index d3608bf86f..c542e44e42 100644 --- a/cli/src/commands/git/push.rs +++ b/cli/src/commands/git/push.rs @@ -140,6 +140,9 @@ pub struct GitPushArgs { /// Only display what will change on the remote #[arg(long)] dry_run: bool, + /// Display per-commit reasons for denied pushes + #[arg(long)] + explain_denied: bool, } fn make_bookmark_term(bookmark_names: &[impl fmt::Display]) -> String { @@ -402,15 +405,33 @@ fn validate_commits_ready_to_push( if commit.has_conflict()? { reasons.push("it has conflicts"); } + let mut exposition = vec![]; if !args.allow_private && is_private(commit.id())? { reasons.push("it is private"); + + if args.explain_denied { + exposition.push(format!( + "commit {}, description: '{}'", + short_commit_hash(commit.id()), + commit + .description() + .lines() + .next() + .expect("commit should have at least one line with private description") + )); + } } if !reasons.is_empty() { - return Err(user_error(format!( + let mut message = format!( "Won't push commit {} since {}", short_commit_hash(commit.id()), reasons.join(" and ") - ))); + ); + if args.explain_denied { + message.push('\n'); + message.push_str(&exposition.join("\n")); + } + return Err(user_error(message)); } } Ok(()) diff --git a/cli/tests/test_git_private_commits.rs b/cli/tests/test_git_private_commits.rs index fadd5ca05b..4ac0faeca5 100644 --- a/cli/tests/test_git_private_commits.rs +++ b/cli/tests/test_git_private_commits.rs @@ -104,6 +104,25 @@ fn test_git_private_commits_block_pushing() { "#); } +#[test] +fn test_git_private_commits_block_pushing_with_explanation() { + let (test_env, workspace_root) = set_up(); + + test_env.jj_cmd_ok(&workspace_root, &["new", "main", "-m=private 1"]); + test_env.jj_cmd_ok(&workspace_root, &["bookmark", "set", "main"]); + + // Will not push when a pushed commit is contained in git.private-commits + test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#); + let stderr = test_env.jj_cmd_failure( + &workspace_root, + &["git", "push", "--all", "--explain-failed"], + ); + insta::assert_snapshot!(stderr, @r###" + Error: Won't push commit aa3058ff8663 since it is private + commit aa3058ff8663, description: 'private 1' + "###); +} + #[test] fn test_git_private_commits_can_be_overridden() { let (test_env, workspace_root) = set_up();