isomorphic-jj 1.1.3 → 1.2.0
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/README.md +48 -0
- package/dist/index.cjs +193 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +193 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/api/repository.js +112 -15
- package/src/core/bookmark-store.js +16 -4
- package/src/core/revset-engine.js +18 -0
- package/src/core/user-config.js +47 -2
package/dist/index.mjs
CHANGED
|
@@ -1309,6 +1309,7 @@ class BookmarkStore {
|
|
|
1309
1309
|
this.storage = storage;
|
|
1310
1310
|
this.local = new Map();
|
|
1311
1311
|
this.remote = new Map(); // remote → (bookmark → changeId)
|
|
1312
|
+
this.tracking = {}; // bookmark → { remote, ref } tracking info (v0.35.0)
|
|
1312
1313
|
}
|
|
1313
1314
|
|
|
1314
1315
|
/**
|
|
@@ -1351,6 +1352,7 @@ class BookmarkStore {
|
|
|
1351
1352
|
|
|
1352
1353
|
this.local.clear();
|
|
1353
1354
|
this.remote.clear();
|
|
1355
|
+
this.tracking = data.tracked || {}; // Load tracking info (v0.35.0)
|
|
1354
1356
|
|
|
1355
1357
|
// Load local bookmarks
|
|
1356
1358
|
for (const [name, target] of Object.entries(data.local)) {
|
|
@@ -1388,7 +1390,7 @@ class BookmarkStore {
|
|
|
1388
1390
|
version: 1,
|
|
1389
1391
|
local,
|
|
1390
1392
|
remote,
|
|
1391
|
-
tracked: {},
|
|
1393
|
+
tracked: this.tracking || {}, // Save tracking info (v0.35.0)
|
|
1392
1394
|
});
|
|
1393
1395
|
}
|
|
1394
1396
|
|
|
@@ -1501,13 +1503,23 @@ class BookmarkStore {
|
|
|
1501
1503
|
async list() {
|
|
1502
1504
|
const result = [];
|
|
1503
1505
|
|
|
1504
|
-
// Add local bookmarks
|
|
1506
|
+
// Add local bookmarks with tracking info (v0.35.0)
|
|
1505
1507
|
for (const [name, target] of this.local.entries()) {
|
|
1506
|
-
|
|
1508
|
+
const bookmark = {
|
|
1507
1509
|
name,
|
|
1508
1510
|
changeId: target,
|
|
1509
1511
|
remote: null,
|
|
1510
|
-
}
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
// Include tracking info if available
|
|
1515
|
+
if (this.tracking && this.tracking[name]) {
|
|
1516
|
+
bookmark.tracking = {
|
|
1517
|
+
remote: this.tracking[name].remote,
|
|
1518
|
+
ref: this.tracking[name].remoteName || name,
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
result.push(bookmark);
|
|
1511
1523
|
}
|
|
1512
1524
|
|
|
1513
1525
|
// Add remote bookmarks
|
|
@@ -1829,6 +1841,24 @@ class RevsetEngine {
|
|
|
1829
1841
|
}
|
|
1830
1842
|
}
|
|
1831
1843
|
|
|
1844
|
+
// v0.36.0: visible() - non-abandoned changes
|
|
1845
|
+
if (trimmed === 'visible()') {
|
|
1846
|
+
await this.graph.load();
|
|
1847
|
+
const all = this.graph.getAll();
|
|
1848
|
+
return all
|
|
1849
|
+
.filter(c => !c.abandoned)
|
|
1850
|
+
.map(c => c.changeId);
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
// v0.36.0: hidden() - abandoned changes
|
|
1854
|
+
if (trimmed === 'hidden()') {
|
|
1855
|
+
await this.graph.load();
|
|
1856
|
+
const all = this.graph.getAll();
|
|
1857
|
+
return all
|
|
1858
|
+
.filter(c => c.abandoned === true)
|
|
1859
|
+
.map(c => c.changeId);
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1832
1862
|
// ancestors(changeId) - all ancestors including the change itself
|
|
1833
1863
|
const ancestorsMatch = trimmed.match(/^ancestors\(([0-9a-f]{32})\)$/);
|
|
1834
1864
|
if (ancestorsMatch) {
|
|
@@ -4276,9 +4306,15 @@ class UserConfig {
|
|
|
4276
4306
|
}
|
|
4277
4307
|
|
|
4278
4308
|
/**
|
|
4279
|
-
* Load config from storage
|
|
4309
|
+
* Load config from storage and/or programmatic object
|
|
4310
|
+
* v0.35.0: Also loads workspace-specific config from workspace-config.json
|
|
4311
|
+
* v0.36.0: Accepts optional config object for programmatic configuration
|
|
4312
|
+
*
|
|
4313
|
+
* @param {Object} [opts] - Optional config options
|
|
4314
|
+
* @param {Object} [opts.workspace] - Workspace-specific config to merge (highest priority)
|
|
4315
|
+
* @param {Object} [opts.override] - Config object to merge over loaded config
|
|
4280
4316
|
*/
|
|
4281
|
-
async load() {
|
|
4317
|
+
async load(opts = {}) {
|
|
4282
4318
|
try {
|
|
4283
4319
|
const data = await this.storage.read('config.json');
|
|
4284
4320
|
if (data) {
|
|
@@ -4288,6 +4324,45 @@ class UserConfig {
|
|
|
4288
4324
|
// No config file yet, use defaults
|
|
4289
4325
|
await this.init();
|
|
4290
4326
|
}
|
|
4327
|
+
|
|
4328
|
+
// Merge programmatic override config if provided
|
|
4329
|
+
if (opts.override) {
|
|
4330
|
+
this.config = this._deepMerge(this.config, opts.override);
|
|
4331
|
+
}
|
|
4332
|
+
|
|
4333
|
+
// Load workspace-specific config from file (v0.35.0)
|
|
4334
|
+
try {
|
|
4335
|
+
const workspaceData = await this.storage.read('workspace-config.json');
|
|
4336
|
+
if (workspaceData) {
|
|
4337
|
+
// Merge workspace config over global config (workspace takes precedence)
|
|
4338
|
+
this.config = this._deepMerge(this.config, workspaceData);
|
|
4339
|
+
}
|
|
4340
|
+
} catch (error) {
|
|
4341
|
+
// No workspace config, that's fine
|
|
4342
|
+
}
|
|
4343
|
+
|
|
4344
|
+
// Merge programmatic workspace config if provided (highest priority)
|
|
4345
|
+
if (opts.workspace) {
|
|
4346
|
+
this.config = this._deepMerge(this.config, opts.workspace);
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
|
|
4350
|
+
/**
|
|
4351
|
+
* Deep merge two config objects (workspace overrides global)
|
|
4352
|
+
* @private
|
|
4353
|
+
*/
|
|
4354
|
+
_deepMerge(target, source) {
|
|
4355
|
+
const result = { ...target };
|
|
4356
|
+
|
|
4357
|
+
for (const key in source) {
|
|
4358
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
4359
|
+
result[key] = this._deepMerge(target[key] || {}, source[key]);
|
|
4360
|
+
} else {
|
|
4361
|
+
result[key] = source[key];
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
|
|
4365
|
+
return result;
|
|
4291
4366
|
}
|
|
4292
4367
|
|
|
4293
4368
|
/**
|
|
@@ -7014,7 +7089,8 @@ async function createJJ(options) {
|
|
|
7014
7089
|
* @param {Object} args - Arguments
|
|
7015
7090
|
* @param {string} [args.revset='all()'] - Revset expression to filter changes
|
|
7016
7091
|
* @param {number} [args.limit] - Maximum number of changes to return
|
|
7017
|
-
* @
|
|
7092
|
+
* @param {boolean} [args.count=false] - If true, return count instead of changes (v0.36.0)
|
|
7093
|
+
* @returns {Promise<Array|number>} Array of changes or count if args.count is true
|
|
7018
7094
|
*/
|
|
7019
7095
|
async log(args = {}) {
|
|
7020
7096
|
await graph.load();
|
|
@@ -7023,6 +7099,11 @@ async function createJJ(options) {
|
|
|
7023
7099
|
const revsetExpr = args.revset || 'all()';
|
|
7024
7100
|
const changeIds = await revset.evaluate(revsetExpr);
|
|
7025
7101
|
|
|
7102
|
+
// If count mode, return count early (v0.36.0 feature)
|
|
7103
|
+
if (args.count) {
|
|
7104
|
+
return changeIds.length;
|
|
7105
|
+
}
|
|
7106
|
+
|
|
7026
7107
|
let changes = [];
|
|
7027
7108
|
for (const changeId of changeIds) {
|
|
7028
7109
|
const change = await graph.getChange(changeId);
|
|
@@ -8043,9 +8124,18 @@ async function createJJ(options) {
|
|
|
8043
8124
|
* @returns {Promise<Object>} The new backout change including changeId, description, and backedOut field
|
|
8044
8125
|
*/
|
|
8045
8126
|
async backout(args) {
|
|
8127
|
+
// Normalize: accept common parameter aliases
|
|
8128
|
+
if (args && args.change && !args.revision) {
|
|
8129
|
+
args = { ...args, revision: args.change };
|
|
8130
|
+
} else if (args && args.changeId && !args.revision) {
|
|
8131
|
+
args = { ...args, revision: args.changeId };
|
|
8132
|
+
} else if (args && args.target && !args.revision) {
|
|
8133
|
+
args = { ...args, revision: args.target };
|
|
8134
|
+
}
|
|
8135
|
+
|
|
8046
8136
|
if (!args || !args.revision) {
|
|
8047
8137
|
throw new JJError('INVALID_ARGUMENT', 'Missing revision argument', {
|
|
8048
|
-
suggestion: 'Provide the change ID to back out: { revision: "abc123..." }',
|
|
8138
|
+
suggestion: 'Provide the change ID to back out: { revision: "abc123..." } (or use change/changeId/target)',
|
|
8049
8139
|
});
|
|
8050
8140
|
}
|
|
8051
8141
|
|
|
@@ -9080,6 +9170,15 @@ async function createJJ(options) {
|
|
|
9080
9170
|
* @param {string[]} [args.paths] - Specific paths to include in first split (full implementation planned for future)
|
|
9081
9171
|
*/
|
|
9082
9172
|
async split(args) {
|
|
9173
|
+
// Normalize: accept common parameter aliases
|
|
9174
|
+
if (args && args.change && !args.changeId) {
|
|
9175
|
+
args = { ...args, changeId: args.change };
|
|
9176
|
+
} else if (args && args.revision && !args.changeId) {
|
|
9177
|
+
args = { ...args, changeId: args.revision };
|
|
9178
|
+
} else if (args && args.target && !args.changeId) {
|
|
9179
|
+
args = { ...args, changeId: args.target };
|
|
9180
|
+
}
|
|
9181
|
+
|
|
9083
9182
|
// Check for interactive mode (not supported)
|
|
9084
9183
|
if (args.interactive) {
|
|
9085
9184
|
throw new JJError(
|
|
@@ -11321,21 +11420,13 @@ async function createJJ(options) {
|
|
|
11321
11420
|
});
|
|
11322
11421
|
}
|
|
11323
11422
|
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
const parts = key.split('.');
|
|
11328
|
-
let value = userConfig.config;
|
|
11329
|
-
|
|
11330
|
-
for (const part of parts) {
|
|
11331
|
-
if (value && typeof value === 'object') {
|
|
11332
|
-
value = value[part];
|
|
11333
|
-
} else {
|
|
11334
|
-
return null;
|
|
11335
|
-
}
|
|
11423
|
+
// Ensure config is loaded at least once
|
|
11424
|
+
if (!userConfig.config) {
|
|
11425
|
+
await userConfig.load();
|
|
11336
11426
|
}
|
|
11337
11427
|
|
|
11338
|
-
|
|
11428
|
+
// Use userConfig.get() to preserve programmatic config
|
|
11429
|
+
return userConfig.get(key);
|
|
11339
11430
|
},
|
|
11340
11431
|
|
|
11341
11432
|
/**
|
|
@@ -11384,6 +11475,87 @@ async function createJJ(options) {
|
|
|
11384
11475
|
await userConfig.load();
|
|
11385
11476
|
return userConfig.config;
|
|
11386
11477
|
},
|
|
11478
|
+
|
|
11479
|
+
/**
|
|
11480
|
+
* Reload configuration (including workspace-specific config)
|
|
11481
|
+
* v0.35.0: Loads workspace-config.json if present
|
|
11482
|
+
* v0.36.0: Accepts optional config object for programmatic configuration
|
|
11483
|
+
*
|
|
11484
|
+
* @param {Object} [opts] - Optional config options
|
|
11485
|
+
* @param {Object} [opts.workspace] - Workspace-specific config to merge (highest priority)
|
|
11486
|
+
* @param {Object} [opts.override] - Config object to merge over loaded config
|
|
11487
|
+
* @returns {Promise<void>}
|
|
11488
|
+
*
|
|
11489
|
+
* @example
|
|
11490
|
+
* // Load from files only
|
|
11491
|
+
* await repo.config.load();
|
|
11492
|
+
*
|
|
11493
|
+
* @example
|
|
11494
|
+
* // Programmatic workspace config (no file I/O needed)
|
|
11495
|
+
* await repo.config.load({
|
|
11496
|
+
* workspace: { user: { email: 'test@example.com' } }
|
|
11497
|
+
* });
|
|
11498
|
+
*
|
|
11499
|
+
* @example
|
|
11500
|
+
* // Override global config programmatically
|
|
11501
|
+
* await repo.config.load({
|
|
11502
|
+
* override: { test: { value: 'override' } }
|
|
11503
|
+
* });
|
|
11504
|
+
*/
|
|
11505
|
+
async load(opts) {
|
|
11506
|
+
await userConfig.load(opts);
|
|
11507
|
+
},
|
|
11508
|
+
},
|
|
11509
|
+
|
|
11510
|
+
/**
|
|
11511
|
+
* Template helper functions (v0.36.0)
|
|
11512
|
+
*
|
|
11513
|
+
* Utilities for displaying and formatting JJ data
|
|
11514
|
+
*/
|
|
11515
|
+
template: {
|
|
11516
|
+
/**
|
|
11517
|
+
* Get files modified in a change
|
|
11518
|
+
*
|
|
11519
|
+
* @param {string} changeId - Change ID
|
|
11520
|
+
* @returns {Promise<string[]>} Array of file paths
|
|
11521
|
+
*/
|
|
11522
|
+
async files(changeId) {
|
|
11523
|
+
await graph.load();
|
|
11524
|
+
const change = await graph.getChange(changeId);
|
|
11525
|
+
if (!change || !change.fileSnapshot) {
|
|
11526
|
+
return [];
|
|
11527
|
+
}
|
|
11528
|
+
return Object.keys(change.fileSnapshot);
|
|
11529
|
+
},
|
|
11530
|
+
|
|
11531
|
+
/**
|
|
11532
|
+
* Join array elements with a separator
|
|
11533
|
+
*
|
|
11534
|
+
* @param {string[]} array - Array of strings
|
|
11535
|
+
* @param {string} separator - Separator string
|
|
11536
|
+
* @returns {string} Joined string
|
|
11537
|
+
*/
|
|
11538
|
+
join(array, separator) {
|
|
11539
|
+
if (!Array.isArray(array)) {
|
|
11540
|
+
return '';
|
|
11541
|
+
}
|
|
11542
|
+
return array.join(separator);
|
|
11543
|
+
},
|
|
11544
|
+
|
|
11545
|
+
/**
|
|
11546
|
+
* Format path for display
|
|
11547
|
+
*
|
|
11548
|
+
* @param {string} path - File path
|
|
11549
|
+
* @returns {string} Formatted path
|
|
11550
|
+
*/
|
|
11551
|
+
format_path(path) {
|
|
11552
|
+
if (!path) {
|
|
11553
|
+
return '';
|
|
11554
|
+
}
|
|
11555
|
+
// Simple formatting: just return the path as-is for now
|
|
11556
|
+
// In real JJ, this might apply colors, truncation, etc.
|
|
11557
|
+
return path;
|
|
11558
|
+
},
|
|
11387
11559
|
},
|
|
11388
11560
|
|
|
11389
11561
|
/**
|