@lix-js/sdk 0.6.0-preview.4 → 0.6.0-preview.5
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 +1 -1
- package/SKILL.md +65 -64
- package/dist/engine-wasm/index.js +4 -4
- package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -5
- package/dist/engine-wasm/wasm/lix_engine.js +130 -118
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +9 -8
- package/dist/generated/builtin-schemas.d.ts +69 -69
- package/dist/generated/builtin-schemas.js +94 -94
- package/dist/open-lix.d.ts +33 -26
- package/dist/open-lix.js +10 -10
- package/dist/sqlite/index.js +86 -30
- package/dist-engine-src/README.md +3 -3
- package/dist-engine-src/src/backend/capabilities.rs +67 -0
- package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
- package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
- package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
- package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
- package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
- package/dist-engine-src/src/backend/conformance/model.rs +28 -0
- package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
- package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
- package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
- package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
- package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
- package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
- package/dist-engine-src/src/backend/conformance/write.rs +16 -0
- package/dist-engine-src/src/backend/error.rs +94 -0
- package/dist-engine-src/src/backend/in_memory.rs +670 -0
- package/dist-engine-src/src/backend/mod.rs +36 -9
- package/dist-engine-src/src/backend/predicate.rs +80 -0
- package/dist-engine-src/src/backend/traits.rs +260 -0
- package/dist-engine-src/src/backend/types.rs +224 -81
- package/dist-engine-src/src/binary_cas/context.rs +8 -8
- package/dist-engine-src/src/binary_cas/kv.rs +234 -259
- package/dist-engine-src/src/{version → branch}/context.rs +12 -12
- package/dist-engine-src/src/branch/lifecycle.rs +221 -0
- package/dist-engine-src/src/branch/mod.rs +13 -0
- package/dist-engine-src/src/branch/refs.rs +321 -0
- package/dist-engine-src/src/branch/stage_rows.rs +67 -0
- package/dist-engine-src/src/branch/types.rs +21 -0
- package/dist-engine-src/src/catalog/context.rs +18 -18
- package/dist-engine-src/src/catalog/snapshot.rs +8 -8
- package/dist-engine-src/src/changelog/bench_support.rs +785 -0
- package/dist-engine-src/src/changelog/change.rs +1 -0
- package/dist-engine-src/src/changelog/codec.rs +497 -0
- package/dist-engine-src/src/changelog/commit.rs +1 -0
- package/dist-engine-src/src/changelog/context.rs +1614 -0
- package/dist-engine-src/src/changelog/mod.rs +29 -0
- package/dist-engine-src/src/changelog/store.rs +163 -0
- package/dist-engine-src/src/changelog/test_support.rs +54 -0
- package/dist-engine-src/src/changelog/types.rs +213 -0
- package/dist-engine-src/src/commit_graph/context.rs +317 -274
- package/dist-engine-src/src/commit_graph/mod.rs +2 -4
- package/dist-engine-src/src/commit_graph/types.rs +22 -42
- package/dist-engine-src/src/commit_graph/walker.rs +133 -103
- package/dist-engine-src/src/common/error.rs +52 -18
- package/dist-engine-src/src/common/identity.rs +2 -2
- package/dist-engine-src/src/common/mod.rs +1 -1
- package/dist-engine-src/src/domain.rs +42 -46
- package/dist-engine-src/src/engine.rs +74 -96
- package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
- package/dist-engine-src/src/functions/context.rs +56 -52
- package/dist-engine-src/src/functions/state.rs +51 -52
- package/dist-engine-src/src/init.rs +288 -154
- package/dist-engine-src/src/json_store/context.rs +15 -266
- package/dist-engine-src/src/json_store/mod.rs +26 -0
- package/dist-engine-src/src/json_store/store.rs +103 -718
- package/dist-engine-src/src/json_store/types.rs +4 -9
- package/dist-engine-src/src/lib.rs +49 -19
- package/dist-engine-src/src/live_state/context.rs +654 -790
- package/dist-engine-src/src/live_state/mod.rs +9 -3
- package/dist-engine-src/src/live_state/overlay.rs +4 -4
- package/dist-engine-src/src/live_state/types.rs +30 -21
- package/dist-engine-src/src/live_state/visibility.rs +514 -71
- package/dist-engine-src/src/plugin/install.rs +48 -48
- package/dist-engine-src/src/plugin/manifest.rs +7 -7
- package/dist-engine-src/src/plugin/materializer.rs +0 -275
- package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
- package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
- package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
- package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
- package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
- package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
- package/dist-engine-src/src/schema/compatibility.rs +11 -11
- package/dist-engine-src/src/schema/definition.json +2 -2
- package/dist-engine-src/src/schema/definition.rs +5 -5
- package/dist-engine-src/src/schema/key.rs +3 -3
- package/dist-engine-src/src/schema/mod.rs +1 -1
- package/dist-engine-src/src/schema/tests.rs +18 -18
- package/dist-engine-src/src/session/context.rs +803 -148
- package/dist-engine-src/src/session/create_branch.rs +94 -0
- package/dist-engine-src/src/session/execute.rs +223 -83
- package/dist-engine-src/src/session/merge/analysis.rs +9 -3
- package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
- package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
- package/dist-engine-src/src/session/merge/mod.rs +5 -6
- package/dist-engine-src/src/session/merge/stats.rs +7 -11
- package/dist-engine-src/src/session/mod.rs +15 -12
- package/dist-engine-src/src/session/switch_branch.rs +113 -0
- package/dist-engine-src/src/session/transaction.rs +495 -14
- package/dist-engine-src/src/sql2/{classify.rs → bind/classify.rs} +3 -75
- package/dist-engine-src/src/sql2/bind/error.rs +5 -0
- package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
- package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
- package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +71 -3
- package/dist-engine-src/src/sql2/bind/read.rs +65 -0
- package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
- package/dist-engine-src/src/sql2/bind/table.rs +273 -0
- package/dist-engine-src/src/sql2/bind/write.rs +86 -0
- package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
- package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
- package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
- package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
- package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
- package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
- package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
- package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
- package/dist-engine-src/src/sql2/context.rs +36 -30
- package/dist-engine-src/src/sql2/error.rs +1 -1
- package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
- package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
- package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
- package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
- package/dist-engine-src/src/sql2/exec/write.rs +661 -0
- package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
- package/dist-engine-src/src/sql2/history_projection.rs +8 -8
- package/dist-engine-src/src/sql2/history_route.rs +35 -31
- package/dist-engine-src/src/sql2/mod.rs +28 -23
- package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
- package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
- package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
- package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
- package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
- package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
- package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
- package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
- package/dist-engine-src/src/sql2/plan/write.rs +147 -0
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
- package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
- package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
- package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
- package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
- package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
- package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
- package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
- package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
- package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
- package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
- package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
- package/dist-engine-src/src/sql2/read_only.rs +2 -2
- package/dist-engine-src/src/sql2/session.rs +47 -96
- package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
- package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
- package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
- package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
- package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
- package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
- package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
- package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
- package/dist-engine-src/src/storage/conformance.rs +399 -0
- package/dist-engine-src/src/storage/context.rs +552 -288
- package/dist-engine-src/src/storage/mod.rs +48 -10
- package/dist-engine-src/src/storage/point.rs +440 -0
- package/dist-engine-src/src/storage/read_scope.rs +43 -64
- package/dist-engine-src/src/storage/reader.rs +867 -0
- package/dist-engine-src/src/storage/scan.rs +784 -0
- package/dist-engine-src/src/storage/spaces.rs +236 -0
- package/dist-engine-src/src/storage/stats.rs +80 -0
- package/dist-engine-src/src/storage/write_set.rs +962 -0
- package/dist-engine-src/src/storage_bench.rs +136 -4828
- package/dist-engine-src/src/test_support.rs +360 -138
- package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
- package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
- package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
- package/dist-engine-src/src/tracked_state/context.rs +1927 -993
- package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
- package/dist-engine-src/src/tracked_state/merge.rs +74 -88
- package/dist-engine-src/src/tracked_state/mod.rs +19 -16
- package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
- package/dist-engine-src/src/tracked_state/storage.rs +243 -191
- package/dist-engine-src/src/tracked_state/tree.rs +247 -371
- package/dist-engine-src/src/tracked_state/types.rs +49 -42
- package/dist-engine-src/src/transaction/bench_support.rs +407 -0
- package/dist-engine-src/src/transaction/commit.rs +821 -713
- package/dist-engine-src/src/transaction/context.rs +705 -600
- package/dist-engine-src/src/transaction/mod.rs +13 -2
- package/dist-engine-src/src/transaction/normalization.rs +63 -76
- package/dist-engine-src/src/transaction/prep.rs +13 -13
- package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
- package/dist-engine-src/src/transaction/staging.rs +228 -434
- package/dist-engine-src/src/transaction/types.rs +41 -98
- package/dist-engine-src/src/transaction/validation.rs +382 -446
- package/dist-engine-src/src/untracked_state/codec.rs +337 -29
- package/dist-engine-src/src/untracked_state/context.rs +7 -7
- package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
- package/dist-engine-src/src/untracked_state/mod.rs +1 -1
- package/dist-engine-src/src/untracked_state/storage.rs +659 -157
- package/dist-engine-src/src/untracked_state/types.rs +21 -21
- package/package.json +71 -68
- package/dist-engine-src/src/backend/kv.rs +0 -358
- package/dist-engine-src/src/backend/testing.rs +0 -658
- package/dist-engine-src/src/commit_store/codec.rs +0 -887
- package/dist-engine-src/src/commit_store/context.rs +0 -944
- package/dist-engine-src/src/commit_store/materialization.rs +0 -84
- package/dist-engine-src/src/commit_store/mod.rs +0 -16
- package/dist-engine-src/src/commit_store/storage.rs +0 -600
- package/dist-engine-src/src/commit_store/types.rs +0 -215
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
- package/dist-engine-src/src/session/create_version.rs +0 -88
- package/dist-engine-src/src/session/merge/apply.rs +0 -23
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
- package/dist-engine-src/src/session/switch_version.rs +0 -110
- package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
- package/dist-engine-src/src/sql2/execute.rs +0 -3533
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
- package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
- package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -172
- package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
- package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
- package/dist-engine-src/src/sql2/version_scope.rs +0 -394
- package/dist-engine-src/src/storage/types.rs +0 -501
- package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
- package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
- package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
- package/dist-engine-src/src/version/lifecycle.rs +0 -221
- package/dist-engine-src/src/version/mod.rs +0 -13
- package/dist-engine-src/src/version/refs.rs +0 -330
- package/dist-engine-src/src/version/stage_rows.rs +0 -67
- package/dist-engine-src/src/version/types.rs +0 -21
|
@@ -1,79 +1,91 @@
|
|
|
1
1
|
use std::collections::BTreeSet;
|
|
2
2
|
|
|
3
|
+
use crate::changelog::{
|
|
4
|
+
ChangeLoadRequest, ChangeRecord, ChangelogContext, ChangelogReader, CommitLoadEntry,
|
|
5
|
+
CommitLoadRequest, CommitProjection, CommitRecord, CommitScanRequest,
|
|
6
|
+
};
|
|
3
7
|
use crate::commit_graph::walker::{best_common_ancestors, walk_reachable_commits};
|
|
4
8
|
use crate::commit_graph::{
|
|
5
|
-
CommitGraphChangeHistoryEntry, CommitGraphChangeHistoryRequest,
|
|
6
|
-
CommitGraphEdge, CommitGraphReader, ReachableCommitGraphCommit,
|
|
9
|
+
CommitGraphChange, CommitGraphChangeHistoryEntry, CommitGraphChangeHistoryRequest,
|
|
10
|
+
CommitGraphCommit, CommitGraphEdge, CommitGraphReader, ReachableCommitGraphCommit,
|
|
7
11
|
};
|
|
8
|
-
use crate::
|
|
9
|
-
use crate::
|
|
10
|
-
use crate::storage::StorageReader;
|
|
11
|
-
use crate::storage::{ScopedStorageReader, StorageReadScope};
|
|
12
|
+
use crate::entity_pk::EntityPk;
|
|
13
|
+
use crate::storage::StorageRead;
|
|
12
14
|
use crate::LixError;
|
|
13
15
|
|
|
14
16
|
const COMMIT_SCHEMA_KEY: &str = "lix_commit";
|
|
15
17
|
|
|
16
|
-
/// Read model for resolving
|
|
18
|
+
/// Read model for resolving changelog commits into entity state at a head.
|
|
17
19
|
///
|
|
18
|
-
/// This module does not own durable storage. It reads immutable
|
|
20
|
+
/// This module does not own durable storage. It reads immutable changelog
|
|
19
21
|
/// facts through a caller-provided KV store and applies commit graph rules on
|
|
20
22
|
/// top.
|
|
21
23
|
#[derive(Clone)]
|
|
22
|
-
pub(crate) struct CommitGraphContext
|
|
23
|
-
commit_store: CommitStoreContext,
|
|
24
|
-
}
|
|
24
|
+
pub(crate) struct CommitGraphContext;
|
|
25
25
|
|
|
26
26
|
impl CommitGraphContext {
|
|
27
27
|
pub(crate) fn new() -> Self {
|
|
28
|
-
Self
|
|
29
|
-
commit_store: CommitStoreContext::new(),
|
|
30
|
-
}
|
|
28
|
+
Self
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
/// Creates a graph reader over a caller-provided KV store.
|
|
34
32
|
pub(crate) fn reader<S>(&self, store: S) -> CommitGraphStoreReader<S>
|
|
35
33
|
where
|
|
36
|
-
S:
|
|
34
|
+
S: StorageRead + Send + Sync,
|
|
37
35
|
{
|
|
38
|
-
|
|
39
|
-
CommitGraphStoreReader {
|
|
40
|
-
commit_store_reader: self.commit_store.reader(read_scope.store()),
|
|
41
|
-
}
|
|
36
|
+
CommitGraphStoreReader { store }
|
|
42
37
|
}
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
/// Commit-graph reader that resolves
|
|
40
|
+
/// Commit-graph reader that resolves changelog entities at a commit head.
|
|
46
41
|
pub(crate) struct CommitGraphStoreReader<S>
|
|
47
42
|
where
|
|
48
|
-
S:
|
|
43
|
+
S: StorageRead + Send + Sync,
|
|
49
44
|
{
|
|
50
|
-
|
|
45
|
+
store: S,
|
|
51
46
|
}
|
|
52
47
|
|
|
53
48
|
impl<S> CommitGraphStoreReader<S>
|
|
54
49
|
where
|
|
55
|
-
S:
|
|
50
|
+
S: StorageRead + Send + Sync,
|
|
56
51
|
{
|
|
57
52
|
/// Loads and parses a `lix_commit` canonical change by commit id.
|
|
58
53
|
pub(crate) async fn load_commit(
|
|
59
54
|
&mut self,
|
|
60
55
|
commit_id: &str,
|
|
61
56
|
) -> Result<Option<CommitGraphCommit>, LixError> {
|
|
62
|
-
|
|
63
|
-
return Ok(None);
|
|
64
|
-
};
|
|
65
|
-
self.graph_commit_from_store_commit(commit).await.map(Some)
|
|
57
|
+
self.load_changelog_commit(commit_id).await
|
|
66
58
|
}
|
|
67
59
|
|
|
68
|
-
/// Loads every commit fact from the
|
|
60
|
+
/// Loads every direct commit fact from the changelog.
|
|
69
61
|
///
|
|
70
62
|
/// This is used by global commit surfaces where the caller wants the durable
|
|
71
|
-
/// graph facts themselves, not reachability from a particular
|
|
63
|
+
/// graph facts themselves, not reachability from a particular branch head.
|
|
72
64
|
pub(crate) async fn all_commits(&mut self) -> Result<Vec<CommitGraphCommit>, LixError> {
|
|
73
|
-
let stored_commits = self.commit_store_reader.scan_commits().await?;
|
|
74
65
|
let mut commits = Vec::new();
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
let mut start_after = None::<String>;
|
|
67
|
+
loop {
|
|
68
|
+
let mut reader = ChangelogContext::new().reader(&self.store);
|
|
69
|
+
let scan = reader
|
|
70
|
+
.scan_commits(CommitScanRequest {
|
|
71
|
+
start_after: start_after.as_deref(),
|
|
72
|
+
limit: Some(1024),
|
|
73
|
+
projection: CommitProjection::Record,
|
|
74
|
+
})
|
|
75
|
+
.await?;
|
|
76
|
+
for entry in scan.entries {
|
|
77
|
+
let CommitLoadEntry::Record(record) = entry else {
|
|
78
|
+
return Err(LixError::new(
|
|
79
|
+
LixError::CODE_INTERNAL_ERROR,
|
|
80
|
+
"changelog commit scan returned non-record entry",
|
|
81
|
+
));
|
|
82
|
+
};
|
|
83
|
+
commits.push(commit_graph_commit_from_commit_record(record, Vec::new()));
|
|
84
|
+
}
|
|
85
|
+
let Some(next) = scan.next_start_after else {
|
|
86
|
+
break;
|
|
87
|
+
};
|
|
88
|
+
start_after = Some(next);
|
|
77
89
|
}
|
|
78
90
|
commits.sort_by(|left, right| left.commit_id.cmp(&right.commit_id));
|
|
79
91
|
Ok(commits)
|
|
@@ -150,9 +162,10 @@ where
|
|
|
150
162
|
|
|
151
163
|
/// Returns canonical changes reachable from `start_commit_id`.
|
|
152
164
|
///
|
|
153
|
-
/// This is the primitive history API. It reports the commit/depth where
|
|
154
|
-
///
|
|
155
|
-
/// traversal and leaves row shaping to callers such as
|
|
165
|
+
/// This is the primitive history API. It reports the commit/depth where a
|
|
166
|
+
/// reachable commit's change-ref set first exposes each matching canonical
|
|
167
|
+
/// change during graph traversal and leaves row shaping to callers such as
|
|
168
|
+
/// SQL providers.
|
|
156
169
|
pub(crate) async fn change_history_from_commit(
|
|
157
170
|
&mut self,
|
|
158
171
|
start_commit_id: &str,
|
|
@@ -168,16 +181,26 @@ where
|
|
|
168
181
|
}
|
|
169
182
|
|
|
170
183
|
let commit_id = reachable.commit.commit_id;
|
|
184
|
+
let canonical_change = reachable.commit.canonical_change;
|
|
185
|
+
if seen_change_ids.insert(canonical_change.id.clone())
|
|
186
|
+
&& change_matches_history_request(&canonical_change, request)
|
|
187
|
+
{
|
|
188
|
+
entries.push(CommitGraphChangeHistoryEntry {
|
|
189
|
+
change: canonical_change,
|
|
190
|
+
observed_commit_id: commit_id.clone(),
|
|
191
|
+
start_commit_id: start_commit_id.to_string(),
|
|
192
|
+
depth: reachable.depth,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
171
196
|
for change_id in reachable.commit.change_ids {
|
|
172
197
|
if !seen_change_ids.insert(change_id.clone()) {
|
|
173
198
|
continue;
|
|
174
199
|
}
|
|
175
|
-
let change = self
|
|
176
|
-
|
|
177
|
-
.await?;
|
|
178
|
-
if change_matches_history_request(&change.record, request) {
|
|
200
|
+
let change = self.load_member_canonical_change(&change_id).await?;
|
|
201
|
+
if change_matches_history_request(&change, request) {
|
|
179
202
|
entries.push(CommitGraphChangeHistoryEntry {
|
|
180
|
-
|
|
203
|
+
change,
|
|
181
204
|
observed_commit_id: commit_id.clone(),
|
|
182
205
|
start_commit_id: start_commit_id.to_string(),
|
|
183
206
|
depth: reachable.depth,
|
|
@@ -192,8 +215,7 @@ where
|
|
|
192
215
|
async fn load_member_canonical_change(
|
|
193
216
|
&mut self,
|
|
194
217
|
change_id: &str,
|
|
195
|
-
|
|
196
|
-
) -> Result<LocatedChange, LixError> {
|
|
218
|
+
) -> Result<CommitGraphChange, LixError> {
|
|
197
219
|
let change_ids = vec![change_id.to_string()];
|
|
198
220
|
self.load_canonical_changes(&change_ids)
|
|
199
221
|
.await?
|
|
@@ -203,72 +225,77 @@ where
|
|
|
203
225
|
.ok_or_else(|| {
|
|
204
226
|
LixError::new(
|
|
205
227
|
"LIX_ERROR_UNKNOWN",
|
|
206
|
-
format!(
|
|
207
|
-
"commit_graph commit '{source_commit_id}' references missing change '{change_id}'"
|
|
208
|
-
),
|
|
228
|
+
format!("commit_graph references missing change '{change_id}'"),
|
|
209
229
|
)
|
|
210
230
|
})
|
|
211
231
|
}
|
|
212
232
|
|
|
213
|
-
async fn
|
|
233
|
+
async fn load_changelog_commit(
|
|
214
234
|
&mut self,
|
|
215
|
-
|
|
216
|
-
) -> Result<CommitGraphCommit
|
|
217
|
-
let
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
commit_id: &str,
|
|
236
|
+
) -> Result<Option<CommitGraphCommit>, LixError> {
|
|
237
|
+
let mut reader = ChangelogContext::new().reader(&self.store);
|
|
238
|
+
let batch = reader
|
|
239
|
+
.load_commits(CommitLoadRequest {
|
|
240
|
+
commit_ids: &[commit_id.to_string()],
|
|
241
|
+
projection: CommitProjection::Full,
|
|
242
|
+
})
|
|
243
|
+
.await?;
|
|
244
|
+
let Some(entry) = batch.entries.into_iter().next().flatten() else {
|
|
245
|
+
return Ok(None);
|
|
246
|
+
};
|
|
247
|
+
match entry {
|
|
248
|
+
CommitLoadEntry::Full {
|
|
249
|
+
record,
|
|
250
|
+
change_ref_chunks,
|
|
251
|
+
} => {
|
|
252
|
+
let change_ids = change_ref_chunks
|
|
253
|
+
.into_iter()
|
|
254
|
+
.flat_map(|chunk| chunk.entries.into_iter().map(|entry| entry.change_id))
|
|
255
|
+
.collect::<Vec<_>>();
|
|
256
|
+
Ok(Some(commit_graph_commit_from_commit_record(
|
|
257
|
+
record, change_ids,
|
|
258
|
+
)))
|
|
259
|
+
}
|
|
260
|
+
_ => Err(LixError::new(
|
|
261
|
+
LixError::CODE_INTERNAL_ERROR,
|
|
262
|
+
"changelog full commit projection returned non-full entry",
|
|
263
|
+
)),
|
|
242
264
|
}
|
|
243
|
-
Ok(change_ids)
|
|
244
265
|
}
|
|
245
266
|
|
|
246
267
|
async fn load_canonical_changes(
|
|
247
268
|
&self,
|
|
248
269
|
change_ids: &[String],
|
|
249
|
-
) -> Result<Vec<Option<
|
|
250
|
-
self.
|
|
251
|
-
|
|
252
|
-
.
|
|
253
|
-
.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
) -> Result<Vec<Option<CommitGraphChange>>, LixError> {
|
|
271
|
+
let mut reader = ChangelogContext::new().reader(&self.store);
|
|
272
|
+
let batch = reader
|
|
273
|
+
.load_changes(ChangeLoadRequest { change_ids })
|
|
274
|
+
.await?;
|
|
275
|
+
batch
|
|
276
|
+
.entries
|
|
277
|
+
.into_iter()
|
|
278
|
+
.map(|entry| Ok(entry.map(commit_graph_change_from_change_record)))
|
|
279
|
+
.collect()
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fn commit_graph_change_from_change_record(change: ChangeRecord) -> CommitGraphChange {
|
|
284
|
+
CommitGraphChange {
|
|
285
|
+
id: change.change_id,
|
|
286
|
+
entity_pk: change.entity_pk,
|
|
287
|
+
schema_key: change.schema_key,
|
|
288
|
+
file_id: change.file_id,
|
|
289
|
+
snapshot_ref: change.snapshot_ref,
|
|
290
|
+
metadata_ref: change.metadata_ref,
|
|
291
|
+
created_at: change.created_at,
|
|
265
292
|
}
|
|
266
293
|
}
|
|
267
294
|
|
|
268
295
|
#[async_trait::async_trait]
|
|
269
296
|
impl<S> CommitGraphReader for CommitGraphStoreReader<S>
|
|
270
297
|
where
|
|
271
|
-
S:
|
|
298
|
+
S: StorageRead + Send + Sync,
|
|
272
299
|
{
|
|
273
300
|
async fn load_commit(
|
|
274
301
|
&mut self,
|
|
@@ -277,10 +304,6 @@ where
|
|
|
277
304
|
CommitGraphStoreReader::load_commit(self, commit_id).await
|
|
278
305
|
}
|
|
279
306
|
|
|
280
|
-
async fn all_commits(&mut self) -> Result<Vec<CommitGraphCommit>, LixError> {
|
|
281
|
-
CommitGraphStoreReader::all_commits(self).await
|
|
282
|
-
}
|
|
283
|
-
|
|
284
307
|
async fn reachable_commits(
|
|
285
308
|
&mut self,
|
|
286
309
|
head_commit_id: &str,
|
|
@@ -288,26 +311,6 @@ where
|
|
|
288
311
|
CommitGraphStoreReader::reachable_commits(self, head_commit_id).await
|
|
289
312
|
}
|
|
290
313
|
|
|
291
|
-
async fn best_common_ancestors(
|
|
292
|
-
&mut self,
|
|
293
|
-
left_commit_id: &str,
|
|
294
|
-
right_commit_id: &str,
|
|
295
|
-
) -> Result<Vec<CommitGraphCommit>, LixError> {
|
|
296
|
-
CommitGraphStoreReader::best_common_ancestors(self, left_commit_id, right_commit_id).await
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
async fn merge_base(
|
|
300
|
-
&mut self,
|
|
301
|
-
left_commit_id: &str,
|
|
302
|
-
right_commit_id: &str,
|
|
303
|
-
) -> Result<CommitGraphCommit, LixError> {
|
|
304
|
-
CommitGraphStoreReader::merge_base(self, left_commit_id, right_commit_id).await
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
fn commit_edges(&self, commits: &[CommitGraphCommit]) -> Vec<CommitGraphEdge> {
|
|
308
|
-
CommitGraphStoreReader::commit_edges(self, commits)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
314
|
async fn change_history_from_commit(
|
|
312
315
|
&mut self,
|
|
313
316
|
start_commit_id: &str,
|
|
@@ -323,11 +326,11 @@ fn depth_matches(depth: u32, request: &CommitGraphChangeHistoryRequest) -> bool
|
|
|
323
326
|
}
|
|
324
327
|
|
|
325
328
|
fn change_matches_history_request(
|
|
326
|
-
change: &
|
|
329
|
+
change: &CommitGraphChange,
|
|
327
330
|
request: &CommitGraphChangeHistoryRequest,
|
|
328
331
|
) -> bool {
|
|
329
332
|
(request.include_tombstones || change.snapshot_ref.is_some())
|
|
330
|
-
&& (request.
|
|
333
|
+
&& (request.entity_pks.is_empty() || request.entity_pks.contains(&change.entity_pk))
|
|
331
334
|
&& (request.schema_keys.is_empty() || request.schema_keys.contains(&change.schema_key))
|
|
332
335
|
&& (request.file_ids.is_empty()
|
|
333
336
|
|| change
|
|
@@ -336,81 +339,76 @@ fn change_matches_history_request(
|
|
|
336
339
|
.is_some_and(|file_id| request.file_ids.contains(file_id)))
|
|
337
340
|
}
|
|
338
341
|
|
|
339
|
-
fn
|
|
340
|
-
|
|
342
|
+
fn commit_graph_commit_from_commit_record(
|
|
343
|
+
record: CommitRecord,
|
|
341
344
|
change_ids: Vec<String>,
|
|
342
|
-
) ->
|
|
343
|
-
let change =
|
|
344
|
-
|
|
345
|
+
) -> CommitGraphCommit {
|
|
346
|
+
let change = commit_record_canonical_change(&record);
|
|
347
|
+
CommitGraphCommit {
|
|
345
348
|
canonical_change: change.clone(),
|
|
346
349
|
change,
|
|
347
|
-
commit_id:
|
|
350
|
+
commit_id: record.commit_id,
|
|
348
351
|
change_ids,
|
|
349
|
-
author_account_ids:
|
|
350
|
-
parent_commit_ids:
|
|
351
|
-
}
|
|
352
|
+
author_account_ids: record.author_account_ids,
|
|
353
|
+
parent_commit_ids: record.parent_commit_ids,
|
|
354
|
+
}
|
|
352
355
|
}
|
|
353
356
|
|
|
354
|
-
fn
|
|
355
|
-
|
|
356
|
-
id:
|
|
357
|
-
|
|
357
|
+
fn commit_record_canonical_change(record: &CommitRecord) -> CommitGraphChange {
|
|
358
|
+
let snapshot_content = serde_json::to_string(&serde_json::json!({
|
|
359
|
+
"id": record.commit_id,
|
|
360
|
+
}))
|
|
361
|
+
.expect("lix_commit snapshot serialization should not fail");
|
|
362
|
+
CommitGraphChange {
|
|
363
|
+
id: record.change_id.clone(),
|
|
364
|
+
entity_pk: EntityPk::single(&record.commit_id),
|
|
358
365
|
schema_key: COMMIT_SCHEMA_KEY.to_string(),
|
|
359
366
|
file_id: None,
|
|
360
|
-
snapshot_ref:
|
|
367
|
+
snapshot_ref: Some(crate::json_store::JsonRef::for_content(
|
|
368
|
+
snapshot_content.as_bytes(),
|
|
369
|
+
)),
|
|
361
370
|
metadata_ref: None,
|
|
362
|
-
created_at:
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
fn canonical_change_from_store_change(change: Change) -> Change {
|
|
367
|
-
Change {
|
|
368
|
-
id: change.id,
|
|
369
|
-
entity_id: change.entity_id,
|
|
370
|
-
schema_key: change.schema_key,
|
|
371
|
-
file_id: change.file_id,
|
|
372
|
-
snapshot_ref: change.snapshot_ref,
|
|
373
|
-
metadata_ref: change.metadata_ref,
|
|
374
|
-
created_at: change.created_at,
|
|
371
|
+
created_at: record.created_at.clone(),
|
|
375
372
|
}
|
|
376
373
|
}
|
|
377
374
|
|
|
378
|
-
fn missing_pack_error(label: &str, commit_id: &str, pack_id: u32) -> LixError {
|
|
379
|
-
LixError::new(
|
|
380
|
-
LixError::CODE_INTERNAL_ERROR,
|
|
381
|
-
format!("commit_graph missing {label} pack ({commit_id}, {pack_id})"),
|
|
382
|
-
)
|
|
383
|
-
}
|
|
384
|
-
|
|
385
375
|
#[cfg(test)]
|
|
386
376
|
mod tests {
|
|
387
377
|
use std::collections::{BTreeMap, BTreeSet};
|
|
388
|
-
use std::sync::Arc;
|
|
389
378
|
|
|
390
|
-
use crate::
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
Change, ChangeLocator, ChangeRef, CommitDraftRef, CommitStoreContext,
|
|
379
|
+
use crate::changelog::{
|
|
380
|
+
ChangeRecord, ChangelogAppend, ChangelogContext, ChangelogWriter, CommitChangeRef,
|
|
381
|
+
CommitChangeRefSet, CommitRecord,
|
|
394
382
|
};
|
|
395
|
-
use crate::
|
|
383
|
+
use crate::commit_graph::{
|
|
384
|
+
CommitGraphChange, CommitGraphChangeHistoryRequest, CommitGraphContext,
|
|
385
|
+
};
|
|
386
|
+
use crate::storage::StorageContext;
|
|
387
|
+
use crate::storage::{InMemoryStorageBackend, StorageReadOptions, StorageWriteOptions};
|
|
396
388
|
|
|
397
389
|
#[tokio::test]
|
|
398
390
|
async fn load_commit_parses_commit_snapshot() {
|
|
399
|
-
let
|
|
400
|
-
let storage = StorageContext::new(backend.clone());
|
|
391
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
401
392
|
append_changes(
|
|
402
|
-
storage
|
|
403
|
-
&[
|
|
404
|
-
"
|
|
405
|
-
"
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
393
|
+
&storage,
|
|
394
|
+
&[
|
|
395
|
+
entity_change("change-1", "entity-1", "example", "{}"),
|
|
396
|
+
entity_change("change-2", "entity-2", "example", "{}"),
|
|
397
|
+
commit_change(
|
|
398
|
+
"commit-1-change",
|
|
399
|
+
"commit-1",
|
|
400
|
+
&["change-1", "change-2"],
|
|
401
|
+
&["parent-1"],
|
|
402
|
+
),
|
|
403
|
+
],
|
|
409
404
|
)
|
|
410
405
|
.await;
|
|
411
406
|
|
|
412
407
|
let graph = CommitGraphContext::new();
|
|
413
|
-
let
|
|
408
|
+
let read = storage
|
|
409
|
+
.begin_read(StorageReadOptions::default())
|
|
410
|
+
.expect("read should open");
|
|
411
|
+
let mut reader = graph.reader(read);
|
|
414
412
|
let commit = reader
|
|
415
413
|
.load_commit("commit-1")
|
|
416
414
|
.await
|
|
@@ -425,10 +423,12 @@ mod tests {
|
|
|
425
423
|
|
|
426
424
|
#[tokio::test]
|
|
427
425
|
async fn load_commit_returns_none_for_missing_commit() {
|
|
428
|
-
let
|
|
429
|
-
let storage = StorageContext::new(backend.clone());
|
|
426
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
430
427
|
let graph = CommitGraphContext::new();
|
|
431
|
-
let
|
|
428
|
+
let read = storage
|
|
429
|
+
.begin_read(StorageReadOptions::default())
|
|
430
|
+
.expect("read should open");
|
|
431
|
+
let mut reader = graph.reader(read);
|
|
432
432
|
|
|
433
433
|
let commit = reader
|
|
434
434
|
.load_commit("missing")
|
|
@@ -440,10 +440,9 @@ mod tests {
|
|
|
440
440
|
|
|
441
441
|
#[tokio::test]
|
|
442
442
|
async fn all_commits_returns_parsed_commits_sorted_by_id() {
|
|
443
|
-
let
|
|
444
|
-
let storage = StorageContext::new(backend.clone());
|
|
443
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
445
444
|
append_changes(
|
|
446
|
-
storage
|
|
445
|
+
&storage,
|
|
447
446
|
&[
|
|
448
447
|
commit_change("commit-b-change", "commit-b", &[], &[]),
|
|
449
448
|
entity_change("change-1", "entity-1", "example", "{}"),
|
|
@@ -453,7 +452,10 @@ mod tests {
|
|
|
453
452
|
.await;
|
|
454
453
|
|
|
455
454
|
let graph = CommitGraphContext::new();
|
|
456
|
-
let
|
|
455
|
+
let read = storage
|
|
456
|
+
.begin_read(StorageReadOptions::default())
|
|
457
|
+
.expect("read should open");
|
|
458
|
+
let mut reader = graph.reader(read);
|
|
457
459
|
let commits = reader
|
|
458
460
|
.all_commits()
|
|
459
461
|
.await
|
|
@@ -471,7 +473,11 @@ mod tests {
|
|
|
471
473
|
#[tokio::test]
|
|
472
474
|
async fn commit_edges_are_derived_from_parent_commit_ids() {
|
|
473
475
|
let graph = CommitGraphContext::new();
|
|
474
|
-
let
|
|
476
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
477
|
+
let read = storage
|
|
478
|
+
.begin_read(StorageReadOptions::default())
|
|
479
|
+
.expect("read should open");
|
|
480
|
+
let reader = graph.reader(read);
|
|
475
481
|
let commits = vec![parsed_commit(
|
|
476
482
|
"commit-head",
|
|
477
483
|
&[],
|
|
@@ -498,10 +504,9 @@ mod tests {
|
|
|
498
504
|
|
|
499
505
|
#[tokio::test]
|
|
500
506
|
async fn change_history_from_commit_reports_matching_canonical_changes_with_depth() {
|
|
501
|
-
let
|
|
502
|
-
let storage = StorageContext::new(backend.clone());
|
|
507
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
503
508
|
append_changes(
|
|
504
|
-
storage
|
|
509
|
+
&storage,
|
|
505
510
|
&[
|
|
506
511
|
entity_change("change-root", "entity-root", "test_schema", "{}"),
|
|
507
512
|
entity_change("change-head", "entity-head", "test_schema", "{}"),
|
|
@@ -517,7 +522,10 @@ mod tests {
|
|
|
517
522
|
.await;
|
|
518
523
|
|
|
519
524
|
let graph = CommitGraphContext::new();
|
|
520
|
-
let
|
|
525
|
+
let read = storage
|
|
526
|
+
.begin_read(StorageReadOptions::default())
|
|
527
|
+
.expect("read should open");
|
|
528
|
+
let mut reader = graph.reader(read);
|
|
521
529
|
let history = reader
|
|
522
530
|
.change_history_from_commit(
|
|
523
531
|
"commit-head",
|
|
@@ -534,7 +542,7 @@ mod tests {
|
|
|
534
542
|
history
|
|
535
543
|
.iter()
|
|
536
544
|
.map(|entry| (
|
|
537
|
-
entry.
|
|
545
|
+
entry.change.id.as_str(),
|
|
538
546
|
entry.observed_commit_id.as_str(),
|
|
539
547
|
entry.start_commit_id.as_str(),
|
|
540
548
|
entry.depth
|
|
@@ -549,10 +557,9 @@ mod tests {
|
|
|
549
557
|
|
|
550
558
|
#[tokio::test]
|
|
551
559
|
async fn change_history_from_commit_filters_depth_entity_file_and_tombstones() {
|
|
552
|
-
let
|
|
553
|
-
let storage = StorageContext::new(backend.clone());
|
|
560
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
554
561
|
append_changes(
|
|
555
|
-
storage
|
|
562
|
+
&storage,
|
|
556
563
|
&[
|
|
557
564
|
entity_change_with_file(
|
|
558
565
|
"change-file-a",
|
|
@@ -581,12 +588,15 @@ mod tests {
|
|
|
581
588
|
.await;
|
|
582
589
|
|
|
583
590
|
let graph = CommitGraphContext::new();
|
|
584
|
-
let
|
|
591
|
+
let read = storage
|
|
592
|
+
.begin_read(StorageReadOptions::default())
|
|
593
|
+
.expect("read should open");
|
|
594
|
+
let mut reader = graph.reader(read);
|
|
585
595
|
let history = reader
|
|
586
596
|
.change_history_from_commit(
|
|
587
597
|
"commit-head",
|
|
588
598
|
&CommitGraphChangeHistoryRequest {
|
|
589
|
-
|
|
599
|
+
entity_pks: vec![crate::entity_pk::EntityPk::single("entity-1")],
|
|
590
600
|
file_ids: vec!["file-a".to_string()],
|
|
591
601
|
min_depth: Some(1),
|
|
592
602
|
max_depth: Some(1),
|
|
@@ -598,16 +608,15 @@ mod tests {
|
|
|
598
608
|
.expect("history should resolve");
|
|
599
609
|
|
|
600
610
|
assert_eq!(history.len(), 1);
|
|
601
|
-
assert_eq!(history[0].
|
|
611
|
+
assert_eq!(history[0].change.id, "change-file-a");
|
|
602
612
|
assert_eq!(history[0].depth, 1);
|
|
603
613
|
}
|
|
604
614
|
|
|
605
615
|
#[tokio::test]
|
|
606
616
|
async fn change_history_from_commit_includes_tombstones_when_requested() {
|
|
607
|
-
let
|
|
608
|
-
let storage = StorageContext::new(backend.clone());
|
|
617
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
609
618
|
append_changes(
|
|
610
|
-
storage
|
|
619
|
+
&storage,
|
|
611
620
|
&[
|
|
612
621
|
entity_tombstone("change-deleted", "entity-1", "test_schema"),
|
|
613
622
|
commit_change(
|
|
@@ -621,15 +630,25 @@ mod tests {
|
|
|
621
630
|
.await;
|
|
622
631
|
|
|
623
632
|
let graph = CommitGraphContext::new();
|
|
624
|
-
let
|
|
633
|
+
let read = storage
|
|
634
|
+
.begin_read(StorageReadOptions::default())
|
|
635
|
+
.expect("read should open");
|
|
636
|
+
let mut reader = graph.reader(read);
|
|
625
637
|
let hidden = reader
|
|
626
|
-
.change_history_from_commit(
|
|
638
|
+
.change_history_from_commit(
|
|
639
|
+
"commit-head",
|
|
640
|
+
&CommitGraphChangeHistoryRequest {
|
|
641
|
+
schema_keys: vec!["test_schema".to_string()],
|
|
642
|
+
..CommitGraphChangeHistoryRequest::default()
|
|
643
|
+
},
|
|
644
|
+
)
|
|
627
645
|
.await
|
|
628
646
|
.expect("history should resolve");
|
|
629
647
|
let visible = reader
|
|
630
648
|
.change_history_from_commit(
|
|
631
649
|
"commit-head",
|
|
632
650
|
&CommitGraphChangeHistoryRequest {
|
|
651
|
+
schema_keys: vec!["test_schema".to_string()],
|
|
633
652
|
include_tombstones: true,
|
|
634
653
|
..CommitGraphChangeHistoryRequest::default()
|
|
635
654
|
},
|
|
@@ -639,12 +658,12 @@ mod tests {
|
|
|
639
658
|
|
|
640
659
|
assert!(hidden.is_empty());
|
|
641
660
|
assert_eq!(visible.len(), 1);
|
|
642
|
-
assert_eq!(visible[0].
|
|
661
|
+
assert_eq!(visible[0].change.id, "change-deleted");
|
|
643
662
|
}
|
|
644
663
|
|
|
645
664
|
#[derive(Clone)]
|
|
646
665
|
struct TestChange {
|
|
647
|
-
change:
|
|
666
|
+
change: CommitGraphChange,
|
|
648
667
|
commit_change_ids: Vec<String>,
|
|
649
668
|
parent_commit_ids: Vec<String>,
|
|
650
669
|
author_account_ids: Vec<String>,
|
|
@@ -658,9 +677,9 @@ mod tests {
|
|
|
658
677
|
parent_commit_ids: &[&str],
|
|
659
678
|
) -> Self {
|
|
660
679
|
Self {
|
|
661
|
-
change:
|
|
680
|
+
change: CommitGraphChange {
|
|
662
681
|
id: change_id.to_string(),
|
|
663
|
-
|
|
682
|
+
entity_pk: crate::entity_pk::EntityPk::single(commit_id),
|
|
664
683
|
schema_key: super::COMMIT_SCHEMA_KEY.to_string(),
|
|
665
684
|
file_id: None,
|
|
666
685
|
snapshot_ref: None,
|
|
@@ -675,16 +694,16 @@ mod tests {
|
|
|
675
694
|
|
|
676
695
|
fn entity(
|
|
677
696
|
change_id: &str,
|
|
678
|
-
|
|
697
|
+
entity_pk: &str,
|
|
679
698
|
schema_key: &str,
|
|
680
699
|
file_id: Option<&str>,
|
|
681
700
|
snapshot_content: Option<&str>,
|
|
682
701
|
created_at: &str,
|
|
683
702
|
) -> Self {
|
|
684
703
|
Self {
|
|
685
|
-
change:
|
|
704
|
+
change: CommitGraphChange {
|
|
686
705
|
id: change_id.to_string(),
|
|
687
|
-
|
|
706
|
+
entity_pk: crate::entity_pk::EntityPk::single(entity_pk),
|
|
688
707
|
schema_key: schema_key.to_string(),
|
|
689
708
|
file_id: file_id.map(str::to_string),
|
|
690
709
|
snapshot_ref: snapshot_content.map(|content| {
|
|
@@ -704,102 +723,126 @@ mod tests {
|
|
|
704
723
|
}
|
|
705
724
|
}
|
|
706
725
|
|
|
707
|
-
async fn append_changes(storage: StorageContext, changes: &[TestChange]) {
|
|
708
|
-
let mut
|
|
709
|
-
.
|
|
710
|
-
.
|
|
711
|
-
|
|
712
|
-
let mut writes = StorageWriteSet::new();
|
|
726
|
+
async fn append_changes(storage: &StorageContext, changes: &[TestChange]) {
|
|
727
|
+
let mut read = storage
|
|
728
|
+
.begin_read(StorageReadOptions::default())
|
|
729
|
+
.expect("read should open");
|
|
730
|
+
let mut writes = storage.new_write_set();
|
|
713
731
|
let canonical_changes = changes
|
|
714
732
|
.iter()
|
|
715
733
|
.filter(|change| !change.is_commit())
|
|
716
|
-
.
|
|
734
|
+
.cloned()
|
|
717
735
|
.collect::<Vec<_>>();
|
|
718
|
-
let changes_by_id: BTreeMap<&str, &
|
|
736
|
+
let changes_by_id: BTreeMap<&str, &TestChange> = canonical_changes
|
|
719
737
|
.iter()
|
|
720
|
-
.map(|change| (change.id.as_str(), change))
|
|
738
|
+
.map(|change| (change.change.id.as_str(), change))
|
|
721
739
|
.collect::<BTreeMap<_, _>>();
|
|
722
740
|
let mut authored_change_ids = BTreeSet::new();
|
|
723
|
-
let
|
|
741
|
+
let provided_commit_ids = changes
|
|
742
|
+
.iter()
|
|
743
|
+
.filter(|change| change.is_commit())
|
|
744
|
+
.map(|change| {
|
|
745
|
+
change
|
|
746
|
+
.change
|
|
747
|
+
.entity_pk
|
|
748
|
+
.as_single_string()
|
|
749
|
+
.expect("commit fixture should use single entity pk")
|
|
750
|
+
.to_string()
|
|
751
|
+
})
|
|
752
|
+
.collect::<BTreeSet<_>>();
|
|
753
|
+
let mut staged_commit_ids = BTreeSet::new();
|
|
754
|
+
let changelog = ChangelogContext::new();
|
|
755
|
+
let mut writer = changelog.writer(&mut read, &mut writes);
|
|
756
|
+
let mut append = ChangelogAppend::default();
|
|
724
757
|
for change in changes.iter().filter(|change| change.is_commit()) {
|
|
725
758
|
let commit = crate::commit_graph::CommitGraphCommit {
|
|
726
759
|
canonical_change: change.change.clone(),
|
|
727
760
|
change: change.change.clone(),
|
|
728
761
|
commit_id: change
|
|
729
762
|
.change
|
|
730
|
-
.
|
|
763
|
+
.entity_pk
|
|
731
764
|
.as_single_string()
|
|
732
|
-
.expect("commit fixture should use single entity
|
|
765
|
+
.expect("commit fixture should use single entity pk")
|
|
733
766
|
.to_string(),
|
|
734
767
|
change_ids: change.commit_change_ids.clone(),
|
|
735
768
|
author_account_ids: change.author_account_ids.clone(),
|
|
736
769
|
parent_commit_ids: change.parent_commit_ids.clone(),
|
|
737
770
|
};
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
};
|
|
747
|
-
|
|
748
|
-
let mut authored_changes = Vec::new();
|
|
749
|
-
let mut adopted_changes = Vec::new();
|
|
750
|
-
let mut corrupt_missing_members = Vec::new();
|
|
771
|
+
for parent_commit_id in &commit.parent_commit_ids {
|
|
772
|
+
if !provided_commit_ids.contains(parent_commit_id)
|
|
773
|
+
&& staged_commit_ids.insert(parent_commit_id.clone())
|
|
774
|
+
{
|
|
775
|
+
append_empty_commit(&mut append, parent_commit_id);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
let mut refs = Vec::new();
|
|
751
779
|
for change_id in &commit.change_ids {
|
|
752
780
|
if let Some(change) = changes_by_id.get(change_id.as_str()) {
|
|
753
781
|
if authored_change_ids.insert(change_id.clone()) {
|
|
754
|
-
|
|
755
|
-
} else {
|
|
756
|
-
adopted_changes.push(change_ref_from_canonical(change.as_ref()));
|
|
782
|
+
append.changes.push(change_record_from_test_change(change));
|
|
757
783
|
}
|
|
758
|
-
|
|
759
|
-
corrupt_missing_members.push(change_id.clone());
|
|
784
|
+
refs.push(commit_change_ref_from_test_change(change));
|
|
760
785
|
}
|
|
761
786
|
}
|
|
762
787
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
.map(|change_id| ChangeLocator {
|
|
777
|
-
source_commit_id: "missing-source-commit".to_string(),
|
|
778
|
-
source_pack_id: 0,
|
|
779
|
-
source_ordinal: 0,
|
|
780
|
-
change_id,
|
|
781
|
-
})
|
|
782
|
-
.collect(),
|
|
783
|
-
)
|
|
784
|
-
.expect("corrupt commit-store fixture should stage");
|
|
785
|
-
}
|
|
788
|
+
append.commits.push(CommitRecord {
|
|
789
|
+
format_version: 1,
|
|
790
|
+
commit_id: commit.commit_id.clone(),
|
|
791
|
+
parent_commit_ids: commit.parent_commit_ids.clone(),
|
|
792
|
+
change_id: commit.canonical_change.id.clone(),
|
|
793
|
+
author_account_ids: commit.author_account_ids.clone(),
|
|
794
|
+
created_at: commit.canonical_change.created_at.clone(),
|
|
795
|
+
});
|
|
796
|
+
append.commit_change_refs.push(CommitChangeRefSet {
|
|
797
|
+
commit_id: commit.commit_id.clone(),
|
|
798
|
+
entries: refs,
|
|
799
|
+
});
|
|
800
|
+
staged_commit_ids.insert(commit.commit_id.clone());
|
|
786
801
|
}
|
|
787
|
-
|
|
788
|
-
.
|
|
802
|
+
writer
|
|
803
|
+
.stage_append(append)
|
|
789
804
|
.await
|
|
790
|
-
.expect("
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
805
|
+
.expect("changelog append should stage");
|
|
806
|
+
storage
|
|
807
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
808
|
+
.expect("commit should succeed");
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
fn append_empty_commit(append: &mut ChangelogAppend, commit_id: &str) {
|
|
812
|
+
let change_id = format!("{commit_id}-change");
|
|
813
|
+
append.commits.push(CommitRecord {
|
|
814
|
+
format_version: 1,
|
|
815
|
+
commit_id: commit_id.to_string(),
|
|
816
|
+
parent_commit_ids: Vec::new(),
|
|
817
|
+
change_id: change_id.clone(),
|
|
818
|
+
author_account_ids: Vec::new(),
|
|
819
|
+
created_at: "2026-01-01T00:00:00Z".to_string(),
|
|
820
|
+
});
|
|
821
|
+
append.commit_change_refs.push(CommitChangeRefSet {
|
|
822
|
+
commit_id: commit_id.to_string(),
|
|
823
|
+
entries: Vec::new(),
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
fn change_record_from_test_change(change: &TestChange) -> ChangeRecord {
|
|
828
|
+
ChangeRecord {
|
|
829
|
+
format_version: 1,
|
|
830
|
+
change_id: change.change.id.clone(),
|
|
831
|
+
entity_pk: change.change.entity_pk.clone(),
|
|
832
|
+
schema_key: change.change.schema_key.clone(),
|
|
833
|
+
file_id: change.change.file_id.clone(),
|
|
834
|
+
snapshot_ref: change.change.snapshot_ref,
|
|
835
|
+
metadata_ref: change.change.metadata_ref,
|
|
836
|
+
created_at: change.change.created_at.clone(),
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
fn commit_change_ref_from_test_change(change: &TestChange) -> CommitChangeRef {
|
|
841
|
+
CommitChangeRef {
|
|
842
|
+
schema_key: change.change.schema_key.clone(),
|
|
843
|
+
file_id: change.change.file_id.clone(),
|
|
844
|
+
entity_pk: change.change.entity_pk.clone(),
|
|
845
|
+
change_id: change.change.id.clone(),
|
|
803
846
|
}
|
|
804
847
|
}
|
|
805
848
|
|
|
@@ -841,13 +884,13 @@ mod tests {
|
|
|
841
884
|
|
|
842
885
|
fn entity_change(
|
|
843
886
|
change_id: &str,
|
|
844
|
-
|
|
887
|
+
entity_pk: &str,
|
|
845
888
|
schema_key: &str,
|
|
846
889
|
snapshot_content: &str,
|
|
847
890
|
) -> TestChange {
|
|
848
891
|
entity_change_at(
|
|
849
892
|
change_id,
|
|
850
|
-
|
|
893
|
+
entity_pk,
|
|
851
894
|
schema_key,
|
|
852
895
|
snapshot_content,
|
|
853
896
|
"2026-01-01T00:00:00Z",
|
|
@@ -856,14 +899,14 @@ mod tests {
|
|
|
856
899
|
|
|
857
900
|
fn entity_change_at(
|
|
858
901
|
change_id: &str,
|
|
859
|
-
|
|
902
|
+
entity_pk: &str,
|
|
860
903
|
schema_key: &str,
|
|
861
904
|
snapshot_content: &str,
|
|
862
905
|
created_at: &str,
|
|
863
906
|
) -> TestChange {
|
|
864
907
|
TestChange::entity(
|
|
865
908
|
change_id,
|
|
866
|
-
|
|
909
|
+
entity_pk,
|
|
867
910
|
schema_key,
|
|
868
911
|
None,
|
|
869
912
|
Some(snapshot_content),
|
|
@@ -873,14 +916,14 @@ mod tests {
|
|
|
873
916
|
|
|
874
917
|
fn entity_change_with_file(
|
|
875
918
|
change_id: &str,
|
|
876
|
-
|
|
919
|
+
entity_pk: &str,
|
|
877
920
|
schema_key: &str,
|
|
878
921
|
file_id: Option<&str>,
|
|
879
922
|
snapshot_content: &str,
|
|
880
923
|
) -> TestChange {
|
|
881
924
|
TestChange::entity(
|
|
882
925
|
change_id,
|
|
883
|
-
|
|
926
|
+
entity_pk,
|
|
884
927
|
schema_key,
|
|
885
928
|
file_id,
|
|
886
929
|
Some(snapshot_content),
|
|
@@ -888,10 +931,10 @@ mod tests {
|
|
|
888
931
|
)
|
|
889
932
|
}
|
|
890
933
|
|
|
891
|
-
fn entity_tombstone(change_id: &str,
|
|
934
|
+
fn entity_tombstone(change_id: &str, entity_pk: &str, schema_key: &str) -> TestChange {
|
|
892
935
|
TestChange::entity(
|
|
893
936
|
change_id,
|
|
894
|
-
|
|
937
|
+
entity_pk,
|
|
895
938
|
schema_key,
|
|
896
939
|
None,
|
|
897
940
|
None,
|