ok/jj
1
0
Fork 0
forked from mirrors/jj

cli: add a mode of pushing with branch taken from change ID

This adds `jj git push --change <revision>` 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.
This commit is contained in:
Martin von Zweigbergk 2022-04-28 20:58:41 -07:00 committed by Martin von Zweigbergk
parent bc75b08f4b
commit 81a8cfefcb
3 changed files with 40 additions and 14 deletions

View file

@ -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 `jj git fetch` will now abandon unreferenced commits and rebase any local
changes you had on top. changes you had on top.
* `jj git push` gained a `--change <revision>` 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 ### Fixed bugs
* When rebasing a conflict where one side modified a file and the other side * When rebasing a conflict where one side modified a file and the other side

View file

@ -1776,6 +1776,9 @@ struct GitPushArgs {
/// Push only this branch /// Push only this branch
#[clap(long)] #[clap(long)]
branch: Option<String>, branch: Option<String>,
/// Push this commit by creating a branch based on its change ID
#[clap(long)]
change: Option<String>,
} }
/// Update repo with changes made in the underlying Git repo /// Update repo with changes made in the underlying Git repo
@ -4738,12 +4741,31 @@ fn cmd_git_push(
args: &GitPushArgs, args: &GitPushArgs,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?; 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(); let mut branch_updates = HashMap::new();
if let Some(branch_name) = &args.branch { if let Some(branch_name) = &args.branch {
if let Some(update) = branch_updates_for_push(repo, &args.remote, branch_name)? { if let Some(update) =
branch_updates.insert(branch_name, 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 { } else {
writeln!( writeln!(
ui, ui,
@ -4772,7 +4794,7 @@ fn cmd_git_push(
continue; 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())?; let git_repo = get_git_repo(repo.store())?;
git::push_updates(&git_repo, &args.remote, &ref_updates) git::push_updates(&git_repo, &args.remote, &ref_updates)
.map_err(|err| CommandError::UserError(err.to_string()))?; .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)?; git::import_refs(tx.mut_repo(), &git_repo)?;
workspace_command.finish_transaction(ui, tx)?; workspace_command.finish_transaction(ui, tx)?;
Ok(()) Ok(())
} }
fn branch_updates_for_push( fn branch_updates_for_push(
repo: &Arc<ReadonlyRepo>, repo: RepoRef,
remote_name: &str, remote_name: &str,
branch_name: &str, branch_name: &str,
) -> Result<Option<BranchPushUpdate>, CommandError> { ) -> Result<Option<BranchPushUpdate>, CommandError> {
let maybe_branch_target = repo.view().get_branch(branch_name); let maybe_branch_target = repo.view().get_branch(branch_name);
if maybe_branch_target.is_none() { let branch_target = maybe_branch_target
return Err(CommandError::UserError(format!( .ok_or_else(|| CommandError::UserError(format!("Branch {} doesn't exist", branch_name)))?;
"Branch {} doesn't exist",
branch_name
)));
}
let branch_target = maybe_branch_target.unwrap();
let push_action = classify_branch_push_action(branch_target, remote_name); let push_action = classify_branch_push_action(branch_target, remote_name);
match push_action { match push_action {

View file

@ -49,6 +49,12 @@ fn test_git_push() {
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Error: Won't push open commit 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 // Try pushing a conflict
std::fs::write(workspace_root.join("file"), "first").unwrap(); 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"]); test_env.jj_cmd_success(&workspace_root, &["close", "-m", "third"]);
let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push"]); let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push"]);
insta::assert_snapshot!(stderr, @r###" 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
"###); "###);
} }