From 81a8cfefcbc853cb6a111500524bbf09dea596d2 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Thu, 28 Apr 2022 20:58:41 -0700 Subject: [PATCH] cli: add a mode of pushing with branch taken from change ID This adds `jj git push --change ` which creates a branch with a name based on the revision's change ID, and then pushes that like with `--branch`. That can be useful so you don't have to manually add the branch (and come up with a name for it). The created branch behaves like any other branch, so it's possible to make it point to a commit with a different change ID. --- CHANGELOG.md | 4 ++++ src/commands.rs | 42 +++++++++++++++++++++++++++++------------- tests/test_git_push.rs | 8 +++++++- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc8ab6dd6..8d3e93e10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `jj git fetch` will now abandon unreferenced commits and rebase any local changes you had on top. +* `jj git push` gained a `--change ` argument. When that's used, it + will create a branch named after the revision's change ID, so you don't have + to create a branch yourself. + ### Fixed bugs * When rebasing a conflict where one side modified a file and the other side diff --git a/src/commands.rs b/src/commands.rs index 15dce3b68..94f5ed71c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1776,6 +1776,9 @@ struct GitPushArgs { /// Push only this branch #[clap(long)] branch: Option, + /// Push this commit by creating a branch based on its change ID + #[clap(long)] + change: Option, } /// Update repo with changes made in the underlying Git repo @@ -4738,12 +4741,31 @@ fn cmd_git_push( args: &GitPushArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let repo = workspace_command.repo(); + let repo = workspace_command.repo().clone(); + let mut tx = workspace_command.start_transaction("import git refs"); let mut branch_updates = HashMap::new(); if let Some(branch_name) = &args.branch { - if let Some(update) = branch_updates_for_push(repo, &args.remote, branch_name)? { - branch_updates.insert(branch_name, update); + if let Some(update) = + branch_updates_for_push(repo.as_repo_ref(), &args.remote, branch_name)? + { + branch_updates.insert(branch_name.clone(), update); + } else { + writeln!( + ui, + "Branch {}@{} already matches {}", + branch_name, &args.remote, branch_name + )?; + } + } else if let Some(change_str) = &args.change { + let commit = workspace_command.resolve_single_rev(ui, change_str)?; + let branch_name = format!("push-{}", commit.change_id().hex()); + tx.mut_repo() + .set_local_branch(branch_name.clone(), RefTarget::Normal(commit.id().clone())); + if let Some(update) = + branch_updates_for_push(tx.mut_repo().as_repo_ref(), &args.remote, &branch_name)? + { + branch_updates.insert(branch_name.clone(), update); } else { writeln!( ui, @@ -4772,7 +4794,7 @@ fn cmd_git_push( continue; } } - branch_updates.insert(branch_name, update); + branch_updates.insert(branch_name.clone(), update); } } } @@ -4828,25 +4850,19 @@ fn cmd_git_push( let git_repo = get_git_repo(repo.store())?; git::push_updates(&git_repo, &args.remote, &ref_updates) .map_err(|err| CommandError::UserError(err.to_string()))?; - let mut tx = workspace_command.start_transaction("import git refs"); git::import_refs(tx.mut_repo(), &git_repo)?; workspace_command.finish_transaction(ui, tx)?; Ok(()) } fn branch_updates_for_push( - repo: &Arc, + repo: RepoRef, remote_name: &str, branch_name: &str, ) -> Result, CommandError> { let maybe_branch_target = repo.view().get_branch(branch_name); - if maybe_branch_target.is_none() { - return Err(CommandError::UserError(format!( - "Branch {} doesn't exist", - branch_name - ))); - } - let branch_target = maybe_branch_target.unwrap(); + let branch_target = maybe_branch_target + .ok_or_else(|| CommandError::UserError(format!("Branch {} doesn't exist", branch_name)))?; let push_action = classify_branch_push_action(branch_target, remote_name); match push_action { diff --git a/tests/test_git_push.rs b/tests/test_git_push.rs index da2106102..7ac3cbd2f 100644 --- a/tests/test_git_push.rs +++ b/tests/test_git_push.rs @@ -49,6 +49,12 @@ fn test_git_push() { insta::assert_snapshot!(stderr, @r###" Error: Won't push open commit "###); + // When pushing with `--change`, won't push if it points to an open commit + let stderr = + test_env.jj_cmd_failure(&workspace_root, &["git", "push", "--change", "my-branch"]); + insta::assert_snapshot!(stderr, @r###" + Error: Won't push open commit + "###); // Try pushing a conflict std::fs::write(workspace_root.join("file"), "first").unwrap(); @@ -61,6 +67,6 @@ fn test_git_push() { test_env.jj_cmd_success(&workspace_root, &["close", "-m", "third"]); let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push"]); insta::assert_snapshot!(stderr, @r###" - Error: Won't push commit 28b5642cb786 since it has conflicts + Error: Won't push commit fcd0490f7df7 since it has conflicts "###); }