relq 1.0.1 → 1.0.3
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 +430 -25
- package/dist/cjs/cli/commands/branch.cjs +131 -0
- package/dist/cjs/cli/commands/checkout.cjs +121 -0
- package/dist/cjs/cli/commands/cherry-pick.cjs +282 -0
- package/dist/cjs/cli/commands/commit.cjs +21 -29
- package/dist/cjs/cli/commands/diff.cjs +144 -69
- package/dist/cjs/cli/commands/export.cjs +70 -11
- package/dist/cjs/cli/commands/fetch.cjs +42 -18
- package/dist/cjs/cli/commands/generate.cjs +28 -54
- package/dist/cjs/cli/commands/history.cjs +19 -40
- package/dist/cjs/cli/commands/import.cjs +305 -41
- package/dist/cjs/cli/commands/init.cjs +69 -59
- package/dist/cjs/cli/commands/introspect.cjs +4 -8
- package/dist/cjs/cli/commands/log.cjs +84 -15
- package/dist/cjs/cli/commands/merge.cjs +207 -0
- package/dist/cjs/cli/commands/migrate.cjs +13 -26
- package/dist/cjs/cli/commands/pull.cjs +321 -95
- package/dist/cjs/cli/commands/push.cjs +228 -52
- package/dist/cjs/cli/commands/remote.cjs +17 -0
- package/dist/cjs/cli/commands/reset.cjs +148 -0
- package/dist/cjs/cli/commands/resolve.cjs +191 -0
- package/dist/cjs/cli/commands/rollback.cjs +17 -39
- package/dist/cjs/cli/commands/stash.cjs +152 -0
- package/dist/cjs/cli/commands/status.cjs +52 -9
- package/dist/cjs/cli/commands/sync.cjs +30 -50
- package/dist/cjs/cli/commands/tag.cjs +146 -0
- package/dist/cjs/cli/index.cjs +117 -10
- package/dist/cjs/cli/utils/change-tracker.cjs +107 -3
- package/dist/cjs/cli/utils/cli-utils.cjs +217 -0
- package/dist/cjs/cli/utils/commit-manager.cjs +3 -3
- package/dist/cjs/cli/utils/config-loader.cjs +34 -8
- package/dist/cjs/cli/utils/env-loader.cjs +3 -2
- package/dist/cjs/cli/utils/fast-introspect.cjs +110 -4
- package/dist/cjs/cli/utils/git-utils.cjs +42 -161
- package/dist/cjs/cli/utils/pool-manager.cjs +156 -0
- package/dist/cjs/cli/utils/project-root.cjs +107 -0
- package/dist/cjs/cli/utils/relqignore.cjs +297 -38
- package/dist/cjs/cli/utils/repo-manager.cjs +92 -3
- package/dist/cjs/cli/utils/schema-comparator.cjs +301 -11
- package/dist/cjs/cli/utils/schema-diff.cjs +202 -1
- package/dist/cjs/cli/utils/schema-hash.cjs +2 -1
- package/dist/cjs/cli/utils/schema-introspect.cjs +9 -5
- package/dist/cjs/cli/utils/snapshot-manager.cjs +1 -0
- package/dist/cjs/cli/utils/spinner.cjs +14 -106
- package/dist/cjs/cli/utils/sql-generator.cjs +2 -2
- package/dist/cjs/cli/utils/sql-parser.cjs +94 -7
- package/dist/cjs/cli/utils/type-generator.cjs +28 -16
- 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/config.d.ts +16 -25
- package/dist/esm/cli/commands/add.js +399 -27
- package/dist/esm/cli/commands/branch.js +95 -0
- package/dist/esm/cli/commands/checkout.js +85 -0
- package/dist/esm/cli/commands/cherry-pick.js +246 -0
- package/dist/esm/cli/commands/commit.js +22 -30
- package/dist/esm/cli/commands/diff.js +144 -69
- package/dist/esm/cli/commands/export.js +71 -12
- package/dist/esm/cli/commands/fetch.js +42 -18
- package/dist/esm/cli/commands/generate.js +28 -54
- package/dist/esm/cli/commands/history.js +11 -32
- package/dist/esm/cli/commands/import.js +306 -42
- package/dist/esm/cli/commands/init.js +65 -55
- package/dist/esm/cli/commands/introspect.js +4 -8
- package/dist/esm/cli/commands/log.js +78 -10
- package/dist/esm/cli/commands/merge.js +171 -0
- package/dist/esm/cli/commands/migrate.js +13 -26
- package/dist/esm/cli/commands/pull.js +313 -87
- package/dist/esm/cli/commands/push.js +223 -47
- package/dist/esm/cli/commands/remote.js +14 -0
- package/dist/esm/cli/commands/reset.js +112 -0
- package/dist/esm/cli/commands/resolve.js +155 -0
- package/dist/esm/cli/commands/rollback.js +17 -39
- package/dist/esm/cli/commands/stash.js +116 -0
- package/dist/esm/cli/commands/status.js +20 -10
- package/dist/esm/cli/commands/sync.js +30 -50
- package/dist/esm/cli/commands/tag.js +110 -0
- package/dist/esm/cli/index.js +118 -11
- package/dist/esm/cli/utils/change-tracker.js +107 -3
- package/dist/esm/cli/utils/cli-utils.js +169 -0
- package/dist/esm/cli/utils/commit-manager.js +3 -3
- package/dist/esm/cli/utils/config-loader.js +34 -8
- package/dist/esm/cli/utils/env-loader.js +3 -2
- package/dist/esm/cli/utils/fast-introspect.js +110 -4
- package/dist/esm/cli/utils/git-utils.js +2 -124
- package/dist/esm/cli/utils/pool-manager.js +114 -0
- package/dist/esm/cli/utils/project-root.js +69 -0
- package/dist/esm/cli/utils/relqignore.js +278 -37
- package/dist/esm/cli/utils/repo-manager.js +83 -3
- package/dist/esm/cli/utils/schema-comparator.js +301 -11
- package/dist/esm/cli/utils/schema-diff.js +202 -1
- package/dist/esm/cli/utils/schema-hash.js +2 -1
- package/dist/esm/cli/utils/schema-introspect.js +9 -5
- package/dist/esm/cli/utils/snapshot-manager.js +1 -0
- package/dist/esm/cli/utils/spinner.js +1 -101
- package/dist/esm/cli/utils/sql-generator.js +2 -2
- package/dist/esm/cli/utils/sql-parser.js +94 -7
- package/dist/esm/cli/utils/type-generator.js +28 -16
- 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/dist/index.d.ts +25 -8
- package/dist/schema-builder.d.ts +16 -6
- 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
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { colors, createSpinner } from "../utils/spinner.js";
|
|
4
|
+
import { isInitialized, loadCommit, loadParentCommit, loadSnapshot, saveSnapshot, createCommit, shortHash, resolveRef, } from "../utils/repo-manager.js";
|
|
5
|
+
export async function cherryPickCommand(context) {
|
|
6
|
+
const { config, args, flags, projectRoot } = context;
|
|
7
|
+
console.log('');
|
|
8
|
+
if (!isInitialized(projectRoot)) {
|
|
9
|
+
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const abort = flags['abort'] === true;
|
|
13
|
+
const cherryPickStatePath = path.join(projectRoot, '.relq', 'CHERRY_PICK_STATE');
|
|
14
|
+
if (abort) {
|
|
15
|
+
if (fs.existsSync(cherryPickStatePath)) {
|
|
16
|
+
fs.unlinkSync(cherryPickStatePath);
|
|
17
|
+
console.log('Cherry-pick aborted');
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
console.log('No cherry-pick in progress.');
|
|
21
|
+
}
|
|
22
|
+
console.log('');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (fs.existsSync(cherryPickStatePath)) {
|
|
26
|
+
const state = JSON.parse(fs.readFileSync(cherryPickStatePath, 'utf-8'));
|
|
27
|
+
console.log(`${colors.red('error:')} Cherry-pick in progress from ${shortHash(state.fromCommit)}`);
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq resolve')} ${colors.muted('to resolve conflicts')}`);
|
|
30
|
+
console.log(`${colors.muted('Or')} ${colors.cyan('relq cherry-pick --abort')} ${colors.muted('to cancel')}`);
|
|
31
|
+
console.log('');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const ref = args[0];
|
|
35
|
+
if (!ref) {
|
|
36
|
+
console.log(`${colors.red('error:')} Please specify a commit`);
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(`Usage: ${colors.cyan('relq cherry-pick <commit>')}`);
|
|
39
|
+
console.log('');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const spinner = createSpinner();
|
|
43
|
+
spinner.start(`Cherry-picking ${shortHash(ref)}...`);
|
|
44
|
+
try {
|
|
45
|
+
const hash = resolveRef(ref, projectRoot);
|
|
46
|
+
if (!hash) {
|
|
47
|
+
spinner.fail(`Commit not found: ${ref}`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const targetCommit = loadCommit(hash, projectRoot);
|
|
51
|
+
const parentCommit = loadParentCommit(hash, projectRoot);
|
|
52
|
+
if (!targetCommit) {
|
|
53
|
+
spinner.fail('Cannot load commit');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!parentCommit) {
|
|
57
|
+
spinner.fail('Cannot cherry-pick first commit (no parent)');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const diff = calculateCommitDiff(parentCommit.schema, targetCommit.schema);
|
|
61
|
+
const currentSnapshot = loadSnapshot(projectRoot);
|
|
62
|
+
if (!currentSnapshot) {
|
|
63
|
+
spinner.fail('No snapshot found');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const conflicts = detectCherryPickConflicts(currentSnapshot, diff);
|
|
67
|
+
if (conflicts.length > 0) {
|
|
68
|
+
const state = {
|
|
69
|
+
fromCommit: hash,
|
|
70
|
+
originalMessage: targetCommit.message,
|
|
71
|
+
conflicts,
|
|
72
|
+
diff,
|
|
73
|
+
createdAt: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
fs.writeFileSync(cherryPickStatePath, JSON.stringify(state, null, 2));
|
|
76
|
+
spinner.fail(`${conflicts.length} conflict(s) detected`);
|
|
77
|
+
console.log('');
|
|
78
|
+
for (const c of conflicts.slice(0, 5)) {
|
|
79
|
+
const name = c.parentName ? `${c.parentName}.${c.objectName}` : c.objectName;
|
|
80
|
+
console.log(` ${colors.red('conflict:')} ${c.objectType.toLowerCase()} ${name}`);
|
|
81
|
+
console.log(` ${colors.muted(c.description)}`);
|
|
82
|
+
}
|
|
83
|
+
if (conflicts.length > 5) {
|
|
84
|
+
console.log(` ${colors.muted(`... and ${conflicts.length - 5} more`)}`);
|
|
85
|
+
}
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq resolve --theirs')} ${colors.muted('to resolve')}`);
|
|
88
|
+
console.log(`${colors.muted('Or')} ${colors.cyan('relq cherry-pick --abort')} ${colors.muted('to cancel')}`);
|
|
89
|
+
console.log('');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const newSnapshot = applyCommitDiff(currentSnapshot, diff);
|
|
93
|
+
saveSnapshot(newSnapshot, projectRoot);
|
|
94
|
+
const author = config?.author || 'Relq CLI';
|
|
95
|
+
const message = `Cherry-picked ${shortHash(hash)}: ${targetCommit.message}`;
|
|
96
|
+
const commit = createCommit(newSnapshot, author, message, projectRoot);
|
|
97
|
+
spinner.succeed(`Cherry-picked ${colors.yellow(shortHash(hash))}`);
|
|
98
|
+
console.log(` ${colors.cyan(commit.message)}`);
|
|
99
|
+
console.log('');
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
spinner.fail('Cherry-pick failed');
|
|
103
|
+
console.error(colors.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function calculateCommitDiff(parent, target) {
|
|
107
|
+
const diff = {
|
|
108
|
+
tablesAdded: [],
|
|
109
|
+
tablesRemoved: [],
|
|
110
|
+
tablesModified: [],
|
|
111
|
+
enumsAdded: [],
|
|
112
|
+
enumsRemoved: [],
|
|
113
|
+
columnsAdded: [],
|
|
114
|
+
columnsRemoved: [],
|
|
115
|
+
};
|
|
116
|
+
const parentTables = new Map(parent.tables.map(t => [t.name, t]));
|
|
117
|
+
const targetTables = new Map(target.tables.map(t => [t.name, t]));
|
|
118
|
+
for (const [name, table] of targetTables) {
|
|
119
|
+
if (!parentTables.has(name)) {
|
|
120
|
+
diff.tablesAdded.push(table);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const [name] of parentTables) {
|
|
124
|
+
if (!targetTables.has(name)) {
|
|
125
|
+
diff.tablesRemoved.push(name);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
for (const [name, targetTable] of targetTables) {
|
|
129
|
+
const parentTable = parentTables.get(name);
|
|
130
|
+
if (!parentTable)
|
|
131
|
+
continue;
|
|
132
|
+
const parentCols = new Map(parentTable.columns.map(c => [c.name, c]));
|
|
133
|
+
const targetCols = new Map(targetTable.columns.map(c => [c.name, c]));
|
|
134
|
+
const changes = { columnsAdded: [], columnsRemoved: [] };
|
|
135
|
+
for (const [colName, col] of targetCols) {
|
|
136
|
+
if (!parentCols.has(colName)) {
|
|
137
|
+
changes.columnsAdded.push(col);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const [colName] of parentCols) {
|
|
141
|
+
if (!targetCols.has(colName)) {
|
|
142
|
+
changes.columnsRemoved.push(colName);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (changes.columnsAdded.length > 0 || changes.columnsRemoved.length > 0) {
|
|
146
|
+
diff.tablesModified.push({ name, changes });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const parentEnums = new Set(parent.enums.map(e => e.name));
|
|
150
|
+
const targetEnums = new Map(target.enums.map(e => [e.name, e]));
|
|
151
|
+
for (const [name, e] of targetEnums) {
|
|
152
|
+
if (!parentEnums.has(name)) {
|
|
153
|
+
diff.enumsAdded.push(e);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for (const name of parentEnums) {
|
|
157
|
+
if (!targetEnums.has(name)) {
|
|
158
|
+
diff.enumsRemoved.push(name);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return diff;
|
|
162
|
+
}
|
|
163
|
+
function detectCherryPickConflicts(current, diff) {
|
|
164
|
+
const conflicts = [];
|
|
165
|
+
for (const table of diff.tablesAdded) {
|
|
166
|
+
if (current.tables.find(t => t.name === table.name)) {
|
|
167
|
+
conflicts.push({
|
|
168
|
+
objectType: 'TABLE',
|
|
169
|
+
objectName: table.name,
|
|
170
|
+
currentValue: 'exists',
|
|
171
|
+
incomingValue: 'add',
|
|
172
|
+
description: 'Table already exists in current snapshot',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
for (const tableName of diff.tablesRemoved) {
|
|
177
|
+
if (!current.tables.find(t => t.name === tableName)) {
|
|
178
|
+
conflicts.push({
|
|
179
|
+
objectType: 'TABLE',
|
|
180
|
+
objectName: tableName,
|
|
181
|
+
currentValue: 'missing',
|
|
182
|
+
incomingValue: 'remove',
|
|
183
|
+
description: 'Table does not exist in current snapshot',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
for (const mod of diff.tablesModified) {
|
|
188
|
+
const currentTable = current.tables.find(t => t.name === mod.name);
|
|
189
|
+
if (!currentTable) {
|
|
190
|
+
conflicts.push({
|
|
191
|
+
objectType: 'TABLE',
|
|
192
|
+
objectName: mod.name,
|
|
193
|
+
currentValue: 'missing',
|
|
194
|
+
incomingValue: 'modify',
|
|
195
|
+
description: 'Table does not exist in current snapshot',
|
|
196
|
+
});
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
for (const col of mod.changes.columnsAdded) {
|
|
200
|
+
if (currentTable.columns.find(c => c.name === col.name)) {
|
|
201
|
+
conflicts.push({
|
|
202
|
+
objectType: 'COLUMN',
|
|
203
|
+
objectName: col.name,
|
|
204
|
+
parentName: mod.name,
|
|
205
|
+
currentValue: 'exists',
|
|
206
|
+
incomingValue: 'add',
|
|
207
|
+
description: 'Column already exists',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const e of diff.enumsAdded) {
|
|
213
|
+
if (current.enums.find(en => en.name === e.name)) {
|
|
214
|
+
conflicts.push({
|
|
215
|
+
objectType: 'ENUM',
|
|
216
|
+
objectName: e.name,
|
|
217
|
+
currentValue: 'exists',
|
|
218
|
+
incomingValue: 'add',
|
|
219
|
+
description: 'Enum already exists',
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return conflicts;
|
|
224
|
+
}
|
|
225
|
+
function applyCommitDiff(snapshot, diff) {
|
|
226
|
+
const result = JSON.parse(JSON.stringify(snapshot));
|
|
227
|
+
for (const table of diff.tablesAdded) {
|
|
228
|
+
result.tables.push(table);
|
|
229
|
+
}
|
|
230
|
+
result.tables = result.tables.filter(t => !diff.tablesRemoved.includes(t.name));
|
|
231
|
+
for (const mod of diff.tablesModified) {
|
|
232
|
+
const table = result.tables.find(t => t.name === mod.name);
|
|
233
|
+
if (!table)
|
|
234
|
+
continue;
|
|
235
|
+
for (const col of mod.changes.columnsAdded) {
|
|
236
|
+
table.columns.push(col);
|
|
237
|
+
}
|
|
238
|
+
table.columns = table.columns.filter(c => !mod.changes.columnsRemoved.includes(c.name));
|
|
239
|
+
}
|
|
240
|
+
for (const e of diff.enumsAdded) {
|
|
241
|
+
result.enums.push(e);
|
|
242
|
+
}
|
|
243
|
+
result.enums = result.enums.filter(e => !diff.enumsRemoved.includes(e.name));
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
export default cherryPickCommand;
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
|
-
import {
|
|
3
|
-
import { isInitialized, getHead, getStagedChanges, getUnstagedChanges, shortHash, } from "../utils/repo-manager.js";
|
|
2
|
+
import { fatal, hint } from "../utils/cli-utils.js";
|
|
3
|
+
import { isInitialized, getHead, getStagedChanges, getUnstagedChanges, shortHash, hashFileContent, saveFileHash, } from "../utils/repo-manager.js";
|
|
4
|
+
import { loadConfig } from "../../config/config.js";
|
|
4
5
|
import { sortChangesByDependency, generateCombinedSQL, } from "../utils/change-tracker.js";
|
|
5
6
|
import * as fs from 'fs';
|
|
6
7
|
import * as path from 'path';
|
|
7
8
|
export async function commitCommand(context) {
|
|
8
|
-
const { config, flags, args } = context;
|
|
9
|
-
const projectRoot = process.cwd();
|
|
9
|
+
const { config, flags, args, projectRoot } = context;
|
|
10
10
|
const author = config?.author || 'Developer <dev@example.com>';
|
|
11
11
|
console.log('');
|
|
12
12
|
if (!isInitialized(projectRoot)) {
|
|
13
|
-
|
|
14
|
-
console.log('');
|
|
15
|
-
console.log(`${colors.muted('Run')} ${colors.cyan('relq init')} ${colors.muted('first.')}`);
|
|
16
|
-
return;
|
|
13
|
+
fatal('not a relq repository (or any parent directories): .relq', "run 'relq init' to initialize");
|
|
17
14
|
}
|
|
18
15
|
const staged = getStagedChanges(projectRoot);
|
|
19
16
|
if (staged.length === 0) {
|
|
20
|
-
console.log(
|
|
21
|
-
console.log('');
|
|
17
|
+
console.log('nothing to commit, working tree clean');
|
|
22
18
|
const unstaged = getUnstagedChanges(projectRoot);
|
|
23
19
|
if (unstaged.length > 0) {
|
|
24
|
-
console.log(`${
|
|
25
|
-
|
|
20
|
+
console.log(`${unstaged.length} unstaged change(s).`);
|
|
21
|
+
hint("run 'relq add .' to stage all changes");
|
|
26
22
|
}
|
|
27
23
|
else {
|
|
28
|
-
|
|
24
|
+
hint("run 'relq add <table>' to stage changes");
|
|
29
25
|
}
|
|
30
26
|
return;
|
|
31
27
|
}
|
|
@@ -35,10 +31,7 @@ export async function commitCommand(context) {
|
|
|
35
31
|
message = args.join(' ');
|
|
36
32
|
}
|
|
37
33
|
else {
|
|
38
|
-
|
|
39
|
-
console.log('');
|
|
40
|
-
console.log(`${colors.muted('Usage:')} relq commit -m "message"`);
|
|
41
|
-
return;
|
|
34
|
+
fatal('commit message required', "usage: relq commit -m '<message>'");
|
|
42
35
|
}
|
|
43
36
|
}
|
|
44
37
|
const sortedChanges = sortChangesByDependency(staged);
|
|
@@ -77,7 +70,7 @@ export async function commitCommand(context) {
|
|
|
77
70
|
fs.writeFileSync(path.join(commitsDir, `${hash}.json`), JSON.stringify(commit, null, 2), 'utf-8');
|
|
78
71
|
fs.writeFileSync(path.join(projectRoot, '.relq', 'HEAD'), hash, 'utf-8');
|
|
79
72
|
const workingPath = path.join(projectRoot, '.relq', 'working.json');
|
|
80
|
-
const unstaged = getUnstagedChanges(projectRoot);
|
|
73
|
+
const unstaged = getUnstagedChanges(projectRoot).filter(c => c.objectType !== 'SCHEMA_FILE');
|
|
81
74
|
if (unstaged.length > 0) {
|
|
82
75
|
fs.writeFileSync(workingPath, JSON.stringify({
|
|
83
76
|
timestamp: new Date().toISOString(),
|
|
@@ -90,20 +83,19 @@ export async function commitCommand(context) {
|
|
|
90
83
|
fs.unlinkSync(workingPath);
|
|
91
84
|
}
|
|
92
85
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
if (drops > 0) {
|
|
102
|
-
console.log(` ${colors.red(`${drops} dropped`)}`);
|
|
86
|
+
const commitConfig = await loadConfig();
|
|
87
|
+
const schemaPathRaw = typeof commitConfig.schema === 'string' ? commitConfig.schema : './db/schema.ts';
|
|
88
|
+
const schemaFilePath = path.resolve(projectRoot, schemaPathRaw);
|
|
89
|
+
if (fs.existsSync(schemaFilePath)) {
|
|
90
|
+
const currentContent = fs.readFileSync(schemaFilePath, 'utf-8');
|
|
91
|
+
const currentHash = hashFileContent(currentContent);
|
|
92
|
+
saveFileHash(currentHash, projectRoot);
|
|
103
93
|
}
|
|
94
|
+
console.log(`[${shortHash(hash)}] ${message}`);
|
|
95
|
+
console.log(` ${creates} create(s), ${alters} alter(s), ${drops} drop(s)`);
|
|
104
96
|
console.log('');
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
hint("run 'relq push' to apply changes to database");
|
|
98
|
+
hint("run 'relq export' to export as SQL file");
|
|
107
99
|
console.log('');
|
|
108
100
|
}
|
|
109
101
|
export default commitCommand;
|
|
@@ -1,81 +1,156 @@
|
|
|
1
1
|
import { requireValidConfig } from "../utils/config-loader.js";
|
|
2
|
-
import {
|
|
3
|
-
import { loadSnapshot, snapshotToDatabaseSchema } from "../utils/snapshot-manager.js";
|
|
4
|
-
import { formatDiff } from "../utils/schema-diff.js";
|
|
5
|
-
import { normalizeSchema } from "../utils/schema-hash.js";
|
|
6
|
-
import { diffSchemas } from "../utils/schema-diff.js";
|
|
7
|
-
import { generateMigration } from "../utils/migration-generator.js";
|
|
2
|
+
import { fastIntrospectDatabase } from "../utils/fast-introspect.js";
|
|
8
3
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
dim: '\x1b[2m',
|
|
13
|
-
red: '\x1b[31m',
|
|
14
|
-
green: '\x1b[32m',
|
|
15
|
-
yellow: '\x1b[33m',
|
|
16
|
-
blue: '\x1b[34m',
|
|
17
|
-
cyan: '\x1b[36m',
|
|
18
|
-
magenta: '\x1b[35m',
|
|
19
|
-
};
|
|
20
|
-
function filterDiff(diff, ignorePatterns) {
|
|
21
|
-
const patterns = ignorePatterns.map(p => {
|
|
22
|
-
const regexStr = p.replace(/\*/g, '.*').replace(/\?/g, '.');
|
|
23
|
-
return new RegExp(`^${regexStr}$`, 'i');
|
|
24
|
-
});
|
|
25
|
-
const matchesPattern = (name) => patterns.some(p => p.test(name));
|
|
26
|
-
const tables = diff.tables.filter(t => !matchesPattern(t.name));
|
|
27
|
-
const hasChanges = tables.length > 0 || diff.extensions.length > 0;
|
|
28
|
-
return {
|
|
29
|
-
...diff,
|
|
30
|
-
tables,
|
|
31
|
-
hasChanges,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
4
|
+
import { colors, createSpinner, fatal } from "../utils/cli-utils.js";
|
|
5
|
+
import { isInitialized, loadSnapshot, getStagedChanges, getUnstagedChanges, } from "../utils/repo-manager.js";
|
|
6
|
+
import { getChangeDisplayName, generateChangeSQL, sortChangesByDependency } from "../utils/change-tracker.js";
|
|
34
7
|
export async function diffCommand(context) {
|
|
35
|
-
const { config, flags } = context;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
8
|
+
const { config, args, flags, projectRoot } = context;
|
|
9
|
+
console.log('');
|
|
10
|
+
if (!isInitialized(projectRoot)) {
|
|
11
|
+
fatal('not a relq repository (or any parent directories): .relq', `Run ${colors.cyan('relq init')} to initialize.`);
|
|
39
12
|
}
|
|
40
|
-
requireValidConfig(config);
|
|
41
|
-
const connection = config.connection;
|
|
42
|
-
const snapshotPath = config.sync?.snapshot || '.relq/snapshot.json';
|
|
43
|
-
const ignorePatterns = config.sync?.ignore || ['_relq_*'];
|
|
44
13
|
const showSQL = flags['sql'] === true;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
14
|
+
const staged = flags['staged'] === true;
|
|
15
|
+
const target = args[0];
|
|
16
|
+
if (staged) {
|
|
17
|
+
await showStagedDiff(projectRoot, showSQL);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (target === 'remote/live' || target === 'remote' || target === 'live' || target === 'origin') {
|
|
21
|
+
if (!config) {
|
|
22
|
+
fatal('No configuration found', `Run ${colors.cyan('relq init')} to create one.`);
|
|
54
23
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
24
|
+
await requireValidConfig(config, { calledFrom: 'diff' });
|
|
25
|
+
await showOriginDiff(config, projectRoot, showSQL);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
await showUnstagedDiff(projectRoot, showSQL);
|
|
29
|
+
}
|
|
30
|
+
function getSymbol(change) {
|
|
31
|
+
if (change.type === 'CREATE')
|
|
32
|
+
return colors.green('+');
|
|
33
|
+
if (change.type === 'DROP')
|
|
34
|
+
return colors.red('-');
|
|
35
|
+
return colors.yellow('~');
|
|
36
|
+
}
|
|
37
|
+
async function showUnstagedDiff(projectRoot, showSQL) {
|
|
38
|
+
const unstaged = getUnstagedChanges(projectRoot);
|
|
39
|
+
if (unstaged.length === 0) {
|
|
40
|
+
console.log(`${colors.green('No unstaged changes.')}`);
|
|
41
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq diff remote/live')} ${colors.muted('to compare with remote.')}`);
|
|
42
|
+
console.log('');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
console.log(`${colors.bold('Unstaged changes:')}`);
|
|
46
|
+
console.log('');
|
|
47
|
+
const sorted = sortChangesByDependency(unstaged);
|
|
48
|
+
for (const change of sorted) {
|
|
49
|
+
console.log(` ${getSymbol(change)} ${change.objectType.toLowerCase()}: ${getChangeDisplayName(change)}`);
|
|
50
|
+
}
|
|
51
|
+
if (showSQL) {
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(`${colors.bold('SQL:')}`);
|
|
54
|
+
for (const change of sorted) {
|
|
55
|
+
const sql = generateChangeSQL(change);
|
|
56
|
+
if (sql)
|
|
57
|
+
console.log(`${colors.cyan(sql)}`);
|
|
63
58
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
}
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq add .')} ${colors.muted('to stage all.')}`);
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
|
64
|
+
async function showStagedDiff(projectRoot, showSQL) {
|
|
65
|
+
const staged = getStagedChanges(projectRoot);
|
|
66
|
+
if (staged.length === 0) {
|
|
67
|
+
console.log(`${colors.green('No staged changes.')}`);
|
|
68
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq add .')} ${colors.muted('to stage changes.')}`);
|
|
69
|
+
console.log('');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
console.log(`${colors.bold('Staged changes:')}`);
|
|
73
|
+
console.log('');
|
|
74
|
+
const sorted = sortChangesByDependency(staged);
|
|
75
|
+
for (const change of sorted) {
|
|
76
|
+
console.log(` ${getSymbol(change)} ${change.objectType.toLowerCase()}: ${getChangeDisplayName(change)}`);
|
|
77
|
+
}
|
|
78
|
+
if (showSQL) {
|
|
79
|
+
console.log('');
|
|
80
|
+
console.log(`${colors.bold('SQL:')}`);
|
|
81
|
+
for (const change of sorted) {
|
|
82
|
+
const sql = generateChangeSQL(change);
|
|
83
|
+
if (sql)
|
|
84
|
+
console.log(`${colors.cyan(sql)}`);
|
|
73
85
|
}
|
|
86
|
+
}
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq commit -m "message"')} ${colors.muted('to commit.')}`);
|
|
89
|
+
console.log('');
|
|
90
|
+
}
|
|
91
|
+
async function showOriginDiff(config, projectRoot, showSQL) {
|
|
92
|
+
const connection = config.connection;
|
|
93
|
+
const spinner = createSpinner();
|
|
94
|
+
const snapshot = loadSnapshot(projectRoot);
|
|
95
|
+
if (!snapshot) {
|
|
96
|
+
console.log(`${colors.yellow('No local snapshot.')}`);
|
|
97
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq pull')} ${colors.muted('first.')}`);
|
|
98
|
+
console.log('');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
spinner.start(`Connecting to ${getConnectionDescription(connection)}...`);
|
|
102
|
+
const remoteDb = await fastIntrospectDatabase(connection, undefined, {
|
|
103
|
+
includeFunctions: config.includeFunctions ?? false,
|
|
104
|
+
includeTriggers: config.includeTriggers ?? false,
|
|
105
|
+
});
|
|
106
|
+
spinner.succeed(`Connected`);
|
|
107
|
+
const diffs = compareSchemas(snapshot, remoteDb);
|
|
108
|
+
if (diffs.length === 0) {
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log('Local and remote are in sync.');
|
|
74
111
|
console.log('');
|
|
75
|
-
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(`${colors.bold('Differences:')}`);
|
|
116
|
+
console.log('');
|
|
117
|
+
for (const d of diffs.slice(0, 20)) {
|
|
118
|
+
const sym = d.type === 'added' ? colors.green('+') : d.type === 'removed' ? colors.red('-') : colors.yellow('~');
|
|
119
|
+
console.log(` ${sym} ${d.description}`);
|
|
76
120
|
}
|
|
77
|
-
|
|
78
|
-
console.
|
|
79
|
-
|
|
121
|
+
if (diffs.length > 20) {
|
|
122
|
+
console.log(` ${colors.muted(`... and ${diffs.length - 20} more`)}`);
|
|
123
|
+
}
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log(`${colors.muted('Use')} ${colors.cyan('relq pull')} ${colors.muted('to sync local with remote.')}`);
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
function compareSchemas(local, remote) {
|
|
129
|
+
const diffs = [];
|
|
130
|
+
const localTableNames = local.tables.map(t => t.name);
|
|
131
|
+
const remoteTableNames = (remote.tables || []).map((t) => t.name);
|
|
132
|
+
for (const name of remoteTableNames) {
|
|
133
|
+
if (!localTableNames.includes(name)) {
|
|
134
|
+
diffs.push({ type: 'added', description: `table: ${name}` });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (const name of localTableNames) {
|
|
138
|
+
if (!remoteTableNames.includes(name)) {
|
|
139
|
+
diffs.push({ type: 'removed', description: `table: ${name}` });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const localEnumNames = local.enums.map(e => e.name);
|
|
143
|
+
const remoteEnumNames = (remote.enums || []).map((e) => e.name);
|
|
144
|
+
for (const name of remoteEnumNames) {
|
|
145
|
+
if (!localEnumNames.includes(name)) {
|
|
146
|
+
diffs.push({ type: 'added', description: `enum: ${name}` });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const name of localEnumNames) {
|
|
150
|
+
if (!remoteEnumNames.includes(name)) {
|
|
151
|
+
diffs.push({ type: 'removed', description: `enum: ${name}` });
|
|
152
|
+
}
|
|
80
153
|
}
|
|
154
|
+
return diffs;
|
|
81
155
|
}
|
|
156
|
+
export default diffCommand;
|