mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-12 07:14:38 +00:00
1aad724798
This function is merely a simple wrapper around `MutableRepo::rebase_descendants_with_options_return_map`.
751 lines
28 KiB
Rust
751 lines
28 KiB
Rust
// 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::op_store::RefTarget;
|
|
use jj_lib::op_store::RemoteRef;
|
|
use jj_lib::op_store::RemoteRefState;
|
|
use jj_lib::op_store::WorkspaceId;
|
|
use jj_lib::repo::Repo;
|
|
use maplit::hashset;
|
|
use testutils::assert_rebased_onto;
|
|
use testutils::create_random_commit;
|
|
use testutils::create_random_tree;
|
|
use testutils::write_random_commit;
|
|
use testutils::CommitGraphBuilder;
|
|
use testutils::TestRepo;
|
|
|
|
#[test]
|
|
fn test_edit() {
|
|
// Test that MutableRepo::edit() uses the requested commit (not a new child)
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let wc_commit = write_random_commit(tx.repo_mut(), &settings);
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let ws_id = WorkspaceId::default();
|
|
tx.repo_mut().edit(ws_id.clone(), &wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
assert_eq!(repo.view().get_wc_commit_id(&ws_id), Some(wc_commit.id()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_checkout() {
|
|
// Test that MutableRepo::check_out() creates a child
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let wc_commit_parent = write_random_commit(tx.repo_mut(), &settings);
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let ws_id = WorkspaceId::default();
|
|
let wc_commit = tx
|
|
.repo_mut()
|
|
.check_out(ws_id.clone(), &settings, &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");
|
|
assert_eq!(repo.view().get_wc_commit_id(&ws_id), Some(wc_commit.id()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_edit_previous_not_empty() {
|
|
// Test that MutableRepo::edit() does not usually abandon the previous
|
|
// commit.
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = write_random_commit(mut_repo, &settings);
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let new_wc_commit = write_random_commit(mut_repo, &settings);
|
|
mut_repo.edit(ws_id, &new_wc_commit).unwrap();
|
|
mut_repo.rebase_descendants(&settings).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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
vec![repo.store().root_commit_id().clone()],
|
|
repo.store().empty_merged_tree_id(),
|
|
)
|
|
.write()
|
|
.unwrap();
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let new_wc_commit = write_random_commit(mut_repo, &settings);
|
|
mut_repo.edit(ws_id, &new_wc_commit).unwrap();
|
|
mut_repo.rebase_descendants(&settings).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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_parent1 = write_random_commit(mut_repo, &settings);
|
|
let old_parent2 = write_random_commit(mut_repo, &settings);
|
|
let empty_tree = repo.store().root_commit().tree().unwrap();
|
|
let old_parent_tree = old_parent1
|
|
.tree()
|
|
.unwrap()
|
|
.merge(&empty_tree, &old_parent2.tree().unwrap())
|
|
.unwrap();
|
|
let old_wc_commit = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
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_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let new_wc_commit = write_random_commit(mut_repo, &settings);
|
|
mut_repo.edit(ws_id, &new_wc_commit).unwrap();
|
|
mut_repo.rebase_descendants(&settings).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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
vec![repo.store().root_commit_id().clone()],
|
|
repo.store().empty_merged_tree_id(),
|
|
)
|
|
.set_description("not empty")
|
|
.write()
|
|
.unwrap();
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let new_wc_commit = write_random_commit(mut_repo, &settings);
|
|
mut_repo.edit(ws_id, &new_wc_commit).unwrap();
|
|
mut_repo.rebase_descendants(&settings).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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
vec![repo.store().root_commit_id().clone()],
|
|
repo.store().empty_merged_tree_id(),
|
|
)
|
|
.write()
|
|
.unwrap();
|
|
mut_repo.set_local_bookmark_target("b", RefTarget::normal(old_wc_commit.id().clone()));
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let new_wc_commit = write_random_commit(mut_repo, &settings);
|
|
mut_repo.edit(ws_id, &new_wc_commit).unwrap();
|
|
mut_repo.rebase_descendants(&settings).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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
vec![repo.store().root_commit_id().clone()],
|
|
repo.store().empty_merged_tree_id(),
|
|
)
|
|
.write()
|
|
.unwrap();
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let other_ws_id = WorkspaceId::new("other".to_string());
|
|
mut_repo.edit(other_ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let new_wc_commit = write_random_commit(mut_repo, &settings);
|
|
mut_repo.edit(ws_id, &new_wc_commit).unwrap();
|
|
mut_repo.rebase_descendants(&settings).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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
vec![repo.store().root_commit_id().clone()],
|
|
repo.store().empty_merged_tree_id(),
|
|
)
|
|
.write()
|
|
.unwrap();
|
|
let old_child = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
vec![old_wc_commit.id().clone()],
|
|
old_wc_commit.tree_id().clone(),
|
|
)
|
|
.write()
|
|
.unwrap();
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let new_wc_commit = write_random_commit(mut_repo, &settings);
|
|
mut_repo.edit(ws_id, &new_wc_commit).unwrap();
|
|
mut_repo.rebase_descendants(&settings).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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let wc_commit = write_random_commit(tx.repo_mut(), &settings);
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let workspace_id = WorkspaceId::new("new-workspace".to_string());
|
|
tx.repo_mut()
|
|
.edit(workspace_id.clone(), &wc_commit)
|
|
.unwrap();
|
|
let repo = tx.commit("test");
|
|
assert_eq!(
|
|
repo.view().get_wc_commit_id(&workspace_id),
|
|
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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let wc_commit = write_random_commit(tx.repo_mut(), &settings);
|
|
|
|
// Intentionally not doing tx.commit, so the commit id is not tracked
|
|
// in the view head ids.
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let ws_id = WorkspaceId::default();
|
|
tx.repo_mut().edit(ws_id.clone(), &wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
assert_eq!(repo.view().get_wc_commit_id(&ws_id), 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 settings = testutils::user_settings();
|
|
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(&settings);
|
|
let new_commit = write_random_commit(tx.repo_mut(), &settings);
|
|
drop(tx);
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
assert!(!mut_repo.view().heads().contains(new_commit.id()));
|
|
assert!(!mut_repo.index().has_id(new_commit.id()));
|
|
mut_repo.add_head(&new_commit).unwrap();
|
|
assert!(mut_repo.view().heads().contains(new_commit.id()));
|
|
assert!(mut_repo.index().has_id(new_commit.id()));
|
|
let repo = tx.commit("test");
|
|
assert!(repo.view().heads().contains(new_commit.id()));
|
|
assert!(repo.index().has_id(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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.repo_mut());
|
|
let commit1 = graph_builder.initial_commit();
|
|
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
|
let repo = tx.commit("test");
|
|
|
|
assert_eq!(repo.view().heads(), &hashset! {commit3.id().clone()});
|
|
let mut tx = repo.start_transaction(&settings);
|
|
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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let initial = write_random_commit(tx.repo_mut(), &settings);
|
|
let repo = tx.commit("test");
|
|
|
|
// 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(&settings);
|
|
let rewritten = create_random_commit(tx.repo_mut(), &settings)
|
|
.set_change_id(initial.change_id().clone())
|
|
.set_predecessors(vec![initial.id().clone()])
|
|
.write()
|
|
.unwrap();
|
|
let child = create_random_commit(tx.repo_mut(), &settings)
|
|
.set_parents(vec![rewritten.id().clone()])
|
|
.write()
|
|
.unwrap();
|
|
drop(tx);
|
|
|
|
assert_eq!(repo.view().heads(), &hashset! {initial.id().clone()});
|
|
let mut tx = repo.start_transaction(&settings);
|
|
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!(mut_repo.index().has_id(initial.id()));
|
|
assert!(mut_repo.index().has_id(rewritten.id()));
|
|
assert!(mut_repo.index().has_id(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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.repo_mut());
|
|
let commit1 = graph_builder.initial_commit();
|
|
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
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!(mut_repo.index().has_id(commit1.id()));
|
|
assert!(mut_repo.index().has_id(commit2.id()));
|
|
assert!(mut_repo.index().has_id(commit3.id()));
|
|
let repo = tx.commit("test");
|
|
let heads = repo.view().heads().clone();
|
|
assert!(!heads.contains(commit3.id()));
|
|
assert!(!heads.contains(commit2.id()));
|
|
assert!(!heads.contains(commit1.id()));
|
|
assert!(repo.index().has_id(commit1.id()));
|
|
assert!(repo.index().has_id(commit2.id()));
|
|
assert!(repo.index().has_id(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 settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
let normal_remote_ref = |id: &CommitId| RemoteRef {
|
|
target: RefTarget::normal(id.clone()),
|
|
state: RemoteRefState::Tracking, // doesn't matter
|
|
};
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let commit1 = write_random_commit(mut_repo, &settings);
|
|
let commit2 = write_random_commit(mut_repo, &settings);
|
|
mut_repo.remove_head(commit2.id());
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo
|
|
.set_wc_commit(ws_id.clone(), commit1.id().clone())
|
|
.unwrap();
|
|
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit1.id().clone()));
|
|
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit1.id()));
|
|
let repo = tx.commit("test");
|
|
// Test the setup
|
|
assert_eq!(repo.view().heads(), &hashset! {commit1.id().clone()});
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
|
|
mut_repo.add_head(&commit1).unwrap();
|
|
mut_repo
|
|
.set_wc_commit(ws_id.clone(), commit1.id().clone())
|
|
.unwrap();
|
|
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit1.id().clone()));
|
|
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit1.id()));
|
|
assert!(!mut_repo.has_changes());
|
|
|
|
mut_repo.remove_head(commit2.id());
|
|
mut_repo.set_local_bookmark_target("stable", RefTarget::absent());
|
|
mut_repo.set_remote_bookmark("stable", "origin", RemoteRef::absent());
|
|
assert!(!mut_repo.has_changes());
|
|
|
|
mut_repo.add_head(&commit2).unwrap();
|
|
assert!(mut_repo.has_changes());
|
|
mut_repo.remove_head(commit2.id());
|
|
assert!(!mut_repo.has_changes());
|
|
|
|
mut_repo
|
|
.set_wc_commit(ws_id.clone(), commit2.id().clone())
|
|
.unwrap();
|
|
assert!(mut_repo.has_changes());
|
|
mut_repo.set_wc_commit(ws_id, commit1.id().clone()).unwrap();
|
|
assert!(!mut_repo.has_changes());
|
|
|
|
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit2.id().clone()));
|
|
assert!(mut_repo.has_changes());
|
|
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit1.id().clone()));
|
|
assert!(!mut_repo.has_changes());
|
|
|
|
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit2.id()));
|
|
assert!(mut_repo.has_changes());
|
|
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit1.id()));
|
|
assert!(!mut_repo.has_changes());
|
|
}
|
|
|
|
#[test]
|
|
fn test_rebase_descendants_simple() {
|
|
// There are many additional tests of this functionality in `test_rewrite.rs`.
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.repo_mut());
|
|
let commit1 = graph_builder.initial_commit();
|
|
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
|
let commit4 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let commit5 = graph_builder.commit_with_parents(&[&commit4]);
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, mut_repo);
|
|
let commit6 = graph_builder.commit_with_parents(&[&commit1]);
|
|
mut_repo.set_rewritten_commit(commit2.id().clone(), commit6.id().clone());
|
|
mut_repo.record_abandoned_commit(commit4.id().clone());
|
|
let rebase_map = tx
|
|
.repo_mut()
|
|
.rebase_descendants_with_options_return_map(&settings, Default::default())
|
|
.unwrap();
|
|
// Commit 3 got rebased onto commit 2's replacement, i.e. commit 6
|
|
assert_rebased_onto(tx.repo_mut(), &rebase_map, &commit3, &[commit6.id()]);
|
|
// Commit 5 got rebased onto commit 4's parent, i.e. commit 1
|
|
assert_rebased_onto(tx.repo_mut(), &rebase_map, &commit5, &[commit1.id()]);
|
|
assert_eq!(rebase_map.len(), 2);
|
|
|
|
// No more descendants to rebase if we try again.
|
|
let rebase_map = tx
|
|
.repo_mut()
|
|
.rebase_descendants_with_options_return_map(&settings, Default::default())
|
|
.unwrap();
|
|
assert_eq!(rebase_map.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rebase_descendants_divergent_rewrite() {
|
|
// Test rebasing descendants when one commit was rewritten to several other
|
|
// commits. There are many additional tests of this functionality in
|
|
// `test_rewrite.rs`.
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.repo_mut());
|
|
let commit1 = graph_builder.initial_commit();
|
|
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let _commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, mut_repo);
|
|
let commit4 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let commit5 = graph_builder.commit_with_parents(&[&commit1]);
|
|
mut_repo.set_divergent_rewrite(
|
|
commit2.id().clone(),
|
|
vec![commit4.id().clone(), commit5.id().clone()],
|
|
);
|
|
// Commit 3 does *not* get rebased because it's unclear if it should go onto
|
|
// commit 4 or commit 5
|
|
let rebase_map = tx
|
|
.repo_mut()
|
|
.rebase_descendants_with_options_return_map(&settings, Default::default())
|
|
.unwrap();
|
|
assert!(rebase_map.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_rename_remote() {
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let commit = write_random_commit(mut_repo, &settings);
|
|
let remote_ref = RemoteRef {
|
|
target: RefTarget::normal(commit.id().clone()),
|
|
state: RemoteRefState::Tracking, // doesn't matter
|
|
};
|
|
mut_repo.set_remote_bookmark("main", "origin", remote_ref.clone());
|
|
mut_repo.rename_remote("origin", "upstream");
|
|
assert_eq!(mut_repo.get_remote_bookmark("main", "upstream"), remote_ref);
|
|
assert_eq!(
|
|
mut_repo.get_remote_bookmark("main", "origin"),
|
|
RemoteRef::absent()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_remove_wc_commit_previous_not_discardable() {
|
|
// Test that MutableRepo::remove_wc_commit() does not usually abandon the
|
|
// previous commit.
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = write_random_commit(mut_repo, &settings);
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
mut_repo.remove_wc_commit(&ws_id).unwrap();
|
|
mut_repo.rebase_descendants(&settings).unwrap();
|
|
assert!(mut_repo.view().heads().contains(old_wc_commit.id()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_remove_wc_commit_previous_discardable() {
|
|
// Test that MutableRepo::remove_wc_commit() abandons the previous commit
|
|
// if it was discardable.
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
let old_wc_commit = mut_repo
|
|
.new_commit(
|
|
&settings,
|
|
vec![repo.store().root_commit_id().clone()],
|
|
repo.store().empty_merged_tree_id(),
|
|
)
|
|
.write()
|
|
.unwrap();
|
|
let ws_id = WorkspaceId::default();
|
|
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
|
|
let repo = tx.commit("test");
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
mut_repo.remove_wc_commit(&ws_id).unwrap();
|
|
mut_repo.rebase_descendants(&settings).unwrap();
|
|
assert!(!mut_repo.view().heads().contains(old_wc_commit.id()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_reparent_descendants() {
|
|
// Test that MutableRepo::reparent_descendants() reparents descendants of
|
|
// rewritten commits without altering their content.
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init();
|
|
let repo = &test_repo.repo;
|
|
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.repo_mut());
|
|
let commit_a = graph_builder.initial_commit();
|
|
let commit_b = graph_builder.initial_commit();
|
|
let commit_child_a_b = graph_builder.commit_with_parents(&[&commit_a, &commit_b]);
|
|
let commit_grandchild_a_b = graph_builder.commit_with_parents(&[&commit_child_a_b]);
|
|
let commit_child_a = graph_builder.commit_with_parents(&[&commit_a]);
|
|
let commit_child_b = graph_builder.commit_with_parents(&[&commit_b]);
|
|
let mut_repo = tx.repo_mut();
|
|
for (bookmark, commit) in [
|
|
("b", &commit_b),
|
|
("child_a_b", &commit_child_a_b),
|
|
("grandchild_a_b", &commit_grandchild_a_b),
|
|
("child_a", &commit_child_a),
|
|
("child_b", &commit_child_b),
|
|
] {
|
|
mut_repo.set_local_bookmark_target(bookmark, RefTarget::normal(commit.id().clone()));
|
|
}
|
|
let repo = tx.commit("test");
|
|
|
|
// Rewrite "commit_a".
|
|
let mut tx = repo.start_transaction(&settings);
|
|
let mut_repo = tx.repo_mut();
|
|
mut_repo
|
|
.rewrite_commit(&settings, &commit_a)
|
|
.set_tree_id(create_random_tree(&repo))
|
|
.write()
|
|
.unwrap();
|
|
let reparented = mut_repo.reparent_descendants(&settings).unwrap();
|
|
// "child_a_b", "grandchild_a_b" and "child_a" (3 commits) must have been
|
|
// reparented.
|
|
assert_eq!(reparented, 3);
|
|
let repo = tx.commit("test");
|
|
|
|
for (bookmark, commit) in [
|
|
("b", &commit_b),
|
|
("child_a_b", &commit_child_a_b),
|
|
("grandchild_a_b", &commit_grandchild_a_b),
|
|
("child_a", &commit_child_a),
|
|
("child_b", &commit_child_b),
|
|
] {
|
|
let rewritten_id = repo
|
|
.view()
|
|
.get_local_bookmark(bookmark)
|
|
.as_normal()
|
|
.unwrap()
|
|
.clone();
|
|
if matches!(bookmark, "b" | "child_b") {
|
|
// "b" and "child_b" have been kept untouched.
|
|
assert_eq!(commit.id(), &rewritten_id);
|
|
} else {
|
|
// All commits except "b", and "child_b" have been reparented while keeping
|
|
// their content.
|
|
assert_ne!(commit.id(), &rewritten_id);
|
|
let rewritten_commit = repo.store().get_commit(&rewritten_id).unwrap();
|
|
assert_eq!(commit.tree_id(), rewritten_commit.tree_id());
|
|
let (parent_ids, rewritten_parent_ids) =
|
|
(commit.parent_ids(), rewritten_commit.parent_ids());
|
|
assert_eq!(parent_ids.len(), rewritten_parent_ids.len());
|
|
assert_ne!(parent_ids, rewritten_parent_ids);
|
|
}
|
|
}
|
|
}
|