diff --git a/lib/tests/test_git.rs b/lib/tests/test_git.rs index f4e36c7151..2b1534106c 100644 --- a/lib/tests/test_git.rs +++ b/lib/tests/test_git.rs @@ -32,13 +32,13 @@ use jj_lib::git::{ use jj_lib::git_backend::GitBackend; use jj_lib::object_id::ObjectId; use jj_lib::op_store::{BranchTarget, RefTarget, RemoteRef, RemoteRefState}; -use jj_lib::refs::BranchPushUpdate; +use jj_lib::refs::{merge_ref_targets, BranchPushUpdate}; use jj_lib::repo::{MutableRepo, ReadonlyRepo, Repo}; use jj_lib::settings::{GitSettings, UserSettings}; use jj_lib::signing::Signer; use jj_lib::str_util::StringPattern; use jj_lib::workspace::Workspace; -use maplit::{btreemap, hashset}; +use maplit::{btreemap, hashmap, hashset}; use tempfile::TempDir; use testutils::{ commit_transactions, create_random_commit, load_repo_at_head, write_random_commit, TestRepo, @@ -2789,31 +2789,77 @@ fn test_push_branches_unexpectedly_moved_sideways_on_remote() { let sideways_commit = write_random_commit(tx.mut_repo(), &settings); setup.jj_repo = tx.commit("test"); let mut tx = setup.jj_repo.start_transaction(&settings); + let index = setup.jj_repo.index(); - // The main branch is actually at `initial_commit` on the remote. If we expected + // The main branch is actually at `main_commit` on the remote. If we expected // it to be at `sideways_commit`, we cannot delete it or move it anywhere else. // However, "moving" it to the same place it already is is OK, following the // behavior in `test_merge_ref_targets`. + let id_dict = hashmap! { + setup.main_commit.id().clone() => "main", + setup.parent_of_main_commit.id().clone() => "parent_of_main", + setup.child_of_main_commit.id().clone() => "child_of_main", + sideways_commit.id().clone() => "sideways", + }; - let targets = GitBranchPushTargets { - branch_updates: vec![( - "main".to_owned(), - BranchPushUpdate { - old_target: Some(sideways_commit.id().clone()), - new_target: None, + let merge_with_main_commit = |r: &RefTarget| { + merge_ref_targets( + index, + r, + &RefTarget::normal(sideways_commit.id().clone()), + &RefTarget::normal(setup.main_commit.id().clone()), + ) + }; + let assert_merge_resolves_to_ref = |r: &RefTarget| assert_eq!(r, &merge_with_main_commit(r)); + let assert_merge_fails_to_resolve_to_ref = |r: &RefTarget| { + let result = merge_with_main_commit(r); + assert_ne!(r, &result); + result.as_merge().map(|maybe_id: &Option| { + maybe_id + .as_ref() + .map(|id: &CommitId| id_dict.get(id).unwrap_or(&"unknown commit")) + }) + }; + + let mut attempt_push = |target: Option| { + let targets = GitBranchPushTargets { + branch_updates: vec![( + "main".to_owned(), + BranchPushUpdate { + old_target: Some(sideways_commit.id().clone()), + new_target: target, + }, + )], + force_pushed_branches: hashset! { + "main".to_owned(), }, - )], - force_pushed_branches: hashset! { - "main".to_owned(), - }, + }; + git::push_branches( + tx.mut_repo(), + &get_git_repo(&setup.jj_repo), + "origin", + &targets, + git::RemoteCallbacks::default(), + ) }; - let result = git::push_branches( - tx.mut_repo(), - &get_git_repo(&setup.jj_repo), - "origin", - &targets, - git::RemoteCallbacks::default(), + + assert_debug_snapshot!( + assert_merge_fails_to_resolve_to_ref(RefTarget::absent_ref()), + @r###" + Conflicted( + [ + None, + Some( + "sideways", + ), + Some( + "main", + ), + ], + ) + "### ); + let result = attempt_push(None); assert_debug_snapshot!(result, @r###" Err( RefInUnexpectedLocation( @@ -2826,25 +2872,57 @@ fn test_push_branches_unexpectedly_moved_sideways_on_remote() { ) "###); - let targets = GitBranchPushTargets { - branch_updates: vec![( - "main".to_owned(), - BranchPushUpdate { - old_target: Some(sideways_commit.id().clone()), - new_target: Some(setup.child_of_main_commit.id().clone()), - }, - )], - force_pushed_branches: hashset! { - "main".to_owned(), - }, - }; - let result = git::push_branches( - tx.mut_repo(), - &get_git_repo(&setup.jj_repo), - "origin", - &targets, - git::RemoteCallbacks::default(), + assert_debug_snapshot!( + assert_merge_fails_to_resolve_to_ref(&RefTarget::normal( + setup.child_of_main_commit.id().clone(), + )), + @r###" + Conflicted( + [ + Some( + "child_of_main", + ), + Some( + "sideways", + ), + Some( + "main", + ), + ], + ) + "### + ); + let result = attempt_push(Some(setup.child_of_main_commit.id().clone())); + assert_debug_snapshot!(result, @r###" + Err( + RefInUnexpectedLocation( + [ + Some( + "refs/heads/main", + ), + ], + ), + ) + "###); + + // Here, the local branch hasn't moved from `sideways_commit`, but it moved to + // `main` on the remote. So, the conflict resolves to `main`. + // + // `jj` should not actually attempt a push in this case, but if it did, the push + // should fail. + assert_debug_snapshot!( + assert_merge_fails_to_resolve_to_ref(&RefTarget::normal( + sideways_commit.id().clone() + )), + @r###" + Resolved( + Some( + "main", + ), + ) + "### ); + let result = attempt_push(Some(sideways_commit.id().clone())); assert_debug_snapshot!(result, @r###" Err( RefInUnexpectedLocation( @@ -2920,6 +2998,14 @@ fn test_push_branches_unexpectedly_moved_sideways_on_remote() { "###); // Moving the branch to the same place it already is is OK. + let result = merge_ref_targets( + index, + &RefTarget::normal(setup.main_commit.id().clone()), + &RefTarget::normal(sideways_commit.id().clone()), + &RefTarget::normal(setup.main_commit.id().clone()), + ); + assert_eq!(result, RefTarget::normal(setup.main_commit.id().clone())); + dbg!(result); let targets = GitBranchPushTargets { branch_updates: vec![( "main".to_owned(),