@lix-js/sdk 0.6.0-preview.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -4
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +19 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.js +3 -3
- package/dist/native.d.ts +1 -0
- package/dist/native.js +47 -0
- package/dist/open-lix.d.ts +38 -207
- package/dist/open-lix.js +59 -284
- package/dist/result.d.ts +18 -0
- package/dist/result.js +48 -0
- package/dist/types.d.ts +114 -1
- package/dist/value.d.ts +28 -0
- package/dist/value.js +245 -0
- package/package.json +38 -71
- package/SKILL.md +0 -507
- package/dist/builtin-schemas.d.ts +0 -1
- package/dist/builtin-schemas.js +0 -1
- package/dist/engine-wasm/index.d.ts +0 -87
- package/dist/engine-wasm/index.js +0 -339
- package/dist/engine-wasm/wasm/lix_engine.d.ts +0 -79
- package/dist/engine-wasm/wasm/lix_engine.js +0 -833
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +0 -27
- package/dist/generated/builtin-schemas.d.ts +0 -427
- package/dist/generated/builtin-schemas.js +0 -643
- package/dist/sqlite/index.d.ts +0 -12
- package/dist/sqlite/index.js +0 -359
- package/dist-engine-src/README.md +0 -18
- package/dist-engine-src/src/backend/capabilities.rs +0 -67
- package/dist-engine-src/src/backend/conformance/baseline.rs +0 -1127
- package/dist-engine-src/src/backend/conformance/factory.rs +0 -93
- package/dist-engine-src/src/backend/conformance/failure_tests.rs +0 -608
- package/dist-engine-src/src/backend/conformance/fixtures.rs +0 -26
- package/dist-engine-src/src/backend/conformance/mod.rs +0 -75
- package/dist-engine-src/src/backend/conformance/model.rs +0 -28
- package/dist-engine-src/src/backend/conformance/model_based.rs +0 -257
- package/dist-engine-src/src/backend/conformance/persistence.rs +0 -204
- package/dist-engine-src/src/backend/conformance/projection.rs +0 -21
- package/dist-engine-src/src/backend/conformance/pushdown.rs +0 -24
- package/dist-engine-src/src/backend/conformance/runner.rs +0 -90
- package/dist-engine-src/src/backend/conformance/scan.rs +0 -24
- package/dist-engine-src/src/backend/conformance/write.rs +0 -16
- package/dist-engine-src/src/backend/error.rs +0 -94
- package/dist-engine-src/src/backend/in_memory.rs +0 -670
- package/dist-engine-src/src/backend/mod.rs +0 -39
- package/dist-engine-src/src/backend/predicate.rs +0 -80
- package/dist-engine-src/src/backend/traits.rs +0 -260
- package/dist-engine-src/src/backend/types.rs +0 -239
- package/dist-engine-src/src/binary_cas/chunking.rs +0 -31
- package/dist-engine-src/src/binary_cas/codec.rs +0 -346
- package/dist-engine-src/src/binary_cas/context.rs +0 -139
- package/dist-engine-src/src/binary_cas/kv.rs +0 -1038
- package/dist-engine-src/src/binary_cas/mod.rs +0 -11
- package/dist-engine-src/src/binary_cas/types.rs +0 -121
- package/dist-engine-src/src/branch/context.rs +0 -40
- package/dist-engine-src/src/branch/lifecycle.rs +0 -221
- package/dist-engine-src/src/branch/mod.rs +0 -13
- package/dist-engine-src/src/branch/refs.rs +0 -321
- package/dist-engine-src/src/branch/stage_rows.rs +0 -67
- package/dist-engine-src/src/branch/types.rs +0 -21
- package/dist-engine-src/src/catalog/context.rs +0 -412
- package/dist-engine-src/src/catalog/mod.rs +0 -10
- package/dist-engine-src/src/catalog/schema.rs +0 -4
- package/dist-engine-src/src/catalog/snapshot.rs +0 -1114
- package/dist-engine-src/src/cel/context.rs +0 -86
- package/dist-engine-src/src/cel/error.rs +0 -19
- package/dist-engine-src/src/cel/mod.rs +0 -8
- package/dist-engine-src/src/cel/provider.rs +0 -9
- package/dist-engine-src/src/cel/runtime.rs +0 -167
- package/dist-engine-src/src/cel/value.rs +0 -50
- package/dist-engine-src/src/changelog/bench_support.rs +0 -785
- package/dist-engine-src/src/changelog/change.rs +0 -1
- package/dist-engine-src/src/changelog/codec.rs +0 -497
- package/dist-engine-src/src/changelog/commit.rs +0 -1
- package/dist-engine-src/src/changelog/context.rs +0 -1614
- package/dist-engine-src/src/changelog/mod.rs +0 -29
- package/dist-engine-src/src/changelog/store.rs +0 -163
- package/dist-engine-src/src/changelog/test_support.rs +0 -54
- package/dist-engine-src/src/changelog/types.rs +0 -213
- package/dist-engine-src/src/commit_graph/context.rs +0 -944
- package/dist-engine-src/src/commit_graph/mod.rs +0 -9
- package/dist-engine-src/src/commit_graph/types.rs +0 -89
- package/dist-engine-src/src/commit_graph/walker.rs +0 -786
- package/dist-engine-src/src/common/error.rs +0 -347
- package/dist-engine-src/src/common/fingerprint.rs +0 -3
- package/dist-engine-src/src/common/fs_path.rs +0 -1336
- package/dist-engine-src/src/common/identity.rs +0 -145
- package/dist-engine-src/src/common/json_pointer.rs +0 -67
- package/dist-engine-src/src/common/metadata.rs +0 -40
- package/dist-engine-src/src/common/mod.rs +0 -23
- package/dist-engine-src/src/common/types.rs +0 -105
- package/dist-engine-src/src/common/wire.rs +0 -222
- package/dist-engine-src/src/domain.rs +0 -320
- package/dist-engine-src/src/engine.rs +0 -203
- package/dist-engine-src/src/entity_pk.rs +0 -402
- package/dist-engine-src/src/functions/context.rs +0 -296
- package/dist-engine-src/src/functions/deterministic.rs +0 -113
- package/dist-engine-src/src/functions/mod.rs +0 -18
- package/dist-engine-src/src/functions/provider.rs +0 -130
- package/dist-engine-src/src/functions/state.rs +0 -335
- package/dist-engine-src/src/functions/types.rs +0 -37
- package/dist-engine-src/src/init.rs +0 -692
- package/dist-engine-src/src/json_store/compression.rs +0 -77
- package/dist-engine-src/src/json_store/context.rs +0 -172
- package/dist-engine-src/src/json_store/encoded.rs +0 -15
- package/dist-engine-src/src/json_store/mod.rs +0 -38
- package/dist-engine-src/src/json_store/store.rs +0 -494
- package/dist-engine-src/src/json_store/types.rs +0 -212
- package/dist-engine-src/src/lib.rs +0 -92
- package/dist-engine-src/src/live_state/context.rs +0 -1883
- package/dist-engine-src/src/live_state/mod.rs +0 -21
- package/dist-engine-src/src/live_state/overlay.rs +0 -75
- package/dist-engine-src/src/live_state/reader.rs +0 -23
- package/dist-engine-src/src/live_state/types.rs +0 -231
- package/dist-engine-src/src/live_state/visibility.rs +0 -666
- package/dist-engine-src/src/plugin/archive.rs +0 -438
- package/dist-engine-src/src/plugin/component.rs +0 -183
- package/dist-engine-src/src/plugin/install.rs +0 -619
- package/dist-engine-src/src/plugin/manifest.rs +0 -516
- package/dist-engine-src/src/plugin/materializer.rs +0 -202
- package/dist-engine-src/src/plugin/mod.rs +0 -33
- package/dist-engine-src/src/plugin/plugin_manifest.json +0 -119
- package/dist-engine-src/src/plugin/storage.rs +0 -74
- package/dist-engine-src/src/schema/annotations/defaults.rs +0 -275
- package/dist-engine-src/src/schema/annotations/mod.rs +0 -1
- package/dist-engine-src/src/schema/builtin/lix_account.json +0 -21
- package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -29
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -29
- package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +0 -34
- package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +0 -48
- package/dist-engine-src/src/schema/builtin/lix_change.json +0 -63
- package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -45
- package/dist-engine-src/src/schema/builtin/lix_commit.json +0 -24
- package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +0 -53
- package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -52
- package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -52
- package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -40
- package/dist-engine-src/src/schema/builtin/lix_label.json +0 -29
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +0 -74
- package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +0 -25
- package/dist-engine-src/src/schema/builtin/mod.rs +0 -220
- package/dist-engine-src/src/schema/compatibility.rs +0 -787
- package/dist-engine-src/src/schema/definition.json +0 -187
- package/dist-engine-src/src/schema/definition.rs +0 -742
- package/dist-engine-src/src/schema/key.rs +0 -138
- package/dist-engine-src/src/schema/mod.rs +0 -20
- package/dist-engine-src/src/schema/seed.rs +0 -14
- package/dist-engine-src/src/schema/tests.rs +0 -780
- package/dist-engine-src/src/session/context.rs +0 -1059
- package/dist-engine-src/src/session/create_branch.rs +0 -94
- package/dist-engine-src/src/session/execute.rs +0 -681
- package/dist-engine-src/src/session/merge/analysis.rs +0 -108
- package/dist-engine-src/src/session/merge/branch.rs +0 -417
- package/dist-engine-src/src/session/merge/conflicts.rs +0 -63
- package/dist-engine-src/src/session/merge/mod.rs +0 -10
- package/dist-engine-src/src/session/merge/stats.rs +0 -61
- package/dist-engine-src/src/session/mod.rs +0 -30
- package/dist-engine-src/src/session/switch_branch.rs +0 -113
- package/dist-engine-src/src/session/transaction.rs +0 -557
- package/dist-engine-src/src/sql2/bind/classify.rs +0 -102
- package/dist-engine-src/src/sql2/bind/error.rs +0 -5
- package/dist-engine-src/src/sql2/bind/expr.rs +0 -29
- package/dist-engine-src/src/sql2/bind/mod.rs +0 -12
- package/dist-engine-src/src/sql2/bind/public_udf.rs +0 -306
- package/dist-engine-src/src/sql2/bind/read.rs +0 -65
- package/dist-engine-src/src/sql2/bind/statement.rs +0 -2236
- package/dist-engine-src/src/sql2/bind/table.rs +0 -273
- package/dist-engine-src/src/sql2/bind/write.rs +0 -86
- package/dist-engine-src/src/sql2/branch_scope.rs +0 -436
- package/dist-engine-src/src/sql2/catalog/capability.rs +0 -20
- package/dist-engine-src/src/sql2/catalog/entity_surface.rs +0 -296
- package/dist-engine-src/src/sql2/catalog/mod.rs +0 -15
- package/dist-engine-src/src/sql2/catalog/registry.rs +0 -556
- package/dist-engine-src/src/sql2/catalog/schema.rs +0 -88
- package/dist-engine-src/src/sql2/catalog/surface.rs +0 -41
- package/dist-engine-src/src/sql2/change_materialization.rs +0 -122
- package/dist-engine-src/src/sql2/context.rs +0 -317
- package/dist-engine-src/src/sql2/dml.rs +0 -148
- package/dist-engine-src/src/sql2/error.rs +0 -215
- package/dist-engine-src/src/sql2/exec/bound_public_write.rs +0 -1593
- package/dist-engine-src/src/sql2/exec/datafusion.rs +0 -5266
- package/dist-engine-src/src/sql2/exec/fast_write.rs +0 -82
- package/dist-engine-src/src/sql2/exec/mod.rs +0 -24
- package/dist-engine-src/src/sql2/exec/write.rs +0 -661
- package/dist-engine-src/src/sql2/filesystem_planner.rs +0 -1485
- package/dist-engine-src/src/sql2/filesystem_predicates.rs +0 -159
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +0 -383
- package/dist-engine-src/src/sql2/history_projection.rs +0 -56
- package/dist-engine-src/src/sql2/history_route.rs +0 -661
- package/dist-engine-src/src/sql2/mod.rs +0 -52
- package/dist-engine-src/src/sql2/optimize/datafusion.rs +0 -1
- package/dist-engine-src/src/sql2/optimize/mod.rs +0 -2
- package/dist-engine-src/src/sql2/optimize/simple_write.rs +0 -116
- package/dist-engine-src/src/sql2/parse/mod.rs +0 -69
- package/dist-engine-src/src/sql2/parse/normalize.rs +0 -1
- package/dist-engine-src/src/sql2/plan/branch_scope.rs +0 -24
- package/dist-engine-src/src/sql2/plan/mod.rs +0 -5
- package/dist-engine-src/src/sql2/plan/predicate.rs +0 -22
- package/dist-engine-src/src/sql2/plan/write.rs +0 -147
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +0 -504
- package/dist-engine-src/src/sql2/providers/branch.rs +0 -1206
- package/dist-engine-src/src/sql2/providers/change.rs +0 -445
- package/dist-engine-src/src/sql2/providers/directory.rs +0 -2422
- package/dist-engine-src/src/sql2/providers/directory_history.rs +0 -645
- package/dist-engine-src/src/sql2/providers/entity.rs +0 -1484
- package/dist-engine-src/src/sql2/providers/entity_history.rs +0 -452
- package/dist-engine-src/src/sql2/providers/file.rs +0 -3686
- package/dist-engine-src/src/sql2/providers/file_history.rs +0 -924
- package/dist-engine-src/src/sql2/providers/history.rs +0 -426
- package/dist-engine-src/src/sql2/providers/lix_state.rs +0 -2542
- package/dist-engine-src/src/sql2/providers/mod.rs +0 -508
- package/dist-engine-src/src/sql2/read_only.rs +0 -63
- package/dist-engine-src/src/sql2/record_batch.rs +0 -17
- package/dist-engine-src/src/sql2/result_metadata.rs +0 -29
- package/dist-engine-src/src/sql2/runtime.rs +0 -60
- package/dist-engine-src/src/sql2/session.rs +0 -83
- package/dist-engine-src/src/sql2/storage/constraints.rs +0 -1
- package/dist-engine-src/src/sql2/storage/mod.rs +0 -1
- package/dist-engine-src/src/sql2/test_support/differential.rs +0 -712
- package/dist-engine-src/src/sql2/test_support/generators.rs +0 -354
- package/dist-engine-src/src/sql2/test_support/mod.rs +0 -2
- package/dist-engine-src/src/sql2/udfs/common.rs +0 -295
- package/dist-engine-src/src/sql2/udfs/lix_active_branch_commit_id.rs +0 -53
- package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +0 -47
- package/dist-engine-src/src/sql2/udfs/lix_json.rs +0 -100
- package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +0 -99
- package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +0 -99
- package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +0 -82
- package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +0 -85
- package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +0 -76
- package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +0 -76
- package/dist-engine-src/src/sql2/udfs/mod.rs +0 -86
- package/dist-engine-src/src/sql2/write_normalization.rs +0 -368
- package/dist-engine-src/src/storage/conformance.rs +0 -399
- package/dist-engine-src/src/storage/context.rs +0 -620
- package/dist-engine-src/src/storage/mod.rs +0 -52
- package/dist-engine-src/src/storage/point.rs +0 -440
- package/dist-engine-src/src/storage/read_scope.rs +0 -67
- package/dist-engine-src/src/storage/reader.rs +0 -867
- package/dist-engine-src/src/storage/scan.rs +0 -784
- package/dist-engine-src/src/storage/spaces.rs +0 -236
- package/dist-engine-src/src/storage/stats.rs +0 -80
- package/dist-engine-src/src/storage/write_set.rs +0 -962
- package/dist-engine-src/src/storage_bench.rs +0 -171
- package/dist-engine-src/src/test_support.rs +0 -450
- package/dist-engine-src/src/tracked_state/bench_support.rs +0 -394
- package/dist-engine-src/src/tracked_state/codec.rs +0 -1183
- package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +0 -358
- package/dist-engine-src/src/tracked_state/context.rs +0 -2801
- package/dist-engine-src/src/tracked_state/diff.rs +0 -2140
- package/dist-engine-src/src/tracked_state/merge.rs +0 -478
- package/dist-engine-src/src/tracked_state/mod.rs +0 -35
- package/dist-engine-src/src/tracked_state/row_materialization.rs +0 -275
- package/dist-engine-src/src/tracked_state/storage.rs +0 -427
- package/dist-engine-src/src/tracked_state/tree.rs +0 -3063
- package/dist-engine-src/src/tracked_state/types.rs +0 -238
- package/dist-engine-src/src/transaction/bench_support.rs +0 -407
- package/dist-engine-src/src/transaction/commit.rs +0 -1592
- package/dist-engine-src/src/transaction/context.rs +0 -1653
- package/dist-engine-src/src/transaction/mod.rs +0 -24
- package/dist-engine-src/src/transaction/normalization.rs +0 -877
- package/dist-engine-src/src/transaction/prep.rs +0 -37
- package/dist-engine-src/src/transaction/schema_resolver.rs +0 -163
- package/dist-engine-src/src/transaction/staging.rs +0 -1525
- package/dist-engine-src/src/transaction/types.rs +0 -403
- package/dist-engine-src/src/transaction/validation.rs +0 -5766
- package/dist-engine-src/src/untracked_state/codec.rs +0 -615
- package/dist-engine-src/src/untracked_state/context.rs +0 -98
- package/dist-engine-src/src/untracked_state/materialization.rs +0 -63
- package/dist-engine-src/src/untracked_state/mod.rs +0 -15
- package/dist-engine-src/src/untracked_state/storage.rs +0 -898
- package/dist-engine-src/src/untracked_state/types.rs +0 -146
- package/dist-engine-src/src/wasm/mod.rs +0 -60
|
@@ -1,786 +0,0 @@
|
|
|
1
|
-
use std::collections::{BTreeMap, BTreeSet};
|
|
2
|
-
|
|
3
|
-
use crate::commit_graph::{CommitGraphCommit, CommitGraphStoreReader, ReachableCommitGraphCommit};
|
|
4
|
-
use crate::storage::StorageRead;
|
|
5
|
-
use crate::LixError;
|
|
6
|
-
|
|
7
|
-
/// Walks parent links from `head_commit_id` and returns reachable commits
|
|
8
|
-
/// nearest-first.
|
|
9
|
-
///
|
|
10
|
-
/// The walker is intentionally storage-free. It asks `CommitGraphReader` to
|
|
11
|
-
/// load parsed commit facts and owns only traversal concerns: caching, cycle
|
|
12
|
-
/// detection, and nearest-depth selection.
|
|
13
|
-
pub(crate) async fn walk_reachable_commits<S>(
|
|
14
|
-
reader: &mut CommitGraphStoreReader<S>,
|
|
15
|
-
head_commit_id: &str,
|
|
16
|
-
) -> Result<Vec<ReachableCommitGraphCommit>, LixError>
|
|
17
|
-
where
|
|
18
|
-
S: StorageRead + Send + Sync,
|
|
19
|
-
{
|
|
20
|
-
let mut loader = CommitTraversalLoader::new(reader);
|
|
21
|
-
let mut visiting = BTreeSet::new();
|
|
22
|
-
let mut nearest_depths = BTreeMap::new();
|
|
23
|
-
loader
|
|
24
|
-
.walk_commit(head_commit_id, 0, &mut visiting, &mut nearest_depths)
|
|
25
|
-
.await?;
|
|
26
|
-
|
|
27
|
-
let mut commits = nearest_depths
|
|
28
|
-
.into_iter()
|
|
29
|
-
.map(|(commit_id, depth)| {
|
|
30
|
-
let commit = loader
|
|
31
|
-
.loaded
|
|
32
|
-
.remove(&commit_id)
|
|
33
|
-
.expect("visited commit should be cached");
|
|
34
|
-
ReachableCommitGraphCommit { commit, depth }
|
|
35
|
-
})
|
|
36
|
-
.collect::<Vec<_>>();
|
|
37
|
-
commits.sort_by(|left, right| {
|
|
38
|
-
left.depth
|
|
39
|
-
.cmp(&right.depth)
|
|
40
|
-
.then_with(|| left.commit.commit_id.cmp(&right.commit.commit_id))
|
|
41
|
-
});
|
|
42
|
-
Ok(commits)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/// Returns the best common ancestors shared by two commit heads.
|
|
46
|
-
///
|
|
47
|
-
/// This is graph math, not merge policy. A commit is "best" when it is a
|
|
48
|
-
/// common ancestor and no descendant of it is also a common ancestor.
|
|
49
|
-
///
|
|
50
|
-
/// Simple history has one best common ancestor:
|
|
51
|
-
///
|
|
52
|
-
/// ```text
|
|
53
|
-
/// A -- B -- C left
|
|
54
|
-
/// \
|
|
55
|
-
/// D right
|
|
56
|
-
/// ```
|
|
57
|
-
///
|
|
58
|
-
/// `best_common_ancestors(C, D)` returns `[B]`.
|
|
59
|
-
///
|
|
60
|
-
/// Commit history is a DAG, not a tree, so criss-cross histories can have
|
|
61
|
-
/// multiple equally good answers. Callers that need one merge base should wrap
|
|
62
|
-
/// this API with an explicit policy instead of pretending the graph always has
|
|
63
|
-
/// a single lowest common ancestor.
|
|
64
|
-
pub(crate) async fn best_common_ancestors<S>(
|
|
65
|
-
reader: &mut CommitGraphStoreReader<S>,
|
|
66
|
-
left_commit_id: &str,
|
|
67
|
-
right_commit_id: &str,
|
|
68
|
-
) -> Result<Vec<CommitGraphCommit>, LixError>
|
|
69
|
-
where
|
|
70
|
-
S: StorageRead + Send + Sync,
|
|
71
|
-
{
|
|
72
|
-
let left_reachable = walk_reachable_commits(reader, left_commit_id).await?;
|
|
73
|
-
let right_reachable = walk_reachable_commits(reader, right_commit_id).await?;
|
|
74
|
-
let right_ids = right_reachable
|
|
75
|
-
.iter()
|
|
76
|
-
.map(|reachable| reachable.commit.commit_id.clone())
|
|
77
|
-
.collect::<BTreeSet<_>>();
|
|
78
|
-
let common_ids = left_reachable
|
|
79
|
-
.iter()
|
|
80
|
-
.filter(|reachable| right_ids.contains(&reachable.commit.commit_id))
|
|
81
|
-
.map(|reachable| reachable.commit.commit_id.clone())
|
|
82
|
-
.collect::<BTreeSet<_>>();
|
|
83
|
-
|
|
84
|
-
let mut best = Vec::new();
|
|
85
|
-
for reachable in left_reachable {
|
|
86
|
-
let commit_id = &reachable.commit.commit_id;
|
|
87
|
-
if !common_ids.contains(commit_id) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if has_descendant_in_set(reader, commit_id, &common_ids).await? {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
best.push(reachable.commit);
|
|
96
|
-
}
|
|
97
|
-
best.sort_by(|left, right| left.commit_id.cmp(&right.commit_id));
|
|
98
|
-
Ok(best)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async fn has_descendant_in_set<S>(
|
|
102
|
-
reader: &mut CommitGraphStoreReader<S>,
|
|
103
|
-
commit_id: &str,
|
|
104
|
-
candidate_descendant_ids: &BTreeSet<String>,
|
|
105
|
-
) -> Result<bool, LixError>
|
|
106
|
-
where
|
|
107
|
-
S: StorageRead + Send + Sync,
|
|
108
|
-
{
|
|
109
|
-
for candidate_descendant_id in candidate_descendant_ids {
|
|
110
|
-
if candidate_descendant_id == commit_id {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
let reachable = walk_reachable_commits(reader, candidate_descendant_id).await?;
|
|
114
|
-
if reachable
|
|
115
|
-
.iter()
|
|
116
|
-
.any(|reachable| reachable.commit.commit_id == commit_id)
|
|
117
|
-
{
|
|
118
|
-
return Ok(true);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
Ok(false)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
struct CommitTraversalLoader<'a, S>
|
|
125
|
-
where
|
|
126
|
-
S: StorageRead + Send + Sync,
|
|
127
|
-
{
|
|
128
|
-
reader: &'a mut CommitGraphStoreReader<S>,
|
|
129
|
-
loaded: BTreeMap<String, CommitGraphCommit>,
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
impl<'a, S> CommitTraversalLoader<'a, S>
|
|
133
|
-
where
|
|
134
|
-
S: StorageRead + Send + Sync,
|
|
135
|
-
{
|
|
136
|
-
fn new(reader: &'a mut CommitGraphStoreReader<S>) -> Self {
|
|
137
|
-
Self {
|
|
138
|
-
reader,
|
|
139
|
-
loaded: BTreeMap::new(),
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async fn walk_commit(
|
|
144
|
-
&mut self,
|
|
145
|
-
commit_id: &str,
|
|
146
|
-
depth: u32,
|
|
147
|
-
visiting: &mut BTreeSet<String>,
|
|
148
|
-
nearest_depths: &mut BTreeMap<String, u32>,
|
|
149
|
-
) -> Result<(), LixError> {
|
|
150
|
-
let mut stack = vec![TraversalFrame {
|
|
151
|
-
commit_id: commit_id.to_string(),
|
|
152
|
-
depth,
|
|
153
|
-
expanded: false,
|
|
154
|
-
}];
|
|
155
|
-
|
|
156
|
-
while let Some(frame) = stack.pop() {
|
|
157
|
-
if frame.expanded {
|
|
158
|
-
visiting.remove(&frame.commit_id);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if visiting.contains(&frame.commit_id) {
|
|
163
|
-
return Err(LixError::new(
|
|
164
|
-
"LIX_ERROR_UNKNOWN",
|
|
165
|
-
format!(
|
|
166
|
-
"commit_graph cycle detected at commit '{}'",
|
|
167
|
-
frame.commit_id
|
|
168
|
-
),
|
|
169
|
-
));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if let Some(previous_depth) = nearest_depths.get(&frame.commit_id) {
|
|
173
|
-
if *previous_depth <= frame.depth {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
let commit = self.load_commit(&frame.commit_id).await?;
|
|
179
|
-
nearest_depths.insert(frame.commit_id.clone(), frame.depth);
|
|
180
|
-
|
|
181
|
-
visiting.insert(frame.commit_id.clone());
|
|
182
|
-
stack.push(TraversalFrame {
|
|
183
|
-
commit_id: frame.commit_id,
|
|
184
|
-
depth: frame.depth,
|
|
185
|
-
expanded: true,
|
|
186
|
-
});
|
|
187
|
-
for parent_commit_id in commit.parent_commit_ids.iter().rev() {
|
|
188
|
-
stack.push(TraversalFrame {
|
|
189
|
-
commit_id: parent_commit_id.clone(),
|
|
190
|
-
depth: frame.depth + 1,
|
|
191
|
-
expanded: false,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
Ok(())
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async fn load_commit(&mut self, commit_id: &str) -> Result<CommitGraphCommit, LixError> {
|
|
199
|
-
if let Some(commit) = self.loaded.get(commit_id) {
|
|
200
|
-
return Ok(commit.clone());
|
|
201
|
-
}
|
|
202
|
-
let Some(commit) = self.reader.load_commit(commit_id).await? else {
|
|
203
|
-
return Err(LixError::new(
|
|
204
|
-
"LIX_ERROR_UNKNOWN",
|
|
205
|
-
format!("commit_graph missing commit '{commit_id}'"),
|
|
206
|
-
));
|
|
207
|
-
};
|
|
208
|
-
self.loaded.insert(commit_id.to_string(), commit.clone());
|
|
209
|
-
Ok(commit)
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
struct TraversalFrame {
|
|
214
|
-
commit_id: String,
|
|
215
|
-
depth: u32,
|
|
216
|
-
expanded: bool,
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
#[cfg(test)]
|
|
220
|
-
mod tests {
|
|
221
|
-
use serde_json::json;
|
|
222
|
-
|
|
223
|
-
use crate::changelog::{
|
|
224
|
-
ChangelogAppend, ChangelogContext, ChangelogWriter, CommitChangeRefSet, CommitRecord,
|
|
225
|
-
};
|
|
226
|
-
use crate::commit_graph::CommitGraphChange;
|
|
227
|
-
use crate::commit_graph::CommitGraphContext;
|
|
228
|
-
use crate::storage::StorageContext;
|
|
229
|
-
use crate::storage::{InMemoryStorageBackend, StorageReadOptions, StorageWriteOptions};
|
|
230
|
-
use crate::LixError;
|
|
231
|
-
|
|
232
|
-
#[tokio::test]
|
|
233
|
-
async fn reachable_commits_returns_commits_nearest_first() {
|
|
234
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
235
|
-
append_changes(
|
|
236
|
-
&storage,
|
|
237
|
-
&[
|
|
238
|
-
commit_change("commit-root-change", "commit-root", &[], &[]),
|
|
239
|
-
commit_change(
|
|
240
|
-
"commit-parent-change",
|
|
241
|
-
"commit-parent",
|
|
242
|
-
&[],
|
|
243
|
-
&["commit-root"],
|
|
244
|
-
),
|
|
245
|
-
commit_change("commit-head-change", "commit-head", &[], &["commit-parent"]),
|
|
246
|
-
],
|
|
247
|
-
)
|
|
248
|
-
.await;
|
|
249
|
-
|
|
250
|
-
let graph = CommitGraphContext::new();
|
|
251
|
-
let read = storage
|
|
252
|
-
.begin_read(StorageReadOptions::default())
|
|
253
|
-
.expect("read should open");
|
|
254
|
-
let mut reader = graph.reader(read);
|
|
255
|
-
let commits = reader
|
|
256
|
-
.reachable_commits("commit-head")
|
|
257
|
-
.await
|
|
258
|
-
.expect("reachable commits should load");
|
|
259
|
-
|
|
260
|
-
assert_eq!(
|
|
261
|
-
commits
|
|
262
|
-
.iter()
|
|
263
|
-
.map(|reachable| (reachable.commit.commit_id.as_str(), reachable.depth))
|
|
264
|
-
.collect::<Vec<_>>(),
|
|
265
|
-
vec![("commit-head", 0), ("commit-parent", 1), ("commit-root", 2)]
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
#[tokio::test]
|
|
270
|
-
async fn reachable_commits_errors_on_missing_parent_commit() {
|
|
271
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
272
|
-
let error = append_changes_result(
|
|
273
|
-
&storage,
|
|
274
|
-
&[commit_change(
|
|
275
|
-
"commit-head-change",
|
|
276
|
-
"commit-head",
|
|
277
|
-
&[],
|
|
278
|
-
&["missing-parent"],
|
|
279
|
-
)],
|
|
280
|
-
)
|
|
281
|
-
.await
|
|
282
|
-
.expect_err("changelog should reject missing parent");
|
|
283
|
-
|
|
284
|
-
assert!(error.message.contains("missing-parent"));
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
#[tokio::test]
|
|
288
|
-
async fn reachable_commits_errors_on_cycle() {
|
|
289
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
290
|
-
append_changes(
|
|
291
|
-
&storage,
|
|
292
|
-
&[
|
|
293
|
-
commit_change("commit-a-change", "commit-a", &[], &["commit-b"]),
|
|
294
|
-
commit_change("commit-b-change", "commit-b", &[], &["commit-a"]),
|
|
295
|
-
],
|
|
296
|
-
)
|
|
297
|
-
.await;
|
|
298
|
-
|
|
299
|
-
let graph = CommitGraphContext::new();
|
|
300
|
-
let read = storage
|
|
301
|
-
.begin_read(StorageReadOptions::default())
|
|
302
|
-
.expect("read should open");
|
|
303
|
-
let mut reader = graph.reader(read);
|
|
304
|
-
let error = reader
|
|
305
|
-
.reachable_commits("commit-a")
|
|
306
|
-
.await
|
|
307
|
-
.expect_err("walker should reject parent cycles");
|
|
308
|
-
|
|
309
|
-
assert!(error.message.contains("cycle"));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
#[tokio::test]
|
|
313
|
-
async fn reachable_commits_dedupes_shared_ancestors_in_diamond() {
|
|
314
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
315
|
-
append_changes(
|
|
316
|
-
&storage,
|
|
317
|
-
&[
|
|
318
|
-
commit_change("commit-root-change", "commit-root", &[], &[]),
|
|
319
|
-
commit_change("commit-left-change", "commit-left", &[], &["commit-root"]),
|
|
320
|
-
commit_change("commit-right-change", "commit-right", &[], &["commit-root"]),
|
|
321
|
-
commit_change(
|
|
322
|
-
"commit-head-change",
|
|
323
|
-
"commit-head",
|
|
324
|
-
&[],
|
|
325
|
-
&["commit-left", "commit-right"],
|
|
326
|
-
),
|
|
327
|
-
],
|
|
328
|
-
)
|
|
329
|
-
.await;
|
|
330
|
-
|
|
331
|
-
let graph = CommitGraphContext::new();
|
|
332
|
-
let read = storage
|
|
333
|
-
.begin_read(StorageReadOptions::default())
|
|
334
|
-
.expect("read should open");
|
|
335
|
-
let mut reader = graph.reader(read);
|
|
336
|
-
let commits = reader
|
|
337
|
-
.reachable_commits("commit-head")
|
|
338
|
-
.await
|
|
339
|
-
.expect("reachable commits should load");
|
|
340
|
-
|
|
341
|
-
assert_eq!(
|
|
342
|
-
commits
|
|
343
|
-
.iter()
|
|
344
|
-
.map(|reachable| (reachable.commit.commit_id.as_str(), reachable.depth))
|
|
345
|
-
.collect::<Vec<_>>(),
|
|
346
|
-
vec![
|
|
347
|
-
("commit-head", 0),
|
|
348
|
-
("commit-left", 1),
|
|
349
|
-
("commit-right", 1),
|
|
350
|
-
("commit-root", 2),
|
|
351
|
-
]
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
#[tokio::test]
|
|
356
|
-
async fn reachable_commits_keeps_nearest_depth_for_multiple_paths() {
|
|
357
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
358
|
-
append_changes(
|
|
359
|
-
&storage,
|
|
360
|
-
&[
|
|
361
|
-
commit_change("commit-root-change", "commit-root", &[], &[]),
|
|
362
|
-
commit_change(
|
|
363
|
-
"commit-parent-change",
|
|
364
|
-
"commit-parent",
|
|
365
|
-
&[],
|
|
366
|
-
&["commit-root"],
|
|
367
|
-
),
|
|
368
|
-
commit_change(
|
|
369
|
-
"commit-head-change",
|
|
370
|
-
"commit-head",
|
|
371
|
-
&[],
|
|
372
|
-
&["commit-root", "commit-parent"],
|
|
373
|
-
),
|
|
374
|
-
],
|
|
375
|
-
)
|
|
376
|
-
.await;
|
|
377
|
-
|
|
378
|
-
let graph = CommitGraphContext::new();
|
|
379
|
-
let read = storage
|
|
380
|
-
.begin_read(StorageReadOptions::default())
|
|
381
|
-
.expect("read should open");
|
|
382
|
-
let mut reader = graph.reader(read);
|
|
383
|
-
let commits = reader
|
|
384
|
-
.reachable_commits("commit-head")
|
|
385
|
-
.await
|
|
386
|
-
.expect("reachable commits should load");
|
|
387
|
-
|
|
388
|
-
assert_eq!(
|
|
389
|
-
commits
|
|
390
|
-
.iter()
|
|
391
|
-
.map(|reachable| (reachable.commit.commit_id.as_str(), reachable.depth))
|
|
392
|
-
.collect::<Vec<_>>(),
|
|
393
|
-
vec![("commit-head", 0), ("commit-parent", 1), ("commit-root", 1)]
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
#[tokio::test]
|
|
398
|
-
async fn reachable_commits_orders_same_depth_commits_by_id() {
|
|
399
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
400
|
-
append_changes(
|
|
401
|
-
&storage,
|
|
402
|
-
&[
|
|
403
|
-
commit_change("commit-z-change", "commit-z", &[], &[]),
|
|
404
|
-
commit_change("commit-a-change", "commit-a", &[], &[]),
|
|
405
|
-
commit_change(
|
|
406
|
-
"commit-head-change",
|
|
407
|
-
"commit-head",
|
|
408
|
-
&[],
|
|
409
|
-
&["commit-z", "commit-a"],
|
|
410
|
-
),
|
|
411
|
-
],
|
|
412
|
-
)
|
|
413
|
-
.await;
|
|
414
|
-
|
|
415
|
-
let graph = CommitGraphContext::new();
|
|
416
|
-
let read = storage
|
|
417
|
-
.begin_read(StorageReadOptions::default())
|
|
418
|
-
.expect("read should open");
|
|
419
|
-
let mut reader = graph.reader(read);
|
|
420
|
-
let commits = reader
|
|
421
|
-
.reachable_commits("commit-head")
|
|
422
|
-
.await
|
|
423
|
-
.expect("reachable commits should load");
|
|
424
|
-
|
|
425
|
-
assert_eq!(
|
|
426
|
-
commits
|
|
427
|
-
.iter()
|
|
428
|
-
.map(|reachable| (reachable.commit.commit_id.as_str(), reachable.depth))
|
|
429
|
-
.collect::<Vec<_>>(),
|
|
430
|
-
vec![("commit-head", 0), ("commit-a", 1), ("commit-z", 1)]
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
#[tokio::test]
|
|
435
|
-
async fn reachable_commits_errors_on_missing_head_commit() {
|
|
436
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
437
|
-
let graph = CommitGraphContext::new();
|
|
438
|
-
let read = storage
|
|
439
|
-
.begin_read(StorageReadOptions::default())
|
|
440
|
-
.expect("read should open");
|
|
441
|
-
let mut reader = graph.reader(read);
|
|
442
|
-
|
|
443
|
-
let error = reader
|
|
444
|
-
.reachable_commits("missing-head")
|
|
445
|
-
.await
|
|
446
|
-
.expect_err("missing head should fail");
|
|
447
|
-
|
|
448
|
-
assert!(error.message.contains("missing-head"));
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
#[tokio::test]
|
|
452
|
-
async fn best_common_ancestors_returns_nearest_common_commit_in_simple_graph() {
|
|
453
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
454
|
-
append_changes(
|
|
455
|
-
&storage,
|
|
456
|
-
&[
|
|
457
|
-
commit_change("commit-a-change", "commit-a", &[], &[]),
|
|
458
|
-
commit_change("commit-b-change", "commit-b", &[], &["commit-a"]),
|
|
459
|
-
commit_change("commit-c-change", "commit-c", &[], &["commit-b"]),
|
|
460
|
-
commit_change("commit-d-change", "commit-d", &[], &["commit-b"]),
|
|
461
|
-
],
|
|
462
|
-
)
|
|
463
|
-
.await;
|
|
464
|
-
|
|
465
|
-
let graph = CommitGraphContext::new();
|
|
466
|
-
let read = storage
|
|
467
|
-
.begin_read(StorageReadOptions::default())
|
|
468
|
-
.expect("read should open");
|
|
469
|
-
let mut reader = graph.reader(read);
|
|
470
|
-
let ancestors = reader
|
|
471
|
-
.best_common_ancestors("commit-c", "commit-d")
|
|
472
|
-
.await
|
|
473
|
-
.expect("best common ancestors should load");
|
|
474
|
-
|
|
475
|
-
assert_eq!(
|
|
476
|
-
ancestors
|
|
477
|
-
.iter()
|
|
478
|
-
.map(|commit| commit.commit_id.as_str())
|
|
479
|
-
.collect::<Vec<_>>(),
|
|
480
|
-
vec!["commit-b"]
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
#[tokio::test]
|
|
485
|
-
async fn best_common_ancestors_returns_shared_fork_in_diamond_graph() {
|
|
486
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
487
|
-
append_changes(
|
|
488
|
-
&storage,
|
|
489
|
-
&[
|
|
490
|
-
commit_change("commit-root-change", "commit-root", &[], &[]),
|
|
491
|
-
commit_change("commit-left-change", "commit-left", &[], &["commit-root"]),
|
|
492
|
-
commit_change("commit-right-change", "commit-right", &[], &["commit-root"]),
|
|
493
|
-
commit_change(
|
|
494
|
-
"commit-left-head-change",
|
|
495
|
-
"commit-left-head",
|
|
496
|
-
&[],
|
|
497
|
-
&["commit-left"],
|
|
498
|
-
),
|
|
499
|
-
commit_change(
|
|
500
|
-
"commit-right-head-change",
|
|
501
|
-
"commit-right-head",
|
|
502
|
-
&[],
|
|
503
|
-
&["commit-right"],
|
|
504
|
-
),
|
|
505
|
-
],
|
|
506
|
-
)
|
|
507
|
-
.await;
|
|
508
|
-
|
|
509
|
-
let graph = CommitGraphContext::new();
|
|
510
|
-
let read = storage
|
|
511
|
-
.begin_read(StorageReadOptions::default())
|
|
512
|
-
.expect("read should open");
|
|
513
|
-
let mut reader = graph.reader(read);
|
|
514
|
-
let ancestors = reader
|
|
515
|
-
.best_common_ancestors("commit-left-head", "commit-right-head")
|
|
516
|
-
.await
|
|
517
|
-
.expect("best common ancestors should load");
|
|
518
|
-
|
|
519
|
-
assert_eq!(
|
|
520
|
-
ancestors
|
|
521
|
-
.iter()
|
|
522
|
-
.map(|commit| commit.commit_id.as_str())
|
|
523
|
-
.collect::<Vec<_>>(),
|
|
524
|
-
vec!["commit-root"]
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
#[tokio::test]
|
|
529
|
-
async fn best_common_ancestors_returns_parent_when_one_side_is_ancestor() {
|
|
530
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
531
|
-
append_changes(
|
|
532
|
-
&storage,
|
|
533
|
-
&[
|
|
534
|
-
commit_change("commit-a-change", "commit-a", &[], &[]),
|
|
535
|
-
commit_change("commit-b-change", "commit-b", &[], &["commit-a"]),
|
|
536
|
-
commit_change("commit-c-change", "commit-c", &[], &["commit-b"]),
|
|
537
|
-
],
|
|
538
|
-
)
|
|
539
|
-
.await;
|
|
540
|
-
|
|
541
|
-
let graph = CommitGraphContext::new();
|
|
542
|
-
let read = storage
|
|
543
|
-
.begin_read(StorageReadOptions::default())
|
|
544
|
-
.expect("read should open");
|
|
545
|
-
let mut reader = graph.reader(read);
|
|
546
|
-
let ancestors = reader
|
|
547
|
-
.best_common_ancestors("commit-b", "commit-c")
|
|
548
|
-
.await
|
|
549
|
-
.expect("best common ancestors should load");
|
|
550
|
-
|
|
551
|
-
assert_eq!(
|
|
552
|
-
ancestors
|
|
553
|
-
.iter()
|
|
554
|
-
.map(|commit| commit.commit_id.as_str())
|
|
555
|
-
.collect::<Vec<_>>(),
|
|
556
|
-
vec!["commit-b"]
|
|
557
|
-
);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
#[tokio::test]
|
|
561
|
-
async fn best_common_ancestors_returns_multiple_bases_for_criss_cross_graph() {
|
|
562
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
563
|
-
append_changes(
|
|
564
|
-
&storage,
|
|
565
|
-
&[
|
|
566
|
-
commit_change("commit-root-change", "commit-root", &[], &[]),
|
|
567
|
-
commit_change("commit-left-change", "commit-left", &[], &["commit-root"]),
|
|
568
|
-
commit_change("commit-right-change", "commit-right", &[], &["commit-root"]),
|
|
569
|
-
commit_change(
|
|
570
|
-
"commit-head-left-change",
|
|
571
|
-
"commit-head-left",
|
|
572
|
-
&[],
|
|
573
|
-
&["commit-left", "commit-right"],
|
|
574
|
-
),
|
|
575
|
-
commit_change(
|
|
576
|
-
"commit-head-right-change",
|
|
577
|
-
"commit-head-right",
|
|
578
|
-
&[],
|
|
579
|
-
&["commit-right", "commit-left"],
|
|
580
|
-
),
|
|
581
|
-
],
|
|
582
|
-
)
|
|
583
|
-
.await;
|
|
584
|
-
|
|
585
|
-
let graph = CommitGraphContext::new();
|
|
586
|
-
let read = storage
|
|
587
|
-
.begin_read(StorageReadOptions::default())
|
|
588
|
-
.expect("read should open");
|
|
589
|
-
let mut reader = graph.reader(read);
|
|
590
|
-
let ancestors = reader
|
|
591
|
-
.best_common_ancestors("commit-head-left", "commit-head-right")
|
|
592
|
-
.await
|
|
593
|
-
.expect("best common ancestors should load");
|
|
594
|
-
|
|
595
|
-
assert_eq!(
|
|
596
|
-
ancestors
|
|
597
|
-
.iter()
|
|
598
|
-
.map(|commit| commit.commit_id.as_str())
|
|
599
|
-
.collect::<Vec<_>>(),
|
|
600
|
-
vec!["commit-left", "commit-right"]
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
#[tokio::test]
|
|
605
|
-
async fn merge_base_returns_single_best_common_ancestor() {
|
|
606
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
607
|
-
append_changes(
|
|
608
|
-
&storage,
|
|
609
|
-
&[
|
|
610
|
-
commit_change("commit-a-change", "commit-a", &[], &[]),
|
|
611
|
-
commit_change("commit-b-change", "commit-b", &[], &["commit-a"]),
|
|
612
|
-
commit_change("commit-c-change", "commit-c", &[], &["commit-b"]),
|
|
613
|
-
commit_change("commit-d-change", "commit-d", &[], &["commit-b"]),
|
|
614
|
-
],
|
|
615
|
-
)
|
|
616
|
-
.await;
|
|
617
|
-
|
|
618
|
-
let graph = CommitGraphContext::new();
|
|
619
|
-
let read = storage
|
|
620
|
-
.begin_read(StorageReadOptions::default())
|
|
621
|
-
.expect("read should open");
|
|
622
|
-
let mut reader = graph.reader(read);
|
|
623
|
-
let base = reader
|
|
624
|
-
.merge_base("commit-c", "commit-d")
|
|
625
|
-
.await
|
|
626
|
-
.expect("single merge base should resolve");
|
|
627
|
-
|
|
628
|
-
assert_eq!(base.commit_id, "commit-b");
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
#[tokio::test]
|
|
632
|
-
async fn merge_base_errors_when_histories_have_no_common_commit() {
|
|
633
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
634
|
-
append_changes(
|
|
635
|
-
&storage,
|
|
636
|
-
&[
|
|
637
|
-
commit_change("commit-left-change", "commit-left", &[], &[]),
|
|
638
|
-
commit_change("commit-right-change", "commit-right", &[], &[]),
|
|
639
|
-
],
|
|
640
|
-
)
|
|
641
|
-
.await;
|
|
642
|
-
|
|
643
|
-
let graph = CommitGraphContext::new();
|
|
644
|
-
let read = storage
|
|
645
|
-
.begin_read(StorageReadOptions::default())
|
|
646
|
-
.expect("read should open");
|
|
647
|
-
let mut reader = graph.reader(read);
|
|
648
|
-
let error = reader
|
|
649
|
-
.merge_base("commit-left", "commit-right")
|
|
650
|
-
.await
|
|
651
|
-
.expect_err("unrelated histories should not have a merge base");
|
|
652
|
-
|
|
653
|
-
assert!(error.message.contains("no common history"));
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
#[tokio::test]
|
|
657
|
-
async fn merge_base_errors_when_best_common_ancestor_is_ambiguous() {
|
|
658
|
-
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
659
|
-
append_changes(
|
|
660
|
-
&storage,
|
|
661
|
-
&[
|
|
662
|
-
commit_change("commit-root-change", "commit-root", &[], &[]),
|
|
663
|
-
commit_change("commit-left-change", "commit-left", &[], &["commit-root"]),
|
|
664
|
-
commit_change("commit-right-change", "commit-right", &[], &["commit-root"]),
|
|
665
|
-
commit_change(
|
|
666
|
-
"commit-head-left-change",
|
|
667
|
-
"commit-head-left",
|
|
668
|
-
&[],
|
|
669
|
-
&["commit-left", "commit-right"],
|
|
670
|
-
),
|
|
671
|
-
commit_change(
|
|
672
|
-
"commit-head-right-change",
|
|
673
|
-
"commit-head-right",
|
|
674
|
-
&[],
|
|
675
|
-
&["commit-right", "commit-left"],
|
|
676
|
-
),
|
|
677
|
-
],
|
|
678
|
-
)
|
|
679
|
-
.await;
|
|
680
|
-
|
|
681
|
-
let graph = CommitGraphContext::new();
|
|
682
|
-
let read = storage
|
|
683
|
-
.begin_read(StorageReadOptions::default())
|
|
684
|
-
.expect("read should open");
|
|
685
|
-
let mut reader = graph.reader(read);
|
|
686
|
-
let error = reader
|
|
687
|
-
.merge_base("commit-head-left", "commit-head-right")
|
|
688
|
-
.await
|
|
689
|
-
.expect_err("ambiguous best common ancestors should fail");
|
|
690
|
-
|
|
691
|
-
assert_eq!(error.code, LixError::CODE_AMBIGUOUS_MERGE_BASE);
|
|
692
|
-
assert_eq!(
|
|
693
|
-
error
|
|
694
|
-
.details
|
|
695
|
-
.as_ref()
|
|
696
|
-
.and_then(|details| details.get("left_commit_id")),
|
|
697
|
-
Some(&json!("commit-head-left"))
|
|
698
|
-
);
|
|
699
|
-
assert_eq!(
|
|
700
|
-
error
|
|
701
|
-
.details
|
|
702
|
-
.as_ref()
|
|
703
|
-
.and_then(|details| details.get("right_commit_id")),
|
|
704
|
-
Some(&json!("commit-head-right"))
|
|
705
|
-
);
|
|
706
|
-
assert_eq!(
|
|
707
|
-
error
|
|
708
|
-
.details
|
|
709
|
-
.as_ref()
|
|
710
|
-
.and_then(|details| details.get("candidates")),
|
|
711
|
-
Some(&json!(["commit-left", "commit-right"]))
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
#[derive(Clone)]
|
|
716
|
-
struct TestCommitChange {
|
|
717
|
-
change: CommitGraphChange,
|
|
718
|
-
parent_commit_ids: Vec<String>,
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
async fn append_changes(storage: &StorageContext, changes: &[TestCommitChange]) {
|
|
722
|
-
append_changes_result(storage, changes)
|
|
723
|
-
.await
|
|
724
|
-
.expect("changelog fixture should append");
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
async fn append_changes_result(
|
|
728
|
-
storage: &StorageContext,
|
|
729
|
-
changes: &[TestCommitChange],
|
|
730
|
-
) -> Result<(), LixError> {
|
|
731
|
-
let mut read = storage
|
|
732
|
-
.begin_read(StorageReadOptions::default())
|
|
733
|
-
.expect("read should open");
|
|
734
|
-
let mut writes = storage.new_write_set();
|
|
735
|
-
let mut append = ChangelogAppend::default();
|
|
736
|
-
for change in changes {
|
|
737
|
-
let commit_id = change
|
|
738
|
-
.change
|
|
739
|
-
.entity_pk
|
|
740
|
-
.as_single_string()
|
|
741
|
-
.expect("commit fixture should have single id")
|
|
742
|
-
.to_string();
|
|
743
|
-
append.commits.push(CommitRecord {
|
|
744
|
-
format_version: 1,
|
|
745
|
-
commit_id: commit_id.clone(),
|
|
746
|
-
parent_commit_ids: change.parent_commit_ids.clone(),
|
|
747
|
-
change_id: change.change.id.clone(),
|
|
748
|
-
author_account_ids: Vec::new(),
|
|
749
|
-
created_at: change.change.created_at.clone(),
|
|
750
|
-
});
|
|
751
|
-
append.commit_change_refs.push(CommitChangeRefSet {
|
|
752
|
-
commit_id: commit_id.clone(),
|
|
753
|
-
entries: Vec::new(),
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
ChangelogContext::new()
|
|
757
|
-
.writer(&mut read, &mut writes)
|
|
758
|
-
.stage_append(append)
|
|
759
|
-
.await?;
|
|
760
|
-
storage
|
|
761
|
-
.commit_write_set(writes, StorageWriteOptions::default())
|
|
762
|
-
.expect("commit should succeed");
|
|
763
|
-
Ok(())
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
fn commit_change(
|
|
767
|
-
change_id: &str,
|
|
768
|
-
commit_id: &str,
|
|
769
|
-
change_ids: &[&str],
|
|
770
|
-
parent_commit_ids: &[&str],
|
|
771
|
-
) -> TestCommitChange {
|
|
772
|
-
let _ = change_ids;
|
|
773
|
-
TestCommitChange {
|
|
774
|
-
change: CommitGraphChange {
|
|
775
|
-
id: change_id.to_string(),
|
|
776
|
-
entity_pk: crate::entity_pk::EntityPk::single(commit_id),
|
|
777
|
-
schema_key: "lix_commit".to_string(),
|
|
778
|
-
file_id: None,
|
|
779
|
-
snapshot_ref: None,
|
|
780
|
-
metadata_ref: None,
|
|
781
|
-
created_at: "2026-01-01T00:00:00Z".to_string(),
|
|
782
|
-
},
|
|
783
|
-
parent_commit_ids: parent_commit_ids.iter().map(|id| id.to_string()).collect(),
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|