@ibgib/vcs-gib 0.0.6 → 0.0.17

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.
Files changed (102) hide show
  1. package/README.md +24 -2
  2. package/VCS_CLI_USER_GUIDE.md +2 -2
  3. package/dist/AUTO-GENERATED-version.d.mts +1 -1
  4. package/dist/AUTO-GENERATED-version.d.mts.map +1 -1
  5. package/dist/AUTO-GENERATED-version.mjs +1 -1
  6. package/dist/AUTO-GENERATED-version.mjs.map +1 -1
  7. package/dist/bootstrap/node-bootstrap.d.mts.map +1 -1
  8. package/dist/bootstrap/node-bootstrap.mjs +6 -0
  9. package/dist/bootstrap/node-bootstrap.mjs.map +1 -1
  10. package/dist/cli/cli-arg-parser.d.mts +3 -0
  11. package/dist/cli/cli-arg-parser.d.mts.map +1 -1
  12. package/dist/cli/cli-arg-parser.mjs +13 -1
  13. package/dist/cli/cli-arg-parser.mjs.map +1 -1
  14. package/dist/cli/cli-constants.d.mts +5 -0
  15. package/dist/cli/cli-constants.d.mts.map +1 -1
  16. package/dist/cli/cli-constants.mjs +23 -0
  17. package/dist/cli/cli-constants.mjs.map +1 -1
  18. package/dist/cli/cli-e2e.respec.node.mjs +1 -1
  19. package/dist/cli/cli-e2e.respec.node.mjs.map +1 -1
  20. package/dist/cli/commands/add-cmd.d.mts.map +1 -1
  21. package/dist/cli/commands/add-cmd.mjs +33 -28
  22. package/dist/cli/commands/add-cmd.mjs.map +1 -1
  23. package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs +70 -0
  24. package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs.map +1 -1
  25. package/dist/cli/commands/branch-cmd.d.mts +2 -1
  26. package/dist/cli/commands/branch-cmd.d.mts.map +1 -1
  27. package/dist/cli/commands/branch-cmd.mjs +57 -6
  28. package/dist/cli/commands/branch-cmd.mjs.map +1 -1
  29. package/dist/cli/commands/checkout-cmd.d.mts.map +1 -1
  30. package/dist/cli/commands/checkout-cmd.mjs +23 -11
  31. package/dist/cli/commands/checkout-cmd.mjs.map +1 -1
  32. package/dist/cli/commands/commit-cmd.d.mts.map +1 -1
  33. package/dist/cli/commands/commit-cmd.mjs +24 -21
  34. package/dist/cli/commands/commit-cmd.mjs.map +1 -1
  35. package/dist/cli/commands/reset-cmd.d.mts +21 -0
  36. package/dist/cli/commands/reset-cmd.d.mts.map +1 -0
  37. package/dist/cli/commands/reset-cmd.mjs +93 -0
  38. package/dist/cli/commands/reset-cmd.mjs.map +1 -0
  39. package/dist/cli/commands/reset-restore-cmd.respec.node.d.mts +7 -0
  40. package/dist/cli/commands/reset-restore-cmd.respec.node.d.mts.map +1 -0
  41. package/dist/cli/commands/reset-restore-cmd.respec.node.mjs +185 -0
  42. package/dist/cli/commands/reset-restore-cmd.respec.node.mjs.map +1 -0
  43. package/dist/cli/commands/restore-cmd.d.mts +22 -0
  44. package/dist/cli/commands/restore-cmd.d.mts.map +1 -0
  45. package/dist/cli/commands/restore-cmd.mjs +115 -0
  46. package/dist/cli/commands/restore-cmd.mjs.map +1 -0
  47. package/dist/cli/commands/status-add-cmd.respec.node.mjs +24 -0
  48. package/dist/cli/commands/status-add-cmd.respec.node.mjs.map +1 -1
  49. package/dist/cli/vcs-cli-main.d.mts.map +1 -1
  50. package/dist/cli/vcs-cli-main.mjs +29 -3
  51. package/dist/cli/vcs-cli-main.mjs.map +1 -1
  52. package/dist/engine/branch/branch-graph-helper.d.mts +12 -3
  53. package/dist/engine/branch/branch-graph-helper.d.mts.map +1 -1
  54. package/dist/engine/branch/branch-graph-helper.mjs +50 -18
  55. package/dist/engine/branch/branch-graph-helper.mjs.map +1 -1
  56. package/dist/engine/branch/branch-space-helper.d.mts +1 -0
  57. package/dist/engine/branch/branch-space-helper.d.mts.map +1 -1
  58. package/dist/engine/branch/branch-space-helper.mjs +1 -0
  59. package/dist/engine/branch/branch-space-helper.mjs.map +1 -1
  60. package/dist/engine/branch/branch-space.respec.node.mjs +11 -6
  61. package/dist/engine/branch/branch-space.respec.node.mjs.map +1 -1
  62. package/dist/engine/index/index-helper.d.mts +35 -0
  63. package/dist/engine/index/index-helper.d.mts.map +1 -1
  64. package/dist/engine/index/index-helper.mjs +241 -0
  65. package/dist/engine/index/index-helper.mjs.map +1 -1
  66. package/dist/engine/item/item-helper.d.mts +29 -3
  67. package/dist/engine/item/item-helper.d.mts.map +1 -1
  68. package/dist/engine/item/item-helper.mjs +234 -56
  69. package/dist/engine/item/item-helper.mjs.map +1 -1
  70. package/dist/engine/item/item.respec.node.d.mts +7 -0
  71. package/dist/engine/item/item.respec.node.d.mts.map +1 -0
  72. package/dist/engine/item/item.respec.node.mjs +75 -0
  73. package/dist/engine/item/item.respec.node.mjs.map +1 -0
  74. package/package.json +3 -3
  75. package/src/AUTO-GENERATED-version.mts +1 -1
  76. package/src/bootstrap/node-bootstrap.mts +2 -0
  77. package/src/cli/cli-arg-parser.mts +15 -0
  78. package/src/cli/cli-constants.mts +26 -0
  79. package/src/cli/cli-e2e.respec.node.mts +2 -2
  80. package/src/cli/commands/add-cmd.mts +36 -26
  81. package/src/cli/commands/branch-checkout-cmd.respec.node.mts +79 -0
  82. package/src/cli/commands/branch-cmd.mts +75 -6
  83. package/src/cli/commands/checkout-cmd.mts +25 -14
  84. package/src/cli/commands/commit-cmd.mts +30 -23
  85. package/src/cli/commands/reset-cmd.mts +128 -0
  86. package/src/cli/commands/reset-restore-cmd.respec.node.mts +222 -0
  87. package/src/cli/commands/restore-cmd.mts +155 -0
  88. package/src/cli/commands/status-add-cmd.respec.node.mts +27 -0
  89. package/src/cli/vcs-cli-main.mts +29 -3
  90. package/src/engine/branch/branch-graph-helper.mts +66 -18
  91. package/src/engine/branch/branch-space-helper.mts +6 -0
  92. package/src/engine/branch/branch-space.respec.node.mts +12 -6
  93. package/src/engine/index/index-helper.mts +324 -0
  94. package/src/engine/item/item-helper.mts +284 -58
  95. package/src/engine/item/item.respec.node.mts +92 -0
  96. package/tsconfig.tsbuildinfo +1 -1
  97. package/test_runs/001-hello-node/20260728_090100_0001-flow-core-main.md +0 -39
  98. package/test_runs/001-hello-node/20260728_090200_0003-flow-branching-worktree.md +0 -54
  99. package/test_runs/001-hello-node/20260728_090300_0002-flow-selective-add.md +0 -45
  100. package/test_runs/001-hello-node/20260728_142100_0007-flow-edge-cases.md +0 -44
  101. package/test_runs/001-hello-node/20260728_142400_0005-flow-hardcoded-ignores.md +0 -36
  102. package/tools/pack-for-test.mjs +0 -73
@@ -22,11 +22,13 @@ import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
22
22
  import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
23
23
  import { getB2tFSItemIb } from '../../engine/item/item-atom-helper.mjs';
24
24
  import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
25
- import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
25
+ import { getIbGibWithMetaspaceFallback, createB2tFSItemIbGib, evolveB2tFSItemIbGib } from '../../engine/item/item-helper.mjs';
26
26
  import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
27
27
  import { isPathFiltered, parseFilterFileContent, globToRegExp } from '../../engine/filter/glob-helper.mjs';
28
28
  import { getB2tFSIndexIbGib, updateB2tFSIndexIbGib } from '../../engine/index/index-helper.mjs';
29
29
  import { B2tFSStagedItemRecord } from '../../engine/index/index-types.mjs';
30
+ import { TransformResult } from '@ibgib/ts-gib/dist/types.mjs';
31
+ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
30
32
  import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
31
33
 
32
34
  const lcFile = `[add-cmd]`;
@@ -97,7 +99,7 @@ export async function executeAddCmd({
97
99
  }
98
100
 
99
101
  // Walk active branch graph items to find committed files in snapshot
100
- const committedBranchItemsMap = new Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder' }>();
102
+ const committedBranchItemsMap = new Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder'; item?: B2tFSItemIbGib_V1 }>();
101
103
  const branchAddrs = [
102
104
  ...(indexIbGib?.rel8ns?.branch || []),
103
105
  ...(indexIbGib?.rel8ns?.branch_tjp || []),
@@ -182,7 +184,7 @@ export async function executeAddCmd({
182
184
  throw new Error(`No files matched glob pattern: ${relOrAbs} (E: e23d4c5f6a7b8c9d0e1f2a3b4c5d6e7f)`);
183
185
  }
184
186
  } else {
185
- const fullPath = path.resolve(activeWorktreeDir, relOrAbs);
187
+ const fullPath = path.resolve(targetDir, relOrAbs);
186
188
  let stat;
187
189
  try {
188
190
  stat = await fs.stat(fullPath);
@@ -191,6 +193,9 @@ export async function executeAddCmd({
191
193
  }
192
194
 
193
195
  const relPath = path.relative(activeWorktreeDir, fullPath).replace(/\\/g, '/');
196
+ if (relPath.startsWith('..') || path.isAbsolute(relPath)) {
197
+ throw new Error(`Path '${relOrAbs}' is outside of active worktree repository (E: 7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f)`);
198
+ }
194
199
 
195
200
  if (isPathFiltered({ relativePath: relPath, isDirectory: stat.isDirectory(), filterPatterns })) {
196
201
  continue;
@@ -243,31 +248,36 @@ export async function executeAddCmd({
243
248
  await putInSpace({ ibGib: binIbGib, space: activeSpace });
244
249
  await metaspace.registerNewIbGib({ ibGib: binIbGib, space: activeSpace });
245
250
 
246
- // 2. Create B2tFSItemIbGib_V1 candidate graph node
251
+ // 2. Create or Evolve B2tFSItemIbGib_V1 candidate graph node
247
252
  const fileName = path.basename(absFilePath);
248
- const fileNameSansExt = path.parse(fileName).name;
249
- const fileExt = path.parse(fileName).ext.replace(/^\./, '');
250
253
  const nameHash = crypto.createHash('sha256').update(fileName).digest('hex');
251
254
 
252
- const itemRes = await Factory_V1.firstGen({
253
- parentIbGib: ROOT,
254
- ib: getB2tFSItemIb('file', fileName, nameHash),
255
- data: {
256
- name: fileName,
257
- nameHash,
255
+ let itemRes: TransformResult<IbGib_V1>;
256
+ if (committedItem && committedItem.item) {
257
+ itemRes = await evolveB2tFSItemIbGib({
258
+ existingItem: committedItem.item,
259
+ dataToUpdate: {
260
+ fileHash,
261
+ },
262
+ rel8nsToUpdate: {
263
+ bin: [binAddr],
264
+ },
265
+ });
266
+ } else {
267
+ itemRes = await createB2tFSItemIbGib({
258
268
  fsType: 'file',
259
- fileNameSansExt,
260
- fileExt,
261
- fileHash,
262
- fileEncoding: 'utf8',
263
- diffTransactionId: await getUUID(),
264
- uuid: await getUUID(),
265
- classname: 'B2tFSItemIbGib_V1',
266
- },
267
- rel8ns: {
268
- bin: [binAddr],
269
- },
270
- });
269
+ name: fileName,
270
+ data: {
271
+ nameHash,
272
+ fileHash,
273
+ fileEncoding: 'utf8',
274
+ },
275
+ rel8ns: {
276
+ bin: [binAddr],
277
+ },
278
+ });
279
+ }
280
+
271
281
  const itemIbGib = itemRes.newIbGib as B2tFSItemIbGib_V1;
272
282
  const itemAddr = getIbGibAddr({ ibGib: itemIbGib });
273
283
  await persistTransformResult({ resTransform: itemRes, space: activeSpace });
@@ -332,13 +342,13 @@ async function addCmd_walkBranchGraph({
332
342
  space: any;
333
343
  metaspace?: any;
334
344
  currentRelPath: string;
335
- outMap: Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder' }>;
345
+ outMap: Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder'; item?: B2tFSItemIbGib_V1 }>;
336
346
  }): Promise<void> {
337
347
  if (!item || !item.data) { return; }
338
348
 
339
349
  const { fsType, fileHash } = item.data;
340
350
  if (currentRelPath) {
341
- outMap.set(currentRelPath, { relPath: currentRelPath, fileHash, fsType });
351
+ outMap.set(currentRelPath, { relPath: currentRelPath, fileHash, fsType, item });
342
352
  }
343
353
 
344
354
  if (fsType === 'folder') {
@@ -319,4 +319,83 @@ await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () =
319
319
  await fs.rm(testDir, { recursive: true, force: true });
320
320
  }
321
321
  });
322
+
323
+ await ifWe(lc, `branches single file-root item and projects into file-branch worktree on checkout`, async () => {
324
+ const testDir = path.join(process.cwd(), '.test_branch_checkout_file_root');
325
+ const vcsFolderName = '.vcsgib_test_bc_file';
326
+ try {
327
+ await executeInitCmd({
328
+ targetDir: testDir,
329
+ vcsFolderName,
330
+ quiet: true,
331
+ });
332
+
333
+ // Create nested file structure src/utils/math.js and commit on main
334
+ const mathRelPath = 'src/utils/math.js';
335
+ await fs.mkdir(path.join(testDir, 'src', 'utils'), { recursive: true });
336
+ await fs.writeFile(path.join(testDir, mathRelPath), 'export function add(a, b) { return a + b; }', 'utf8');
337
+
338
+ await executeAddCmd({
339
+ paths: [mathRelPath],
340
+ targetDir: testDir,
341
+ vcsFolderName,
342
+ quiet: true,
343
+ });
344
+
345
+ await executeCommitCmd({
346
+ message: 'Add math.js',
347
+ targetDir: testDir,
348
+ vcsFolderName,
349
+ quiet: true,
350
+ });
351
+
352
+ // Execute branch command for single file math-opt targeting src/utils/math.js
353
+ const branchRes = await executeBranchCmd({
354
+ branchName: 'math-opt',
355
+ itemPath: mathRelPath,
356
+ targetDir: testDir,
357
+ vcsFolderName,
358
+ quiet: true,
359
+ });
360
+
361
+ iReckon(lc, branchRes.createdBranchName).willEqual('math-opt');
362
+
363
+ // Attempt branching uncommitted/non-existent item
364
+ let uncommittedErr = false;
365
+ try {
366
+ await executeBranchCmd({
367
+ branchName: 'invalid-file-branch',
368
+ itemPath: 'src/utils/non-existent.js',
369
+ targetDir: testDir,
370
+ vcsFolderName,
371
+ quiet: true,
372
+ });
373
+ } catch (err: any) {
374
+ uncommittedErr = true;
375
+ iReckon(lc, err.message).includes("not found in active branch snapshot");
376
+ }
377
+ iReckon(lc, uncommittedErr).isGonnaBeTrue();
378
+
379
+ // Execute checkout command for math-opt
380
+ const checkoutRes = await executeCheckoutCmd({
381
+ branchName: 'math-opt',
382
+ targetDir: testDir,
383
+ vcsFolderName,
384
+ quiet: true,
385
+ });
386
+
387
+ const expectedWorktree = path.join(testDir, 'b', 'math-opt');
388
+ iReckon(lc, checkoutRes.worktreePath).willEqual(expectedWorktree);
389
+
390
+ // Verify single file projected at expectedWorktree/math.js (my-repo/b/math-opt/math.js)
391
+ const projectedFilePath = path.join(expectedWorktree, 'math.js');
392
+ const fileExists = await fs.stat(projectedFilePath).then(s => s.isFile()).catch(() => false);
393
+ iReckon(lc, fileExists).isGonnaBeTrue();
394
+
395
+ const fileContent = await fs.readFile(projectedFilePath, 'utf8');
396
+ iReckon(lc, fileContent).willEqual('export function add(a, b) { return a + b; }');
397
+ } finally {
398
+ await fs.rm(testDir, { recursive: true, force: true });
399
+ }
400
+ });
322
401
  });
@@ -18,7 +18,7 @@ import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
18
18
  import { createB2tFSBranchIbGib } from '../../engine/branch/branch-helper.mjs';
19
19
  import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
20
20
  import { populateBranchSpaceFromGraph } from '../../engine/branch/branch-graph-helper.mjs';
21
- import { B2tFSItemIbGib_V1 } from '../../engine/item/item-types.mjs';
21
+ import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
22
22
  import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
23
23
  import { getB2tFSIndexIbGib, updateB2tFSIndexIbGib, getSpaceQualifiedIbGibAddr } from '../../engine/index/index-helper.mjs';
24
24
  import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
@@ -28,6 +28,7 @@ const logalot = GLOBAL_LOG_A_LOT;
28
28
 
29
29
  export interface BranchCmdOpts {
30
30
  branchName?: string;
31
+ itemPath?: string;
31
32
  targetDir?: string;
32
33
  vcsFolderName?: string;
33
34
  quiet?: boolean;
@@ -48,6 +49,7 @@ export interface BranchCmdResult {
48
49
 
49
50
  export async function executeBranchCmd({
50
51
  branchName,
52
+ itemPath,
51
53
  targetDir = process.cwd(),
52
54
  vcsFolderName = '.vcsgib',
53
55
  quiet = false,
@@ -152,7 +154,7 @@ export async function executeBranchCmd({
152
154
  const rootItemAddr = activeBranchIbGib?.rel8ns?.b2tfs_item?.at(0);
153
155
 
154
156
  if (!rootItemAddr) {
155
- throw new Error(`Cannot find active branch root item to fork branch from (E: 8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d)`);
157
+ throw new Error(`Cannot find active branch root item to fork branch from (E: 8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c)`);
156
158
  }
157
159
 
158
160
  const rootItem = await getIbGibWithMetaspaceFallback({
@@ -165,6 +167,25 @@ export async function executeBranchCmd({
165
167
  throw new Error(`Root item node for address '${rootItemAddr}' not found in localSpace (E: 9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e)`);
166
168
  }
167
169
 
170
+ let targetRootItem: B2tFSItemIbGib_V1 = rootItem;
171
+ if (itemPath && itemPath.trim().length > 0) {
172
+ const normalizedItemPath = itemPath.trim().replace(/\\/g, '/').replace(/^\.\//, '');
173
+ const committedBranchItemsMap = new Map<string, B2tFSItemIbGib_V1>();
174
+ await executeBranchCmd_walkBranchGraphForItems({
175
+ item: rootItem,
176
+ space: activeBranchSpace,
177
+ metaspace,
178
+ currentRelPath: '',
179
+ outMap: committedBranchItemsMap,
180
+ });
181
+
182
+ const targetItemNode = committedBranchItemsMap.get(normalizedItemPath);
183
+ if (!targetItemNode) {
184
+ throw new Error(`Item '${itemPath}' not found in active branch snapshot (E: 8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c)`);
185
+ }
186
+ targetRootItem = targetItemNode;
187
+ }
188
+
168
189
  // Instantiate new branch space b2s_<newBranch>
169
190
  const { space: newBranchSpace, spaceId: newBranchSpaceId } = await getOrCreateBranchSpace({
170
191
  metaspace,
@@ -172,18 +193,18 @@ export async function executeBranchCmd({
172
193
  branchName: trimmedBranchName,
173
194
  });
174
195
 
175
- // Populate new branch space from live dependency graph of rootItem
196
+ // Populate new branch space from live dependency graph of targetRootItem
176
197
  await populateBranchSpaceFromGraph({
177
198
  sourceSpace: activeBranchSpace,
178
199
  targetBranchSpace: newBranchSpace,
179
- rootItemIbGib: rootItem,
200
+ rootItemIbGib: targetRootItem,
180
201
  metaspace,
181
202
  });
182
203
 
183
204
  // Create new B2tFSBranchIbGib_V1 branch node inside newBranchSpace
184
205
  const newBranchIbGib = await createB2tFSBranchIbGib({
185
206
  branchName: trimmedBranchName,
186
- rootItem,
207
+ rootItem: targetRootItem,
187
208
  space: newBranchSpace,
188
209
  metaspace,
189
210
  });
@@ -210,7 +231,7 @@ export async function executeBranchCmd({
210
231
  });
211
232
 
212
233
  if (!quiet) {
213
- console.log(`Created branch '${trimmedBranchName}' (I: 0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f)`);
234
+ console.log(`Created branch '${trimmedBranchName}' (I: 0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5e)`);
214
235
  }
215
236
 
216
237
  // Return updated branch list
@@ -241,3 +262,51 @@ export async function executeBranchCmd({
241
262
  }
242
263
  }
243
264
  }
265
+
266
+ // #region executeBranchCmd_walkBranchGraphForItems
267
+ async function executeBranchCmd_walkBranchGraphForItems({
268
+ item,
269
+ space,
270
+ metaspace,
271
+ currentRelPath,
272
+ outMap,
273
+ }: {
274
+ item: B2tFSItemIbGib_V1;
275
+ space: any;
276
+ metaspace?: any;
277
+ currentRelPath: string;
278
+ outMap: Map<string, B2tFSItemIbGib_V1>;
279
+ }): Promise<void> {
280
+ if (!item || !item.data) { return; }
281
+
282
+ const { fsType } = item.data;
283
+
284
+ if (currentRelPath) {
285
+ outMap.set(currentRelPath, item);
286
+ }
287
+
288
+ if (fsType === 'folder') {
289
+ const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
290
+ for (const childAddr of childAddrs) {
291
+ const childItem = await getIbGibWithMetaspaceFallback({
292
+ addr: childAddr,
293
+ space,
294
+ metaspace,
295
+ }) as B2tFSItemIbGib_V1;
296
+
297
+ if (childItem && childItem.data) {
298
+ const childRelPath = currentRelPath
299
+ ? `${currentRelPath}/${childItem.data.name}`
300
+ : childItem.data.name;
301
+ await executeBranchCmd_walkBranchGraphForItems({
302
+ item: childItem,
303
+ space,
304
+ metaspace,
305
+ currentRelPath: childRelPath,
306
+ outMap,
307
+ });
308
+ }
309
+ }
310
+ }
311
+ }
312
+ // #endregion executeBranchCmd_walkBranchGraphForItems
@@ -49,7 +49,7 @@ export async function executeCheckoutCmd({
49
49
  const lc = `${lcFile}[${executeCheckoutCmd.name}]`;
50
50
  try {
51
51
  if (logalot) {
52
- console.log(`${lc} starting... (I: 3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c)`);
52
+ console.log(`${lc} starting... (I: 3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8e)`);
53
53
  }
54
54
 
55
55
  const trimmedBranchName = branchName?.trim();
@@ -68,7 +68,7 @@ export async function executeCheckoutCmd({
68
68
  try {
69
69
  await fs.access(vcsPath);
70
70
  } catch {
71
- throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e)`);
71
+ throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0e)`);
72
72
  }
73
73
 
74
74
  // 1. Fetch authoritative repository index from localSpace
@@ -152,7 +152,9 @@ export async function executeCheckoutCmd({
152
152
 
153
153
  await fs.mkdir(worktreePath, { recursive: true });
154
154
 
155
- // Retrieve root item and project child items via writeB2tFSItemToFS
155
+ // Retrieve root item and project branch snapshot onto worktree subfolder:
156
+ // If rootItem is a single file, write it directly to worktreePath (my-repo/b/<branchName>/<fileName>).
157
+ // If rootItem is a folder, write child items into worktreePath.
156
158
  const rootItem = await getIbGibWithMetaspaceFallback({
157
159
  addr: rootItemAddr,
158
160
  space: targetBranchSpace,
@@ -160,21 +162,30 @@ export async function executeCheckoutCmd({
160
162
  }) as B2tFSItemIbGib_V1;
161
163
 
162
164
  if (rootItem) {
163
- const childAddrs = rootItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
164
- for (const childAddr of childAddrs) {
165
- const childItem = await getIbGibWithMetaspaceFallback({
166
- addr: childAddr,
165
+ if (rootItem.data?.fsType === 'file') {
166
+ await writeB2tFSItemToFS({
167
+ item: rootItem,
168
+ destPath: worktreePath,
167
169
  space: targetBranchSpace,
168
170
  metaspace,
169
- }) as B2tFSItemIbGib_V1;
170
-
171
- if (childItem) {
172
- await writeB2tFSItemToFS({
173
- item: childItem,
174
- destPath: worktreePath,
171
+ });
172
+ } else {
173
+ const childAddrs = rootItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
174
+ for (const childAddr of childAddrs) {
175
+ const childItem = await getIbGibWithMetaspaceFallback({
176
+ addr: childAddr,
175
177
  space: targetBranchSpace,
176
178
  metaspace,
177
- });
179
+ }) as B2tFSItemIbGib_V1;
180
+
181
+ if (childItem) {
182
+ await writeB2tFSItemToFS({
183
+ item: childItem,
184
+ destPath: worktreePath,
185
+ space: targetBranchSpace,
186
+ metaspace,
187
+ });
188
+ }
178
189
  }
179
190
  }
180
191
  }
@@ -24,11 +24,13 @@ import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
24
24
  import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
25
25
  import { getB2tFSItemIb } from '../../engine/item/item-atom-helper.mjs';
26
26
  import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
27
- import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
27
+ import { getIbGibWithMetaspaceFallback, createB2tFSItemIbGib, evolveB2tFSItemIbGib } from '../../engine/item/item-helper.mjs';
28
28
  import { getB2tFSBranchSpaceName } from '../../engine/branch/branch-helper.mjs';
29
29
  import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
30
30
  import { getB2tFSIndexIbGib, updateB2tFSIndexIbGib, getSpaceQualifiedIbGibAddr } from '../../engine/index/index-helper.mjs';
31
31
  import { B2tFSStagedItemRecord } from '../../engine/index/index-types.mjs';
32
+ import { TransformResult } from '@ibgib/ts-gib/dist/types.mjs';
33
+ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
32
34
  import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
33
35
 
34
36
  const lcFile = `[commit-cmd]`;
@@ -244,7 +246,7 @@ async function commitCmd_buildEvolvedRootItem({
244
246
  metaspace: any;
245
247
  repoName: string;
246
248
  }): Promise<{ newRootItem: B2tFSItemIbGib_V1; newRootAddr: string }> {
247
- const itemsByRelPath = new Map<string, { itemAddr: string; fsType: 'file' | 'folder' }>();
249
+ const itemsByRelPath = new Map<string, { itemAddr: string; fsType: 'file' | 'folder'; item?: B2tFSItemIbGib_V1 }>();
248
250
 
249
251
  if (currentRootAddr) {
250
252
  const currentRootItem = await getIbGibWithMetaspaceFallback({
@@ -328,27 +330,34 @@ async function commitCmd_buildEvolvedRootItem({
328
330
  }
329
331
 
330
332
  const folderName = folderRelPath ? path.basename(folderRelPath) : repoName;
331
- const diffTxId = await getUUID();
332
333
  const nameHash = crypto.createHash('sha256').update(folderName).digest('hex');
333
334
 
334
- const rel8ns: Record<string, string[]> = {};
335
- if (childAddrs.length > 0) {
336
- rel8ns[B2TFS_CHILD_ITEM_REL8N_NAME] = childAddrs;
337
- }
335
+ const existingFolderRec = itemsByRelPath.get(folderRelPath);
338
336
 
339
- const folderRes = await Factory_V1.firstGen({
340
- parentIbGib: ROOT,
341
- ib: getB2tFSItemIb('folder', folderName, nameHash),
342
- data: {
343
- name: folderName,
344
- nameHash,
337
+ let folderRes: TransformResult<IbGib_V1>;
338
+
339
+ if (existingFolderRec && existingFolderRec.item) {
340
+ const oldChildAddrs = existingFolderRec.item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
341
+ const toRemoveChildAddrs = oldChildAddrs.filter(a => !childAddrs.includes(a));
342
+
343
+ folderRes = await evolveB2tFSItemIbGib({
344
+ existingItem: existingFolderRec.item,
345
+ dataToUpdate: {
346
+ nameHash,
347
+ },
348
+ rel8nsToUpdate: childAddrs.length > 0 ? { [B2TFS_CHILD_ITEM_REL8N_NAME]: childAddrs } : undefined,
349
+ rel8nsToRemove: toRemoveChildAddrs.length > 0 ? { [B2TFS_CHILD_ITEM_REL8N_NAME]: toRemoveChildAddrs } : undefined,
350
+ });
351
+ } else {
352
+ folderRes = await createB2tFSItemIbGib({
345
353
  fsType: 'folder',
346
- diffTransactionId: diffTxId,
347
- uuid: await getUUID(),
348
- classname: 'B2tFSItemIbGib_V1',
349
- },
350
- rel8ns,
351
- });
354
+ name: folderName,
355
+ data: {
356
+ nameHash,
357
+ },
358
+ rel8ns: childAddrs.length > 0 ? { [B2TFS_CHILD_ITEM_REL8N_NAME]: childAddrs } : undefined,
359
+ });
360
+ }
352
361
 
353
362
  const folderItem = folderRes.newIbGib as B2tFSItemIbGib_V1;
354
363
  const folderAddr = getIbGibAddr({ ibGib: folderItem });
@@ -383,16 +392,14 @@ async function commitCmd_walkBranchGraph({
383
392
  space: any;
384
393
  metaspace?: any;
385
394
  currentRelPath: string;
386
- outMap: Map<string, { itemAddr: string; fsType: 'file' | 'folder' }>;
395
+ outMap: Map<string, { itemAddr: string; fsType: 'file' | 'folder'; item?: B2tFSItemIbGib_V1 }>;
387
396
  }): Promise<void> {
388
397
  if (!item || !item.data) { return; }
389
398
 
390
399
  const { fsType } = item.data;
391
400
  const itemAddr = getIbGibAddr({ ibGib: item });
392
401
 
393
- if (currentRelPath) {
394
- outMap.set(currentRelPath, { itemAddr, fsType });
395
- }
402
+ outMap.set(currentRelPath, { itemAddr, fsType, item });
396
403
 
397
404
  if (fsType === 'folder') {
398
405
  const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @module reset-cmd
3
+ *
4
+ * Command handler for `vcs reset [path...]`.
5
+ * Unstages specified files (or all files) from active branch index in localSpace.
6
+ */
7
+
8
+ import path from 'node:path';
9
+ import fs from 'node:fs/promises';
10
+
11
+ import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
12
+ import { promptForConfirm } from '@ibgib/helper-gib/dist/helpers/node-helper.mjs';
13
+ import { putInSpace } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
14
+
15
+ import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
16
+ import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
17
+ import { getB2tFSIndexIbGib, unstagePathsFromIndex } from '../../engine/index/index-helper.mjs';
18
+ import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
19
+
20
+ const lcFile = `[reset-cmd]`;
21
+ const logalot = GLOBAL_LOG_A_LOT;
22
+
23
+ export interface ResetCmdOpts {
24
+ paths?: string[];
25
+ targetDir?: string;
26
+ vcsFolderName?: string;
27
+ all?: boolean;
28
+ yes?: boolean;
29
+ force?: boolean;
30
+ quiet?: boolean;
31
+ }
32
+
33
+ export interface ResetCmdResult {
34
+ unstagedPaths: string[];
35
+ stagedCount: number;
36
+ }
37
+
38
+ export async function executeResetCmd({
39
+ paths = [],
40
+ targetDir = process.cwd(),
41
+ vcsFolderName = '.vcsgib',
42
+ all = false,
43
+ yes = false,
44
+ force = false,
45
+ quiet = false,
46
+ }: ResetCmdOpts = {}): Promise<ResetCmdResult> {
47
+ const lc = `${lcFile}[${executeResetCmd.name}]`;
48
+ try {
49
+ if (logalot) {
50
+ console.log(`${lc} starting... (I: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d)`);
51
+ }
52
+
53
+ const absTargetDir = path.resolve(targetDir);
54
+
55
+ const { metaspace, localSpace, vcsPath, repoRoot } = await bootstrapVcsMetaspace({
56
+ repoPath: absTargetDir,
57
+ vcsFolderName,
58
+ });
59
+
60
+ // Verify repo exists
61
+ try {
62
+ await fs.access(vcsPath);
63
+ } catch {
64
+ throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b)`);
65
+ }
66
+
67
+ // Retrieve authoritative B2tFSIndexIbGib_V1 from space
68
+ const indexIbGib = await getB2tFSIndexIbGib({ space: localSpace, vcsPath });
69
+ if (!indexIbGib) {
70
+ throw new Error(`Repository not initialized. Run 'vcs init' first. (E: 3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b)`);
71
+ }
72
+
73
+ const activeBranch = indexIbGib.data?.activeBranch || 'main';
74
+ const activeWorktreeDir = activeBranch === 'main' ? repoRoot : path.join(repoRoot, 'b', activeBranch);
75
+
76
+ const { space: activeSpace } = await getOrCreateBranchSpace({
77
+ metaspace,
78
+ vcsPath,
79
+ branchName: activeBranch,
80
+ });
81
+
82
+ const skipPrompt = yes || force || quiet || !process.stdin.isTTY;
83
+ if (!skipPrompt) {
84
+ const confirmed = await promptForConfirm({ msg: 'Unstage specified files from index?' });
85
+ if (!confirmed) {
86
+ if (!quiet) {
87
+ console.log('Operation cancelled.');
88
+ }
89
+ const stagedCount = Object.keys(indexIbGib.data?.stagedPaths || {}).length;
90
+ return { unstagedPaths: [], stagedCount };
91
+ }
92
+ }
93
+
94
+ const unstageRes = await unstagePathsFromIndex({
95
+ indexIbGib,
96
+ paths,
97
+ targetDir,
98
+ activeWorktreeDir,
99
+ vcsPath,
100
+ space: activeSpace,
101
+ metaspace,
102
+ all,
103
+ });
104
+
105
+ if (localSpace !== activeSpace) {
106
+ await putInSpace({ space: localSpace, ibGib: unstageRes.updatedIndex });
107
+ }
108
+
109
+ if (!quiet) {
110
+ for (const unstagedPath of unstageRes.unstagedPaths) {
111
+ console.log(`unstaged: ${unstagedPath}`);
112
+ }
113
+ }
114
+
115
+ const finalStagedCount = Object.keys(unstageRes.updatedIndex.data?.stagedPaths || {}).length;
116
+ return {
117
+ unstagedPaths: unstageRes.unstagedPaths,
118
+ stagedCount: finalStagedCount,
119
+ };
120
+ } catch (error) {
121
+ console.error(`${lc} ${extractErrorMsg(error)} (E: 4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b)`);
122
+ throw error;
123
+ } finally {
124
+ if (logalot) {
125
+ console.log(`${lc} complete. (I: 5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c)`);
126
+ }
127
+ }
128
+ }