diff --git a/CHANGELOG.md b/CHANGELOG.md index 131355fb2..96f5bdf0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * The new `jj git remote rename` command allows git remotes to be renamed in-place. +* `jj git push` will search `@-` for branches to push if `@` has none. + ## [0.5.1] - 2022-10-17 No changes (just trying to get automated GitHub release to work). diff --git a/src/commands.rs b/src/commands.rs index cc7cd3eb5..5e3e264ec 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -33,7 +33,7 @@ use jujutsu_lib::files::DiffLine; use jujutsu_lib::git::{GitFetchError, GitRefUpdate}; use jujutsu_lib::index::IndexEntry; use jujutsu_lib::matchers::{EverythingMatcher, Matcher}; -use jujutsu_lib::op_store::{RefTarget, WorkspaceId}; +use jujutsu_lib::op_store::{BranchTarget, RefTarget, WorkspaceId}; use jujutsu_lib::operation::Operation; use jujutsu_lib::refs::{classify_branch_push_action, BranchPushAction, BranchPushUpdate}; use jujutsu_lib::repo::{ReadonlyRepo, RepoRef}; @@ -899,9 +899,10 @@ struct GitCloneArgs { /// Push to a Git remote /// -/// By default, pushes any branches pointing to `@`. Use `--branch` to push a -/// specific branch. Use `--all` to push all branches. Use `--change` to -/// generate a branch name based on a specific commit's change ID. +/// By default, pushes any branches pointing to `@`, or `@-` if no branches +/// point to `@`. Use `--branch` to push a specific branch. Use `--all` to push +/// all branches. Use `--change` to generate a branch name based on a specific +/// commit's change ID. #[derive(clap::Args, Clone, Debug)] #[command(group(ArgGroup::new("what").args(&["branch", "all", "change"])))] struct GitPushArgs { @@ -4239,17 +4240,41 @@ fn cmd_git_push( )); } Some(checkout) => { - let desired_target = Some(RefTarget::Normal(checkout.clone())); - for (branch_name, branch_target) in workspace_command.repo().view().branches() { - if branch_target.local_target == desired_target { - let push_action = classify_branch_push_action(branch_target, &args.remote); - match push_action { - BranchPushAction::AlreadyMatches => {} - BranchPushAction::LocalConflicted => {} - BranchPushAction::RemoteConflicted => {} - BranchPushAction::Update(update) => { - branch_updates.push((branch_name.clone(), update)); - } + fn find_branches_targeting<'a>( + view: &'a View, + target: &RefTarget, + ) -> Vec<(&'a String, &'a BranchTarget)> { + view.branches() + .iter() + .filter(|(_, branch_target)| { + branch_target.local_target.as_ref() == Some(target) + }) + .collect() + } + + // Search for branches targeting @ + let mut branches = find_branches_targeting( + workspace_command.repo().view(), + &RefTarget::Normal(checkout.clone()), + ); + if branches.is_empty() { + // Try @- instead if it has exactly one parent, such as after `jj squash` + let commit = workspace_command.repo().store().get_commit(checkout)?; + if let [parent] = commit.parent_ids() { + branches = find_branches_targeting( + workspace_command.repo().view(), + &RefTarget::Normal(parent.clone()), + ); + } + } + for (branch_name, branch_target) in branches { + let push_action = classify_branch_push_action(branch_target, &args.remote); + match push_action { + BranchPushAction::AlreadyMatches => {} + BranchPushAction::LocalConflicted => {} + BranchPushAction::RemoteConflicted => {} + BranchPushAction::Update(update) => { + branch_updates.push((branch_name.clone(), update)); } } } diff --git a/tests/test_git_push.rs b/tests/test_git_push.rs index fd66195ce..7b10c7cd8 100644 --- a/tests/test_git_push.rs +++ b/tests/test_git_push.rs @@ -115,6 +115,23 @@ fn test_git_push_current_branch() { "###); } +#[test] +fn test_git_push_parent_branch() { + let (test_env, workspace_root) = set_up(); + test_env.jj_cmd_success(&workspace_root, &["edit", "branch1"]); + test_env.jj_cmd_success( + &workspace_root, + &["describe", "-m", "modified branch1 commit"], + ); + test_env.jj_cmd_success(&workspace_root, &["new"]); + let stdout = test_env.jj_cmd_success(&workspace_root, &["git", "push", "--dry-run"]); + insta::assert_snapshot!(stdout, @r###" + Branch changes to push to origin: + Force branch branch1 from a3ccc578ea7b to ad7201b22c46 + Dry-run requested, not pushing. + "###); +} + #[test] fn test_git_push_no_current_branch() { let (test_env, workspace_root) = set_up();