chkit snapshot
Rewrites chkit/meta/snapshot.json from your schema definitions without writing a migration, for example when two branches that each ran chkit generate conflict on it. TypeScript only: chkit-py does not have this command yet.
Synopsis
Section titled “Synopsis”chkit snapshot rebuild [flags]| Flag | Type | Default | Description |
|---|---|---|---|
--dryrun | boolean | false | Print the report without writing snapshot.json |
Global flags documented on CLI Overview. rebuild always rewrites the whole snapshot and rejects --table.
Behavior
Section titled “Behavior”Why snapshot.json conflicts
Section titled “Why snapshot.json conflicts”chkit generate rewrites the whole snapshot.json on every run, including its generatedAt timestamp. When two branches each run generate, the branch that merges or rebases second conflicts on the file, even when the branches changed unrelated objects. Git merges the JSON as plain text: a conflict inside the definitions array can split an entry between the two sides, and taking either side drops the other branch’s changes.
While the file has conflict markers, generate, migrate (except migrate --abandon), drift, and check stop with Snapshot ... contains unresolved merge conflict markers.
Rebuild
Section titled “Rebuild”chkit snapshot rebuild writes the snapshot that chkit generate would write for your current schema definitions. It never writes a migration file and never connects to ClickHouse.
- Loads the config and schema definitions and runs the
onConfigLoadedandonSchemaLoadedplugin hooks, likegenerate. A schema file that does not load, for example one that still has conflict markers, stops the command before anything is written. - Validates the definitions. A validation error stops the command before anything is written.
- Compares the existing
snapshot.jsonwith the rebuilt definitions, entry by entry. - Writes
snapshot.json, unless--dryrunis set or the existing file already matches.
The report lists each entry that differs by its key: + added, - removed, ~ changed (for example ~ view:app.events_by_source). The comparison ignores generatedAt, key order, and formatting. If the existing file has unresolved merge conflict markers, is empty, or cannot be read as a snapshot, the report says so and the command rebuilds without a comparison. With nothing to compare, --dryrun only checks that the definitions load and pass validation; review the rebuilt file with git instead (see Review the result).
Config functions (defineConfig((env) => ...)) and plugin hooks receive command: 'snapshot' during a rebuild. A config or plugin that only acts when the command is generate makes the rebuilt snapshot differ from the one generate writes, so treat snapshot like generate.
Before you rebuild
Section titled “Before you rebuild”- Every branch is fully generated. On each branch,
chkit generate --dryrunreports 0 operations before you merge or rebase. Then every schema change on either branch has a migration file. - Generate after upgrading chkit. A new chkit version can store a definition in a different canonical form and plan a repair migration for it. Run
chkit generateand commit its migration before you rebuild; a rebuild would absorb the repair.
When not to rebuild
Section titled “When not to rebuild”- The file is damaged but committed, and no merge or rebase is in progress. Restore the committed version with
git checkout HEAD -- chkit/meta/snapshot.json. During a merge or rebase,HEADholds only one side of the conflict. - You abandoned a failed migration to generate it again (for example with
chkit migrate --abandon). Restoresnapshot.jsonfrom git to its state before that migration was generated. A rebuild records the current schema, so the followinggeneratewould plan nothing. - You scope
generatewith--table. A scopedgenerateleaves the entries of other tables behind the schema on purpose. A rebuild records every definition and does not support--table.
Review the result
Section titled “Review the result”chkit generate --dryrun reports 0 operations right after a rebuild by construction, so it does not check the result. Review the report instead: every listed entry must come from a migration file of one of the branches. After a conflict, compare the rebuilt file with both sides before you commit it. During a merge the other side is MERGE_HEAD; during a rebase it is REBASE_HEAD, the commit being replayed:
git diff HEAD -- chkit/meta/snapshot.jsongit diff MERGE_HEAD -- chkit/meta/snapshot.json # during a mergegit diff REBASE_HEAD -- chkit/meta/snapshot.json # during a rebaseEvery entry that differs from one side must come from a migration file of the other side. For tables, chkit drift against an environment where every migration is applied confirms the shape, matching columns by name. drift does not compare column order or view queries.
When both branches change the same object
Section titled “When both branches change the same object”chkit migrate applies pending migration files in filename order, and the version of an object that is applied last wins. When both branches changed the same table or view, an environment that applied one branch before the other was merged runs the two migrations in a different order than a fresh environment, so environments can end up with different versions of that object. A rebuild records the definition from your merged schema files as applied, and chkit generate plans nothing for it afterwards. For a table, this matters only for what both branches changed, such as the same column. Changes to different columns give the same columns in either order, but when each branch adds a column, the new columns can end up in a different order in each environment: chkit appends an added column, and drift compares columns by name, so it does not report the difference. The order shows in SELECT * and in an INSERT without a column list.
After the rebuild, add a migration that sorts after both branches’ migrations and applies the merged definition of that object again, so every environment converges:
chkit generate --empty --name reapply_events_by_sourceCopy the object’s statements from the branch migrations into the new file and adjust them to the merged definition: the DROP VIEW IF EXISTS and CREATE VIEW IF NOT EXISTS pair for a view, or the ALTER TABLE statements for a table. Keep the DROP for a view: on its own, CREATE VIEW IF NOT EXISTS does nothing where the view exists. Copy each statement with its -- operation: comment line; without it, chkit migrate --apply treats a DROP VIEW as unmarked destructive SQL and requires --allow-destructive.
-- operation: drop_view key=view:app.events_by_source risk=cautionDROP VIEW IF EXISTS app.events_by_source;
-- operation: create_view key=view:app.events_by_source risk=cautionCREATE VIEW IF NOT EXISTS app.events_by_source ASSELECT source, count() AS total FROM app.events GROUP BY source;Removing the entry from snapshot.json instead does not help: generate would plan a CREATE ... IF NOT EXISTS, which does nothing where the object exists.
Examples
Section titled “Examples”Resolve a conflicted snapshot during a rebase:
chkit snapshot rebuildgit diff HEAD -- chkit/meta/snapshot.jsongit diff REBASE_HEAD -- chkit/meta/snapshot.jsongit add chkit/meta/snapshot.jsongit rebase --continueResolve a conflicted snapshot during a merge:
chkit snapshot rebuildgit diff HEAD -- chkit/meta/snapshot.jsongit diff MERGE_HEAD -- chkit/meta/snapshot.jsongit add chkit/meta/snapshot.jsongit commit --no-editPreview a rebuild as JSON:
chkit snapshot rebuild --dryrun --jsonExit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
| 0 | Snapshot written, already up to date, or dry run |
| 1 | Schema file that does not load (for example with conflict markers), validation error, usage error (missing or unknown subcommand, --table), or config error |
JSON output
Section titled “JSON output”Rebuild
Section titled “Rebuild”{ "command": "snapshot", "schemaVersion": 1, "subcommand": "rebuild", "mode": "write", "snapshotFile": "/repo/chkit/meta/snapshot.json", "written": true, "definitionCount": 16, "previous": { "status": "parsed", "added": ["table:app.users", "view:app.users_daily"], "removed": [], "changed": ["view:app.events_by_source"] }}mode is plan with --dryrun, which never writes. written is false when the existing snapshot already matches. previous.status is one of:
| Status | Meaning | Extra fields |
|---|---|---|
missing | No snapshot.json existed | none |
parsed | Compared with the existing snapshot | added, removed, changed |
conflicted | The file had unresolved merge conflict markers; not compared | none |
unreadable | The file was empty, invalid JSON, or not a chkit snapshot; not compared | reason: empty, invalid_json, or invalid_shape |
Validation error
Section titled “Validation error”{ "command": "snapshot", "schemaVersion": 1, "error": "validation_failed", "issues": [{ "code": "...", "message": "..." }]}Related commands
Section titled “Related commands”- The migration workflow — the flow for schema changes on parallel branches
chkit generate— writes the snapshot on every runchkit drift— compare the snapshot with a database where every migration is applied