From 4834d12c37e5863c6db453c5553e7b0d4c8827cf Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 16 Jul 2023 00:54:50 +0900 Subject: [PATCH] simple_op_store: serialize RefTarget in new format (breaks downgrades) This is breaking change. Old jj binary will panic if it sees a view saved by new jj. Alternatively, we can store both new and legacy data for backward compatibility. --- CHANGELOG.md | 3 + lib/src/op_store.rs | 105 +++------------------------- lib/src/refs.rs | 23 +++--- lib/src/simple_op_store.rs | 74 +++++++++++++++----- lib/tests/test_git.rs | 72 +++++++++---------- lib/tests/test_view.rs | 8 +-- tests/test_concurrent_operations.rs | 20 +++--- tests/test_debug_command.rs | 2 +- tests/test_operations.rs | 38 +++++----- tests/test_workspaces.rs | 6 +- 10 files changed, 152 insertions(+), 199 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5029fad83..4f9e75625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * The minimum supported Rust version (MSRV) is now 1.71.0. +* The storage format of branches, tags, and git refs has changed. Newly-stored + repository data will no longer be loadable by older binaries. + ### New features * `jj log` output is now topologically grouped. diff --git a/lib/src/op_store.rs b/lib/src/op_store.rs index 77efafd9a..ee441955f 100644 --- a/lib/src/op_store.rs +++ b/lib/src/op_store.rs @@ -14,17 +14,14 @@ #![allow(missing_docs)] -use std::collections::{btree_map, BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::fmt::{Debug, Error, Formatter}; -use std::ops::{Deref, DerefMut}; -use itertools::Itertools as _; use once_cell::sync::Lazy; use thiserror::Error; use crate::backend::{id_type, CommitId, ObjectId, Timestamp}; use crate::conflicts::Conflict; -use crate::content_hash::ContentHash; content_hash! { #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] @@ -56,9 +53,11 @@ impl WorkspaceId { id_type!(pub ViewId); id_type!(pub OperationId); -#[derive(PartialEq, Eq, Hash, Clone, Debug)] -pub struct RefTarget { - conflict: Conflict>, +content_hash! { + #[derive(PartialEq, Eq, Hash, Clone, Debug)] + pub struct RefTarget { + conflict: Conflict>, + } } impl Default for RefTarget { @@ -67,26 +66,6 @@ impl Default for RefTarget { } } -impl ContentHash for RefTarget { - fn hash(&self, state: &mut impl digest::Update) { - // TODO: Leverage generic implementation. Unlike RefTargetMap, this just exists - // in order to minimize the test changes. We can freely switch to the generic - // version. - if self.is_absent() { - state.update(&[0]); // None - } else if let Some(id) = self.as_normal() { - state.update(&[1]); // Some( - state.update(&0u32.to_le_bytes()); // Normal( - id.hash(state); - } else { - state.update(&[1]); // Some( - state.update(&1u32.to_le_bytes()); // Conflict( - self.removed_ids().cloned().collect_vec().hash(state); - self.added_ids().cloned().collect_vec().hash(state); - } - } -} - impl RefTarget { /// Creates non-conflicting target pointing to no commit. pub fn absent() -> Self { @@ -176,72 +155,6 @@ impl<'a> RefTargetOptionExt for Option<&'a RefTarget> { } } -/// Wrapper to exclude absent `RefTarget` entries from `ContentHash`. -#[derive(Default, PartialEq, Eq, Clone, Debug)] -pub struct RefTargetMap(pub BTreeMap); - -impl RefTargetMap { - pub fn new() -> Self { - RefTargetMap(BTreeMap::new()) - } -} - -// TODO: Update serialization code to preserve absent RefTarget entries, and -// remove this wrapper. -impl ContentHash for RefTargetMap { - fn hash(&self, state: &mut impl digest::Update) { - // Derived from content_hash.rs. It's okay for this to produce a different hash - // value than the inner map, but the value must not be equal to the map which - // preserves absent RefTarget entries. - let iter = self.0.iter().filter(|(_, v)| v.is_present()); - state.update(&(iter.clone().count() as u64).to_le_bytes()); - for (k, v) in iter { - k.hash(state); - if let Some(id) = v.as_normal() { - state.update(&0u32.to_le_bytes()); // Normal( - id.hash(state); - } else { - state.update(&1u32.to_le_bytes()); // Conflict( - v.removed_ids().cloned().collect_vec().hash(state); - v.added_ids().cloned().collect_vec().hash(state); - } - } - } -} - -// Abuse Deref as this is a temporary workaround. See the comment above. -impl Deref for RefTargetMap { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for RefTargetMap { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl IntoIterator for RefTargetMap { - type Item = (String, RefTarget); - type IntoIter = btree_map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a RefTargetMap { - type Item = (&'a String, &'a RefTarget); - type IntoIter = btree_map::Iter<'a, String, RefTarget>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - content_hash! { #[derive(Default, PartialEq, Eq, Clone, Debug)] pub struct BranchTarget { @@ -252,7 +165,7 @@ content_hash! { // has been deleted locally and you pull from a remote, maybe it should make a difference // whether the branch is known to have existed on the remote. We may not want to resurrect // the branch if the branch's state on the remote was just not known. - pub remote_targets: RefTargetMap, + pub remote_targets: BTreeMap, } } @@ -266,8 +179,8 @@ content_hash! { /// Heads of the set of public commits. pub public_head_ids: HashSet, pub branches: BTreeMap, - pub tags: RefTargetMap, - pub git_refs: RefTargetMap, + pub tags: BTreeMap, + pub git_refs: BTreeMap, /// The commit the Git HEAD points to. // TODO: Support multiple Git worktrees? // TODO: Do we want to store the current branch name too? diff --git a/lib/src/refs.rs b/lib/src/refs.rs index f4b6c0cfe..d4900b231 100644 --- a/lib/src/refs.rs +++ b/lib/src/refs.rs @@ -128,16 +128,15 @@ mod tests { use super::*; use crate::backend::ObjectId; - use crate::op_store::RefTargetMap; #[test] fn test_classify_branch_push_action_unchanged() { let commit_id1 = CommitId::from_hex("11"); let branch = BranchTarget { local_target: RefTarget::normal(commit_id1.clone()), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(commit_id1), - }), + }, }; assert_eq!( classify_branch_push_action(&branch, "origin"), @@ -150,7 +149,7 @@ mod tests { let commit_id1 = CommitId::from_hex("11"); let branch = BranchTarget { local_target: RefTarget::normal(commit_id1.clone()), - remote_targets: RefTargetMap::new(), + remote_targets: btreemap! {}, }; assert_eq!( classify_branch_push_action(&branch, "origin"), @@ -166,9 +165,9 @@ mod tests { let commit_id1 = CommitId::from_hex("11"); let branch = BranchTarget { local_target: RefTarget::absent(), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(commit_id1.clone()), - }), + }, }; assert_eq!( classify_branch_push_action(&branch, "origin"), @@ -185,9 +184,9 @@ mod tests { let commit_id2 = CommitId::from_hex("22"); let branch = BranchTarget { local_target: RefTarget::normal(commit_id2.clone()), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(commit_id1.clone()), - }), + }, }; assert_eq!( classify_branch_push_action(&branch, "origin"), @@ -204,9 +203,9 @@ mod tests { let commit_id2 = CommitId::from_hex("22"); let branch = BranchTarget { local_target: RefTarget::from_legacy_form([], [commit_id1.clone(), commit_id2]), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(commit_id1), - }), + }, }; assert_eq!( classify_branch_push_action(&branch, "origin"), @@ -220,12 +219,12 @@ mod tests { let commit_id2 = CommitId::from_hex("22"); let branch = BranchTarget { local_target: RefTarget::normal(commit_id1.clone()), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::from_legacy_form( [], [commit_id1, commit_id2], ), - }), + }, }; assert_eq!( classify_branch_push_action(&branch, "origin"), diff --git a/lib/src/simple_op_store.rs b/lib/src/simple_op_store.rs index 266112966..3e089c300 100644 --- a/lib/src/simple_op_store.rs +++ b/lib/src/simple_op_store.rs @@ -14,6 +14,7 @@ #![allow(missing_docs)] +use std::collections::BTreeMap; use std::fmt::Debug; use std::fs; use std::io::{ErrorKind, Write}; @@ -29,7 +30,7 @@ use crate::content_hash::blake2b_hash; use crate::file_util::persist_content_addressed_temp_file; use crate::op_store::{ BranchTarget, OpStore, OpStoreError, OpStoreResult, Operation, OperationId, OperationMetadata, - RefTarget, RefTargetMap, View, ViewId, WorkspaceId, + RefTarget, View, ViewId, WorkspaceId, }; impl From for OpStoreError { @@ -314,7 +315,7 @@ fn view_from_proto(proto: crate::protos::op_store::View) -> View { for branch_proto in proto.branches { let local_target = ref_target_from_proto(branch_proto.local_target); - let mut remote_targets = RefTargetMap::new(); + let mut remote_targets = BTreeMap::new(); for remote_branch in branch_proto.remote_branches { remote_targets.insert( remote_branch.remote_name, @@ -356,8 +357,26 @@ fn view_from_proto(proto: crate::protos::op_store::View) -> View { view } -#[allow(deprecated)] fn ref_target_to_proto(value: &RefTarget) -> Option { + let term_to_proto = |term: &Option| crate::protos::op_store::ref_conflict::Term { + value: term.as_ref().map(|id| id.to_bytes()), + }; + let conflict = value.as_conflict(); + let conflict_proto = crate::protos::op_store::RefConflict { + removes: conflict.removes().iter().map(term_to_proto).collect(), + adds: conflict.adds().iter().map(term_to_proto).collect(), + }; + let proto = crate::protos::op_store::RefTarget { + value: Some(crate::protos::op_store::ref_target::Value::Conflict( + conflict_proto, + )), + }; + Some(proto) +} + +#[allow(deprecated)] +#[cfg(test)] +fn ref_target_to_proto_legacy(value: &RefTarget) -> Option { if let Some(id) = value.as_normal() { let proto = crate::protos::op_store::RefTarget { value: Some(crate::protos::op_store::ref_target::Value::CommitId( @@ -366,7 +385,6 @@ fn ref_target_to_proto(value: &RefTarget) -> Option Option) -> RefTarget { + // TODO: Delete legacy format handling when we decide to drop support for views + // saved by jj <= 0.8. let Some(proto) = maybe_proto else { // Legacy absent id return RefTarget::absent(); @@ -443,24 +463,24 @@ mod tests { branches: btreemap! { "main".to_string() => BranchTarget { local_target: branch_main_local_target, - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => branch_main_origin_target, - }), + }, }, "deleted".to_string() => BranchTarget { local_target: RefTarget::absent(), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => branch_deleted_origin_target, - }), + }, }, }, - tags: RefTargetMap(btreemap! { + tags: btreemap! { "v1.0".to_string() => tag_v1_target, - }), - git_refs: RefTargetMap(btreemap! { + }, + git_refs: btreemap! { "refs/heads/main".to_string() => git_refs_main_target, "refs/heads/feature".to_string() => git_refs_feature_target, - }), + }, git_head: RefTarget::normal(CommitId::from_hex("fff111")), wc_commit_ids: hashmap! { WorkspaceId::default() => default_wc_commit_id, @@ -501,7 +521,7 @@ mod tests { // Test exact output so we detect regressions in compatibility assert_snapshot!( ViewId::new(blake2b_hash(&create_view()).to_vec()).hex(), - @"7f47fa81494d7189cb1827b83b3f834662f0f61b4c4090298067e85cdc60f773bf639c4e6a3554a4e401650218ca240291ce591f45a1c501ade1d2b9f97e1a37" + @"3c1c6efecfc0809130a5bf139aec77e6299cd7d5985b95c01a29318d40a5e2defc9bd12329e91511e545fbad065f60ce5da91f5f0368c9bf549ca761bb047f7e" ); } @@ -535,14 +555,32 @@ mod tests { } #[test] - fn test_ref_target_legacy_roundtrip() { - let target = RefTarget::absent(); + fn test_ref_target_change_delete_order_roundtrip() { + let target = RefTarget::from_conflict(Conflict::new( + vec![Some(CommitId::from_hex("111111"))], + vec![Some(CommitId::from_hex("222222")), None], + )); let maybe_proto = ref_target_to_proto(&target); assert_eq!(ref_target_from_proto(maybe_proto), target); - let target = RefTarget::normal(CommitId::from_hex("111111")); + // If it were legacy format, order of None entry would be lost. + let target = RefTarget::from_conflict(Conflict::new( + vec![Some(CommitId::from_hex("111111"))], + vec![None, Some(CommitId::from_hex("222222"))], + )); let maybe_proto = ref_target_to_proto(&target); assert_eq!(ref_target_from_proto(maybe_proto), target); + } + + #[test] + fn test_ref_target_legacy_roundtrip() { + let target = RefTarget::absent(); + let maybe_proto = ref_target_to_proto_legacy(&target); + assert_eq!(ref_target_from_proto(maybe_proto), target); + + let target = RefTarget::normal(CommitId::from_hex("111111")); + let maybe_proto = ref_target_to_proto_legacy(&target); + assert_eq!(ref_target_from_proto(maybe_proto), target); // N-way conflict let target = RefTarget::from_legacy_form( @@ -553,7 +591,7 @@ mod tests { CommitId::from_hex("555555"), ], ); - let maybe_proto = ref_target_to_proto(&target); + let maybe_proto = ref_target_to_proto_legacy(&target); assert_eq!(ref_target_from_proto(maybe_proto), target); // Change-delete conflict @@ -561,7 +599,7 @@ mod tests { [CommitId::from_hex("111111")], [CommitId::from_hex("222222")], ); - let maybe_proto = ref_target_to_proto(&target); + let maybe_proto = ref_target_to_proto_legacy(&target); assert_eq!(ref_target_from_proto(maybe_proto), target); } } diff --git a/lib/tests/test_git.rs b/lib/tests/test_git.rs index 37050ec20..4f846fe2d 100644 --- a/lib/tests/test_git.rs +++ b/lib/tests/test_git.rs @@ -27,7 +27,7 @@ use jj_lib::commit_builder::CommitBuilder; use jj_lib::git; use jj_lib::git::{GitFetchError, GitPushError, GitRefUpdate, SubmoduleConfig}; use jj_lib::git_backend::GitBackend; -use jj_lib::op_store::{BranchTarget, RefTarget, RefTargetMap}; +use jj_lib::op_store::{BranchTarget, RefTarget}; use jj_lib::repo::{MutableRepo, ReadonlyRepo, Repo}; use jj_lib::settings::{GitSettings, UserSettings}; use jj_lib::view::RefName; @@ -110,14 +110,14 @@ fn test_import_refs() { let expected_main_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit2)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&commit1)), - }), + }, }; assert_eq!(view.get_branch("main"), Some(expected_main_branch).as_ref()); let expected_feature1_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit3)), - remote_targets: RefTargetMap::new(), + remote_targets: btreemap! {}, }; assert_eq!( view.get_branch("feature1"), @@ -125,7 +125,7 @@ fn test_import_refs() { ); let expected_feature2_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit4)), - remote_targets: RefTargetMap::new(), + remote_targets: btreemap! {}, }; assert_eq!( view.get_branch("feature2"), @@ -133,9 +133,9 @@ fn test_import_refs() { ); let expected_feature3_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit6)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&commit6)), - }), + }, }; assert_eq!( view.get_branch("feature3"), @@ -234,9 +234,9 @@ fn test_import_refs_reimport() { let commit2_target = RefTarget::normal(jj_id(&commit2)); let expected_main_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit2)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => commit1_target.clone(), - }), + }, }; assert_eq!(view.get_branch("main"), Some(expected_main_branch).as_ref()); let expected_feature2_branch = BranchTarget { @@ -244,7 +244,7 @@ fn test_import_refs_reimport() { [jj_id(&commit4)], [commit6.id().clone(), jj_id(&commit5)], ), - remote_targets: RefTargetMap::new(), + remote_targets: btreemap! {}, }; assert_eq!( view.get_branch("feature2"), @@ -432,18 +432,18 @@ fn test_import_refs_reimport_with_deleted_remote_ref() { // Even though the git repo does not have a local branch for `feature-remote-only`, jj // creates one. This follows the model explained in docs/branches.md. local_target: RefTarget::normal(jj_id(&commit_remote_only)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&commit_remote_only)), - }), + }, }), ); assert_eq!( view.get_branch("feature-remote-and-local"), Some(&BranchTarget { local_target: RefTarget::normal(jj_id(&commit_remote_and_local)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&commit_remote_and_local)), - }), + }, }), ); view.get_branch("main").unwrap(); // branch #3 of 3 @@ -521,18 +521,18 @@ fn test_import_refs_reimport_with_moved_remote_ref() { // Even though the git repo does not have a local branch for `feature-remote-only`, jj // creates one. This follows the model explained in docs/branches.md. local_target: RefTarget::normal(jj_id(&commit_remote_only)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&commit_remote_only)), - }), + }, }), ); assert_eq!( view.get_branch("feature-remote-and-local"), Some(&BranchTarget { local_target: RefTarget::normal(jj_id(&commit_remote_and_local)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&commit_remote_and_local)), - }), + }, }), ); view.get_branch("main").unwrap(); // branch #3 of 3 @@ -565,18 +565,18 @@ fn test_import_refs_reimport_with_moved_remote_ref() { view.get_branch("feature-remote-only"), Some(&BranchTarget { local_target: RefTarget::normal(jj_id(&new_commit_remote_only)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&new_commit_remote_only)), - }), + }, }), ); assert_eq!( view.get_branch("feature-remote-and-local"), Some(&BranchTarget { local_target: RefTarget::normal(jj_id(&new_commit_remote_and_local)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&new_commit_remote_and_local)), - }), + }, }), ); view.get_branch("main").unwrap(); // branch #3 of 3 @@ -706,9 +706,9 @@ fn test_import_some_refs() { let commit_feat4_target = RefTarget::normal(jj_id(&commit_feat4)); let expected_feature1_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit_feat1)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => commit_feat1_target, - }), + }, }; assert_eq!( view.get_branch("feature1"), @@ -716,9 +716,9 @@ fn test_import_some_refs() { ); let expected_feature2_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit_feat2)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => commit_feat2_target, - }), + }, }; assert_eq!( view.get_branch("feature2"), @@ -726,9 +726,9 @@ fn test_import_some_refs() { ); let expected_feature3_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit_feat3)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => commit_feat3_target, - }), + }, }; assert_eq!( view.get_branch("feature3"), @@ -736,9 +736,9 @@ fn test_import_some_refs() { ); let expected_feature4_branch = BranchTarget { local_target: RefTarget::normal(jj_id(&commit_feat4)), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => commit_feat4_target, - }), + }, }; assert_eq!( view.get_branch("feature4"), @@ -1143,9 +1143,9 @@ fn test_import_export_no_auto_local_branch() { let expected_branch = BranchTarget { local_target: RefTarget::absent(), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(jj_id(&git_commit)), - }), + }, }; assert_eq!( mut_repo.view().get_branch("main"), @@ -1495,9 +1495,9 @@ fn test_fetch_initial_commit() { btreemap! { "main".to_string() => BranchTarget { local_target: initial_commit_target.clone(), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => initial_commit_target, - }) + }, }, } ); @@ -1560,9 +1560,9 @@ fn test_fetch_success() { btreemap! { "main".to_string() => BranchTarget { local_target: new_commit_target.clone(), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => new_commit_target, - }), + }, }, } ); diff --git a/lib/tests/test_view.rs b/lib/tests/test_view.rs index 8995f96c9..a870a1d84 100644 --- a/lib/tests/test_view.rs +++ b/lib/tests/test_view.rs @@ -14,7 +14,7 @@ use std::sync::Arc; -use jj_lib::op_store::{BranchTarget, RefTarget, RefTargetMap, WorkspaceId}; +use jj_lib::op_store::{BranchTarget, RefTarget, WorkspaceId}; use jj_lib::repo::{ReadonlyRepo, Repo}; use jj_lib::settings::UserSettings; use jj_lib::transaction::Transaction; @@ -309,14 +309,14 @@ fn test_merge_views_branches() { main_branch_local_tx2.id().clone(), ], ), - remote_targets: RefTargetMap(btreemap! { + remote_targets: btreemap! { "origin".to_string() => RefTarget::normal(main_branch_origin_tx1.id().clone()), "alternate".to_string() => RefTarget::normal(main_branch_alternate_tx0.id().clone()), - }), + }, }; let expected_feature_branch = BranchTarget { local_target: RefTarget::normal(feature_branch_tx1.id().clone()), - remote_targets: RefTargetMap::new(), + remote_targets: btreemap! {}, }; assert_eq!( repo.view().branches(), diff --git a/tests/test_concurrent_operations.rs b/tests/test_concurrent_operations.rs index 2a247f8b0..fd2876821 100644 --- a/tests/test_concurrent_operations.rs +++ b/tests/test_concurrent_operations.rs @@ -53,15 +53,15 @@ fn test_concurrent_operations_auto_rebase() { test_env.jj_cmd_success(&repo_path, &["describe", "-m", "initial"]); let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]); insta::assert_snapshot!(stdout, @r###" - @ cde29280d4a9 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 + @ cfc96ff553b9 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ describe commit 123ed18e4c4c0d77428df41112bc02ffc83fb935 │ args: jj describe -m initial - ◉ 7c212e0863fd test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 + ◉ 65a6c90b9544 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ snapshot working copy │ args: jj describe -m initial - ◉ a99a3fd5c51e test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ 19b8089fc78b test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' - ◉ 56b94dfc38e7 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ f1c462c494be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 initialize repo "###); let op_id_hex = stdout[3..15].to_string(); @@ -160,21 +160,21 @@ fn test_concurrent_snapshot_wc_reloadable() { let template = r#"id ++ "\n" ++ description ++ "\n" ++ tags"#; let op_log_stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "-T", template]); insta::assert_snapshot!(op_log_stdout, @r###" - @ bacc8f507ccede29c282bb1459b71ffd233a9e29f4ec11b027422923592c4ef949e4465fd101c99ee6cfd39af26f29cd5910ef3b16985538e50fd21523bcf3e1 + @ 9be517934aaabc351597e88ed4119aa9454ae3588ab7f28646a810272c82f3dafb1deb20b3c978dbb58ba9abc8f08fe870fe3c7ce5f682411991e83eee40a77f │ commit 323b414dd255b51375d7f4392b7b2641ffe4289f │ args: jj commit -m 'new child1' - ◉ 6aa8b9099660e021813be72b4230692f782699448947cc76ffe5245d23108184dd60bb795fba350eb39abc2b0643169623cf59bb0c04130102388f8d87791070 + ◉ d967c09eb12b38dad2065a0bc9e251824247f9f84ba406a7356f5405e4c93c21562178a3f00cafedfa1df1435ba496265f39da9d1ccebaccb78bdcb4bd7031e1 │ snapshot working copy │ args: jj commit -m 'new child1' - ◉ 7a31786821de03db03d5b9b7ec97454d10b90eee33844a86ebd282124c8ab43babcf79091cd1c47796ed8cc6ff23febabb35a10e0aad1f96428f958eb834b30d + ◉ b6d168ba4fb4534257b6e58d53eb407582567342358eab07cf5a01a7e4d797313b692f27664c2fb7935b2380d398d0298233c9732f821b8c687e35607ea08a55 │ commit 3d918700494a9895696e955b85fa05eb0d314cc6 │ args: jj commit -m initial - ◉ 36440b76f049c4999505fa1dd3b21bfdb1a1f93c64e04f0a3797e704145780df959afcd3babd59c536544c6e7a6b883594abe7b38e8c4be5fe6d66a3cd8f4f9d + ◉ 5e9e3f82fc14750ff985c5a39f1935ed8876b973b8800b56bc03d1c9754795e724956d862d1fcb2c533d06ca36abc9fa9f7cb7d3b2b64e993e9a87f80d5af670 │ snapshot working copy │ args: jj commit -m initial - ◉ a99a3fd5c51e8f7ccb9ae2f9fb749612a23f0a7cf25d8c644f36c35c077449ce3c66f49d098a5a704ca5e47089a7f019563a5b8cbc7d451619e0f90c82241ceb + ◉ 19b8089fc78b7c49171f3c8934248be6f89f52311005e961cab5780f9f138b142456d77b27d223d7ee84d21d8c30c4a80100eaf6735b548b1acd0da688f94c80 │ add workspace 'default' - ◉ 56b94dfc38e7d54340377f566e96ab97dc6163ea7841daf49fb2e1d1ceb27e26274db1245835a1a421fb9d06e6e0fe1e4f4aa1b0258c6e86df676ad9111d0dab + ◉ f1c462c494be39f6690928603c5393f908866bc8d81d8cd1ae0bb2ea02cb4f78cafa47165fa5b7cda258e2178f846881de199066991960a80954ba6066ba0821 initialize repo "###); let op_log_lines = op_log_stdout.lines().collect_vec(); diff --git a/tests/test_debug_command.rs b/tests/test_debug_command.rs index a783f2491..da3f15797 100644 --- a/tests/test_debug_command.rs +++ b/tests/test_debug_command.rs @@ -126,7 +126,7 @@ fn test_debug_operation_id() { let stdout = test_env.jj_cmd_success(&workspace_path, &["debug", "operation", "--display", "id"]); assert_snapshot!(filter_index_stats(&stdout), @r###" - a99a3fd5c51e8f7ccb9ae2f9fb749612a23f0a7cf25d8c644f36c35c077449ce3c66f49d098a5a704ca5e47089a7f019563a5b8cbc7d451619e0f90c82241ceb + 19b8089fc78b7c49171f3c8934248be6f89f52311005e961cab5780f9f138b142456d77b27d223d7ee84d21d8c30c4a80100eaf6735b548b1acd0da688f94c80 "### ); } diff --git a/tests/test_operations.rs b/tests/test_operations.rs index 5d0db91be..91629e9e6 100644 --- a/tests/test_operations.rs +++ b/tests/test_operations.rs @@ -38,12 +38,12 @@ fn test_op_log() { ], ); insta::assert_snapshot!(&stdout, @r###" - @ 45108169c0f8 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 + @ 98f7262e4a06 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22 │ args: jj describe -m 'description 0' - ◉ a99a3fd5c51e test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ 19b8089fc78b test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' - ◉ 56b94dfc38e7 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ f1c462c494be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 initialize repo "###); let op_log_lines = stdout.lines().collect_vec(); @@ -126,14 +126,14 @@ fn test_op_log_template() { let render = |template| test_env.jj_cmd_success(&repo_path, &["op", "log", "-T", template]); insta::assert_snapshot!(render(r#"id ++ "\n""#), @r###" - @ a99a3fd5c51e8f7ccb9ae2f9fb749612a23f0a7cf25d8c644f36c35c077449ce3c66f49d098a5a704ca5e47089a7f019563a5b8cbc7d451619e0f90c82241ceb - ◉ 56b94dfc38e7d54340377f566e96ab97dc6163ea7841daf49fb2e1d1ceb27e26274db1245835a1a421fb9d06e6e0fe1e4f4aa1b0258c6e86df676ad9111d0dab + @ 19b8089fc78b7c49171f3c8934248be6f89f52311005e961cab5780f9f138b142456d77b27d223d7ee84d21d8c30c4a80100eaf6735b548b1acd0da688f94c80 + ◉ f1c462c494be39f6690928603c5393f908866bc8d81d8cd1ae0bb2ea02cb4f78cafa47165fa5b7cda258e2178f846881de199066991960a80954ba6066ba0821 "###); insta::assert_snapshot!( render(r#"separate(" ", id.short(5), current_operation, user, time.start(), time.end(), time.duration()) ++ "\n""#), @r###" - @ a99a3 true test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond - ◉ 56b94 false test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond + @ 19b80 true test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond + ◉ f1c46 false test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond "###); // Test the default template, i.e. with relative start time and duration. We // don't generally use that template because it depends on the current time, @@ -147,9 +147,9 @@ fn test_op_log_template() { let regex = Regex::new(r"\d\d years").unwrap(); let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]); insta::assert_snapshot!(regex.replace_all(&stdout, "NN years"), @r###" - @ a99a3fd5c51e test-username@host.example.com NN years ago, lasted less than a microsecond + @ 19b8089fc78b test-username@host.example.com NN years ago, lasted less than a microsecond │ add workspace 'default' - ◉ 56b94dfc38e7 test-username@host.example.com NN years ago, lasted less than a microsecond + ◉ f1c462c494be test-username@host.example.com NN years ago, lasted less than a microsecond initialize repo "###); } @@ -163,24 +163,24 @@ fn test_op_log_builtin_templates() { test_env.jj_cmd_success(&repo_path, &["describe", "-m", "description 0"]); insta::assert_snapshot!(render(r#"builtin_op_log_compact"#), @r###" - @ 45108169c0f8 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 + @ 98f7262e4a06 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22 │ args: jj describe -m 'description 0' - ◉ a99a3fd5c51e test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ 19b8089fc78b test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' - ◉ 56b94dfc38e7 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ f1c462c494be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 initialize repo "###); insta::assert_snapshot!(render(r#"builtin_op_log_comfortable"#), @r###" - @ 45108169c0f8 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 + @ 98f7262e4a06 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00 │ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22 │ args: jj describe -m 'description 0' │ - ◉ a99a3fd5c51e test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ 19b8089fc78b test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' │ - ◉ 56b94dfc38e7 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ f1c462c494be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 initialize repo "###); @@ -207,18 +207,18 @@ fn test_op_log_word_wrap() { // ui.log-word-wrap option works insta::assert_snapshot!(render(&["op", "log"], 40, false), @r###" - @ a99a3fd5c51e test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + @ 19b8089fc78b test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' - ◉ 56b94dfc38e7 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 + ◉ f1c462c494be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 initialize repo "###); insta::assert_snapshot!(render(&["op", "log"], 40, true), @r###" - @ a99a3fd5c51e + @ 19b8089fc78b │ test-username@host.example.com │ 2001-02-03 04:05:07.000 +07:00 - │ 2001-02-03 04:05:07.000 +07:00 │ add workspace 'default' - ◉ 56b94dfc38e7 + ◉ f1c462c494be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 diff --git a/tests/test_workspaces.rs b/tests/test_workspaces.rs index 74193994a..84cc702af 100644 --- a/tests/test_workspaces.rs +++ b/tests/test_workspaces.rs @@ -114,14 +114,14 @@ fn test_workspaces_conflicting_edits() { "###); let stderr = test_env.jj_cmd_failure(&secondary_path, &["st"]); insta::assert_snapshot!(stderr, @r###" - Error: The working copy is stale (not updated since operation 815bb8fcbd7a). + Error: The working copy is stale (not updated since operation 5c95db542ebd). Hint: Run `jj workspace update-stale` to update it. See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-working-copy for more information. "###); // Same error on second run, and from another command let stderr = test_env.jj_cmd_failure(&secondary_path, &["log"]); insta::assert_snapshot!(stderr, @r###" - Error: The working copy is stale (not updated since operation 815bb8fcbd7a). + Error: The working copy is stale (not updated since operation 5c95db542ebd). Hint: Run `jj workspace update-stale` to update it. See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-working-copy for more information. "###); @@ -199,7 +199,7 @@ fn test_workspaces_updated_by_other() { "###); let stderr = test_env.jj_cmd_failure(&secondary_path, &["st"]); insta::assert_snapshot!(stderr, @r###" - Error: The working copy is stale (not updated since operation 815bb8fcbd7a). + Error: The working copy is stale (not updated since operation 5c95db542ebd). Hint: Run `jj workspace update-stale` to update it. See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-working-copy for more information. "###);