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
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import * as readline from 'readline';
|
|
4
3
|
import { requireValidConfig } from "../utils/config-loader.js";
|
|
5
4
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
6
|
-
|
|
7
|
-
reset: '\x1b[0m',
|
|
8
|
-
bold: '\x1b[1m',
|
|
9
|
-
dim: '\x1b[2m',
|
|
10
|
-
red: '\x1b[31m',
|
|
11
|
-
green: '\x1b[32m',
|
|
12
|
-
yellow: '\x1b[33m',
|
|
13
|
-
cyan: '\x1b[36m',
|
|
14
|
-
};
|
|
5
|
+
import { colors, confirm, fatal, warning } from "../utils/cli-utils.js";
|
|
15
6
|
function parseMigration(content) {
|
|
16
7
|
const upMatch = content.match(/--\s*UP\s*\n([\s\S]*?)(?=--\s*DOWN|$)/i);
|
|
17
8
|
const downMatch = content.match(/--\s*DOWN\s*\n([\s\S]*?)$/i);
|
|
@@ -20,36 +11,24 @@ function parseMigration(content) {
|
|
|
20
11
|
down: downMatch?.[1]?.trim() || '',
|
|
21
12
|
};
|
|
22
13
|
}
|
|
23
|
-
function askConfirm(question) {
|
|
24
|
-
const rl = readline.createInterface({
|
|
25
|
-
input: process.stdin,
|
|
26
|
-
output: process.stdout,
|
|
27
|
-
});
|
|
28
|
-
return new Promise((resolve) => {
|
|
29
|
-
rl.question(`${question} [y/N]: `, (answer) => {
|
|
30
|
-
rl.close();
|
|
31
|
-
resolve(answer.trim().toLowerCase() === 'y');
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
14
|
export async function rollbackCommand(context) {
|
|
36
15
|
const { config, args, flags } = context;
|
|
37
16
|
if (!config) {
|
|
38
|
-
|
|
39
|
-
|
|
17
|
+
fatal('No configuration found', `run ${colors.cyan('relq init')} to create a configuration file`);
|
|
18
|
+
return;
|
|
40
19
|
}
|
|
41
|
-
requireValidConfig(config);
|
|
20
|
+
await requireValidConfig(config, { calledFrom: 'rollback' });
|
|
42
21
|
const connection = config.connection;
|
|
43
22
|
const migrationsDir = config.migrations?.directory || './migrations';
|
|
44
23
|
const tableName = config.migrations?.tableName || '_relq_migrations';
|
|
45
24
|
const count = parseInt(args[0]) || 1;
|
|
46
25
|
const dryRun = flags['dry-run'] === true;
|
|
47
26
|
const force = flags['force'] === true;
|
|
48
|
-
console.log(
|
|
27
|
+
console.log(`Rolling back ${count} migration(s)...`);
|
|
49
28
|
console.log(` Connection: ${getConnectionDescription(connection)}`);
|
|
50
29
|
console.log('');
|
|
51
30
|
try {
|
|
52
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
31
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
53
32
|
const pool = new Pool({
|
|
54
33
|
host: connection.host,
|
|
55
34
|
port: connection.port || 5432,
|
|
@@ -65,17 +44,17 @@ export async function rollbackCommand(context) {
|
|
|
65
44
|
LIMIT $1;
|
|
66
45
|
`, [count]);
|
|
67
46
|
if (result.rows.length === 0) {
|
|
68
|
-
console.log(
|
|
47
|
+
console.log('No migrations to rollback.');
|
|
69
48
|
return;
|
|
70
49
|
}
|
|
71
50
|
const toRollback = result.rows.map(r => r.name);
|
|
72
|
-
console.log(
|
|
51
|
+
console.log('Migrations to rollback:');
|
|
73
52
|
for (const name of toRollback) {
|
|
74
|
-
console.log(`
|
|
53
|
+
console.log(` - ${name}`);
|
|
75
54
|
}
|
|
76
55
|
console.log('');
|
|
77
56
|
if (!force && !dryRun) {
|
|
78
|
-
const proceed = await
|
|
57
|
+
const proceed = await confirm(`${colors.red('This will undo ' + toRollback.length + ' migration(s). Continue?')}`, false);
|
|
79
58
|
if (!proceed) {
|
|
80
59
|
console.log('Cancelled.');
|
|
81
60
|
return;
|
|
@@ -84,18 +63,18 @@ export async function rollbackCommand(context) {
|
|
|
84
63
|
for (const name of toRollback) {
|
|
85
64
|
const filePath = path.join(migrationsDir, name);
|
|
86
65
|
if (!fs.existsSync(filePath)) {
|
|
87
|
-
|
|
66
|
+
warning(`Migration file not found: ${name}`);
|
|
88
67
|
continue;
|
|
89
68
|
}
|
|
90
69
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
91
70
|
const { down } = parseMigration(content);
|
|
92
71
|
if (!down) {
|
|
93
|
-
|
|
72
|
+
warning(`No DOWN section in: ${name}`);
|
|
94
73
|
continue;
|
|
95
74
|
}
|
|
96
75
|
if (dryRun) {
|
|
97
|
-
console.log(
|
|
98
|
-
console.log(
|
|
76
|
+
console.log(`[dry-run] Would rollback: ${name}`);
|
|
77
|
+
console.log(down);
|
|
99
78
|
console.log('');
|
|
100
79
|
}
|
|
101
80
|
else {
|
|
@@ -106,7 +85,7 @@ export async function rollbackCommand(context) {
|
|
|
106
85
|
await client.query(down);
|
|
107
86
|
await client.query(`DELETE FROM "${tableName}" WHERE name = $1`, [name]);
|
|
108
87
|
await client.query('COMMIT');
|
|
109
|
-
console.log(
|
|
88
|
+
console.log(' Rolled back');
|
|
110
89
|
}
|
|
111
90
|
catch (error) {
|
|
112
91
|
await client.query('ROLLBACK');
|
|
@@ -119,7 +98,7 @@ export async function rollbackCommand(context) {
|
|
|
119
98
|
}
|
|
120
99
|
if (!dryRun) {
|
|
121
100
|
console.log('');
|
|
122
|
-
console.log(
|
|
101
|
+
console.log(`Rolled back ${toRollback.length} migration(s).`);
|
|
123
102
|
}
|
|
124
103
|
}
|
|
125
104
|
finally {
|
|
@@ -127,7 +106,6 @@ export async function rollbackCommand(context) {
|
|
|
127
106
|
}
|
|
128
107
|
}
|
|
129
108
|
catch (error) {
|
|
130
|
-
|
|
131
|
-
process.exit(1);
|
|
109
|
+
fatal('Rollback failed', error instanceof Error ? error.message : String(error));
|
|
132
110
|
}
|
|
133
111
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { colors, fatal } from "../utils/spinner.js";
|
|
4
|
+
import { isInitialized, getStagedChanges, getUnstagedChanges, } from "../utils/repo-manager.js";
|
|
5
|
+
export async function stashCommand(context) {
|
|
6
|
+
const { args, flags, projectRoot } = context;
|
|
7
|
+
const subcommand = args[0] || 'push';
|
|
8
|
+
console.log('');
|
|
9
|
+
if (!isInitialized(projectRoot)) {
|
|
10
|
+
fatal('not a relq repository (or any parent directories): .relq', `Run ${colors.cyan('relq init')} to initialize.`);
|
|
11
|
+
}
|
|
12
|
+
const stashDir = path.join(projectRoot, '.relq', 'stash');
|
|
13
|
+
switch (subcommand) {
|
|
14
|
+
case 'push':
|
|
15
|
+
case 'save':
|
|
16
|
+
await stashPush(projectRoot, stashDir, flags['m'] || 'WIP');
|
|
17
|
+
break;
|
|
18
|
+
case 'pop':
|
|
19
|
+
await stashPop(projectRoot, stashDir);
|
|
20
|
+
break;
|
|
21
|
+
case 'list':
|
|
22
|
+
await stashList(stashDir);
|
|
23
|
+
break;
|
|
24
|
+
case 'drop':
|
|
25
|
+
await stashDrop(stashDir);
|
|
26
|
+
break;
|
|
27
|
+
default:
|
|
28
|
+
await stashPush(projectRoot, stashDir, subcommand);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function stashPush(projectRoot, stashDir, message) {
|
|
32
|
+
const staged = getStagedChanges(projectRoot);
|
|
33
|
+
const unstaged = getUnstagedChanges(projectRoot);
|
|
34
|
+
if (staged.length === 0 && unstaged.length === 0) {
|
|
35
|
+
console.log(`${colors.muted('No changes to stash.')}`);
|
|
36
|
+
console.log('');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!fs.existsSync(stashDir)) {
|
|
40
|
+
fs.mkdirSync(stashDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
const stashFiles = fs.readdirSync(stashDir).filter(f => f.endsWith('.json'));
|
|
43
|
+
const stashIdx = stashFiles.length;
|
|
44
|
+
const stash = {
|
|
45
|
+
message,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
staged,
|
|
48
|
+
unstaged,
|
|
49
|
+
};
|
|
50
|
+
fs.writeFileSync(path.join(stashDir, `stash-${stashIdx}.json`), JSON.stringify(stash, null, 2));
|
|
51
|
+
const stagedPath = path.join(projectRoot, '.relq', 'staged.json');
|
|
52
|
+
const unstagedPath = path.join(projectRoot, '.relq', 'unstaged.json');
|
|
53
|
+
if (fs.existsSync(stagedPath))
|
|
54
|
+
fs.writeFileSync(stagedPath, '[]');
|
|
55
|
+
if (fs.existsSync(unstagedPath))
|
|
56
|
+
fs.writeFileSync(unstagedPath, '[]');
|
|
57
|
+
console.log('Saved working directory');
|
|
58
|
+
console.log(` stash@{${stashIdx}}: ${message}`);
|
|
59
|
+
console.log('');
|
|
60
|
+
}
|
|
61
|
+
async function stashPop(projectRoot, stashDir) {
|
|
62
|
+
if (!fs.existsSync(stashDir)) {
|
|
63
|
+
console.log(`${colors.muted('No stashes found.')}`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const stashFiles = fs.readdirSync(stashDir).filter(f => f.endsWith('.json')).sort().reverse();
|
|
67
|
+
if (stashFiles.length === 0) {
|
|
68
|
+
console.log(`${colors.muted('No stashes found.')}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const stashPath = path.join(stashDir, stashFiles[0]);
|
|
72
|
+
const stash = JSON.parse(fs.readFileSync(stashPath, 'utf-8'));
|
|
73
|
+
const stagedPath = path.join(projectRoot, '.relq', 'staged.json');
|
|
74
|
+
const unstagedPath = path.join(projectRoot, '.relq', 'unstaged.json');
|
|
75
|
+
if (stash.staged.length > 0) {
|
|
76
|
+
fs.writeFileSync(stagedPath, JSON.stringify(stash.staged, null, 2));
|
|
77
|
+
}
|
|
78
|
+
if (stash.unstaged.length > 0) {
|
|
79
|
+
fs.writeFileSync(unstagedPath, JSON.stringify(stash.unstaged, null, 2));
|
|
80
|
+
}
|
|
81
|
+
fs.unlinkSync(stashPath);
|
|
82
|
+
console.log('Applied stash and dropped');
|
|
83
|
+
console.log(` ${stash.message}`);
|
|
84
|
+
console.log('');
|
|
85
|
+
}
|
|
86
|
+
async function stashList(stashDir) {
|
|
87
|
+
if (!fs.existsSync(stashDir)) {
|
|
88
|
+
console.log(`${colors.muted('No stashes.')}`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const stashFiles = fs.readdirSync(stashDir).filter(f => f.endsWith('.json')).sort();
|
|
92
|
+
if (stashFiles.length === 0) {
|
|
93
|
+
console.log(`${colors.muted('No stashes.')}`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < stashFiles.length; i++) {
|
|
97
|
+
const stash = JSON.parse(fs.readFileSync(path.join(stashDir, stashFiles[i]), 'utf-8'));
|
|
98
|
+
console.log(`stash@{${i}}: ${stash.message} (${stash.staged.length + stash.unstaged.length} changes)`);
|
|
99
|
+
}
|
|
100
|
+
console.log('');
|
|
101
|
+
}
|
|
102
|
+
async function stashDrop(stashDir) {
|
|
103
|
+
if (!fs.existsSync(stashDir)) {
|
|
104
|
+
console.log(`${colors.muted('No stashes.')}`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const stashFiles = fs.readdirSync(stashDir).filter(f => f.endsWith('.json')).sort().reverse();
|
|
108
|
+
if (stashFiles.length === 0) {
|
|
109
|
+
console.log(`${colors.muted('No stashes.')}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
fs.unlinkSync(path.join(stashDir, stashFiles[0]));
|
|
113
|
+
console.log('Dropped stash');
|
|
114
|
+
console.log('');
|
|
115
|
+
}
|
|
116
|
+
export default stashCommand;
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
1
3
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
2
|
-
import { colors } from "../utils/spinner.js";
|
|
4
|
+
import { colors, fatal } from "../utils/spinner.js";
|
|
5
|
+
import { loadRelqignore, } from "../utils/relqignore.js";
|
|
3
6
|
import { isInitialized, getHead, loadCommit, shortHash, getStagedChanges, getUnstagedChanges, } from "../utils/repo-manager.js";
|
|
4
7
|
export async function statusCommand(context) {
|
|
5
|
-
const { config, flags } = context;
|
|
6
|
-
const projectRoot = process.cwd();
|
|
8
|
+
const { config, flags, projectRoot } = context;
|
|
7
9
|
const connection = config?.connection;
|
|
8
10
|
console.log('');
|
|
9
11
|
if (!isInitialized(projectRoot)) {
|
|
10
|
-
|
|
11
|
-
console.log('');
|
|
12
|
-
console.log(`${colors.muted('Run')} ${colors.cyan('relq init')} ${colors.muted('to initialize.')}`);
|
|
13
|
-
console.log('');
|
|
14
|
-
return;
|
|
12
|
+
fatal('not a relq repository (or any parent directories): .relq', `Run ${colors.cyan('relq init')} to initialize.`);
|
|
15
13
|
}
|
|
16
14
|
const head = getHead(projectRoot);
|
|
17
15
|
const staged = getStagedChanges(projectRoot);
|
|
@@ -34,7 +32,7 @@ export async function statusCommand(context) {
|
|
|
34
32
|
console.log(`${colors.green('Changes to be committed:')}`);
|
|
35
33
|
console.log(` ${colors.muted('(use "relq restore --staged <name>..." to unstage)')}`);
|
|
36
34
|
console.log('');
|
|
37
|
-
displayChanges(staged, '
|
|
35
|
+
displayChanges(staged, ' ');
|
|
38
36
|
console.log('');
|
|
39
37
|
}
|
|
40
38
|
if (unstaged.length > 0) {
|
|
@@ -42,9 +40,21 @@ export async function statusCommand(context) {
|
|
|
42
40
|
console.log(` ${colors.muted('(use "relq add <name>..." to stage)')}`);
|
|
43
41
|
console.log(` ${colors.muted('(use "relq restore <name>..." to discard)')}`);
|
|
44
42
|
console.log('');
|
|
45
|
-
displayChanges(unstaged, '
|
|
43
|
+
displayChanges(unstaged, ' ');
|
|
46
44
|
console.log('');
|
|
47
45
|
}
|
|
46
|
+
const ignorePatterns = loadRelqignore(projectRoot);
|
|
47
|
+
const relqignorePath = path.join(projectRoot, '.relqignore');
|
|
48
|
+
if (ignorePatterns.length > 0 && fs.existsSync(relqignorePath)) {
|
|
49
|
+
const userPatterns = ignorePatterns.filter(p => !p.raw.startsWith('_relq_') &&
|
|
50
|
+
!p.raw.startsWith('pg_') &&
|
|
51
|
+
!p.raw.startsWith('_temp_') &&
|
|
52
|
+
!p.raw.startsWith('tmp_'));
|
|
53
|
+
if (userPatterns.length > 0) {
|
|
54
|
+
console.log(`${colors.muted(`${userPatterns.length} pattern(s) active from .relqignore`)}`);
|
|
55
|
+
console.log('');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
48
58
|
if (staged.length === 0 && unstaged.length === 0) {
|
|
49
59
|
console.log(`${colors.green('nothing to commit, working tree clean')}`);
|
|
50
60
|
console.log('');
|
|
@@ -1,76 +1,56 @@
|
|
|
1
1
|
import { requireValidConfig } from "../utils/config-loader.js";
|
|
2
2
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
3
|
-
import { colors, createSpinner } from "../utils/
|
|
4
|
-
import { isInitialized, fetchRemoteCommits, pushCommit, ensureRemoteTable, getAllCommits,
|
|
3
|
+
import { colors, createSpinner, fatal, success } from "../utils/cli-utils.js";
|
|
4
|
+
import { isInitialized, shortHash, fetchRemoteCommits, pushCommit, ensureRemoteTable, getAllCommits, } from "../utils/repo-manager.js";
|
|
5
5
|
import { pullCommand } from "./pull.js";
|
|
6
6
|
export async function syncCommand(context) {
|
|
7
7
|
const { config, flags } = context;
|
|
8
8
|
if (!config) {
|
|
9
|
-
|
|
10
|
-
process.exit(1);
|
|
9
|
+
fatal('No configuration found', `Run ${colors.cyan('relq init')} to create one.`);
|
|
11
10
|
}
|
|
12
|
-
requireValidConfig(config);
|
|
11
|
+
await requireValidConfig(config, { calledFrom: 'sync' });
|
|
13
12
|
const connection = config.connection;
|
|
14
|
-
const projectRoot =
|
|
13
|
+
const { projectRoot } = context;
|
|
15
14
|
console.log('');
|
|
16
15
|
if (!isInitialized(projectRoot)) {
|
|
17
|
-
|
|
18
|
-
console.log('');
|
|
19
|
-
console.log(`${colors.muted('Run')} ${colors.cyan('relq init')} ${colors.muted('to initialize.')}`);
|
|
20
|
-
return;
|
|
16
|
+
fatal('not a relq repository (or any parent directories): .relq', `Run ${colors.cyan('relq init')} to initialize.`);
|
|
21
17
|
}
|
|
22
18
|
const spinner = createSpinner();
|
|
23
19
|
try {
|
|
24
|
-
|
|
25
|
-
const status = await getRepoStatus(connection, projectRoot);
|
|
26
|
-
spinner.stop();
|
|
27
|
-
console.log(`On database: ${colors.cyan(getConnectionDescription(connection))}`);
|
|
20
|
+
console.log(`Syncing with ${colors.cyan(getConnectionDescription(connection))}...`);
|
|
28
21
|
console.log('');
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
22
|
+
await pullCommand(context);
|
|
23
|
+
await ensureRemoteTable(connection);
|
|
24
|
+
const remoteCommits = await fetchRemoteCommits(connection, 100);
|
|
25
|
+
const remoteHashes = new Set(remoteCommits.map(c => c.hash));
|
|
26
|
+
const localCommitsAfter = getAllCommits(projectRoot);
|
|
27
|
+
const toPush = localCommitsAfter.filter(c => !remoteHashes.has(c.hash));
|
|
28
|
+
if (toPush.length === 0) {
|
|
29
|
+
success('Sync complete - up to date');
|
|
37
30
|
console.log('');
|
|
38
31
|
return;
|
|
39
32
|
}
|
|
33
|
+
console.log(`Pushing ${toPush.length} local commit(s)...`);
|
|
40
34
|
console.log('');
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const pullContext = {
|
|
45
|
-
...context,
|
|
46
|
-
flags: { ...flags, force: true },
|
|
47
|
-
};
|
|
48
|
-
await pullCommand(pullContext);
|
|
35
|
+
spinner.start(`Pushing ${toPush.length} commit(s)...`);
|
|
36
|
+
for (const commit of toPush.reverse()) {
|
|
37
|
+
await pushCommit(connection, commit);
|
|
49
38
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
spinner.start(`Pushing ${toPush.length} commit(s)...`);
|
|
59
|
-
for (const commit of toPush.reverse()) {
|
|
60
|
-
await pushCommit(connection, commit);
|
|
61
|
-
}
|
|
62
|
-
spinner.succeed(`Pushed ${toPush.length} commit(s)`);
|
|
39
|
+
spinner.succeed(`Pushed ${toPush.length} commit(s)`);
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log('Pushed commits:');
|
|
42
|
+
for (const commit of toPush.slice(0, 5)) {
|
|
43
|
+
console.log(` ${colors.yellow(shortHash(commit.hash))} ${commit.message}`);
|
|
44
|
+
}
|
|
45
|
+
if (toPush.length > 5) {
|
|
46
|
+
console.log(` ${colors.muted(`... and ${toPush.length - 5} more`)}`);
|
|
63
47
|
}
|
|
64
48
|
console.log('');
|
|
65
|
-
|
|
66
|
-
const pushedText = status.aheadBy > 0 ? `${colors.green(`↑ ${status.aheadBy}`)} pushed` : '';
|
|
67
|
-
const separator = pulledText && pushedText ? ' | ' : '';
|
|
68
|
-
console.log(`${colors.green('✓')} Sync complete: ${pulledText}${separator}${pushedText}`);
|
|
49
|
+
success(`Sync complete - pushed ${toPush.length} commit(s)`);
|
|
69
50
|
}
|
|
70
|
-
catch (
|
|
51
|
+
catch (err) {
|
|
71
52
|
spinner.fail('Sync failed');
|
|
72
|
-
|
|
73
|
-
process.exit(1);
|
|
53
|
+
fatal(err instanceof Error ? err.message : String(err));
|
|
74
54
|
}
|
|
75
55
|
console.log('');
|
|
76
56
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { colors } from "../utils/spinner.js";
|
|
4
|
+
import { isInitialized, getHead, loadCommit, shortHash } from "../utils/repo-manager.js";
|
|
5
|
+
function loadTags(projectRoot) {
|
|
6
|
+
const tagPath = path.join(projectRoot, '.relq', 'tags.json');
|
|
7
|
+
if (fs.existsSync(tagPath)) {
|
|
8
|
+
return JSON.parse(fs.readFileSync(tagPath, 'utf-8'));
|
|
9
|
+
}
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
function saveTags(tags, projectRoot) {
|
|
13
|
+
const tagPath = path.join(projectRoot, '.relq', 'tags.json');
|
|
14
|
+
fs.writeFileSync(tagPath, JSON.stringify(tags, null, 2));
|
|
15
|
+
}
|
|
16
|
+
export async function tagCommand(context) {
|
|
17
|
+
const { args, flags, projectRoot } = context;
|
|
18
|
+
console.log('');
|
|
19
|
+
if (!isInitialized(projectRoot)) {
|
|
20
|
+
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const tags = loadTags(projectRoot);
|
|
24
|
+
const deleteFlag = flags['d'] === true || flags['delete'] === true;
|
|
25
|
+
const force = flags['f'] === true || flags['force'] === true;
|
|
26
|
+
if (deleteFlag) {
|
|
27
|
+
const tagName = args[0];
|
|
28
|
+
if (!tagName) {
|
|
29
|
+
console.log(`${colors.red('error:')} Please specify tag name`);
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log(`Usage: ${colors.cyan('relq tag -d <name>')}`);
|
|
32
|
+
console.log('');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!tags[tagName]) {
|
|
36
|
+
console.log(`${colors.red('error:')} Tag not found: ${tagName}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
delete tags[tagName];
|
|
40
|
+
saveTags(tags, projectRoot);
|
|
41
|
+
console.log(`Deleted tag '${tagName}'`);
|
|
42
|
+
console.log('');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (args[0]) {
|
|
46
|
+
const tagName = args[0];
|
|
47
|
+
const targetHash = args[1];
|
|
48
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(tagName)) {
|
|
49
|
+
console.log(`${colors.red('error:')} Invalid tag name`);
|
|
50
|
+
console.log('Tag names can only contain: letters, numbers, dots, dashes, underscores');
|
|
51
|
+
console.log('');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (tags[tagName] && !force) {
|
|
55
|
+
console.log(`${colors.red('error:')} Tag already exists: ${tagName}`);
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(`Use ${colors.cyan(`relq tag -f ${tagName}`)} to overwrite`);
|
|
58
|
+
console.log('');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let hash;
|
|
62
|
+
if (targetHash) {
|
|
63
|
+
const commit = loadCommit(targetHash, projectRoot);
|
|
64
|
+
if (!commit) {
|
|
65
|
+
console.log(`${colors.red('error:')} Commit not found: ${targetHash}`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
hash = commit.hash;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const head = getHead(projectRoot);
|
|
72
|
+
if (!head) {
|
|
73
|
+
console.log(`${colors.red('error:')} No commits yet`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
hash = head;
|
|
77
|
+
}
|
|
78
|
+
const commit = loadCommit(hash, projectRoot);
|
|
79
|
+
if (!commit) {
|
|
80
|
+
console.log(`${colors.red('error:')} Cannot load commit`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
tags[tagName] = {
|
|
84
|
+
hash,
|
|
85
|
+
message: commit.message,
|
|
86
|
+
createdAt: new Date().toISOString(),
|
|
87
|
+
};
|
|
88
|
+
saveTags(tags, projectRoot);
|
|
89
|
+
console.log(`Tagged ${colors.yellow(shortHash(hash))} as '${tagName}'`);
|
|
90
|
+
console.log(` ${commit.message}`);
|
|
91
|
+
console.log('');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const tagNames = Object.keys(tags).sort();
|
|
95
|
+
if (tagNames.length === 0) {
|
|
96
|
+
console.log(`${colors.muted('No tags.')}`);
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log(`Create one with: ${colors.cyan('relq tag <name>')}`);
|
|
99
|
+
console.log('');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
for (const name of tagNames) {
|
|
103
|
+
const tag = tags[name];
|
|
104
|
+
const hash = colors.yellow(shortHash(tag.hash));
|
|
105
|
+
const tagName = colors.cyan(name);
|
|
106
|
+
console.log(`${tagName.padEnd(20)} ${hash} ${tag.message}`);
|
|
107
|
+
}
|
|
108
|
+
console.log('');
|
|
109
|
+
}
|
|
110
|
+
export default tagCommand;
|