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:
parent
bc75b08f4b
commit
81a8cfefcb
3 changed files with 40 additions and 14 deletions
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue