ok/jj
1
0
Fork 0
forked from mirrors/jj

merged_tree: add config option to write trees using new format

We're finally ready to start writing trees using the new format where
we represent conflicts by having multiple trees in the commit instead
of having a single tree with multiple entries at a path. This patch
adds a config option for that. It's not ready to be used yet, so I
haven't updated the release notes or other documentation.

I added only a simple CLI test for testing what happens when the
config is enabled in an existing repo. 108 tests currently fail if we
flip the default.
This commit is contained in:
Martin von Zweigbergk 2023-08-28 21:52:35 -07:00 committed by Martin von Zweigbergk
parent bb755ae754
commit 8e47d2d66f
5 changed files with 119 additions and 7 deletions

View file

@ -0,0 +1,85 @@
// Copyright 2022 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 common::TestEnvironment;
pub mod common;
#[test]
fn test_enable_tree_level_conflicts() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
// Create a few commits before we enable tree-level conflicts
let file_path = repo_path.join("file");
test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=left"]);
std::fs::write(&file_path, "left").unwrap();
test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=right"]);
std::fs::write(&file_path, "right").unwrap();
test_env.jj_cmd_success(
&repo_path,
&[
"new",
r#"description("left")"#,
r#"description("right")"#,
"-m=merge",
],
);
test_env.jj_cmd_success(&repo_path, &["new"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r###"
@ mzvwutvl test.user@example.com 2001-02-03 04:05:11.000 +07:00 f2101bed conflict
(empty) (no description set)
zsuskuln test.user@example.com 2001-02-03 04:05:10.000 +07:00 5100e4e1 conflict
(empty) merge
kkmpptxz test.user@example.com 2001-02-03 04:05:10.000 +07:00 0b65c8fb
right
rlvkpnrz test.user@example.com 2001-02-03 04:05:09.000 +07:00 32003b88
left
zzzzzzzz root 00000000
"###);
// Enable tree-level conflicts
test_env.add_config(r#"format.tree-level-conflicts = true"#);
// We get a new working-copy commit. The working copy unfortunately appears
// non-empty
let stdout = test_env.jj_cmd_success(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r###"
@ mzvwutvl test.user@example.com 2001-02-03 04:05:13.000 +07:00 54c562fa conflict
(no description set)
zsuskuln test.user@example.com 2001-02-03 04:05:10.000 +07:00 5100e4e1 conflict
(empty) merge
kkmpptxz test.user@example.com 2001-02-03 04:05:10.000 +07:00 0b65c8fb
right
rlvkpnrz test.user@example.com 2001-02-03 04:05:09.000 +07:00 32003b88
left
zzzzzzzz root 00000000
"###);
// ...but at least it has no diff
let stdout = test_env.jj_cmd_success(&repo_path, &["diff"]);
insta::assert_snapshot!(stdout, @"");
// If we create new commit off of an unconflicted commit, it also appears
// non-empty
test_env.jj_cmd_success(&repo_path, &["new", "k"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r=@"]);
insta::assert_snapshot!(stdout, @r###"
@ yostqsxw test.user@example.com 2001-02-03 04:05:16.000 +07:00 f5e911f1
(no description set)
~
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["diff"]);
insta::assert_snapshot!(stdout, @"");
}

View file

@ -940,9 +940,13 @@ impl MergedTreeBuilder {
/// Create new tree(s) from the base tree(s) and overrides.
///
/// When the base tree was a legacy tree, then the result will be another
/// legacy tree. Overrides with conflicts will result in conflict objects
/// being written to the store.
/// When the base tree was a legacy tree and the
/// `format.tree-level-conflicts` config is disabled, then the result will
/// be another legacy tree. Overrides with conflicts will result in
/// conflict objects being written to the store. If
/// `format.tree-tree-level-conflicts` is enabled, then a legacy tree will
/// still be written and immediately converted and returned as a merged
/// tree.
pub fn write_tree(self, store: &Arc<Store>) -> BackendResult<MergedTreeId> {
match self.base_tree_id.clone() {
MergedTreeId::Legacy(base_tree_id) => {
@ -959,7 +963,14 @@ impl MergedTreeBuilder {
}
}
}
Ok(MergedTreeId::Legacy(tree_builder.write_tree()))
let legacy_id = tree_builder.write_tree();
if store.use_tree_conflict_format() {
let legacy_tree = store.get_tree(&RepoPath::root(), &legacy_id)?;
let merged_tree = MergedTree::from_legacy_tree(legacy_tree)?;
Ok(merged_tree.id())
} else {
Ok(MergedTreeId::Legacy(legacy_id))
}
}
MergedTreeId::Merge(base_tree_ids) => {
let new_tree_ids = self.write_merged_trees(base_tree_ids, store)?;

View file

@ -152,7 +152,7 @@ impl ReadonlyRepo {
let backend = backend_factory(&store_path)?;
let backend_path = store_path.join("type");
fs::write(&backend_path, backend.name()).context(&backend_path)?;
let store = Store::new(backend);
let store = Store::new(backend, user_settings.use_tree_conflict_format());
let repo_settings = user_settings.with_repo(&repo_path).unwrap();
let op_store_path = repo_path.join("op_store");
@ -585,7 +585,10 @@ impl RepoLoader {
repo_path: &Path,
store_factories: &StoreFactories,
) -> Result<Self, StoreLoadError> {
let store = Store::new(store_factories.load_backend(&repo_path.join("store"))?);
let store = Store::new(
store_factories.load_backend(&repo_path.join("store"))?,
user_settings.use_tree_conflict_format(),
);
let repo_settings = user_settings.with_repo(repo_path).unwrap();
let op_store = Arc::from(store_factories.load_op_store(&repo_path.join("op_store"))?);
let op_heads_store =

View file

@ -97,6 +97,12 @@ impl UserSettings {
self.rng.clone()
}
pub fn use_tree_conflict_format(&self) -> bool {
self.config
.get_bool("format.tree-level-conflicts")
.unwrap_or(false)
}
pub fn user_name(&self) -> String {
self.config.get_string("user.name").unwrap_or_default()
}

View file

@ -38,14 +38,16 @@ pub struct Store {
backend: Box<dyn Backend>,
commit_cache: RwLock<HashMap<CommitId, Arc<backend::Commit>>>,
tree_cache: RwLock<HashMap<(RepoPath, TreeId), Arc<backend::Tree>>>,
use_tree_conflict_format: bool,
}
impl Store {
pub fn new(backend: Box<dyn Backend>) -> Arc<Self> {
pub fn new(backend: Box<dyn Backend>, use_tree_conflict_format: bool) -> Arc<Self> {
Arc::new(Store {
backend,
commit_cache: Default::default(),
tree_cache: Default::default(),
use_tree_conflict_format,
})
}
@ -53,6 +55,11 @@ impl Store {
self.backend.as_any()
}
/// Whether new tree should be written using the tree-level format.
pub fn use_tree_conflict_format(&self) -> bool {
self.use_tree_conflict_format
}
pub fn commit_id_length(&self) -> usize {
self.backend.commit_id_length()
}