relq 1.0.25 → 1.0.27

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.
Files changed (52) hide show
  1. package/dist/cjs/cli/commands/commit.cjs +80 -0
  2. package/dist/cjs/cli/commands/import.cjs +1 -0
  3. package/dist/cjs/cli/commands/pull.cjs +108 -34
  4. package/dist/cjs/cli/commands/push.cjs +48 -8
  5. package/dist/cjs/cli/commands/rollback.cjs +205 -84
  6. package/dist/cjs/cli/commands/schema-ast.cjs +219 -0
  7. package/dist/cjs/cli/index.cjs +6 -0
  8. package/dist/cjs/cli/utils/ast-codegen.cjs +95 -3
  9. package/dist/cjs/cli/utils/ast-transformer.cjs +12 -0
  10. package/dist/cjs/cli/utils/change-tracker.cjs +135 -0
  11. package/dist/cjs/cli/utils/commit-manager.cjs +54 -0
  12. package/dist/cjs/cli/utils/migration-generator.cjs +319 -0
  13. package/dist/cjs/cli/utils/repo-manager.cjs +99 -3
  14. package/dist/cjs/cli/utils/schema-diff.cjs +390 -0
  15. package/dist/cjs/cli/utils/schema-hash.cjs +4 -0
  16. package/dist/cjs/cli/utils/schema-to-ast.cjs +477 -0
  17. package/dist/cjs/schema-definition/column-types.cjs +50 -4
  18. package/dist/cjs/schema-definition/pg-enum.cjs +10 -0
  19. package/dist/cjs/schema-definition/pg-function.cjs +19 -0
  20. package/dist/cjs/schema-definition/pg-sequence.cjs +22 -1
  21. package/dist/cjs/schema-definition/pg-trigger.cjs +39 -0
  22. package/dist/cjs/schema-definition/pg-view.cjs +17 -0
  23. package/dist/cjs/schema-definition/sql-expressions.cjs +3 -0
  24. package/dist/cjs/schema-definition/table-definition.cjs +4 -0
  25. package/dist/config.d.ts +98 -0
  26. package/dist/esm/cli/commands/commit.js +83 -3
  27. package/dist/esm/cli/commands/import.js +1 -0
  28. package/dist/esm/cli/commands/pull.js +109 -35
  29. package/dist/esm/cli/commands/push.js +49 -9
  30. package/dist/esm/cli/commands/rollback.js +206 -85
  31. package/dist/esm/cli/commands/schema-ast.js +183 -0
  32. package/dist/esm/cli/index.js +6 -0
  33. package/dist/esm/cli/utils/ast-codegen.js +93 -3
  34. package/dist/esm/cli/utils/ast-transformer.js +12 -0
  35. package/dist/esm/cli/utils/change-tracker.js +134 -0
  36. package/dist/esm/cli/utils/commit-manager.js +51 -0
  37. package/dist/esm/cli/utils/migration-generator.js +318 -0
  38. package/dist/esm/cli/utils/repo-manager.js +96 -3
  39. package/dist/esm/cli/utils/schema-diff.js +389 -0
  40. package/dist/esm/cli/utils/schema-hash.js +4 -0
  41. package/dist/esm/cli/utils/schema-to-ast.js +447 -0
  42. package/dist/esm/schema-definition/column-types.js +50 -4
  43. package/dist/esm/schema-definition/pg-enum.js +10 -0
  44. package/dist/esm/schema-definition/pg-function.js +19 -0
  45. package/dist/esm/schema-definition/pg-sequence.js +22 -1
  46. package/dist/esm/schema-definition/pg-trigger.js +39 -0
  47. package/dist/esm/schema-definition/pg-view.js +17 -0
  48. package/dist/esm/schema-definition/sql-expressions.js +3 -0
  49. package/dist/esm/schema-definition/table-definition.js +4 -0
  50. package/dist/index.d.ts +98 -0
  51. package/dist/schema-builder.d.ts +223 -24
  52. package/package.json +1 -1
@@ -40,8 +40,11 @@ const repo_manager_1 = require("../utils/repo-manager.cjs");
40
40
  const config_1 = require("../../config/config.cjs");
41
41
  const config_loader_1 = require("../utils/config-loader.cjs");
42
42
  const change_tracker_1 = require("../utils/change-tracker.cjs");
43
+ const commit_manager_1 = require("../utils/commit-manager.cjs");
44
+ const schema_to_ast_1 = require("../utils/schema-to-ast.cjs");
43
45
  const fs = __importStar(require("fs"));
44
46
  const path = __importStar(require("path"));
47
+ const jiti_1 = require("jiti");
45
48
  async function commitCommand(context) {
46
49
  const { config, flags, args, projectRoot } = context;
47
50
  const author = config?.author || 'Developer <dev@example.com>';
@@ -52,6 +55,9 @@ async function commitCommand(context) {
52
55
  const schemaPath = path.resolve(projectRoot, (0, config_loader_1.getSchemaPath)(config ?? undefined));
53
56
  const { requireValidSchema } = await Promise.resolve().then(() => __importStar(require("../utils/config-loader.cjs")));
54
57
  await requireValidSchema(schemaPath, flags);
58
+ if (flags['from-schema']) {
59
+ return commitFromSchema(context, schemaPath, author);
60
+ }
55
61
  const staged = (0, repo_manager_1.getStagedChanges)(projectRoot);
56
62
  if (staged.length === 0) {
57
63
  console.log('nothing to commit, working tree clean');
@@ -76,6 +82,8 @@ async function commitCommand(context) {
76
82
  }
77
83
  const sortedChanges = (0, change_tracker_1.sortChangesByDependency)(staged);
78
84
  const sql = (0, change_tracker_1.generateCombinedSQL)(sortedChanges);
85
+ const downSQL = (0, change_tracker_1.generateDownSQL)(sortedChanges);
86
+ const snapshot = (0, repo_manager_1.loadSnapshot)(projectRoot);
79
87
  const creates = staged.filter(c => c.type === 'CREATE').length;
80
88
  const alters = staged.filter(c => c.type === 'ALTER').length;
81
89
  const drops = staged.filter(c => c.type === 'DROP').length;
@@ -96,6 +104,8 @@ async function commitCommand(context) {
96
104
  timestamp: new Date().toISOString(),
97
105
  changes: sortedChanges,
98
106
  sql,
107
+ downSQL,
108
+ schema: snapshot,
99
109
  snapshotHash: hash.substring(0, 12),
100
110
  stats: {
101
111
  creates,
@@ -149,4 +159,74 @@ async function commitCommand(context) {
149
159
  (0, cli_utils_1.hint)("run 'relq export' to export as SQL file");
150
160
  console.log('');
151
161
  }
162
+ async function commitFromSchema(context, schemaPath, author) {
163
+ const { config, flags, args, projectRoot } = context;
164
+ const spinner = (0, cli_utils_1.createSpinner)();
165
+ let message = flags['m'] || flags['message'];
166
+ if (!message) {
167
+ if (args.length > 0) {
168
+ message = args.join(' ');
169
+ }
170
+ else {
171
+ (0, cli_utils_1.fatal)('commit message required', "usage: relq commit --from-schema -m '<message>'");
172
+ }
173
+ }
174
+ spinner.start('Loading schema file');
175
+ let schemaModule;
176
+ try {
177
+ const jiti = (0, jiti_1.createJiti)(path.dirname(schemaPath), { interopDefault: true });
178
+ const module = await jiti.import(schemaPath);
179
+ if (module && module.default && typeof module.default === 'object') {
180
+ schemaModule = module.default;
181
+ }
182
+ else if (module && typeof module === 'object') {
183
+ schemaModule = module;
184
+ }
185
+ else {
186
+ throw new Error('Schema file must export an object with table/enum definitions');
187
+ }
188
+ spinner.succeed('Loaded schema file');
189
+ }
190
+ catch (err) {
191
+ spinner.fail('Failed to load schema');
192
+ (0, cli_utils_1.fatal)(`Could not load schema: ${err instanceof Error ? err.message : String(err)}`);
193
+ }
194
+ spinner.start('Converting schema to AST');
195
+ const ast = (0, schema_to_ast_1.schemaToAST)(schemaModule);
196
+ spinner.succeed('Converted schema to AST');
197
+ const schemaHash = (0, commit_manager_1.generateASTHash)(ast);
198
+ spinner.start('Creating commit');
199
+ try {
200
+ const commit = (0, commit_manager_1.createCommitFromSchema)(schemaModule, author, message, config?.commitLimit ?? 1000, projectRoot);
201
+ spinner.succeed('Created commit');
202
+ const tableCount = ast.tables.length;
203
+ const enumCount = ast.enums.length;
204
+ const functionCount = ast.functions.length;
205
+ const viewCount = ast.views.length;
206
+ const triggerCount = ast.triggers.length;
207
+ console.log('');
208
+ console.log(`[${(0, repo_manager_1.shortHash)(commit.hash)}] ${message}`);
209
+ const statsParts = [];
210
+ if (tableCount > 0)
211
+ statsParts.push(`${tableCount} table(s)`);
212
+ if (enumCount > 0)
213
+ statsParts.push(`${enumCount} enum(s)`);
214
+ if (functionCount > 0)
215
+ statsParts.push(`${functionCount} function(s)`);
216
+ if (viewCount > 0)
217
+ statsParts.push(`${viewCount} view(s)`);
218
+ if (triggerCount > 0)
219
+ statsParts.push(`${triggerCount} trigger(s)`);
220
+ console.log(` ${statsParts.length > 0 ? statsParts.join(', ') : 'empty schema'}`);
221
+ console.log('');
222
+ console.log(cli_utils_1.colors.muted(`Schema hash: ${schemaHash.substring(0, 12)}`));
223
+ console.log('');
224
+ (0, cli_utils_1.hint)("run 'relq push' to apply changes to database");
225
+ (0, cli_utils_1.hint)("run 'relq log' to view commit history");
226
+ }
227
+ catch (err) {
228
+ spinner.fail('Failed to create commit');
229
+ (0, cli_utils_1.fatal)(`Could not create commit: ${err instanceof Error ? err.message : String(err)}`);
230
+ }
231
+ }
152
232
  exports.default = commitCommand;
@@ -211,6 +211,7 @@ async function importCommand(sqlFilePath, options = {}, projectRoot = process.cw
211
211
  tables: filteredTables,
212
212
  enums: filteredEnums,
213
213
  domains: filteredDomains,
214
+ compositeTypes: parsedSchema.compositeTypes || [],
214
215
  sequences: filteredSequences,
215
216
  views: parsedSchema.views,
216
217
  functions: parsedSchema.functions,
@@ -375,10 +375,92 @@ async function pullCommand(context) {
375
375
  (0, cli_utils_1.fatal)('You have unresolved merge conflicts', `Use ${cli_utils_1.colors.cyan('relq resolve')} to see and resolve conflicts\nOr use ${cli_utils_1.colors.cyan('relq pull --force')} to overwrite local`);
376
376
  }
377
377
  if (schemaExists && localSnapshot && !force) {
378
- const localTables = new Set(localSnapshot.tables.map(t => t.name));
379
- const remoteTables = new Set(currentSchema.tables.map(t => t.name));
380
- const added = [...remoteTables].filter(t => !localTables.has(t));
381
- const removed = [...localTables].filter(t => !remoteTables.has(t));
378
+ const localForCompare = {
379
+ extensions: localSnapshot.extensions?.map(e => e.name) || [],
380
+ enums: localSnapshot.enums || [],
381
+ domains: localSnapshot.domains?.map(d => ({
382
+ name: d.name,
383
+ baseType: d.baseType,
384
+ isNotNull: d.notNull,
385
+ defaultValue: d.default,
386
+ checkExpression: d.check,
387
+ })) || [],
388
+ compositeTypes: localSnapshot.compositeTypes || [],
389
+ sequences: localSnapshot.sequences || [],
390
+ tables: localSnapshot.tables.map(t => ({
391
+ name: t.name,
392
+ schema: t.schema,
393
+ columns: t.columns.map(c => ({
394
+ name: c.name,
395
+ dataType: c.type,
396
+ isNullable: c.nullable,
397
+ defaultValue: c.default,
398
+ isPrimaryKey: c.primaryKey,
399
+ isUnique: c.unique,
400
+ comment: c.comment,
401
+ })),
402
+ indexes: t.indexes.map(i => ({
403
+ name: i.name,
404
+ columns: i.columns,
405
+ isUnique: i.unique,
406
+ type: i.type,
407
+ comment: i.comment,
408
+ })),
409
+ constraints: t.constraints || [],
410
+ isPartitioned: t.isPartitioned,
411
+ partitionType: t.partitionType,
412
+ partitionKey: t.partitionKey,
413
+ comment: t.comment,
414
+ })),
415
+ functions: localSnapshot.functions || [],
416
+ triggers: localSnapshot.triggers || [],
417
+ };
418
+ const remoteForCompare = {
419
+ extensions: dbSchema.extensions || [],
420
+ enums: filteredEnums || [],
421
+ domains: filteredDomains?.map(d => ({
422
+ name: d.name,
423
+ baseType: d.baseType,
424
+ isNotNull: d.isNotNull,
425
+ defaultValue: d.defaultValue,
426
+ checkExpression: d.checkExpression,
427
+ })) || [],
428
+ compositeTypes: filteredCompositeTypes || [],
429
+ sequences: [],
430
+ tables: filteredTables.map(t => ({
431
+ name: t.name,
432
+ schema: t.schema,
433
+ columns: t.columns.map(c => ({
434
+ name: c.name,
435
+ dataType: c.dataType,
436
+ isNullable: c.isNullable,
437
+ defaultValue: c.defaultValue,
438
+ isPrimaryKey: c.isPrimaryKey,
439
+ isUnique: c.isUnique,
440
+ comment: c.comment,
441
+ })),
442
+ indexes: t.indexes.map(i => ({
443
+ name: i.name,
444
+ columns: i.columns,
445
+ isUnique: i.isUnique,
446
+ type: i.type,
447
+ comment: i.comment,
448
+ })),
449
+ constraints: t.constraints || [],
450
+ isPartitioned: t.isPartitioned,
451
+ partitionType: t.partitionType,
452
+ partitionKey: t.partitionKey,
453
+ comment: t.comment,
454
+ })),
455
+ functions: filteredFunctions || [],
456
+ triggers: filteredTriggers || [],
457
+ };
458
+ const allChanges = (0, schema_comparator_1.compareSchemas)(localForCompare, remoteForCompare);
459
+ const tablesAdded = allChanges.filter(c => c.type === 'CREATE' && c.objectType === 'TABLE').length;
460
+ const tablesRemoved = allChanges.filter(c => c.type === 'DROP' && c.objectType === 'TABLE').length;
461
+ const columnsChanged = allChanges.filter(c => c.objectType === 'COLUMN').length;
462
+ const indexesChanged = allChanges.filter(c => c.objectType === 'INDEX').length;
463
+ const otherChanges = allChanges.filter(c => c.objectType !== 'TABLE' && c.objectType !== 'COLUMN' && c.objectType !== 'INDEX').length;
382
464
  const conflicts = detectObjectConflicts(localSnapshot, currentSchema);
383
465
  if (conflicts.length > 0 && !force) {
384
466
  const mergeState = {
@@ -400,26 +482,40 @@ async function pullCommand(context) {
400
482
  }
401
483
  (0, cli_utils_1.fatal)('Automatic merge failed; fix conflicts and then commit', `${cli_utils_1.colors.cyan('relq resolve --theirs <name>')} Take remote version\n${cli_utils_1.colors.cyan('relq resolve --all-theirs')} Take all remote\n${cli_utils_1.colors.cyan('relq pull --force')} Force overwrite local`);
402
484
  }
403
- if (added.length === 0 && removed.length === 0) {
485
+ if (allChanges.length === 0) {
404
486
  console.log('Already up to date with remote');
405
487
  console.log('');
406
488
  return;
407
489
  }
408
490
  console.log(`${cli_utils_1.colors.yellow('Remote has changes:')}`);
409
- if (added.length > 0) {
410
- console.log(` ${cli_utils_1.colors.green(`+${added.length}`)} tables added`);
491
+ if (tablesAdded > 0) {
492
+ console.log(` ${cli_utils_1.colors.green(`+${tablesAdded}`)} table(s) added`);
493
+ }
494
+ if (tablesRemoved > 0) {
495
+ console.log(` ${cli_utils_1.colors.red(`-${tablesRemoved}`)} table(s) removed`);
496
+ }
497
+ if (columnsChanged > 0) {
498
+ console.log(` ${cli_utils_1.colors.cyan(`~${columnsChanged}`)} column change(s)`);
411
499
  }
412
- if (removed.length > 0) {
413
- console.log(` ${cli_utils_1.colors.red(`-${removed.length}`)} tables removed`);
500
+ if (indexesChanged > 0) {
501
+ console.log(` ${cli_utils_1.colors.cyan(`~${indexesChanged}`)} index change(s)`);
502
+ }
503
+ if (otherChanges > 0) {
504
+ console.log(` ${cli_utils_1.colors.cyan(`~${otherChanges}`)} other change(s)`);
414
505
  }
415
506
  console.log('');
416
- if (!dryRun) {
507
+ const noAutoMerge = flags['no-auto-merge'] === true;
508
+ if (!dryRun && noAutoMerge) {
417
509
  const proceed = await (0, cli_utils_1.confirm)(`${cli_utils_1.colors.bold('Pull these changes?')}`, true);
418
510
  if (!proceed) {
419
511
  (0, cli_utils_1.fatal)('Operation cancelled by user');
420
512
  }
421
513
  console.log('');
422
514
  }
515
+ else if (!dryRun) {
516
+ console.log(`${cli_utils_1.colors.green('Auto-merging')} (no conflicts detected)`);
517
+ console.log('');
518
+ }
423
519
  }
424
520
  else if (schemaExists && !force) {
425
521
  (0, cli_utils_1.warning)('Local schema exists but not tracked');
@@ -481,6 +577,7 @@ async function pullCommand(context) {
481
577
  }
482
578
  spinner.start('Generating TypeScript schema...');
483
579
  const parsedSchema = await (0, ast_transformer_1.introspectedToParsedSchema)(dbSchema);
580
+ (0, ast_codegen_1.assignTrackingIds)(parsedSchema);
484
581
  const typescript = (0, ast_codegen_1.generateTypeScriptFromAST)(parsedSchema, {
485
582
  camelCase: config.generate?.camelCase ?? true,
486
583
  importPath: 'relq/schema-builder',
@@ -586,7 +683,7 @@ async function pullCommand(context) {
586
683
  triggers: filteredTriggers || [],
587
684
  };
588
685
  const schemaChanges = (0, schema_comparator_1.compareSchemas)(beforeSchema, afterSchema);
589
- applyTrackingIdsToSnapshot(typescript, currentSchema);
686
+ (0, ast_codegen_1.copyTrackingIdsToNormalized)(parsedSchema, currentSchema);
590
687
  (0, repo_manager_1.saveSnapshot)(currentSchema, projectRoot);
591
688
  const duration = Date.now() - startTime;
592
689
  if (noCommit) {
@@ -692,26 +789,3 @@ function detectObjectConflicts(local, remote) {
692
789
  }
693
790
  return conflicts;
694
791
  }
695
- function applyTrackingIdsToSnapshot(typescript, snapshot) {
696
- for (const table of snapshot.tables) {
697
- const tablePattern = new RegExp(`defineTable\\s*\\(\\s*['"]${table.name}['"]\\s*,\\s*\\{[^}]+\\}\\s*,\\s*\\{[^}]*\\$trackingId:\\s*['"]([^'"]+)['"]`, 's');
698
- const tableMatch = typescript.match(tablePattern);
699
- if (tableMatch) {
700
- table.trackingId = tableMatch[1];
701
- }
702
- for (const col of table.columns) {
703
- const colPattern = new RegExp(`(?:${col.tsName}|${col.name}):\\s*\\w+\\([^)]*\\)[^\\n]*\\.\\\$id\\(['"]([^'"]+)['"]\\)`);
704
- const colMatch = typescript.match(colPattern);
705
- if (colMatch) {
706
- col.trackingId = colMatch[1];
707
- }
708
- }
709
- for (const idx of table.indexes) {
710
- const idxPattern = new RegExp(`index\\s*\\(\\s*['"]${idx.name}['"]\\s*\\)[^\\n]*\\.\\\$id\\(['"]([^'"]+)['"]\\)`);
711
- const idxMatch = typescript.match(idxPattern);
712
- if (idxMatch) {
713
- idx.trackingId = idxMatch[1];
714
- }
715
- }
716
- }
717
- }
@@ -52,7 +52,7 @@ async function pushCommand(context) {
52
52
  const { projectRoot } = context;
53
53
  const force = flags['force'] === true;
54
54
  const dryRun = flags['dry-run'] === true;
55
- const applySQL = flags['apply'] === true;
55
+ const metadataOnly = flags['metadata-only'] === true;
56
56
  const noVerify = flags['no-verify'] === true;
57
57
  const skipPrompt = flags['yes'] === true || flags['y'] === true;
58
58
  const includeFunctions = config.includeFunctions ?? false;
@@ -165,9 +165,46 @@ async function pushCommand(context) {
165
165
  console.log('');
166
166
  }
167
167
  if (dryRun) {
168
- console.log(`${cli_utils_1.colors.yellow('Dry run')} - no changes applied`);
168
+ console.log(`${cli_utils_1.colors.yellow('Dry run')} - showing changes that would be applied`);
169
169
  console.log('');
170
- console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq push --apply')} ${cli_utils_1.colors.muted('to execute.')}`);
170
+ if (hasObjectsToDrop && force) {
171
+ console.log(`${cli_utils_1.colors.red('DROP statements:')}`);
172
+ for (const obj of analysis.objectsToDrop.slice(0, 5)) {
173
+ console.log(` ${generateDropSQL(obj)}`);
174
+ }
175
+ if (analysis.objectsToDrop.length > 5) {
176
+ console.log(` ${cli_utils_1.colors.muted(`... and ${analysis.objectsToDrop.length - 5} more`)}`);
177
+ }
178
+ console.log('');
179
+ }
180
+ const commitsToProcess = [...toPush].reverse();
181
+ let totalStatements = 0;
182
+ for (const commit of commitsToProcess) {
183
+ const commitPath = path.join(projectRoot, '.relq', 'commits', `${commit.hash}.json`);
184
+ if (fs.existsSync(commitPath)) {
185
+ const enhancedCommit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
186
+ if (enhancedCommit.sql && enhancedCommit.sql.trim()) {
187
+ const statements = enhancedCommit.sql.split(';').filter(s => s.trim());
188
+ totalStatements += statements.length;
189
+ console.log(`${cli_utils_1.colors.cyan(`Commit ${(0, repo_manager_1.shortHash)(commit.hash)}:`)} ${commit.message}`);
190
+ for (const stmt of statements.slice(0, 3)) {
191
+ console.log(` ${stmt.trim().substring(0, 80)}${stmt.trim().length > 80 ? '...' : ''};`);
192
+ }
193
+ if (statements.length > 3) {
194
+ console.log(` ${cli_utils_1.colors.muted(`... and ${statements.length - 3} more statements`)}`);
195
+ }
196
+ console.log('');
197
+ }
198
+ }
199
+ }
200
+ if (totalStatements === 0 && !hasObjectsToDrop) {
201
+ console.log(`${cli_utils_1.colors.muted('No SQL changes to apply')}`);
202
+ }
203
+ else {
204
+ console.log(`${cli_utils_1.colors.muted('Total:')} ${totalStatements + (hasObjectsToDrop ? analysis.objectsToDrop.length : 0)} statements`);
205
+ }
206
+ console.log('');
207
+ console.log(`${cli_utils_1.colors.muted('Remove')} ${cli_utils_1.colors.cyan('--dry-run')} ${cli_utils_1.colors.muted('to execute these changes.')}`);
171
208
  console.log('');
172
209
  return;
173
210
  }
@@ -180,8 +217,8 @@ async function pushCommand(context) {
180
217
  }
181
218
  spinner.succeed(`Pushed ${toPush.length} commit(s) to ${(0, repo_manager_1.getConnectionLabel)(connection)}`);
182
219
  }
183
- if (applySQL) {
184
- spinner.start('Applying SQL changes...');
220
+ if (!metadataOnly && !dryRun) {
221
+ spinner.start('Applying schema changes...');
185
222
  const pg = await Promise.resolve().then(() => __importStar(require("../../addon/pg/index.cjs")));
186
223
  const client = new pg.Client({
187
224
  host: connection.host,
@@ -218,6 +255,9 @@ async function pushCommand(context) {
218
255
  }
219
256
  await client.query('COMMIT');
220
257
  spinner.succeed(`Applied ${statementsRun} statement(s) from ${sqlExecuted} commit(s)`);
258
+ for (const commit of commitsToProcess) {
259
+ await (0, repo_manager_1.markCommitAsApplied)(connection, commit.hash);
260
+ }
221
261
  let hasRenameOperations = false;
222
262
  for (const commit of commitsToProcess) {
223
263
  const commitPath = path.join(projectRoot, '.relq', 'commits', `${commit.hash}.json`);
@@ -262,13 +302,13 @@ async function pushCommand(context) {
262
302
  const oldHash = remoteHead ? (0, repo_manager_1.shortHash)(remoteHead) : '(none)';
263
303
  const newHash = (0, repo_manager_1.shortHash)(localHead);
264
304
  console.log(` ${oldHash}..${newHash} ${cli_utils_1.colors.muted('main -> main')}`);
265
- if (hasObjectsToDrop && force && applySQL) {
305
+ if (hasObjectsToDrop && force && !metadataOnly && !dryRun) {
266
306
  console.log('');
267
307
  (0, cli_utils_1.warning)(`Dropped ${analysis.objectsToDrop.length} object(s) from remote`);
268
308
  }
269
- if (!applySQL) {
309
+ if (metadataOnly) {
270
310
  console.log('');
271
- console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq push --apply')} ${cli_utils_1.colors.muted('to execute SQL.')}`);
311
+ console.log(`${cli_utils_1.colors.muted('Metadata only - SQL not executed. Remove')} ${cli_utils_1.colors.cyan('--metadata-only')} ${cli_utils_1.colors.muted('to apply changes.')}`);
272
312
  }
273
313
  console.log('');
274
314
  }