diff --git a/lib/src/git.rs b/lib/src/git.rs index 11fc301e3..ec97024f1 100644 --- a/lib/src/git.rs +++ b/lib/src/git.rs @@ -206,7 +206,7 @@ pub fn import_some_refs( mut_repo.set_git_head_target(RefTarget::normal(head_commit_id)); } } else { - mut_repo.set_git_head_target(None); + mut_repo.set_git_head_target(RefTarget::absent()); } let mut changed_git_refs = BTreeMap::new(); @@ -253,7 +253,7 @@ pub fn import_some_refs( // TODO: or clean up invalid ref in case it was stored due to historical bug? let ref_name = parse_git_ref(&full_name).expect("stored git ref should be parsable"); if git_ref_filter(&ref_name) { - mut_repo.set_git_ref_target(&full_name, None); + mut_repo.set_git_ref_target(&full_name, RefTarget::absent()); changed_git_refs.insert(ref_name, (Some(target), None)); } else { pinned_git_heads.insert(ref_name, target.added_ids().cloned().collect()); @@ -463,7 +463,7 @@ pub fn export_some_refs( true }; if success { - mut_repo.set_git_ref_target(&git_ref_name, None); + mut_repo.set_git_ref_target(&git_ref_name, RefTarget::absent()); } else { failed_branches.push(parsed_ref_name); } @@ -536,10 +536,10 @@ pub fn remove_remote( .filter_map(|r| r.starts_with(&prefix).then(|| r.clone())) .collect_vec(); for branch in branches_to_delete { - mut_repo.set_remote_branch_target(&branch, remote_name, None); + mut_repo.set_remote_branch_target(&branch, remote_name, RefTarget::absent()); } for git_ref in git_refs_to_delete { - mut_repo.set_git_ref_target(&git_ref, None); + mut_repo.set_git_ref_target(&git_ref, RefTarget::absent()); } Ok(()) } @@ -568,7 +568,7 @@ pub fn rename_remote( }) .collect_vec(); for (old, new, target) in git_refs { - mut_repo.set_git_ref_target(&old, None); + mut_repo.set_git_ref_target(&old, RefTarget::absent()); mut_repo.set_git_ref_target(&new, Some(target)); } Ok(()) diff --git a/lib/src/op_store.rs b/lib/src/op_store.rs index 7a290e19d..d99517409 100644 --- a/lib/src/op_store.rs +++ b/lib/src/op_store.rs @@ -143,6 +143,19 @@ impl ContentHash for RefTarget { } impl RefTarget { + /// Creates non-conflicting target pointing to no commit. + pub fn absent() -> Option { + None + } + + /// Returns non-conflicting target pointing to no commit. + /// + /// This will typically be used in place of `None` returned by map lookup. + pub fn absent_ref() -> Option<&'static Self> { + // TODO: This will be static ref to Conflict::resolved(None). + None + } + /// Creates non-conflicting target pointing to a commit. pub fn normal(id: CommitId) -> Option { Some(RefTarget::Normal(id)) diff --git a/lib/src/refs.rs b/lib/src/refs.rs index 62087094c..23ef19bfc 100644 --- a/lib/src/refs.rs +++ b/lib/src/refs.rs @@ -187,7 +187,7 @@ mod tests { fn test_classify_branch_push_action_removed() { let commit_id1 = CommitId::from_hex("11"); let branch = BranchTarget { - local_target: None, + local_target: RefTarget::absent(), remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(commit_id1.clone()).unwrap(), }, diff --git a/lib/src/simple_op_store.rs b/lib/src/simple_op_store.rs index 622cbcdd0..df118cc76 100644 --- a/lib/src/simple_op_store.rs +++ b/lib/src/simple_op_store.rs @@ -396,7 +396,7 @@ mod tests { } }, "deleted".to_string() => BranchTarget { - local_target: None, + local_target: RefTarget::absent(), remote_targets: btreemap! { "origin".to_string() => branch_deleted_origin_target.unwrap(), } diff --git a/lib/src/view.rs b/lib/src/view.rs index 6a5e5fe3f..3c5d44e5b 100644 --- a/lib/src/view.rs +++ b/lib/src/view.rs @@ -183,7 +183,7 @@ impl View { fn remove_local_branch(&mut self, name: &str) { if let Some(branch) = self.data.branches.get_mut(name) { - branch.local_target = None; + branch.local_target = RefTarget::absent(); if branch.remote_targets.is_empty() { self.remove_branch(name); } diff --git a/lib/tests/test_git.rs b/lib/tests/test_git.rs index 430211dd9..b5a8d9094 100644 --- a/lib/tests/test_git.rs +++ b/lib/tests/test_git.rs @@ -888,7 +888,7 @@ fn test_import_refs_empty_git_repo() { assert_eq!(repo.view().branches().len(), 0); assert_eq!(repo.view().tags().len(), 0); assert_eq!(repo.view().git_refs().len(), 0); - assert_eq!(repo.view().git_head(), None); + assert_eq!(repo.view().git_head(), RefTarget::absent_ref()); } #[test] @@ -1143,7 +1143,7 @@ fn test_import_export_no_auto_local_branch() { git::import_refs(mut_repo, &git_repo, &git_settings).unwrap(); let expected_branch = BranchTarget { - local_target: None, + local_target: RefTarget::absent(), remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&git_commit)).unwrap(), }, @@ -1159,7 +1159,7 @@ fn test_import_export_no_auto_local_branch() { // Export the branch to git assert_eq!(git::export_refs(mut_repo, &git_repo), Ok(vec![])); - assert_eq!(mut_repo.get_git_ref("refs/heads/main"), None); + assert_eq!(mut_repo.get_git_ref("refs/heads/main"), RefTarget::absent()); } #[test] @@ -1250,7 +1250,7 @@ fn test_export_partial_failure() { // Now remove the `main` branch and make sure that the `main/sub` gets exported // even though it didn't change - mut_repo.set_local_branch_target("main", None); + mut_repo.set_local_branch_target("main", RefTarget::absent()); assert_eq!( git::export_refs(mut_repo, &git_repo), Ok(vec![ @@ -1313,7 +1313,7 @@ fn test_export_reexport_transitions() { // Make changes on the jj side for branch in ["AXA", "AXB", "AXX"] { - mut_repo.set_local_branch_target(branch, None); + mut_repo.set_local_branch_target(branch, RefTarget::absent()); } for branch in ["XAA", "XAB", "XAX"] { mut_repo.set_local_branch_target(branch, RefTarget::normal(commit_a.id().clone())); diff --git a/lib/tests/test_mut_repo.rs b/lib/tests/test_mut_repo.rs index 1c9cb5353..06d34e93e 100644 --- a/lib/tests/test_mut_repo.rs +++ b/lib/tests/test_mut_repo.rs @@ -490,8 +490,8 @@ fn test_has_changed(use_git: bool) { mut_repo.remove_public_head(commit2.id()); mut_repo.remove_head(commit2.id()); - mut_repo.set_local_branch_target("stable", None); - mut_repo.set_remote_branch_target("stable", "origin", None); + mut_repo.set_local_branch_target("stable", RefTarget::absent()); + mut_repo.set_remote_branch_target("stable", "origin", RefTarget::absent()); assert!(!mut_repo.has_changes()); mut_repo.add_head(&commit2); @@ -614,5 +614,8 @@ fn test_rename_remote(use_git: bool) { mut_repo.set_remote_branch_target("main", "origin", target.clone()); mut_repo.rename_remote("origin", "upstream"); assert_eq!(mut_repo.get_remote_branch("main", "upstream"), target); - assert_eq!(mut_repo.get_remote_branch("main", "origin"), None); + assert_eq!( + mut_repo.get_remote_branch("main", "origin"), + RefTarget::absent() + ); } diff --git a/lib/tests/test_refs.rs b/lib/tests/test_refs.rs index 643ce480d..3ac38b059 100644 --- a/lib/tests/test_refs.rs +++ b/lib/tests/test_refs.rs @@ -154,13 +154,13 @@ fn test_merge_ref_targets() { // Left removed assert_eq!( merge_ref_targets(index, None, target3.as_ref(), target3.as_ref()), - None + RefTarget::absent() ); // Right removed assert_eq!( merge_ref_targets(index, target3.as_ref(), target3.as_ref(), None), - None + RefTarget::absent() ); // Left removed, right moved forward diff --git a/lib/tests/test_revset.rs b/lib/tests/test_revset.rs index cd3c135d4..5d6ce36b1 100644 --- a/lib/tests/test_revset.rs +++ b/lib/tests/test_revset.rs @@ -1624,7 +1624,7 @@ fn test_evaluate_expression_git_refs(use_git: bool) { [commit3.id().clone(), commit4.id().clone()], ), ); - mut_repo.set_git_ref_target("refs/tags/tag2", None); + mut_repo.set_git_ref_target("refs/tags/tag2", RefTarget::absent()); assert_eq!( resolve_commit_ids(mut_repo, "git_refs()"), vec![ @@ -1713,7 +1713,7 @@ fn test_evaluate_expression_branches(use_git: bool) { [commit3.id().clone(), commit4.id().clone()], ), ); - mut_repo.set_local_branch_target("branch3", None); + mut_repo.set_local_branch_target("branch3", RefTarget::absent()); assert_eq!( resolve_commit_ids(mut_repo, "branches()"), vec![ @@ -1822,7 +1822,7 @@ fn test_evaluate_expression_remote_branches(use_git: bool) { [commit3.id().clone(), commit4.id().clone()], ), ); - mut_repo.set_remote_branch_target("branch3", "origin", None); + mut_repo.set_remote_branch_target("branch3", "origin", RefTarget::absent()); assert_eq!( resolve_commit_ids(mut_repo, "remote_branches()"), vec![ diff --git a/lib/tests/test_rewrite.rs b/lib/tests/test_rewrite.rs index 61b004200..653c8d94e 100644 --- a/lib/tests/test_rewrite.rs +++ b/lib/tests/test_rewrite.rs @@ -1313,7 +1313,7 @@ fn test_rebase_descendants_branch_delete_modify_abandon() { let mut tx = repo.start_transaction(&settings, "test"); tx.mut_repo().record_abandoned_commit(commit_b.id().clone()); tx.mut_repo().rebase_descendants(&settings).unwrap(); - assert_eq!(tx.mut_repo().get_local_branch("main"), None); + assert_eq!(tx.mut_repo().get_local_branch("main"), RefTarget::absent()); } #[test_case(false ; "local backend")] diff --git a/src/commands/branch.rs b/src/commands/branch.rs index ff11b1fe6..3e618c957 100644 --- a/src/commands/branch.rs +++ b/src/commands/branch.rs @@ -279,7 +279,8 @@ fn cmd_branch_delete( let branch_term = make_branch_term(names.iter().collect_vec().as_slice()); let mut tx = workspace_command.start_transaction(&format!("delete {branch_term}")); for branch_name in names.iter() { - tx.mut_repo().set_local_branch_target(branch_name, None); + tx.mut_repo() + .set_local_branch_target(branch_name, RefTarget::absent()); } tx.finish(ui)?; if names.len() > 1 { diff --git a/src/commands/operation.rs b/src/commands/operation.rs index 258bddffb..9e5892cfd 100644 --- a/src/commands/operation.rs +++ b/src/commands/operation.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use clap::Subcommand; -use jj_lib::op_store::{BranchTarget, RefTargetExt as _}; +use jj_lib::op_store::{BranchTarget, RefTarget, RefTargetExt as _}; use jj_lib::operation; use jj_lib::repo::Repo; @@ -187,7 +187,8 @@ fn view_with_desired_portions_restored( let local_target = new_view .branches .get(branch_name) - .and_then(|br| br.local_target.clone()); + .map(|br| br.local_target.clone()) + .unwrap_or_else(RefTarget::absent); let remote_targets = branch_source_view .branches .get(branch_name)