// Copyright 2020 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use jj_lib::backend::CommitId;use jj_lib::index::Index;use jj_lib::op_store::RefTarget;use jj_lib::op_store::RemoteRef;use jj_lib::op_store::RemoteRefState;use jj_lib::ref_name::RefName;use jj_lib::ref_name::RemoteName;use jj_lib::ref_name::RemoteRefSymbol;use jj_lib::ref_name::WorkspaceName;use jj_lib::ref_name::WorkspaceNameBuf;use jj_lib::repo::Repo as _;use jj_lib::rewrite::RebaseOptions;use maplit::hashset;use pollster::FutureExt as _;use testutils::TestRepo;use testutils::assert_rebased_onto;use testutils::create_random_commit;use testutils::create_random_tree;use testutils::rebase_descendants_with_options_return_map;use testutils::write_random_commit;use testutils::write_random_commit_with_parents; fn remote_symbol<'a, N, M>(name: &'a N, remote: &'a M) -> RemoteRefSymbol<'a>where N: AsRef<RefName> + ?Sized, M: AsRef<RemoteName> + ?Sized,{ RemoteRefSymbol { name: name.as_ref(), remote: remote.as_ref(), }} fn index_has_id(index: &dyn Index, commit_id: &CommitId) -> bool { index.has_id(commit_id).unwrap()} #[test]fn test_edit() { // Test that MutableRepo::edit() uses the requested commit (not a new child) let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let ws_name = WorkspaceName::DEFAULT.to_owned(); tx.repo_mut().edit(ws_name.clone(), &wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id()));} #[test]fn test_checkout() { // Test that MutableRepo::check_out() creates a child let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit_parent = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let ws_name = WorkspaceName::DEFAULT.to_owned(); let wc_commit = tx .repo_mut() .check_out(ws_name.clone(), &wc_commit_parent) .unwrap(); assert_eq!(wc_commit.tree_id(), wc_commit_parent.tree_id()); assert_eq!(wc_commit.parent_ids().len(), 1); assert_eq!(&wc_commit.parent_ids()[0], wc_commit_parent.id()); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id()));} #[test]fn test_edit_previous_not_empty() { // Test that MutableRepo::edit() does not usually abandon the previous // commit. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = write_random_commit(mut_repo); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id()));} #[test]fn test_edit_previous_empty() { // Test that MutableRepo::edit() abandons the previous commit if it was // empty. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(!mut_repo.view().heads().contains(old_wc_commit.id()));} #[test]fn test_edit_previous_empty_merge() { // Test that MutableRepo::edit() abandons the previous commit if it was // an empty merge commit. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_parent1 = write_random_commit(mut_repo); let old_parent2 = write_random_commit(mut_repo); let empty_tree = repo.store().root_commit().tree().unwrap(); let old_parent_tree = old_parent1 .tree() .unwrap() .merge(empty_tree, old_parent2.tree().unwrap()) .block_on() .unwrap(); let old_wc_commit = mut_repo .new_commit( vec![old_parent1.id().clone(), old_parent2.id().clone()], repo.store().empty_merged_tree_id(), ) .set_tree_id(old_parent_tree.id()) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(!mut_repo.view().heads().contains(old_wc_commit.id()));} #[test]fn test_edit_previous_empty_with_description() { // Test that MutableRepo::edit() does not abandon the previous commit if it // has a non-empty description. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .set_description("not empty") .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id()));} #[test]fn test_edit_previous_empty_with_local_bookmark() { // Test that MutableRepo::edit() does not abandon the previous commit if it // is pointed by local bookmark. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); mut_repo.set_local_bookmark_target("b".as_ref(), RefTarget::normal(old_wc_commit.id().clone())); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id()));} #[test]fn test_edit_previous_empty_with_other_workspace() { // Test that MutableRepo::edit() does not abandon the previous commit if it // is pointed by another workspace let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let other_ws_name = WorkspaceNameBuf::from("other"); mut_repo .edit(other_ws_name.clone(), &old_wc_commit) .unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id()));} #[test]fn test_edit_previous_empty_non_head() { // Test that MutableRepo::edit() does not abandon the previous commit if it // was empty and is not a head let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); let old_child = mut_repo .new_commit( vec![old_wc_commit.id().clone()], old_wc_commit.tree_id().clone(), ) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert_eq!( *mut_repo.view().heads(), hashset! {old_child.id().clone(), new_wc_commit.id().clone()} );} #[test]fn test_edit_initial() { // Test that MutableRepo::edit() can be used on the initial working-copy commit // in a workspace let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let ws_name = WorkspaceNameBuf::from("new-workspace"); tx.repo_mut().edit(ws_name.clone(), &wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id()));} #[test]fn test_edit_hidden_commit() { // Test that MutableRepo::edit() edits a hidden commit and updates // the view head ids. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit = write_random_commit(tx.repo_mut()); // Intentionally not doing tx.commit, so the commit id is not tracked // in the view head ids. let mut tx = repo.start_transaction(); let ws_name = WorkspaceName::DEFAULT.to_owned(); tx.repo_mut().edit(ws_name.clone(), &wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id())); assert_eq!(*repo.view().heads(), hashset! {wc_commit.id().clone()});} #[test]fn test_add_head_success() { // Test that MutableRepo::add_head() adds the head, and that it's still there // after commit. It should also be indexed. let test_repo = TestRepo::init(); let repo = &test_repo.repo; // Create a commit outside of the repo by using a temporary transaction. Then // add that as a head. let mut tx = repo.start_transaction(); let new_commit = write_random_commit(tx.repo_mut()); drop(tx); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); assert!(!mut_repo.view().heads().contains(new_commit.id())); assert!(!index_has_id(mut_repo.index(), new_commit.id())); mut_repo.add_head(&new_commit).unwrap(); assert!(mut_repo.view().heads().contains(new_commit.id())); assert!(index_has_id(mut_repo.index(), new_commit.id())); let repo = tx.commit("test").unwrap(); assert!(repo.view().heads().contains(new_commit.id())); assert!(index_has_id(repo.index(), new_commit.id()));} #[test]fn test_add_head_ancestor() { // Test that MutableRepo::add_head() does not add a head if it's an ancestor of // an existing head. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let commit1 = write_random_commit(tx.repo_mut()); let commit2 = write_random_commit_with_parents(tx.repo_mut(), &[&commit1]); let commit3 = write_random_commit_with_parents(tx.repo_mut(), &[&commit2]); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().heads(), &hashset! {commit3.id().clone()}); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); mut_repo.add_head(&commit1).unwrap(); assert_eq!(repo.view().heads(), &hashset! {commit3.id().clone()});} #[test]fn test_add_head_not_immediate_child() { // Test that MutableRepo::add_head() can be used for adding a head that is not // an immediate child of a current head. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let initial = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); // Create some commits outside of the repo by using a temporary transaction. // Then add one of them as a head. let mut tx = repo.start_transaction(); let rewritten = create_random_commit(tx.repo_mut()) .set_change_id(initial.change_id().clone()) .set_predecessors(vec![initial.id().clone()]) .write() .unwrap(); let child = write_random_commit_with_parents(tx.repo_mut(), &[&rewritten]); drop(tx); assert_eq!(repo.view().heads(), &hashset! {initial.id().clone()}); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); mut_repo.add_head(&child).unwrap(); assert_eq!( mut_repo.view().heads(), &hashset! {initial.id().clone(), child.id().clone()} ); assert!(index_has_id(mut_repo.index(), initial.id())); assert!(index_has_id(mut_repo.index(), rewritten.id())); assert!(index_has_id(mut_repo.index(), child.id()));} #[test]fn test_remove_head() { // Test that MutableRepo::remove_head() removes the head, and that it's still // removed after commit. It should remain in the index, since we otherwise would // have to reindex everything. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let commit1 = write_random_commit(tx.repo_mut()); let commit2 = write_random_commit_with_parents(tx.repo_mut(), &[&commit1]); let commit3 = write_random_commit_with_parents(tx.repo_mut(), &[&commit2]); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); assert!(mut_repo.view().heads().contains(commit3.id())); mut_repo.remove_head(commit3.id()); let heads = mut_repo.view().heads().clone(); assert!(!heads.contains(commit3.id())); assert!(!heads.contains(commit2.id())); assert!(!heads.contains(commit1.id())); assert!(index_has_id(mut_repo.index(), commit1.id())); assert!(index_has_id(mut_repo.index(), commit2.id())); assert!(index_has_id(mut_repo.index(), commit3.id())); let repo = tx.commit("test").unwrap(); let heads = repo.view().heads().clone(); assert!(!heads.contains(commit3.id())); assert!(!heads.contains(commit2.id())); assert!(!heads.contains(commit1.id())); assert!(index_has_id(repo.index(), commit1.id())); assert!(index_has_id(repo.index(), commit2.id())); assert!(index_has_id(repo.index(), commit3.id()));} #[test]fn test_has_changed() { // Test that MutableRepo::has_changed() reports changes iff the view has changed // (e.g. not after setting a bookmark to point to where it was already // pointing). let test_repo = TestRepo::init(); let repo = &test_repo.repo; let normal_remote_ref = |id: &CommitId| RemoteRef { target: RefTarget::normal(id.clone()), state: RemoteRefState::Tracked, // doesn't matter }; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let commit1 = write_random_commit(mut_repo); let commit2 = write_random_commit(mut_repo); mut_repo.remove_head(commit2.id()); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo .set_wc_commit(ws_name.clone(), commit1.id().clone()) .unwrap(); mut_repo.set_local_bookmark_target("main".as_ref(), RefTarget::normal(commit1.id().clone())); mut_repo.set_remote_bookmark( remote_symbol("main", "origin"), normal_remote_ref(commit1.id()), ); let repo = tx.commit("test").unwrap(); // Test the setup assert_eq!(repo.view().heads(), &hashset! {commit1.id().clone()}); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); mut_repo.add_head(&commit1).unwrap(); mut_repo .set_wc_commit(ws_name.clone(), commit1.id().clone()) .unwrap(); mut_repo.set_local_bookmark_target("main".as_ref(), RefTarget::normal(commit1.id().clone()));Lines 1–500 of 769. Load more
// Copyright 2020 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use jj_lib::backend::CommitId; use jj_lib::index::Index; use jj_lib::op_store::RefTarget; use jj_lib::op_store::RemoteRef; use jj_lib::op_store::RemoteRefState; use jj_lib::ref_name::RefName; use jj_lib::ref_name::RemoteName; use jj_lib::ref_name::RemoteRefSymbol; use jj_lib::ref_name::WorkspaceName; use jj_lib::ref_name::WorkspaceNameBuf; use jj_lib::repo::Repo as _; use jj_lib::rewrite::RebaseOptions; use maplit::hashset; use pollster::FutureExt as _; use testutils::TestRepo; use testutils::assert_rebased_onto; use testutils::create_random_commit; use testutils::create_random_tree; use testutils::rebase_descendants_with_options_return_map; use testutils::write_random_commit; use testutils::write_random_commit_with_parents; fn remote_symbol<'a, N, M>(name: &'a N, remote: &'a M) -> RemoteRefSymbol<'a> where N: AsRef<RefName> + ?Sized, M: AsRef<RemoteName> + ?Sized, { RemoteRefSymbol { name: name.as_ref(), remote: remote.as_ref(), } } fn index_has_id(index: &dyn Index, commit_id: &CommitId) -> bool { index.has_id(commit_id).unwrap() } #[test] fn test_edit() { // Test that MutableRepo::edit() uses the requested commit (not a new child) let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let ws_name = WorkspaceName::DEFAULT.to_owned(); tx.repo_mut().edit(ws_name.clone(), &wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id())); } #[test] fn test_checkout() { // Test that MutableRepo::check_out() creates a child let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit_parent = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let ws_name = WorkspaceName::DEFAULT.to_owned(); let wc_commit = tx .repo_mut() .check_out(ws_name.clone(), &wc_commit_parent) .unwrap(); assert_eq!(wc_commit.tree_id(), wc_commit_parent.tree_id()); assert_eq!(wc_commit.parent_ids().len(), 1); assert_eq!(&wc_commit.parent_ids()[0], wc_commit_parent.id()); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id())); } #[test] fn test_edit_previous_not_empty() { // Test that MutableRepo::edit() does not usually abandon the previous // commit. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = write_random_commit(mut_repo); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id())); } #[test] fn test_edit_previous_empty() { // Test that MutableRepo::edit() abandons the previous commit if it was // empty. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(!mut_repo.view().heads().contains(old_wc_commit.id())); } #[test] fn test_edit_previous_empty_merge() { // Test that MutableRepo::edit() abandons the previous commit if it was // an empty merge commit. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_parent1 = write_random_commit(mut_repo); let old_parent2 = write_random_commit(mut_repo); let empty_tree = repo.store().root_commit().tree().unwrap(); let old_parent_tree = old_parent1 .tree() .unwrap() .merge(empty_tree, old_parent2.tree().unwrap()) .block_on() .unwrap(); let old_wc_commit = mut_repo .new_commit( vec![old_parent1.id().clone(), old_parent2.id().clone()], repo.store().empty_merged_tree_id(), ) .set_tree_id(old_parent_tree.id()) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(!mut_repo.view().heads().contains(old_wc_commit.id())); } #[test] fn test_edit_previous_empty_with_description() { // Test that MutableRepo::edit() does not abandon the previous commit if it // has a non-empty description. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .set_description("not empty") .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id())); } #[test] fn test_edit_previous_empty_with_local_bookmark() { // Test that MutableRepo::edit() does not abandon the previous commit if it // is pointed by local bookmark. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); mut_repo.set_local_bookmark_target("b".as_ref(), RefTarget::normal(old_wc_commit.id().clone())); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id())); } #[test] fn test_edit_previous_empty_with_other_workspace() { // Test that MutableRepo::edit() does not abandon the previous commit if it // is pointed by another workspace let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let other_ws_name = WorkspaceNameBuf::from("other"); mut_repo .edit(other_ws_name.clone(), &old_wc_commit) .unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert!(mut_repo.view().heads().contains(old_wc_commit.id())); } #[test] fn test_edit_previous_empty_non_head() { // Test that MutableRepo::edit() does not abandon the previous commit if it // was empty and is not a head let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let old_wc_commit = mut_repo .new_commit( vec![repo.store().root_commit_id().clone()], repo.store().empty_merged_tree_id(), ) .write() .unwrap(); let old_child = mut_repo .new_commit( vec![old_wc_commit.id().clone()], old_wc_commit.tree_id().clone(), ) .write() .unwrap(); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo.edit(ws_name.clone(), &old_wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let new_wc_commit = write_random_commit(mut_repo); mut_repo.edit(ws_name, &new_wc_commit).unwrap(); mut_repo.rebase_descendants().unwrap(); assert_eq!( *mut_repo.view().heads(), hashset! {old_child.id().clone(), new_wc_commit.id().clone()} ); } #[test] fn test_edit_initial() { // Test that MutableRepo::edit() can be used on the initial working-copy commit // in a workspace let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let ws_name = WorkspaceNameBuf::from("new-workspace"); tx.repo_mut().edit(ws_name.clone(), &wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id())); } #[test] fn test_edit_hidden_commit() { // Test that MutableRepo::edit() edits a hidden commit and updates // the view head ids. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let wc_commit = write_random_commit(tx.repo_mut()); // Intentionally not doing tx.commit, so the commit id is not tracked // in the view head ids. let mut tx = repo.start_transaction(); let ws_name = WorkspaceName::DEFAULT.to_owned(); tx.repo_mut().edit(ws_name.clone(), &wc_commit).unwrap(); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().get_wc_commit_id(&ws_name), Some(wc_commit.id())); assert_eq!(*repo.view().heads(), hashset! {wc_commit.id().clone()}); } #[test] fn test_add_head_success() { // Test that MutableRepo::add_head() adds the head, and that it's still there // after commit. It should also be indexed. let test_repo = TestRepo::init(); let repo = &test_repo.repo; // Create a commit outside of the repo by using a temporary transaction. Then // add that as a head. let mut tx = repo.start_transaction(); let new_commit = write_random_commit(tx.repo_mut()); drop(tx); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); assert!(!mut_repo.view().heads().contains(new_commit.id())); assert!(!index_has_id(mut_repo.index(), new_commit.id())); mut_repo.add_head(&new_commit).unwrap(); assert!(mut_repo.view().heads().contains(new_commit.id())); assert!(index_has_id(mut_repo.index(), new_commit.id())); let repo = tx.commit("test").unwrap(); assert!(repo.view().heads().contains(new_commit.id())); assert!(index_has_id(repo.index(), new_commit.id())); } #[test] fn test_add_head_ancestor() { // Test that MutableRepo::add_head() does not add a head if it's an ancestor of // an existing head. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let commit1 = write_random_commit(tx.repo_mut()); let commit2 = write_random_commit_with_parents(tx.repo_mut(), &[&commit1]); let commit3 = write_random_commit_with_parents(tx.repo_mut(), &[&commit2]); let repo = tx.commit("test").unwrap(); assert_eq!(repo.view().heads(), &hashset! {commit3.id().clone()}); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); mut_repo.add_head(&commit1).unwrap(); assert_eq!(repo.view().heads(), &hashset! {commit3.id().clone()}); } #[test] fn test_add_head_not_immediate_child() { // Test that MutableRepo::add_head() can be used for adding a head that is not // an immediate child of a current head. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let initial = write_random_commit(tx.repo_mut()); let repo = tx.commit("test").unwrap(); // Create some commits outside of the repo by using a temporary transaction. // Then add one of them as a head. let mut tx = repo.start_transaction(); let rewritten = create_random_commit(tx.repo_mut()) .set_change_id(initial.change_id().clone()) .set_predecessors(vec![initial.id().clone()]) .write() .unwrap(); let child = write_random_commit_with_parents(tx.repo_mut(), &[&rewritten]); drop(tx); assert_eq!(repo.view().heads(), &hashset! {initial.id().clone()}); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); mut_repo.add_head(&child).unwrap(); assert_eq!( mut_repo.view().heads(), &hashset! {initial.id().clone(), child.id().clone()} ); assert!(index_has_id(mut_repo.index(), initial.id())); assert!(index_has_id(mut_repo.index(), rewritten.id())); assert!(index_has_id(mut_repo.index(), child.id())); } #[test] fn test_remove_head() { // Test that MutableRepo::remove_head() removes the head, and that it's still // removed after commit. It should remain in the index, since we otherwise would // have to reindex everything. let test_repo = TestRepo::init(); let repo = &test_repo.repo; let mut tx = repo.start_transaction(); let commit1 = write_random_commit(tx.repo_mut()); let commit2 = write_random_commit_with_parents(tx.repo_mut(), &[&commit1]); let commit3 = write_random_commit_with_parents(tx.repo_mut(), &[&commit2]); let repo = tx.commit("test").unwrap(); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); assert!(mut_repo.view().heads().contains(commit3.id())); mut_repo.remove_head(commit3.id()); let heads = mut_repo.view().heads().clone(); assert!(!heads.contains(commit3.id())); assert!(!heads.contains(commit2.id())); assert!(!heads.contains(commit1.id())); assert!(index_has_id(mut_repo.index(), commit1.id())); assert!(index_has_id(mut_repo.index(), commit2.id())); assert!(index_has_id(mut_repo.index(), commit3.id())); let repo = tx.commit("test").unwrap(); let heads = repo.view().heads().clone(); assert!(!heads.contains(commit3.id())); assert!(!heads.contains(commit2.id())); assert!(!heads.contains(commit1.id())); assert!(index_has_id(repo.index(), commit1.id())); assert!(index_has_id(repo.index(), commit2.id())); assert!(index_has_id(repo.index(), commit3.id())); } #[test] fn test_has_changed() { // Test that MutableRepo::has_changed() reports changes iff the view has changed // (e.g. not after setting a bookmark to point to where it was already // pointing). let test_repo = TestRepo::init(); let repo = &test_repo.repo; let normal_remote_ref = |id: &CommitId| RemoteRef { target: RefTarget::normal(id.clone()), state: RemoteRefState::Tracked, // doesn't matter }; let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); let commit1 = write_random_commit(mut_repo); let commit2 = write_random_commit(mut_repo); mut_repo.remove_head(commit2.id()); let ws_name = WorkspaceName::DEFAULT.to_owned(); mut_repo .set_wc_commit(ws_name.clone(), commit1.id().clone()) .unwrap(); mut_repo.set_local_bookmark_target("main".as_ref(), RefTarget::normal(commit1.id().clone())); mut_repo.set_remote_bookmark( remote_symbol("main", "origin"), normal_remote_ref(commit1.id()), ); let repo = tx.commit("test").unwrap(); // Test the setup assert_eq!(repo.view().heads(), &hashset! {commit1.id().clone()}); let mut tx = repo.start_transaction(); let mut_repo = tx.repo_mut(); mut_repo.add_head(&commit1).unwrap(); mut_repo .set_wc_commit(ws_name.clone(), commit1.id().clone()) .unwrap(); mut_repo.set_local_bookmark_target("main".as_ref(), RefTarget::normal(commit1.id().clone()));