relq 1.0.46 → 1.0.48
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/cli/commands/pull.cjs +2 -2
- package/dist/cjs/cli/commands/push.cjs +12 -1
- package/dist/cjs/cli/commands/reset.cjs +16 -1
- package/dist/cjs/cli/index.cjs +26 -10
- package/dist/cjs/cli/utils/change-tracker.cjs +8 -8
- package/dist/cjs/cli/utils/commit-manager.cjs +27 -1
- package/dist/cjs/cli/utils/repo-manager.cjs +13 -1
- package/dist/esm/cli/commands/pull.js +2 -2
- package/dist/esm/cli/commands/push.js +12 -1
- package/dist/esm/cli/commands/reset.js +16 -1
- package/dist/esm/cli/index.js +26 -10
- package/dist/esm/cli/utils/change-tracker.js +8 -8
- package/dist/esm/cli/utils/commit-manager.js +27 -1
- package/dist/esm/cli/utils/repo-manager.js +13 -1
- package/package.json +1 -1
|
@@ -178,7 +178,7 @@ async function pullCommand(context) {
|
|
|
178
178
|
const connection = config.connection;
|
|
179
179
|
const force = flags['force'] === true;
|
|
180
180
|
const merge = flags['merge'] === true;
|
|
181
|
-
const
|
|
181
|
+
const autoCommit = flags['commit'] === true;
|
|
182
182
|
const dryRun = flags['dry-run'] === true;
|
|
183
183
|
const author = config.author || 'Relq CLI';
|
|
184
184
|
const schemaPathRaw = (0, config_loader_1.getSchemaPath)(config);
|
|
@@ -880,7 +880,7 @@ async function pullCommand(context) {
|
|
|
880
880
|
(0, ast_codegen_1.copyTrackingIdsToNormalized)(parsedSchema, currentSchema);
|
|
881
881
|
(0, repo_manager_1.saveSnapshot)(currentSchema, projectRoot);
|
|
882
882
|
const duration = Date.now() - startTime;
|
|
883
|
-
if (
|
|
883
|
+
if (!autoCommit) {
|
|
884
884
|
if (schemaChanges.length > 0) {
|
|
885
885
|
(0, repo_manager_1.addUnstagedChanges)(schemaChanges, projectRoot);
|
|
886
886
|
spinner.succeed(`Detected ${schemaChanges.length} schema change(s)`);
|
|
@@ -380,7 +380,18 @@ async function pushCommand(context) {
|
|
|
380
380
|
catch {
|
|
381
381
|
spinner.fail('SQL execution failed');
|
|
382
382
|
}
|
|
383
|
-
|
|
383
|
+
const dbError = error?.message || String(error);
|
|
384
|
+
const enhancedError = new Error(`${dbError}\n\n` +
|
|
385
|
+
`${cli_utils_1.colors.muted('This usually means:')}\n` +
|
|
386
|
+
` • The SQL statements were generated in the wrong order\n` +
|
|
387
|
+
` • A column/table is referenced before it's created\n` +
|
|
388
|
+
` • The schema is out of sync with the database\n\n` +
|
|
389
|
+
`${cli_utils_1.colors.muted('To fix:')}\n` +
|
|
390
|
+
` 1. Run ${cli_utils_1.colors.cyan('relq reset --hard HEAD~1')} to undo the commit\n` +
|
|
391
|
+
` 2. Run ${cli_utils_1.colors.cyan('relq pull')} to sync with database\n` +
|
|
392
|
+
` 3. Run ${cli_utils_1.colors.cyan('relq add')} and ${cli_utils_1.colors.cyan('relq commit')} again\n\n` +
|
|
393
|
+
`${cli_utils_1.colors.muted('To debug, run:')} ${cli_utils_1.colors.cyan('relq push --dry-run')} ${cli_utils_1.colors.muted('to see the SQL')}`);
|
|
394
|
+
throw enhancedError;
|
|
384
395
|
}
|
|
385
396
|
finally {
|
|
386
397
|
await client.end();
|
|
@@ -114,7 +114,7 @@ async function resetCommand(context) {
|
|
|
114
114
|
(0, cli_utils_1.fatal)('Cannot find commit data - repository may be corrupt');
|
|
115
115
|
}
|
|
116
116
|
const commitData = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
117
|
-
const targetSnapshot = commitData.snapshot;
|
|
117
|
+
const targetSnapshot = (commitData.snapshot || commitData.schema);
|
|
118
118
|
if (!targetSnapshot) {
|
|
119
119
|
spinner.stop();
|
|
120
120
|
(0, cli_utils_1.fatal)('Commit has no snapshot data - repository may be corrupt');
|
|
@@ -122,6 +122,18 @@ async function resetCommand(context) {
|
|
|
122
122
|
if (hard) {
|
|
123
123
|
(0, repo_manager_1.saveSnapshot)(targetSnapshot, projectRoot);
|
|
124
124
|
(0, repo_manager_1.setHead)(targetHash, projectRoot);
|
|
125
|
+
const commitsDir = path.join(projectRoot, '.relq', 'commits');
|
|
126
|
+
const headIndex = allCommits.findIndex(c => c.hash === currentHead);
|
|
127
|
+
const targetIndex = allCommits.findIndex(c => c.hash === targetHash);
|
|
128
|
+
if (headIndex >= 0 && targetIndex > headIndex) {
|
|
129
|
+
for (let i = headIndex; i < targetIndex; i++) {
|
|
130
|
+
const commitToDelete = allCommits[i];
|
|
131
|
+
const commitFile = path.join(commitsDir, `${commitToDelete.hash}.json`);
|
|
132
|
+
if (fs.existsSync(commitFile)) {
|
|
133
|
+
fs.unlinkSync(commitFile);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
125
137
|
const stagedPath = path.join(projectRoot, '.relq', 'staged.json');
|
|
126
138
|
const unstagedPath = path.join(projectRoot, '.relq', 'unstaged.json');
|
|
127
139
|
if (fs.existsSync(stagedPath))
|
|
@@ -131,6 +143,9 @@ async function resetCommand(context) {
|
|
|
131
143
|
const mergeStatePath = path.join(projectRoot, '.relq', 'MERGE_STATE');
|
|
132
144
|
if (fs.existsSync(mergeStatePath))
|
|
133
145
|
fs.unlinkSync(mergeStatePath);
|
|
146
|
+
const fileHashPath = path.join(projectRoot, '.relq', 'file_hash');
|
|
147
|
+
if (fs.existsSync(fileHashPath))
|
|
148
|
+
fs.unlinkSync(fileHashPath);
|
|
134
149
|
}
|
|
135
150
|
spinner.succeed(`Reset to ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(targetHash))}`);
|
|
136
151
|
console.log('');
|
package/dist/cjs/cli/index.cjs
CHANGED
|
@@ -92,6 +92,11 @@ function parseArgs(argv) {
|
|
|
92
92
|
const args = [];
|
|
93
93
|
const flags = {};
|
|
94
94
|
let command = '';
|
|
95
|
+
const booleanFlags = new Set([
|
|
96
|
+
'hard', 'soft', 'force', 'yes', 'y', 'dry-run', 'verbose', 'quiet', 'q',
|
|
97
|
+
'help', 'h', 'version', 'v', 'all', 'a', 'cached', 'staged', 'no-verify',
|
|
98
|
+
'metadata-only', 'skip-prompt', 'interactive', 'i'
|
|
99
|
+
]);
|
|
95
100
|
for (let i = 2; i < argv.length; i++) {
|
|
96
101
|
const arg = argv[i];
|
|
97
102
|
if (arg.startsWith('--')) {
|
|
@@ -100,25 +105,36 @@ function parseArgs(argv) {
|
|
|
100
105
|
flags[arg.slice(2, eqIndex)] = arg.slice(eqIndex + 1);
|
|
101
106
|
}
|
|
102
107
|
else {
|
|
103
|
-
const
|
|
104
|
-
if (
|
|
105
|
-
flags[
|
|
106
|
-
i++;
|
|
108
|
+
const flagName = arg.slice(2);
|
|
109
|
+
if (booleanFlags.has(flagName)) {
|
|
110
|
+
flags[flagName] = true;
|
|
107
111
|
}
|
|
108
112
|
else {
|
|
109
|
-
|
|
113
|
+
const nextArg = argv[i + 1];
|
|
114
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
115
|
+
flags[flagName] = nextArg;
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
flags[flagName] = true;
|
|
120
|
+
}
|
|
110
121
|
}
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
else if (arg.startsWith('-') && arg.length === 2) {
|
|
114
125
|
const flag = arg.slice(1);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
flags[flag] = nextArg;
|
|
118
|
-
i++;
|
|
126
|
+
if (booleanFlags.has(flag)) {
|
|
127
|
+
flags[flag] = true;
|
|
119
128
|
}
|
|
120
129
|
else {
|
|
121
|
-
|
|
130
|
+
const nextArg = argv[i + 1];
|
|
131
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
132
|
+
flags[flag] = nextArg;
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
flags[flag] = true;
|
|
137
|
+
}
|
|
122
138
|
}
|
|
123
139
|
}
|
|
124
140
|
else if (arg.startsWith('-') && arg.length > 2) {
|
|
@@ -559,14 +559,14 @@ function sortChangesByDependency(changes) {
|
|
|
559
559
|
'PARTITION': 11,
|
|
560
560
|
'PARTITION_CHILD': 12,
|
|
561
561
|
'COLUMN': 13,
|
|
562
|
-
'COLUMN_COMMENT':
|
|
563
|
-
'INDEX':
|
|
564
|
-
'INDEX_COMMENT':
|
|
565
|
-
'CONSTRAINT':
|
|
566
|
-
'PRIMARY_KEY':
|
|
567
|
-
'FOREIGN_KEY':
|
|
568
|
-
'CHECK':
|
|
569
|
-
'EXCLUSION':
|
|
562
|
+
'COLUMN_COMMENT': 14,
|
|
563
|
+
'INDEX': 15,
|
|
564
|
+
'INDEX_COMMENT': 16,
|
|
565
|
+
'CONSTRAINT': 17,
|
|
566
|
+
'PRIMARY_KEY': 17,
|
|
567
|
+
'FOREIGN_KEY': 18,
|
|
568
|
+
'CHECK': 17,
|
|
569
|
+
'EXCLUSION': 17,
|
|
570
570
|
'VIEW': 20,
|
|
571
571
|
'MATERIALIZED_VIEW': 21,
|
|
572
572
|
'FUNCTION': 30,
|
|
@@ -77,6 +77,18 @@ function getLocalCommits(baseDir = process.cwd()) {
|
|
|
77
77
|
return [];
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
function getLocalCommitByHash(hash, baseDir = process.cwd()) {
|
|
81
|
+
const commitPath = path.join(baseDir, '.relq', 'commits', `${hash}.json`);
|
|
82
|
+
if (!fs.existsSync(commitPath)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
80
92
|
function saveLocalCommits(commits, baseDir = process.cwd()) {
|
|
81
93
|
ensureRelqDir(baseDir);
|
|
82
94
|
const filePath = path.join(baseDir, RELQ_DIR, COMMITS_FILE);
|
|
@@ -263,7 +275,21 @@ function generateASTHash(ast) {
|
|
|
263
275
|
function createCommitFromSchema(schema, author, message, commitLimit = 1000, baseDir = process.cwd()) {
|
|
264
276
|
const parsedSchema = (0, schema_to_ast_1.schemaToAST)(schema);
|
|
265
277
|
const hash = generateASTHash(parsedSchema);
|
|
266
|
-
const
|
|
278
|
+
const currentHead = getLocalHead(baseDir);
|
|
279
|
+
let parentHash = currentHead;
|
|
280
|
+
if (currentHead) {
|
|
281
|
+
const headCommit = getLocalCommitByHash(currentHead, baseDir);
|
|
282
|
+
const isUnpushed = headCommit && (!headCommit.remotes?.pushed || headCommit.remotes.pushed.length === 0);
|
|
283
|
+
const isPullCommit = headCommit?.message?.startsWith('pull: sync from ');
|
|
284
|
+
if (isUnpushed && !isPullCommit) {
|
|
285
|
+
parentHash = headCommit?.parentHash || null;
|
|
286
|
+
const commitsDir = path.join(baseDir, '.relq', 'commits');
|
|
287
|
+
const oldCommitFile = path.join(commitsDir, `${currentHead}.json`);
|
|
288
|
+
if (fs.existsSync(oldCommitFile)) {
|
|
289
|
+
fs.unlinkSync(oldCommitFile);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
267
293
|
const commit = {
|
|
268
294
|
hash,
|
|
269
295
|
parentHash,
|
|
@@ -404,9 +404,21 @@ function detectFileChanges(schemaPath, projectRoot = process.cwd()) {
|
|
|
404
404
|
const currentContent = fs.readFileSync(schemaPath, 'utf-8');
|
|
405
405
|
const currentHash = hashFileContent(currentContent);
|
|
406
406
|
const savedHash = getSavedFileHash(projectRoot);
|
|
407
|
-
if (
|
|
407
|
+
if (savedHash && currentHash === savedHash) {
|
|
408
408
|
return null;
|
|
409
409
|
}
|
|
410
|
+
if (!savedHash) {
|
|
411
|
+
return {
|
|
412
|
+
id: `file_${currentHash.substring(0, 8)}`,
|
|
413
|
+
type: 'ALTER',
|
|
414
|
+
objectType: 'SCHEMA_FILE',
|
|
415
|
+
objectName: path.basename(schemaPath),
|
|
416
|
+
before: null,
|
|
417
|
+
after: { hash: currentHash },
|
|
418
|
+
sql: '-- Schema file needs comparison (no previous hash)',
|
|
419
|
+
detectedAt: new Date().toISOString(),
|
|
420
|
+
};
|
|
421
|
+
}
|
|
410
422
|
return {
|
|
411
423
|
id: `file_${currentHash.substring(0, 8)}`,
|
|
412
424
|
type: 'ALTER',
|
|
@@ -142,7 +142,7 @@ export async function pullCommand(context) {
|
|
|
142
142
|
const connection = config.connection;
|
|
143
143
|
const force = flags['force'] === true;
|
|
144
144
|
const merge = flags['merge'] === true;
|
|
145
|
-
const
|
|
145
|
+
const autoCommit = flags['commit'] === true;
|
|
146
146
|
const dryRun = flags['dry-run'] === true;
|
|
147
147
|
const author = config.author || 'Relq CLI';
|
|
148
148
|
const schemaPathRaw = getSchemaPath(config);
|
|
@@ -844,7 +844,7 @@ export async function pullCommand(context) {
|
|
|
844
844
|
copyTrackingIdsToNormalized(parsedSchema, currentSchema);
|
|
845
845
|
saveSnapshot(currentSchema, projectRoot);
|
|
846
846
|
const duration = Date.now() - startTime;
|
|
847
|
-
if (
|
|
847
|
+
if (!autoCommit) {
|
|
848
848
|
if (schemaChanges.length > 0) {
|
|
849
849
|
addUnstagedChanges(schemaChanges, projectRoot);
|
|
850
850
|
spinner.succeed(`Detected ${schemaChanges.length} schema change(s)`);
|
|
@@ -344,7 +344,18 @@ export async function pushCommand(context) {
|
|
|
344
344
|
catch {
|
|
345
345
|
spinner.fail('SQL execution failed');
|
|
346
346
|
}
|
|
347
|
-
|
|
347
|
+
const dbError = error?.message || String(error);
|
|
348
|
+
const enhancedError = new Error(`${dbError}\n\n` +
|
|
349
|
+
`${colors.muted('This usually means:')}\n` +
|
|
350
|
+
` • The SQL statements were generated in the wrong order\n` +
|
|
351
|
+
` • A column/table is referenced before it's created\n` +
|
|
352
|
+
` • The schema is out of sync with the database\n\n` +
|
|
353
|
+
`${colors.muted('To fix:')}\n` +
|
|
354
|
+
` 1. Run ${colors.cyan('relq reset --hard HEAD~1')} to undo the commit\n` +
|
|
355
|
+
` 2. Run ${colors.cyan('relq pull')} to sync with database\n` +
|
|
356
|
+
` 3. Run ${colors.cyan('relq add')} and ${colors.cyan('relq commit')} again\n\n` +
|
|
357
|
+
`${colors.muted('To debug, run:')} ${colors.cyan('relq push --dry-run')} ${colors.muted('to see the SQL')}`);
|
|
358
|
+
throw enhancedError;
|
|
348
359
|
}
|
|
349
360
|
finally {
|
|
350
361
|
await client.end();
|
|
@@ -78,7 +78,7 @@ export async function resetCommand(context) {
|
|
|
78
78
|
fatal('Cannot find commit data - repository may be corrupt');
|
|
79
79
|
}
|
|
80
80
|
const commitData = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
81
|
-
const targetSnapshot = commitData.snapshot;
|
|
81
|
+
const targetSnapshot = (commitData.snapshot || commitData.schema);
|
|
82
82
|
if (!targetSnapshot) {
|
|
83
83
|
spinner.stop();
|
|
84
84
|
fatal('Commit has no snapshot data - repository may be corrupt');
|
|
@@ -86,6 +86,18 @@ export async function resetCommand(context) {
|
|
|
86
86
|
if (hard) {
|
|
87
87
|
saveSnapshot(targetSnapshot, projectRoot);
|
|
88
88
|
setHead(targetHash, projectRoot);
|
|
89
|
+
const commitsDir = path.join(projectRoot, '.relq', 'commits');
|
|
90
|
+
const headIndex = allCommits.findIndex(c => c.hash === currentHead);
|
|
91
|
+
const targetIndex = allCommits.findIndex(c => c.hash === targetHash);
|
|
92
|
+
if (headIndex >= 0 && targetIndex > headIndex) {
|
|
93
|
+
for (let i = headIndex; i < targetIndex; i++) {
|
|
94
|
+
const commitToDelete = allCommits[i];
|
|
95
|
+
const commitFile = path.join(commitsDir, `${commitToDelete.hash}.json`);
|
|
96
|
+
if (fs.existsSync(commitFile)) {
|
|
97
|
+
fs.unlinkSync(commitFile);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
89
101
|
const stagedPath = path.join(projectRoot, '.relq', 'staged.json');
|
|
90
102
|
const unstagedPath = path.join(projectRoot, '.relq', 'unstaged.json');
|
|
91
103
|
if (fs.existsSync(stagedPath))
|
|
@@ -95,6 +107,9 @@ export async function resetCommand(context) {
|
|
|
95
107
|
const mergeStatePath = path.join(projectRoot, '.relq', 'MERGE_STATE');
|
|
96
108
|
if (fs.existsSync(mergeStatePath))
|
|
97
109
|
fs.unlinkSync(mergeStatePath);
|
|
110
|
+
const fileHashPath = path.join(projectRoot, '.relq', 'file_hash');
|
|
111
|
+
if (fs.existsSync(fileHashPath))
|
|
112
|
+
fs.unlinkSync(fileHashPath);
|
|
98
113
|
}
|
|
99
114
|
spinner.succeed(`Reset to ${colors.yellow(shortHash(targetHash))}`);
|
|
100
115
|
console.log('');
|
package/dist/esm/cli/index.js
CHANGED
|
@@ -56,6 +56,11 @@ function parseArgs(argv) {
|
|
|
56
56
|
const args = [];
|
|
57
57
|
const flags = {};
|
|
58
58
|
let command = '';
|
|
59
|
+
const booleanFlags = new Set([
|
|
60
|
+
'hard', 'soft', 'force', 'yes', 'y', 'dry-run', 'verbose', 'quiet', 'q',
|
|
61
|
+
'help', 'h', 'version', 'v', 'all', 'a', 'cached', 'staged', 'no-verify',
|
|
62
|
+
'metadata-only', 'skip-prompt', 'interactive', 'i'
|
|
63
|
+
]);
|
|
59
64
|
for (let i = 2; i < argv.length; i++) {
|
|
60
65
|
const arg = argv[i];
|
|
61
66
|
if (arg.startsWith('--')) {
|
|
@@ -64,25 +69,36 @@ function parseArgs(argv) {
|
|
|
64
69
|
flags[arg.slice(2, eqIndex)] = arg.slice(eqIndex + 1);
|
|
65
70
|
}
|
|
66
71
|
else {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
flags[
|
|
70
|
-
i++;
|
|
72
|
+
const flagName = arg.slice(2);
|
|
73
|
+
if (booleanFlags.has(flagName)) {
|
|
74
|
+
flags[flagName] = true;
|
|
71
75
|
}
|
|
72
76
|
else {
|
|
73
|
-
|
|
77
|
+
const nextArg = argv[i + 1];
|
|
78
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
79
|
+
flags[flagName] = nextArg;
|
|
80
|
+
i++;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
flags[flagName] = true;
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
}
|
|
76
87
|
}
|
|
77
88
|
else if (arg.startsWith('-') && arg.length === 2) {
|
|
78
89
|
const flag = arg.slice(1);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
flags[flag] = nextArg;
|
|
82
|
-
i++;
|
|
90
|
+
if (booleanFlags.has(flag)) {
|
|
91
|
+
flags[flag] = true;
|
|
83
92
|
}
|
|
84
93
|
else {
|
|
85
|
-
|
|
94
|
+
const nextArg = argv[i + 1];
|
|
95
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
96
|
+
flags[flag] = nextArg;
|
|
97
|
+
i++;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
flags[flag] = true;
|
|
101
|
+
}
|
|
86
102
|
}
|
|
87
103
|
}
|
|
88
104
|
else if (arg.startsWith('-') && arg.length > 2) {
|
|
@@ -517,14 +517,14 @@ export function sortChangesByDependency(changes) {
|
|
|
517
517
|
'PARTITION': 11,
|
|
518
518
|
'PARTITION_CHILD': 12,
|
|
519
519
|
'COLUMN': 13,
|
|
520
|
-
'COLUMN_COMMENT':
|
|
521
|
-
'INDEX':
|
|
522
|
-
'INDEX_COMMENT':
|
|
523
|
-
'CONSTRAINT':
|
|
524
|
-
'PRIMARY_KEY':
|
|
525
|
-
'FOREIGN_KEY':
|
|
526
|
-
'CHECK':
|
|
527
|
-
'EXCLUSION':
|
|
520
|
+
'COLUMN_COMMENT': 14,
|
|
521
|
+
'INDEX': 15,
|
|
522
|
+
'INDEX_COMMENT': 16,
|
|
523
|
+
'CONSTRAINT': 17,
|
|
524
|
+
'PRIMARY_KEY': 17,
|
|
525
|
+
'FOREIGN_KEY': 18,
|
|
526
|
+
'CHECK': 17,
|
|
527
|
+
'EXCLUSION': 17,
|
|
528
528
|
'VIEW': 20,
|
|
529
529
|
'MATERIALIZED_VIEW': 21,
|
|
530
530
|
'FUNCTION': 30,
|
|
@@ -26,6 +26,18 @@ export function getLocalCommits(baseDir = process.cwd()) {
|
|
|
26
26
|
return [];
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
function getLocalCommitByHash(hash, baseDir = process.cwd()) {
|
|
30
|
+
const commitPath = path.join(baseDir, '.relq', 'commits', `${hash}.json`);
|
|
31
|
+
if (!fs.existsSync(commitPath)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
29
41
|
export function saveLocalCommits(commits, baseDir = process.cwd()) {
|
|
30
42
|
ensureRelqDir(baseDir);
|
|
31
43
|
const filePath = path.join(baseDir, RELQ_DIR, COMMITS_FILE);
|
|
@@ -212,7 +224,21 @@ export function generateASTHash(ast) {
|
|
|
212
224
|
export function createCommitFromSchema(schema, author, message, commitLimit = 1000, baseDir = process.cwd()) {
|
|
213
225
|
const parsedSchema = schemaToAST(schema);
|
|
214
226
|
const hash = generateASTHash(parsedSchema);
|
|
215
|
-
const
|
|
227
|
+
const currentHead = getLocalHead(baseDir);
|
|
228
|
+
let parentHash = currentHead;
|
|
229
|
+
if (currentHead) {
|
|
230
|
+
const headCommit = getLocalCommitByHash(currentHead, baseDir);
|
|
231
|
+
const isUnpushed = headCommit && (!headCommit.remotes?.pushed || headCommit.remotes.pushed.length === 0);
|
|
232
|
+
const isPullCommit = headCommit?.message?.startsWith('pull: sync from ');
|
|
233
|
+
if (isUnpushed && !isPullCommit) {
|
|
234
|
+
parentHash = headCommit?.parentHash || null;
|
|
235
|
+
const commitsDir = path.join(baseDir, '.relq', 'commits');
|
|
236
|
+
const oldCommitFile = path.join(commitsDir, `${currentHead}.json`);
|
|
237
|
+
if (fs.existsSync(oldCommitFile)) {
|
|
238
|
+
fs.unlinkSync(oldCommitFile);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
216
242
|
const commit = {
|
|
217
243
|
hash,
|
|
218
244
|
parentHash,
|
|
@@ -313,9 +313,21 @@ export function detectFileChanges(schemaPath, projectRoot = process.cwd()) {
|
|
|
313
313
|
const currentContent = fs.readFileSync(schemaPath, 'utf-8');
|
|
314
314
|
const currentHash = hashFileContent(currentContent);
|
|
315
315
|
const savedHash = getSavedFileHash(projectRoot);
|
|
316
|
-
if (
|
|
316
|
+
if (savedHash && currentHash === savedHash) {
|
|
317
317
|
return null;
|
|
318
318
|
}
|
|
319
|
+
if (!savedHash) {
|
|
320
|
+
return {
|
|
321
|
+
id: `file_${currentHash.substring(0, 8)}`,
|
|
322
|
+
type: 'ALTER',
|
|
323
|
+
objectType: 'SCHEMA_FILE',
|
|
324
|
+
objectName: path.basename(schemaPath),
|
|
325
|
+
before: null,
|
|
326
|
+
after: { hash: currentHash },
|
|
327
|
+
sql: '-- Schema file needs comparison (no previous hash)',
|
|
328
|
+
detectedAt: new Date().toISOString(),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
319
331
|
return {
|
|
320
332
|
id: `file_${currentHash.substring(0, 8)}`,
|
|
321
333
|
type: 'ALTER',
|