relq 1.0.0 → 1.0.2
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/dist/cjs/addon/buffer/index.cjs +1881 -0
- package/dist/cjs/addon/pg/index.cjs +4812 -0
- package/dist/cjs/addon/pg-cursor/index.cjs +1451 -0
- package/dist/cjs/addon/pg-format/index.cjs +2270 -0
- package/dist/cjs/cli/commands/add.cjs +30 -1
- package/dist/cjs/cli/commands/branch.cjs +141 -0
- package/dist/cjs/cli/commands/checkout.cjs +134 -0
- package/dist/cjs/cli/commands/cherry-pick.cjs +283 -0
- package/dist/cjs/cli/commands/diff.cjs +148 -69
- package/dist/cjs/cli/commands/export.cjs +64 -5
- package/dist/cjs/cli/commands/fetch.cjs +34 -4
- package/dist/cjs/cli/commands/history.cjs +1 -1
- package/dist/cjs/cli/commands/import.cjs +283 -12
- package/dist/cjs/cli/commands/log.cjs +75 -0
- package/dist/cjs/cli/commands/merge.cjs +224 -0
- package/dist/cjs/cli/commands/migrate.cjs +1 -1
- package/dist/cjs/cli/commands/pull.cjs +123 -7
- package/dist/cjs/cli/commands/push.cjs +245 -29
- package/dist/cjs/cli/commands/remote.cjs +16 -0
- package/dist/cjs/cli/commands/reset.cjs +169 -0
- package/dist/cjs/cli/commands/resolve.cjs +193 -0
- package/dist/cjs/cli/commands/rollback.cjs +1 -1
- package/dist/cjs/cli/commands/stash.cjs +154 -0
- package/dist/cjs/cli/commands/status.cjs +48 -0
- package/dist/cjs/cli/commands/tag.cjs +147 -0
- package/dist/cjs/cli/index.cjs +46 -2
- package/dist/cjs/cli/utils/commit-manager.cjs +3 -3
- package/dist/cjs/cli/utils/env-loader.cjs +3 -2
- package/dist/cjs/cli/utils/fast-introspect.cjs +1 -1
- package/dist/cjs/cli/utils/project-root.cjs +56 -0
- package/dist/cjs/cli/utils/relqignore.cjs +296 -38
- package/dist/cjs/cli/utils/repo-manager.cjs +45 -3
- package/dist/cjs/cli/utils/schema-introspect.cjs +2 -2
- package/dist/cjs/cli/utils/sql-generator.cjs +1 -1
- package/dist/cjs/cli/utils/sql-parser.cjs +102 -13
- package/dist/cjs/condition/array-condition-builder.cjs +1 -1
- package/dist/cjs/condition/condition-collector.cjs +1 -1
- package/dist/cjs/condition/fulltext-condition-builder.cjs +1 -1
- package/dist/cjs/condition/geometric-condition-builder.cjs +1 -1
- package/dist/cjs/condition/jsonb-condition-builder.cjs +1 -1
- package/dist/cjs/condition/network-condition-builder.cjs +1 -1
- package/dist/cjs/condition/range-condition-builder.cjs +1 -1
- package/dist/cjs/copy/copy-builder.cjs +1 -1
- package/dist/cjs/core/query-builder.cjs +1 -1
- package/dist/cjs/core/relq-client.cjs +2 -2
- package/dist/cjs/count/count-builder.cjs +1 -1
- package/dist/cjs/cte/cte-builder.cjs +1 -1
- package/dist/cjs/delete/delete-builder.cjs +1 -1
- package/dist/cjs/function/create-function-builder.cjs +1 -1
- package/dist/cjs/functions/advanced-functions.cjs +1 -1
- package/dist/cjs/functions/case-builder.cjs +1 -1
- package/dist/cjs/functions/geometric-functions.cjs +1 -1
- package/dist/cjs/functions/network-functions.cjs +1 -1
- package/dist/cjs/functions/sql-functions.cjs +1 -1
- package/dist/cjs/indexing/create-index-builder.cjs +1 -1
- package/dist/cjs/indexing/drop-index-builder.cjs +1 -1
- package/dist/cjs/insert/conflict-builder.cjs +1 -1
- package/dist/cjs/insert/insert-builder.cjs +1 -1
- package/dist/cjs/maintenance/vacuum-builder.cjs +1 -1
- package/dist/cjs/pubsub/listen-notify-builder.cjs +1 -1
- package/dist/cjs/pubsub/listener-connection.cjs +2 -2
- package/dist/cjs/raw/raw-query-builder.cjs +1 -1
- package/dist/cjs/schema/schema-builder.cjs +1 -1
- package/dist/cjs/schema-definition/table-definition.cjs +1 -1
- package/dist/cjs/select/aggregate-builder.cjs +1 -1
- package/dist/cjs/select/select-builder.cjs +1 -1
- package/dist/cjs/sequence/sequence-builder.cjs +1 -1
- package/dist/cjs/table/alter-table-builder.cjs +1 -1
- package/dist/cjs/table/constraint-builder.cjs +1 -1
- package/dist/cjs/table/create-table-builder.cjs +1 -1
- package/dist/cjs/table/partition-builder.cjs +1 -1
- package/dist/cjs/table/truncate-builder.cjs +1 -1
- package/dist/cjs/transaction/transaction-builder.cjs +1 -1
- package/dist/cjs/trigger/create-trigger-builder.cjs +1 -1
- package/dist/cjs/update/array-update-builder.cjs +1 -1
- package/dist/cjs/update/update-builder.cjs +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/view/create-view-builder.cjs +1 -1
- package/dist/cjs/window/window-builder.cjs +1 -1
- package/dist/esm/cli/commands/add.js +30 -1
- package/dist/esm/cli/commands/branch.js +105 -0
- package/dist/esm/cli/commands/checkout.js +98 -0
- package/dist/esm/cli/commands/cherry-pick.js +247 -0
- package/dist/esm/cli/commands/diff.js +148 -69
- package/dist/esm/cli/commands/export.js +64 -5
- package/dist/esm/cli/commands/fetch.js +35 -5
- package/dist/esm/cli/commands/history.js +1 -1
- package/dist/esm/cli/commands/import.js +283 -12
- package/dist/esm/cli/commands/log.js +74 -0
- package/dist/esm/cli/commands/merge.js +188 -0
- package/dist/esm/cli/commands/migrate.js +1 -1
- package/dist/esm/cli/commands/pull.js +124 -8
- package/dist/esm/cli/commands/push.js +246 -30
- package/dist/esm/cli/commands/remote.js +13 -0
- package/dist/esm/cli/commands/reset.js +133 -0
- package/dist/esm/cli/commands/resolve.js +157 -0
- package/dist/esm/cli/commands/rollback.js +1 -1
- package/dist/esm/cli/commands/stash.js +118 -0
- package/dist/esm/cli/commands/status.js +15 -0
- package/dist/esm/cli/commands/tag.js +111 -0
- package/dist/esm/cli/index.js +47 -3
- package/dist/esm/cli/utils/commit-manager.js +3 -3
- package/dist/esm/cli/utils/env-loader.js +3 -2
- package/dist/esm/cli/utils/fast-introspect.js +1 -1
- package/dist/esm/cli/utils/project-root.js +19 -0
- package/dist/esm/cli/utils/relqignore.js +277 -37
- package/dist/esm/cli/utils/repo-manager.js +41 -3
- package/dist/esm/cli/utils/schema-introspect.js +2 -2
- package/dist/esm/cli/utils/sql-generator.js +1 -1
- package/dist/esm/cli/utils/sql-parser.js +102 -13
- package/dist/esm/condition/array-condition-builder.js +1 -1
- package/dist/esm/condition/condition-collector.js +1 -1
- package/dist/esm/condition/fulltext-condition-builder.js +1 -1
- package/dist/esm/condition/geometric-condition-builder.js +1 -1
- package/dist/esm/condition/jsonb-condition-builder.js +1 -1
- package/dist/esm/condition/network-condition-builder.js +1 -1
- package/dist/esm/condition/range-condition-builder.js +1 -1
- package/dist/esm/copy/copy-builder.js +1 -1
- package/dist/esm/core/query-builder.js +1 -1
- package/dist/esm/core/relq-client.js +2 -2
- package/dist/esm/count/count-builder.js +1 -1
- package/dist/esm/cte/cte-builder.js +1 -1
- package/dist/esm/delete/delete-builder.js +1 -1
- package/dist/esm/function/create-function-builder.js +1 -1
- package/dist/esm/functions/advanced-functions.js +1 -1
- package/dist/esm/functions/case-builder.js +1 -1
- package/dist/esm/functions/geometric-functions.js +1 -1
- package/dist/esm/functions/network-functions.js +1 -1
- package/dist/esm/functions/sql-functions.js +1 -1
- package/dist/esm/indexing/create-index-builder.js +1 -1
- package/dist/esm/indexing/drop-index-builder.js +1 -1
- package/dist/esm/insert/conflict-builder.js +1 -1
- package/dist/esm/insert/insert-builder.js +1 -1
- package/dist/esm/maintenance/vacuum-builder.js +1 -1
- package/dist/esm/pubsub/listen-notify-builder.js +1 -1
- package/dist/esm/pubsub/listener-connection.js +2 -2
- package/dist/esm/raw/raw-query-builder.js +1 -1
- package/dist/esm/schema/schema-builder.js +1 -1
- package/dist/esm/schema-definition/table-definition.js +1 -1
- package/dist/esm/select/aggregate-builder.js +1 -1
- package/dist/esm/select/select-builder.js +1 -1
- package/dist/esm/sequence/sequence-builder.js +1 -1
- package/dist/esm/table/alter-table-builder.js +1 -1
- package/dist/esm/table/constraint-builder.js +1 -1
- package/dist/esm/table/create-table-builder.js +1 -1
- package/dist/esm/table/partition-builder.js +1 -1
- package/dist/esm/table/truncate-builder.js +1 -1
- package/dist/esm/transaction/transaction-builder.js +1 -1
- package/dist/esm/trigger/create-trigger-builder.js +1 -1
- package/dist/esm/update/array-update-builder.js +1 -1
- package/dist/esm/update/update-builder.js +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/view/create-view-builder.js +1 -1
- package/dist/esm/window/window-builder.js +1 -1
- package/package.json +1 -1
- /package/dist/{addons/buffer.js → esm/addon/buffer/index.js} +0 -0
- /package/dist/{addons/pg.js → esm/addon/pg/index.js} +0 -0
- /package/dist/{addons/pg-cursor.js → esm/addon/pg-cursor/index.js} +0 -0
- /package/dist/{addons/pg-format.js → esm/addon/pg-format/index.js} +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import * as readline from 'readline';
|
|
3
4
|
import { requireValidConfig } from "../utils/config-loader.js";
|
|
4
5
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
5
6
|
import { colors, createSpinner } from "../utils/spinner.js";
|
|
6
|
-
import {
|
|
7
|
+
import { fastIntrospectDatabase } from "../utils/fast-introspect.js";
|
|
8
|
+
import { loadRelqignore, isTableIgnored, isColumnIgnored, isEnumIgnored, isDomainIgnored, isFunctionIgnored, } from "../utils/relqignore.js";
|
|
9
|
+
import { isInitialized, getHead, shortHash, fetchRemoteCommits, pushCommit, ensureRemoteTable, getAllCommits, loadSnapshot, } from "../utils/repo-manager.js";
|
|
7
10
|
export async function pushCommand(context) {
|
|
8
11
|
const { config, flags } = context;
|
|
9
12
|
if (!config) {
|
|
@@ -16,6 +19,11 @@ export async function pushCommand(context) {
|
|
|
16
19
|
const force = flags['force'] === true;
|
|
17
20
|
const dryRun = flags['dry-run'] === true;
|
|
18
21
|
const applySQL = flags['apply'] === true;
|
|
22
|
+
const noVerify = flags['no-verify'] === true;
|
|
23
|
+
const skipPrompt = flags['yes'] === true || flags['y'] === true;
|
|
24
|
+
const includeFunctions = config.includeFunctions ?? false;
|
|
25
|
+
const includeTriggers = config.includeTriggers ?? false;
|
|
26
|
+
const includeViews = config.includeViews ?? false;
|
|
19
27
|
console.log('');
|
|
20
28
|
if (!isInitialized(projectRoot)) {
|
|
21
29
|
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
@@ -35,32 +43,101 @@ export async function pushCommand(context) {
|
|
|
35
43
|
spinner.start('Connecting to remote...');
|
|
36
44
|
await ensureRemoteTable(connection);
|
|
37
45
|
spinner.succeed(`Connected to ${colors.cyan(getConnectionDescription(connection))}`);
|
|
38
|
-
spinner.start('Checking remote...');
|
|
46
|
+
spinner.start('Checking remote commits...');
|
|
39
47
|
const remoteCommits = await fetchRemoteCommits(connection, 100);
|
|
40
48
|
const remoteHead = remoteCommits.length > 0 ? remoteCommits[0].hash : null;
|
|
41
|
-
spinner.stop();
|
|
42
49
|
const localCommits = getAllCommits(projectRoot);
|
|
43
50
|
const remoteHashes = new Set(remoteCommits.map(c => c.hash));
|
|
44
51
|
const localHashes = new Set(localCommits.map(c => c.hash));
|
|
45
52
|
const toPush = localCommits.filter(c => !remoteHashes.has(c.hash));
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
const remoteMissing = remoteCommits.filter(c => !localHashes.has(c.hash));
|
|
54
|
+
spinner.succeed('Checked remote commits');
|
|
55
|
+
spinner.start('Introspecting remote database...');
|
|
56
|
+
const remoteDb = await fastIntrospectDatabase(connection, undefined, {
|
|
57
|
+
includeFunctions,
|
|
58
|
+
includeTriggers,
|
|
59
|
+
});
|
|
60
|
+
spinner.succeed(`Found ${remoteDb.tables.length} tables in remote`);
|
|
61
|
+
const localSnapshot = loadSnapshot(projectRoot);
|
|
62
|
+
if (!localSnapshot) {
|
|
63
|
+
console.log(`${colors.red('error:')} No local snapshot found`);
|
|
64
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq pull')} ${colors.muted('or')} ${colors.cyan('relq import')} ${colors.muted('first.')}`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const ignorePatterns = loadRelqignore(projectRoot);
|
|
68
|
+
const analysis = analyzeSync(localSnapshot, remoteDb, ignorePatterns, { includeFunctions, includeTriggers, includeViews });
|
|
69
|
+
const hasRemoteAhead = remoteMissing.length > 0;
|
|
70
|
+
const hasObjectsToDrop = analysis.objectsToDrop.length > 0;
|
|
71
|
+
const hasSchemaDrift = analysis.schemaDrift.length > 0;
|
|
72
|
+
if (hasRemoteAhead && !force) {
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log(`${colors.red('error:')} Remote has ${remoteMissing.length} commit(s) you don't have locally`);
|
|
75
|
+
console.log('');
|
|
76
|
+
for (const commit of remoteMissing.slice(0, 3)) {
|
|
77
|
+
console.log(` ${colors.yellow(shortHash(commit.hash))} ${commit.message}`);
|
|
78
|
+
}
|
|
79
|
+
if (remoteMissing.length > 3) {
|
|
80
|
+
console.log(` ${colors.muted(`... and ${remoteMissing.length - 3} more`)}`);
|
|
81
|
+
}
|
|
82
|
+
console.log('');
|
|
83
|
+
console.log(`${colors.yellow('hint:')} Run ${colors.cyan('relq pull')} to integrate remote changes.`);
|
|
84
|
+
console.log(`${colors.yellow('hint:')} Use ${colors.cyan('relq push --force')} to override (may cause data loss).`);
|
|
48
85
|
console.log('');
|
|
49
86
|
return;
|
|
50
87
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
console.log(`${colors.
|
|
88
|
+
if (hasObjectsToDrop) {
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(`${colors.yellow('⚠')} Remote has ${analysis.objectsToDrop.length} object(s) not in your local schema:`);
|
|
91
|
+
console.log('');
|
|
92
|
+
for (const obj of analysis.objectsToDrop.slice(0, 10)) {
|
|
93
|
+
console.log(` ${colors.red('DROP')} ${obj.type.toLowerCase()}: ${obj.name}`);
|
|
94
|
+
}
|
|
95
|
+
if (analysis.objectsToDrop.length > 10) {
|
|
96
|
+
console.log(` ${colors.muted(`... and ${analysis.objectsToDrop.length - 10} more`)}`);
|
|
97
|
+
}
|
|
54
98
|
console.log('');
|
|
55
|
-
|
|
56
|
-
|
|
99
|
+
if (!force) {
|
|
100
|
+
console.log(`${colors.red('error:')} Cannot push - remote has objects not in your history`);
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(`${colors.yellow('hint:')} Run ${colors.cyan('relq pull')} first to sync your local schema.`);
|
|
103
|
+
console.log(`${colors.yellow('hint:')} Use ${colors.cyan('relq push --force')} to DROP these objects from remote.`);
|
|
104
|
+
console.log('');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const dependencyErrors = checkDropDependencies(analysis.objectsToDrop, remoteDb, ignorePatterns);
|
|
108
|
+
if (dependencyErrors.length > 0) {
|
|
109
|
+
console.log(`${colors.red('error:')} Cannot drop objects due to dependencies:`);
|
|
110
|
+
console.log('');
|
|
111
|
+
for (const err of dependencyErrors) {
|
|
112
|
+
console.log(` ${colors.red('✗')} ${err}`);
|
|
113
|
+
}
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(`${colors.muted('These objects are used by non-ignored objects in your schema.')}`);
|
|
116
|
+
console.log(`${colors.muted('Either add them to .relqignore or import them with')} ${colors.cyan('relq pull')}`);
|
|
117
|
+
console.log('');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!skipPrompt && !dryRun) {
|
|
121
|
+
console.log(`${colors.red('⚠ WARNING: This will DROP data from your database!')}`);
|
|
122
|
+
console.log('');
|
|
123
|
+
const confirmed = await askConfirmation(`Drop ${analysis.objectsToDrop.length} object(s) from remote? [y/N] `);
|
|
124
|
+
if (!confirmed) {
|
|
125
|
+
console.log(`${colors.muted('Cancelled.')}`);
|
|
126
|
+
console.log('');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (toPush.length === 0 && !hasObjectsToDrop) {
|
|
132
|
+
console.log(`${colors.green('✓')} Everything up-to-date`);
|
|
57
133
|
console.log('');
|
|
58
134
|
return;
|
|
59
135
|
}
|
|
136
|
+
console.log('');
|
|
60
137
|
console.log(`Pushing to ${colors.cyan(getConnectionDescription(connection))}`);
|
|
61
138
|
console.log('');
|
|
62
|
-
if (
|
|
63
|
-
console.log(`${colors.
|
|
139
|
+
if (toPush.length > 0) {
|
|
140
|
+
console.log(`${colors.cyan('Commits:')} ${toPush.length}`);
|
|
64
141
|
for (const commit of toPush.slice(0, 5)) {
|
|
65
142
|
console.log(` ${colors.yellow(shortHash(commit.hash))} ${commit.message}`);
|
|
66
143
|
}
|
|
@@ -68,17 +145,25 @@ export async function pushCommand(context) {
|
|
|
68
145
|
console.log(` ${colors.muted(`... and ${toPush.length - 5} more`)}`);
|
|
69
146
|
}
|
|
70
147
|
console.log('');
|
|
148
|
+
}
|
|
149
|
+
if (dryRun) {
|
|
150
|
+
console.log(`${colors.yellow('Dry run')} - no changes applied`);
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq push --apply')} ${colors.muted('to execute.')}`);
|
|
153
|
+
console.log('');
|
|
71
154
|
return;
|
|
72
155
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
156
|
+
if (toPush.length > 0) {
|
|
157
|
+
const commitsToProcess = [...toPush].reverse();
|
|
158
|
+
spinner.start(`Pushing ${toPush.length} commit(s)...`);
|
|
159
|
+
for (const commit of commitsToProcess) {
|
|
160
|
+
await pushCommit(connection, commit);
|
|
161
|
+
}
|
|
162
|
+
spinner.succeed(`Pushed ${toPush.length} commit(s)`);
|
|
77
163
|
}
|
|
78
|
-
spinner.succeed(`Pushed ${toPush.length} commit(s)`);
|
|
79
164
|
if (applySQL) {
|
|
80
165
|
spinner.start('Applying SQL changes...');
|
|
81
|
-
const pg = await import("../../addon/pg.js");
|
|
166
|
+
const pg = await import("../../addon/pg/index.js");
|
|
82
167
|
const client = new pg.Client({
|
|
83
168
|
host: connection.host,
|
|
84
169
|
port: connection.port,
|
|
@@ -88,18 +173,42 @@ export async function pushCommand(context) {
|
|
|
88
173
|
});
|
|
89
174
|
try {
|
|
90
175
|
await client.connect();
|
|
176
|
+
await client.query('BEGIN');
|
|
91
177
|
let sqlExecuted = 0;
|
|
178
|
+
let statementsRun = 0;
|
|
179
|
+
if (hasObjectsToDrop && force) {
|
|
180
|
+
for (const obj of analysis.objectsToDrop) {
|
|
181
|
+
const dropSQL = generateDropSQL(obj);
|
|
182
|
+
if (dropSQL) {
|
|
183
|
+
await client.query(dropSQL);
|
|
184
|
+
statementsRun++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const commitsToProcess = [...toPush].reverse();
|
|
92
189
|
for (const commit of commitsToProcess) {
|
|
93
190
|
const commitPath = path.join(projectRoot, '.relq', 'commits', `${commit.hash}.json`);
|
|
94
191
|
if (fs.existsSync(commitPath)) {
|
|
95
192
|
const enhancedCommit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
96
|
-
if (enhancedCommit.sql) {
|
|
193
|
+
if (enhancedCommit.sql && enhancedCommit.sql.trim()) {
|
|
97
194
|
await client.query(enhancedCommit.sql);
|
|
98
195
|
sqlExecuted++;
|
|
196
|
+
statementsRun += enhancedCommit.sql.split(';').filter(s => s.trim()).length;
|
|
99
197
|
}
|
|
100
198
|
}
|
|
101
199
|
}
|
|
102
|
-
|
|
200
|
+
await client.query('COMMIT');
|
|
201
|
+
spinner.succeed(`Applied ${statementsRun} statement(s) from ${sqlExecuted} commit(s)`);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
try {
|
|
205
|
+
await client.query('ROLLBACK');
|
|
206
|
+
spinner.fail('SQL execution failed - rolled back');
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
spinner.fail('SQL execution failed');
|
|
210
|
+
}
|
|
211
|
+
throw error;
|
|
103
212
|
}
|
|
104
213
|
finally {
|
|
105
214
|
await client.end();
|
|
@@ -108,22 +217,129 @@ export async function pushCommand(context) {
|
|
|
108
217
|
const oldHash = remoteHead ? shortHash(remoteHead) : '(none)';
|
|
109
218
|
const newHash = shortHash(localHead);
|
|
110
219
|
console.log(` ${oldHash}..${newHash} ${colors.muted('main -> main')}`);
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
return false;
|
|
118
|
-
})) {
|
|
220
|
+
if (hasObjectsToDrop && force && applySQL) {
|
|
221
|
+
console.log('');
|
|
222
|
+
console.log(`${colors.yellow('⚠')} Dropped ${analysis.objectsToDrop.length} object(s) from remote`);
|
|
223
|
+
}
|
|
224
|
+
if (!applySQL) {
|
|
119
225
|
console.log('');
|
|
120
|
-
console.log(`${colors.muted('Use')} ${colors.cyan('relq push --apply')} ${colors.muted('to
|
|
226
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq push --apply')} ${colors.muted('to execute SQL.')}`);
|
|
121
227
|
}
|
|
228
|
+
console.log('');
|
|
122
229
|
}
|
|
123
230
|
catch (error) {
|
|
124
231
|
spinner.fail('Push failed');
|
|
125
232
|
console.error(colors.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
126
233
|
process.exit(1);
|
|
127
234
|
}
|
|
128
|
-
console.log('');
|
|
129
235
|
}
|
|
236
|
+
function analyzeSync(local, remote, patterns, options) {
|
|
237
|
+
const objectsToDrop = [];
|
|
238
|
+
const schemaDrift = [];
|
|
239
|
+
const localTables = new Set(local.tables.map(t => t.name));
|
|
240
|
+
const localEnums = new Set(local.enums.map(e => e.name));
|
|
241
|
+
const localDomains = new Set(local.domains.map(d => d.name));
|
|
242
|
+
const localSequences = new Set(local.sequences.map(s => s.name));
|
|
243
|
+
const localFunctions = new Set((local.functions || []).map(f => f.name));
|
|
244
|
+
const localViews = new Set((local.views || []).map(v => v.name));
|
|
245
|
+
const localMViews = new Set((local.materializedViews || []).map(v => v.name));
|
|
246
|
+
for (const table of remote.tables) {
|
|
247
|
+
if (isTableIgnored(table.name, patterns).ignored)
|
|
248
|
+
continue;
|
|
249
|
+
if (!localTables.has(table.name)) {
|
|
250
|
+
objectsToDrop.push({ type: 'TABLE', name: table.name });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const enumType of remote.enums) {
|
|
254
|
+
if (isEnumIgnored(enumType.name, patterns).ignored)
|
|
255
|
+
continue;
|
|
256
|
+
if (!localEnums.has(enumType.name)) {
|
|
257
|
+
objectsToDrop.push({ type: 'ENUM', name: enumType.name });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const domain of remote.domains) {
|
|
261
|
+
if (isDomainIgnored(domain.name, patterns).ignored)
|
|
262
|
+
continue;
|
|
263
|
+
if (!localDomains.has(domain.name)) {
|
|
264
|
+
objectsToDrop.push({ type: 'DOMAIN', name: domain.name });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (options.includeFunctions) {
|
|
268
|
+
for (const func of remote.functions || []) {
|
|
269
|
+
if (isFunctionIgnored(func.name, patterns).ignored)
|
|
270
|
+
continue;
|
|
271
|
+
if (!localFunctions.has(func.name)) {
|
|
272
|
+
objectsToDrop.push({ type: 'FUNCTION', name: func.name });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
toPush: [],
|
|
278
|
+
objectsToDrop,
|
|
279
|
+
schemaDrift,
|
|
280
|
+
remoteAhead: false,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function checkDropDependencies(objectsToDrop, remoteDb, patterns) {
|
|
284
|
+
const errors = [];
|
|
285
|
+
const droppingTables = new Set(objectsToDrop.filter(o => o.type === 'TABLE').map(o => o.name));
|
|
286
|
+
const droppingEnums = new Set(objectsToDrop.filter(o => o.type === 'ENUM').map(o => o.name));
|
|
287
|
+
const droppingDomains = new Set(objectsToDrop.filter(o => o.type === 'DOMAIN').map(o => o.name));
|
|
288
|
+
const droppingSequences = new Set(objectsToDrop.filter(o => o.type === 'SEQUENCE').map(o => o.name));
|
|
289
|
+
for (const table of remoteDb.tables) {
|
|
290
|
+
if (droppingTables.has(table.name))
|
|
291
|
+
continue;
|
|
292
|
+
if (isTableIgnored(table.name, patterns).ignored)
|
|
293
|
+
continue;
|
|
294
|
+
for (const col of table.columns) {
|
|
295
|
+
if (isColumnIgnored(table.name, col.name, patterns).ignored)
|
|
296
|
+
continue;
|
|
297
|
+
const colType = (col.dataType || '').toLowerCase();
|
|
298
|
+
for (const enumName of droppingEnums) {
|
|
299
|
+
if (colType.includes(enumName.toLowerCase())) {
|
|
300
|
+
errors.push(`Cannot drop ENUM "${enumName}" - used by column "${table.name}.${col.name}"`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const domainName of droppingDomains) {
|
|
304
|
+
if (colType.includes(domainName.toLowerCase())) {
|
|
305
|
+
errors.push(`Cannot drop DOMAIN "${domainName}" - used by column "${table.name}.${col.name}"`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return errors;
|
|
311
|
+
}
|
|
312
|
+
function generateDropSQL(obj) {
|
|
313
|
+
switch (obj.type) {
|
|
314
|
+
case 'TABLE':
|
|
315
|
+
return `DROP TABLE IF EXISTS "${obj.name}" CASCADE;`;
|
|
316
|
+
case 'ENUM':
|
|
317
|
+
return `DROP TYPE IF EXISTS "${obj.name}" CASCADE;`;
|
|
318
|
+
case 'DOMAIN':
|
|
319
|
+
return `DROP DOMAIN IF EXISTS "${obj.name}" CASCADE;`;
|
|
320
|
+
case 'FUNCTION':
|
|
321
|
+
return `DROP FUNCTION IF EXISTS "${obj.name}" CASCADE;`;
|
|
322
|
+
case 'VIEW':
|
|
323
|
+
return `DROP VIEW IF EXISTS "${obj.name}" CASCADE;`;
|
|
324
|
+
case 'MATERIALIZED_VIEW':
|
|
325
|
+
return `DROP MATERIALIZED VIEW IF EXISTS "${obj.name}" CASCADE;`;
|
|
326
|
+
case 'SEQUENCE':
|
|
327
|
+
return `DROP SEQUENCE IF EXISTS "${obj.name}" CASCADE;`;
|
|
328
|
+
default:
|
|
329
|
+
return '';
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function askConfirmation(question) {
|
|
333
|
+
const rl = readline.createInterface({
|
|
334
|
+
input: process.stdin,
|
|
335
|
+
output: process.stdout,
|
|
336
|
+
});
|
|
337
|
+
return new Promise((resolve) => {
|
|
338
|
+
rl.question(question, (answer) => {
|
|
339
|
+
rl.close();
|
|
340
|
+
const a = answer.trim().toLowerCase();
|
|
341
|
+
resolve(a === 'y' || a === 'yes');
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
export default pushCommand;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export async function remoteCommand(context) {
|
|
2
|
+
console.log('');
|
|
3
|
+
console.log('⚠️ Remote tracking is coming soon.');
|
|
4
|
+
console.log('');
|
|
5
|
+
console.log('This will allow you to:');
|
|
6
|
+
console.log(' • Track multiple remote databases');
|
|
7
|
+
console.log(' • Push/pull to different environments');
|
|
8
|
+
console.log(' • Switch between staging/production');
|
|
9
|
+
console.log('');
|
|
10
|
+
console.log('For now, configure your remote in relq.config.ts');
|
|
11
|
+
console.log('');
|
|
12
|
+
}
|
|
13
|
+
export default remoteCommand;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as readline from 'readline';
|
|
4
|
+
import { colors, createSpinner } from "../utils/spinner.js";
|
|
5
|
+
import { isInitialized, getHead, setHead, loadCommit, saveSnapshot, shortHash, getAllCommits, } from "../utils/repo-manager.js";
|
|
6
|
+
export async function resetCommand(context) {
|
|
7
|
+
const { flags, args } = context;
|
|
8
|
+
const projectRoot = process.cwd();
|
|
9
|
+
console.log('');
|
|
10
|
+
if (!isInitialized(projectRoot)) {
|
|
11
|
+
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const hard = flags['hard'] === true;
|
|
15
|
+
const soft = flags['soft'] === true;
|
|
16
|
+
const target = args[0];
|
|
17
|
+
if (!target) {
|
|
18
|
+
console.log(`${colors.red('error:')} Please specify a target`);
|
|
19
|
+
console.log('');
|
|
20
|
+
console.log('Usage:');
|
|
21
|
+
console.log(` ${colors.cyan('relq reset --hard HEAD~1')} Reset to previous commit`);
|
|
22
|
+
console.log(` ${colors.cyan('relq reset --hard <hash>')} Reset to specific commit`);
|
|
23
|
+
console.log('');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!hard && !soft) {
|
|
27
|
+
console.log(`${colors.red('error:')} Please specify --hard or --soft`);
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log(` ${colors.cyan('--hard')} Discard all changes (DANGEROUS)`);
|
|
30
|
+
console.log(` ${colors.cyan('--soft')} Keep changes unstaged`);
|
|
31
|
+
console.log('');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const currentHead = getHead(projectRoot);
|
|
35
|
+
if (!currentHead) {
|
|
36
|
+
console.log(`${colors.red('error:')} No commits yet`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const allCommits = getAllCommits(projectRoot);
|
|
40
|
+
let targetHash = null;
|
|
41
|
+
if (target.startsWith('HEAD~')) {
|
|
42
|
+
const n = parseInt(target.slice(5)) || 1;
|
|
43
|
+
const headIndex = allCommits.findIndex(c => c.hash === currentHead);
|
|
44
|
+
if (headIndex >= 0 && headIndex + n < allCommits.length) {
|
|
45
|
+
targetHash = allCommits[headIndex + n].hash;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (target === 'HEAD') {
|
|
49
|
+
targetHash = currentHead;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const match = allCommits.find(c => c.hash.startsWith(target));
|
|
53
|
+
if (match) {
|
|
54
|
+
targetHash = match.hash;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!targetHash) {
|
|
58
|
+
console.log(`${colors.red('error:')} Cannot find commit: ${target}`);
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log('Available commits:');
|
|
61
|
+
for (const c of allCommits.slice(0, 5)) {
|
|
62
|
+
console.log(` ${colors.yellow(shortHash(c.hash))} ${c.message}`);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const targetCommit = loadCommit(targetHash, projectRoot);
|
|
67
|
+
if (!targetCommit) {
|
|
68
|
+
console.log(`${colors.red('error:')} Cannot load commit: ${targetHash}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (hard) {
|
|
72
|
+
console.log(`${colors.red('⚠ WARNING: This will discard all local changes!')}`);
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log(`Reset to: ${colors.yellow(shortHash(targetHash))} ${targetCommit.message}`);
|
|
75
|
+
console.log('');
|
|
76
|
+
const rl = readline.createInterface({
|
|
77
|
+
input: process.stdin,
|
|
78
|
+
output: process.stdout
|
|
79
|
+
});
|
|
80
|
+
const confirmed = await new Promise((resolve) => {
|
|
81
|
+
rl.question(`Continue? [y/N] `, (answer) => {
|
|
82
|
+
rl.close();
|
|
83
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
if (!confirmed) {
|
|
87
|
+
console.log(`${colors.muted('Cancelled.')}`);
|
|
88
|
+
console.log('');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const spinner = createSpinner();
|
|
93
|
+
try {
|
|
94
|
+
spinner.start(`Resetting to ${shortHash(targetHash)}...`);
|
|
95
|
+
const commitPath = path.join(projectRoot, '.relq', 'commits', `${targetHash}.json`);
|
|
96
|
+
if (!fs.existsSync(commitPath)) {
|
|
97
|
+
spinner.fail('Cannot find commit data');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const commitData = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
101
|
+
const targetSnapshot = commitData.snapshot;
|
|
102
|
+
if (!targetSnapshot) {
|
|
103
|
+
spinner.fail('Commit has no snapshot data');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (hard) {
|
|
107
|
+
saveSnapshot(targetSnapshot, projectRoot);
|
|
108
|
+
setHead(targetHash, projectRoot);
|
|
109
|
+
const stagedPath = path.join(projectRoot, '.relq', 'staged.json');
|
|
110
|
+
const unstagedPath = path.join(projectRoot, '.relq', 'unstaged.json');
|
|
111
|
+
if (fs.existsSync(stagedPath))
|
|
112
|
+
fs.unlinkSync(stagedPath);
|
|
113
|
+
if (fs.existsSync(unstagedPath))
|
|
114
|
+
fs.unlinkSync(unstagedPath);
|
|
115
|
+
const mergeStatePath = path.join(projectRoot, '.relq', 'MERGE_STATE');
|
|
116
|
+
if (fs.existsSync(mergeStatePath))
|
|
117
|
+
fs.unlinkSync(mergeStatePath);
|
|
118
|
+
}
|
|
119
|
+
spinner.succeed(`Reset to ${colors.yellow(shortHash(targetHash))}`);
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log(`HEAD is now at ${colors.yellow(shortHash(targetHash))} ${targetCommit.message}`);
|
|
122
|
+
console.log('');
|
|
123
|
+
if (soft) {
|
|
124
|
+
console.log(`${colors.muted('Changes are unstaged. Use')} ${colors.cyan('relq status')} ${colors.muted('to see.')}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
spinner.fail('Reset failed');
|
|
129
|
+
console.error(colors.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export default resetCommand;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { colors } from "../utils/spinner.js";
|
|
4
|
+
import { isInitialized, loadSnapshot, saveSnapshot, } from "../utils/repo-manager.js";
|
|
5
|
+
export async function resolveCommand(context) {
|
|
6
|
+
const { flags, args } = context;
|
|
7
|
+
const projectRoot = process.cwd();
|
|
8
|
+
console.log('');
|
|
9
|
+
if (!isInitialized(projectRoot)) {
|
|
10
|
+
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const mergeStatePath = path.join(projectRoot, '.relq', 'MERGE_STATE');
|
|
14
|
+
if (!fs.existsSync(mergeStatePath)) {
|
|
15
|
+
console.log(`${colors.green('✓')} No conflicts to resolve`);
|
|
16
|
+
console.log('');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const mergeState = JSON.parse(fs.readFileSync(mergeStatePath, 'utf-8'));
|
|
20
|
+
if (mergeState.conflicts.length === 0) {
|
|
21
|
+
fs.unlinkSync(mergeStatePath);
|
|
22
|
+
console.log(`${colors.green('✓')} All conflicts resolved`);
|
|
23
|
+
console.log('');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const allTheirs = flags['all-theirs'] === true;
|
|
27
|
+
const allOurs = flags['all-ours'] === true;
|
|
28
|
+
const theirs = flags['theirs'] === true;
|
|
29
|
+
const ours = flags['ours'] === true;
|
|
30
|
+
const objectName = args[0];
|
|
31
|
+
if (allTheirs) {
|
|
32
|
+
console.log(`Resolving ${mergeState.conflicts.length} conflict(s) with --all-theirs`);
|
|
33
|
+
console.log('');
|
|
34
|
+
saveSnapshot(mergeState.remoteSnapshot, projectRoot);
|
|
35
|
+
fs.unlinkSync(mergeStatePath);
|
|
36
|
+
console.log(`${colors.green('✓')} Applied remote versions for all conflicts`);
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq commit -m "Merge remote changes"')} ${colors.muted('to complete.')}`);
|
|
39
|
+
console.log('');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (allOurs) {
|
|
43
|
+
console.log(`Resolving ${mergeState.conflicts.length} conflict(s) with --all-ours`);
|
|
44
|
+
console.log('');
|
|
45
|
+
fs.unlinkSync(mergeStatePath);
|
|
46
|
+
console.log(`${colors.green('✓')} Kept local versions for all conflicts`);
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq commit -m "Keep local changes"')} ${colors.muted('to complete.')}`);
|
|
49
|
+
console.log('');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if ((theirs || ours) && objectName) {
|
|
53
|
+
const conflict = mergeState.conflicts.find(c => c.objectName === objectName ||
|
|
54
|
+
`${c.parentName}.${c.objectName}` === objectName);
|
|
55
|
+
if (!conflict) {
|
|
56
|
+
console.log(`${colors.red('error:')} No conflict found for '${objectName}'`);
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log('Conflicts:');
|
|
59
|
+
for (const c of mergeState.conflicts) {
|
|
60
|
+
const name = c.parentName ? `${c.parentName}.${c.objectName}` : c.objectName;
|
|
61
|
+
console.log(` ${c.objectType}: ${name}`);
|
|
62
|
+
}
|
|
63
|
+
console.log('');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const localSnapshot = loadSnapshot(projectRoot);
|
|
67
|
+
if (!localSnapshot) {
|
|
68
|
+
console.log(`${colors.red('error:')} No local snapshot found`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (theirs) {
|
|
72
|
+
applyResolution(localSnapshot, conflict, mergeState.remoteSnapshot, 'theirs');
|
|
73
|
+
saveSnapshot(localSnapshot, projectRoot);
|
|
74
|
+
}
|
|
75
|
+
mergeState.conflicts = mergeState.conflicts.filter(c => c !== conflict);
|
|
76
|
+
if (mergeState.conflicts.length === 0) {
|
|
77
|
+
fs.unlinkSync(mergeStatePath);
|
|
78
|
+
console.log(`${colors.green('✓')} All conflicts resolved`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
fs.writeFileSync(mergeStatePath, JSON.stringify(mergeState, null, 2));
|
|
82
|
+
console.log(`${colors.green('✓')} Resolved: ${conflict.objectType} ${objectName}`);
|
|
83
|
+
console.log(`${colors.yellow(String(mergeState.conflicts.length))} conflict(s) remaining`);
|
|
84
|
+
}
|
|
85
|
+
console.log('');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(`${colors.yellow('⚠')} You have ${mergeState.conflicts.length} unresolved conflict(s):`);
|
|
89
|
+
console.log('');
|
|
90
|
+
for (const conflict of mergeState.conflicts) {
|
|
91
|
+
const name = conflict.parentName
|
|
92
|
+
? `${conflict.parentName}.${conflict.objectName}`
|
|
93
|
+
: conflict.objectName;
|
|
94
|
+
console.log(` ${colors.red('conflict:')} ${conflict.objectType.toLowerCase()} ${colors.bold(name)}`);
|
|
95
|
+
console.log(` ${colors.muted(conflict.description)}`);
|
|
96
|
+
}
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log('To resolve:');
|
|
99
|
+
console.log(` ${colors.cyan('relq resolve --theirs <name>')} Take remote version`);
|
|
100
|
+
console.log(` ${colors.cyan('relq resolve --ours <name>')} Keep local version`);
|
|
101
|
+
console.log(` ${colors.cyan('relq resolve --all-theirs')} Take all remote`);
|
|
102
|
+
console.log(` ${colors.cyan('relq resolve --all-ours')} Keep all local`);
|
|
103
|
+
console.log('');
|
|
104
|
+
}
|
|
105
|
+
function applyResolution(local, conflict, remote, resolution) {
|
|
106
|
+
if (resolution !== 'theirs')
|
|
107
|
+
return;
|
|
108
|
+
switch (conflict.objectType) {
|
|
109
|
+
case 'TABLE': {
|
|
110
|
+
const remoteTable = remote.tables.find(t => t.name === conflict.objectName);
|
|
111
|
+
const localIdx = local.tables.findIndex(t => t.name === conflict.objectName);
|
|
112
|
+
if (remoteTable) {
|
|
113
|
+
if (localIdx >= 0) {
|
|
114
|
+
local.tables[localIdx] = remoteTable;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
local.tables.push(remoteTable);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'COLUMN': {
|
|
123
|
+
const [tableName, colName] = conflict.objectName.includes('.')
|
|
124
|
+
? conflict.objectName.split('.')
|
|
125
|
+
: [conflict.parentName, conflict.objectName];
|
|
126
|
+
const remoteTable = remote.tables.find(t => t.name === tableName);
|
|
127
|
+
const localTable = local.tables.find(t => t.name === tableName);
|
|
128
|
+
if (remoteTable && localTable) {
|
|
129
|
+
const remoteCol = remoteTable.columns.find(c => c.name === colName);
|
|
130
|
+
const localIdx = localTable.columns.findIndex(c => c.name === colName);
|
|
131
|
+
if (remoteCol) {
|
|
132
|
+
if (localIdx >= 0) {
|
|
133
|
+
localTable.columns[localIdx] = remoteCol;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
localTable.columns.push(remoteCol);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'ENUM': {
|
|
143
|
+
const remoteEnum = remote.enums.find(e => e.name === conflict.objectName);
|
|
144
|
+
const localIdx = local.enums.findIndex(e => e.name === conflict.objectName);
|
|
145
|
+
if (remoteEnum) {
|
|
146
|
+
if (localIdx >= 0) {
|
|
147
|
+
local.enums[localIdx] = remoteEnum;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
local.enums.push(remoteEnum);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export default resolveCommand;
|
|
@@ -49,7 +49,7 @@ export async function rollbackCommand(context) {
|
|
|
49
49
|
console.log(` Connection: ${getConnectionDescription(connection)}`);
|
|
50
50
|
console.log('');
|
|
51
51
|
try {
|
|
52
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
52
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
53
53
|
const pool = new Pool({
|
|
54
54
|
host: connection.host,
|
|
55
55
|
port: connection.port || 5432,
|