From 957c7b2b954ec9297dc6ee178739da7515d558d6 Mon Sep 17 00:00:00 2001 From: Shane Sveller Date: Fri, 29 Nov 2024 15:16:38 -0600 Subject: [PATCH] cli: git push: optionally describe commits that would fail push --- cli/src/commands/git/push.rs | 25 +++++++++++++++++++++++-- cli/tests/test_git_private_commits.rs | 19 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) 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..25eb53cbdf 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-denied"], + ); + 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();