@ibgib/vcs-gib 0.0.6 → 0.0.18

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 (185) 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 +9 -0
  9. package/dist/bootstrap/node-bootstrap.mjs.map +1 -1
  10. package/dist/bootstrap/{node-bootstrap.respec.node.d.mts → node-bootstrap.respec.d.mts} +1 -1
  11. package/dist/bootstrap/node-bootstrap.respec.d.mts.map +1 -0
  12. package/dist/bootstrap/{node-bootstrap.respec.node.mjs → node-bootstrap.respec.mjs} +1 -1
  13. package/dist/bootstrap/node-bootstrap.respec.mjs.map +1 -0
  14. package/dist/cli/cli-arg-parser.d.mts +3 -0
  15. package/dist/cli/cli-arg-parser.d.mts.map +1 -1
  16. package/dist/cli/cli-arg-parser.mjs +13 -1
  17. package/dist/cli/cli-arg-parser.mjs.map +1 -1
  18. package/dist/cli/{cli-arg-parser.respec.node.d.mts → cli-arg-parser.respec.d.mts} +1 -1
  19. package/dist/cli/cli-arg-parser.respec.d.mts.map +1 -0
  20. package/dist/cli/{cli-arg-parser.respec.node.mjs → cli-arg-parser.respec.mjs} +1 -1
  21. package/dist/cli/cli-arg-parser.respec.mjs.map +1 -0
  22. package/dist/cli/cli-constants.d.mts +6 -0
  23. package/dist/cli/cli-constants.d.mts.map +1 -1
  24. package/dist/cli/cli-constants.mjs +24 -0
  25. package/dist/cli/cli-constants.mjs.map +1 -1
  26. package/dist/cli/{cli-e2e.respec.node.d.mts → cli-e2e.respec.d.mts} +1 -1
  27. package/dist/cli/cli-e2e.respec.d.mts.map +1 -0
  28. package/dist/cli/{cli-e2e.respec.node.mjs → cli-e2e.respec.mjs} +2 -2
  29. package/dist/cli/cli-e2e.respec.mjs.map +1 -0
  30. package/dist/cli/commands/add-cmd.d.mts.map +1 -1
  31. package/dist/cli/commands/add-cmd.mjs +35 -30
  32. package/dist/cli/commands/add-cmd.mjs.map +1 -1
  33. package/dist/cli/commands/{branch-checkout-cmd.respec.node.d.mts → branch-checkout-cmd.respec.d.mts} +1 -1
  34. package/dist/cli/commands/branch-checkout-cmd.respec.d.mts.map +1 -0
  35. package/dist/cli/commands/{branch-checkout-cmd.respec.node.mjs → branch-checkout-cmd.respec.mjs} +172 -8
  36. package/dist/cli/commands/branch-checkout-cmd.respec.mjs.map +1 -0
  37. package/dist/cli/commands/branch-cmd.d.mts +2 -1
  38. package/dist/cli/commands/branch-cmd.d.mts.map +1 -1
  39. package/dist/cli/commands/branch-cmd.mjs +59 -8
  40. package/dist/cli/commands/branch-cmd.mjs.map +1 -1
  41. package/dist/cli/commands/checkout-cmd.d.mts.map +1 -1
  42. package/dist/cli/commands/checkout-cmd.mjs +38 -18
  43. package/dist/cli/commands/checkout-cmd.mjs.map +1 -1
  44. package/dist/cli/commands/commit-cmd.d.mts.map +1 -1
  45. package/dist/cli/commands/commit-cmd.mjs +26 -23
  46. package/dist/cli/commands/commit-cmd.mjs.map +1 -1
  47. package/dist/cli/commands/{commit-log-cmd.respec.node.d.mts → commit-log-cmd.respec.d.mts} +1 -1
  48. package/dist/cli/commands/commit-log-cmd.respec.d.mts.map +1 -0
  49. package/dist/cli/commands/{commit-log-cmd.respec.node.mjs → commit-log-cmd.respec.mjs} +1 -1
  50. package/dist/cli/commands/commit-log-cmd.respec.mjs.map +1 -0
  51. package/dist/cli/commands/{init-cmd.respec.node.d.mts → init-cmd.respec.d.mts} +1 -1
  52. package/dist/cli/commands/init-cmd.respec.d.mts.map +1 -0
  53. package/dist/cli/commands/{init-cmd.respec.node.mjs → init-cmd.respec.mjs} +1 -1
  54. package/dist/cli/commands/init-cmd.respec.mjs.map +1 -0
  55. package/dist/cli/commands/merge-cmd.d.mts +23 -0
  56. package/dist/cli/commands/merge-cmd.d.mts.map +1 -0
  57. package/dist/cli/commands/merge-cmd.mjs +224 -0
  58. package/dist/cli/commands/merge-cmd.mjs.map +1 -0
  59. package/dist/cli/commands/reset-cmd.d.mts +21 -0
  60. package/dist/cli/commands/reset-cmd.d.mts.map +1 -0
  61. package/dist/cli/commands/reset-cmd.mjs +93 -0
  62. package/dist/cli/commands/reset-cmd.mjs.map +1 -0
  63. package/dist/cli/commands/reset-restore-cmd.respec.d.mts +7 -0
  64. package/dist/cli/commands/reset-restore-cmd.respec.d.mts.map +1 -0
  65. package/dist/cli/commands/reset-restore-cmd.respec.mjs +185 -0
  66. package/dist/cli/commands/reset-restore-cmd.respec.mjs.map +1 -0
  67. package/dist/cli/commands/restore-cmd.d.mts +22 -0
  68. package/dist/cli/commands/restore-cmd.d.mts.map +1 -0
  69. package/dist/cli/commands/restore-cmd.mjs +115 -0
  70. package/dist/cli/commands/restore-cmd.mjs.map +1 -0
  71. package/dist/cli/commands/{status-add-cmd.respec.node.d.mts → status-add-cmd.respec.d.mts} +1 -1
  72. package/dist/cli/commands/status-add-cmd.respec.d.mts.map +1 -0
  73. package/dist/cli/commands/{status-add-cmd.respec.node.mjs → status-add-cmd.respec.mjs} +25 -1
  74. package/dist/cli/commands/status-add-cmd.respec.mjs.map +1 -0
  75. package/dist/cli/vcs-cli-main.d.mts.map +1 -1
  76. package/dist/cli/vcs-cli-main.mjs +36 -3
  77. package/dist/cli/vcs-cli-main.mjs.map +1 -1
  78. package/dist/engine/branch/branch-graph-helper.d.mts +12 -3
  79. package/dist/engine/branch/branch-graph-helper.d.mts.map +1 -1
  80. package/dist/engine/branch/branch-graph-helper.mjs +50 -18
  81. package/dist/engine/branch/branch-graph-helper.mjs.map +1 -1
  82. package/dist/engine/branch/branch-space-helper.d.mts +1 -0
  83. package/dist/engine/branch/branch-space-helper.d.mts.map +1 -1
  84. package/dist/engine/branch/branch-space-helper.mjs +1 -0
  85. package/dist/engine/branch/branch-space-helper.mjs.map +1 -1
  86. package/dist/engine/branch/{branch-space.respec.node.d.mts → branch-space.respec.d.mts} +1 -1
  87. package/dist/engine/branch/branch-space.respec.d.mts.map +1 -0
  88. package/dist/engine/branch/{branch-space.respec.node.mjs → branch-space.respec.mjs} +12 -7
  89. package/dist/engine/branch/branch-space.respec.mjs.map +1 -0
  90. package/dist/engine/diff/{line-diff-helper.respec.node.d.mts → line-diff-helper.respec.d.mts} +1 -1
  91. package/dist/engine/diff/line-diff-helper.respec.d.mts.map +1 -0
  92. package/dist/engine/diff/{line-diff-helper.respec.node.mjs → line-diff-helper.respec.mjs} +1 -1
  93. package/dist/engine/diff/line-diff-helper.respec.mjs.map +1 -0
  94. package/dist/engine/filter/{glob-helper.respec.node.d.mts → glob-helper.respec.d.mts} +1 -1
  95. package/dist/engine/filter/glob-helper.respec.d.mts.map +1 -0
  96. package/dist/engine/filter/{glob-helper.respec.node.mjs → glob-helper.respec.mjs} +1 -1
  97. package/dist/engine/filter/glob-helper.respec.mjs.map +1 -0
  98. package/dist/engine/index/index-helper.d.mts +35 -0
  99. package/dist/engine/index/index-helper.d.mts.map +1 -1
  100. package/dist/engine/index/index-helper.mjs +241 -0
  101. package/dist/engine/index/index-helper.mjs.map +1 -1
  102. package/dist/engine/item/item-helper.d.mts +29 -3
  103. package/dist/engine/item/item-helper.d.mts.map +1 -1
  104. package/dist/engine/item/item-helper.mjs +234 -56
  105. package/dist/engine/item/item-helper.mjs.map +1 -1
  106. package/dist/engine/item/item.respec.d.mts +7 -0
  107. package/dist/engine/item/item.respec.d.mts.map +1 -0
  108. package/dist/engine/item/item.respec.mjs +75 -0
  109. package/dist/engine/item/item.respec.mjs.map +1 -0
  110. package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts +38 -0
  111. package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts.map +1 -0
  112. package/dist/engine/merge/b2tfs-3way-merge-strategy.mjs +593 -0
  113. package/dist/engine/merge/b2tfs-3way-merge-strategy.mjs.map +1 -0
  114. package/dist/engine/merge/b2tfs-merge-strategy.respec.d.mts +7 -0
  115. package/dist/engine/merge/b2tfs-merge-strategy.respec.d.mts.map +1 -0
  116. package/dist/engine/merge/b2tfs-merge-strategy.respec.mjs +335 -0
  117. package/dist/engine/merge/b2tfs-merge-strategy.respec.mjs.map +1 -0
  118. package/dist/respec-gib.node.mjs +1 -1
  119. package/dist/respec-gib.node.mjs.map +1 -1
  120. package/dist/{vcs-gib.respec.node.d.mts → vcs-gib.respec.d.mts} +1 -1
  121. package/dist/vcs-gib.respec.d.mts.map +1 -0
  122. package/dist/{vcs-gib.respec.node.mjs → vcs-gib.respec.mjs} +1 -1
  123. package/dist/vcs-gib.respec.mjs.map +1 -0
  124. package/package.json +3 -3
  125. package/src/AUTO-GENERATED-version.mts +1 -1
  126. package/src/bootstrap/node-bootstrap.mts +6 -0
  127. package/src/cli/cli-arg-parser.mts +15 -0
  128. package/src/cli/cli-constants.mts +27 -0
  129. package/src/cli/{cli-e2e.respec.node.mts → cli-e2e.respec.mts} +2 -2
  130. package/src/cli/commands/add-cmd.mts +41 -29
  131. package/src/cli/commands/{branch-checkout-cmd.respec.node.mts → branch-checkout-cmd.respec.mts} +190 -8
  132. package/src/cli/commands/branch-cmd.mts +78 -8
  133. package/src/cli/commands/checkout-cmd.mts +48 -23
  134. package/src/cli/commands/commit-cmd.mts +35 -26
  135. package/src/cli/commands/merge-cmd.mts +289 -0
  136. package/src/cli/commands/reset-cmd.mts +128 -0
  137. package/src/cli/commands/reset-restore-cmd.respec.mts +222 -0
  138. package/src/cli/commands/restore-cmd.mts +155 -0
  139. package/src/cli/commands/{status-add-cmd.respec.node.mts → status-add-cmd.respec.mts} +27 -0
  140. package/src/cli/vcs-cli-main.mts +36 -3
  141. package/src/engine/branch/branch-graph-helper.mts +66 -18
  142. package/src/engine/branch/branch-space-helper.mts +6 -0
  143. package/src/engine/branch/{branch-space.respec.node.mts → branch-space.respec.mts} +12 -6
  144. package/src/engine/index/index-helper.mts +324 -0
  145. package/src/engine/item/item-helper.mts +284 -58
  146. package/src/engine/item/item.respec.mts +92 -0
  147. package/src/engine/merge/b2tfs-3way-merge-strategy.mts +725 -0
  148. package/src/engine/merge/b2tfs-merge-strategy.respec.mts +438 -0
  149. package/src/respec-gib.node.mts +2 -2
  150. package/tsconfig.tsbuildinfo +1 -1
  151. package/dist/bootstrap/node-bootstrap.respec.node.d.mts.map +0 -1
  152. package/dist/bootstrap/node-bootstrap.respec.node.mjs.map +0 -1
  153. package/dist/cli/cli-arg-parser.respec.node.d.mts.map +0 -1
  154. package/dist/cli/cli-arg-parser.respec.node.mjs.map +0 -1
  155. package/dist/cli/cli-e2e.respec.node.d.mts.map +0 -1
  156. package/dist/cli/cli-e2e.respec.node.mjs.map +0 -1
  157. package/dist/cli/commands/branch-checkout-cmd.respec.node.d.mts.map +0 -1
  158. package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs.map +0 -1
  159. package/dist/cli/commands/commit-log-cmd.respec.node.d.mts.map +0 -1
  160. package/dist/cli/commands/commit-log-cmd.respec.node.mjs.map +0 -1
  161. package/dist/cli/commands/init-cmd.respec.node.d.mts.map +0 -1
  162. package/dist/cli/commands/init-cmd.respec.node.mjs.map +0 -1
  163. package/dist/cli/commands/status-add-cmd.respec.node.d.mts.map +0 -1
  164. package/dist/cli/commands/status-add-cmd.respec.node.mjs.map +0 -1
  165. package/dist/engine/branch/branch-space.respec.node.d.mts.map +0 -1
  166. package/dist/engine/branch/branch-space.respec.node.mjs.map +0 -1
  167. package/dist/engine/diff/line-diff-helper.respec.node.d.mts.map +0 -1
  168. package/dist/engine/diff/line-diff-helper.respec.node.mjs.map +0 -1
  169. package/dist/engine/filter/glob-helper.respec.node.d.mts.map +0 -1
  170. package/dist/engine/filter/glob-helper.respec.node.mjs.map +0 -1
  171. package/dist/vcs-gib.respec.node.d.mts.map +0 -1
  172. package/dist/vcs-gib.respec.node.mjs.map +0 -1
  173. package/test_runs/001-hello-node/20260728_090100_0001-flow-core-main.md +0 -39
  174. package/test_runs/001-hello-node/20260728_090200_0003-flow-branching-worktree.md +0 -54
  175. package/test_runs/001-hello-node/20260728_090300_0002-flow-selective-add.md +0 -45
  176. package/test_runs/001-hello-node/20260728_142100_0007-flow-edge-cases.md +0 -44
  177. package/test_runs/001-hello-node/20260728_142400_0005-flow-hardcoded-ignores.md +0 -36
  178. package/tools/pack-for-test.mjs +0 -73
  179. /package/src/bootstrap/{node-bootstrap.respec.node.mts → node-bootstrap.respec.mts} +0 -0
  180. /package/src/cli/{cli-arg-parser.respec.node.mts → cli-arg-parser.respec.mts} +0 -0
  181. /package/src/cli/commands/{commit-log-cmd.respec.node.mts → commit-log-cmd.respec.mts} +0 -0
  182. /package/src/cli/commands/{init-cmd.respec.node.mts → init-cmd.respec.mts} +0 -0
  183. /package/src/engine/diff/{line-diff-helper.respec.node.mts → line-diff-helper.respec.mts} +0 -0
  184. /package/src/engine/filter/{glob-helper.respec.node.mts → glob-helper.respec.mts} +0 -0
  185. /package/src/{vcs-gib.respec.node.mts → vcs-gib.respec.mts} +0 -0
@@ -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 || []),
@@ -116,11 +118,13 @@ export async function executeAddCmd({
116
118
  }
117
119
  }
118
120
 
119
- const activeBranchIbGib = branchIbGibs.find(
121
+ const activeBranchIbGib = branchIbGibs.findLast(
120
122
  (b) => (b as B2tFSBranchIbGib_V1)?.data?.name === activeBranch,
121
- ) as B2tFSBranchIbGib_V1 || (branchIbGibs.at(0) as B2tFSBranchIbGib_V1);
123
+ ) as B2tFSBranchIbGib_V1 || (branchIbGibs.at(-1) as B2tFSBranchIbGib_V1);
124
+
125
+
126
+ const rootItemAddr = activeBranchIbGib?.rel8ns?.b2tfs_item?.at(-1);
122
127
 
123
- const rootItemAddr = activeBranchIbGib?.rel8ns?.b2tfs_item?.at(0);
124
128
  if (rootItemAddr) {
125
129
  const rootItem = await getIbGibWithMetaspaceFallback({
126
130
  addr: rootItemAddr,
@@ -182,7 +186,7 @@ export async function executeAddCmd({
182
186
  throw new Error(`No files matched glob pattern: ${relOrAbs} (E: e23d4c5f6a7b8c9d0e1f2a3b4c5d6e7f)`);
183
187
  }
184
188
  } else {
185
- const fullPath = path.resolve(activeWorktreeDir, relOrAbs);
189
+ const fullPath = path.resolve(targetDir, relOrAbs);
186
190
  let stat;
187
191
  try {
188
192
  stat = await fs.stat(fullPath);
@@ -191,6 +195,9 @@ export async function executeAddCmd({
191
195
  }
192
196
 
193
197
  const relPath = path.relative(activeWorktreeDir, fullPath).replace(/\\/g, '/');
198
+ if (relPath.startsWith('..') || path.isAbsolute(relPath)) {
199
+ throw new Error(`Path '${relOrAbs}' is outside of active worktree repository (E: 7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f)`);
200
+ }
194
201
 
195
202
  if (isPathFiltered({ relativePath: relPath, isDirectory: stat.isDirectory(), filterPatterns })) {
196
203
  continue;
@@ -243,31 +250,36 @@ export async function executeAddCmd({
243
250
  await putInSpace({ ibGib: binIbGib, space: activeSpace });
244
251
  await metaspace.registerNewIbGib({ ibGib: binIbGib, space: activeSpace });
245
252
 
246
- // 2. Create B2tFSItemIbGib_V1 candidate graph node
253
+ // 2. Create or Evolve B2tFSItemIbGib_V1 candidate graph node
247
254
  const fileName = path.basename(absFilePath);
248
- const fileNameSansExt = path.parse(fileName).name;
249
- const fileExt = path.parse(fileName).ext.replace(/^\./, '');
250
255
  const nameHash = crypto.createHash('sha256').update(fileName).digest('hex');
251
256
 
252
- const itemRes = await Factory_V1.firstGen({
253
- parentIbGib: ROOT,
254
- ib: getB2tFSItemIb('file', fileName, nameHash),
255
- data: {
256
- name: fileName,
257
- nameHash,
257
+ let itemRes: TransformResult<IbGib_V1>;
258
+ if (committedItem && committedItem.item) {
259
+ itemRes = await evolveB2tFSItemIbGib({
260
+ existingItem: committedItem.item,
261
+ dataToUpdate: {
262
+ fileHash,
263
+ },
264
+ rel8nsToUpdate: {
265
+ bin: [binAddr],
266
+ },
267
+ });
268
+ } else {
269
+ itemRes = await createB2tFSItemIbGib({
258
270
  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
- });
271
+ name: fileName,
272
+ data: {
273
+ nameHash,
274
+ fileHash,
275
+ fileEncoding: 'utf8',
276
+ },
277
+ rel8ns: {
278
+ bin: [binAddr],
279
+ },
280
+ });
281
+ }
282
+
271
283
  const itemIbGib = itemRes.newIbGib as B2tFSItemIbGib_V1;
272
284
  const itemAddr = getIbGibAddr({ ibGib: itemIbGib });
273
285
  await persistTransformResult({ resTransform: itemRes, space: activeSpace });
@@ -332,13 +344,13 @@ async function addCmd_walkBranchGraph({
332
344
  space: any;
333
345
  metaspace?: any;
334
346
  currentRelPath: string;
335
- outMap: Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder' }>;
347
+ outMap: Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder'; item?: B2tFSItemIbGib_V1 }>;
336
348
  }): Promise<void> {
337
349
  if (!item || !item.data) { return; }
338
350
 
339
351
  const { fsType, fileHash } = item.data;
340
352
  if (currentRelPath) {
341
- outMap.set(currentRelPath, { relPath: currentRelPath, fileHash, fsType });
353
+ outMap.set(currentRelPath, { relPath: currentRelPath, fileHash, fsType, item });
342
354
  }
343
355
 
344
356
  if (fsType === 'folder') {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @module branch-checkout-cmd.respec
3
- *
3
+ *
4
4
  * Unit tests for `vcs branch` and `vcs checkout` command handlers & worktree projections.
5
5
  */
6
6
 
@@ -8,7 +8,8 @@ import path from 'node:path';
8
8
  import fs from 'node:fs/promises';
9
9
 
10
10
  import {
11
- respecfully, ifWe, iReckon
11
+ respecfully, ifWe, iReckon,
12
+ ifWeMight
12
13
  } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
13
14
 
14
15
  import { executeInitCmd } from './init-cmd.mjs';
@@ -16,13 +17,14 @@ import { executeAddCmd } from './add-cmd.mjs';
16
17
  import { executeCommitCmd } from './commit-cmd.mjs';
17
18
  import { executeBranchCmd } from './branch-cmd.mjs';
18
19
  import { executeCheckoutCmd } from './checkout-cmd.mjs';
20
+ import { executeMergeCmd } from './merge-cmd.mjs';
19
21
  import { executeStatusCmd } from './status-cmd.mjs';
20
22
 
21
23
  const lc = `[apps/vcs-gib/src/cli/commands/branch-checkout-cmd.respec.node.mts]`;
22
24
 
23
- await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () => {
25
+ await respecfully(lc, `vcs branch, checkout, and merge Command Handlers`, async () => {
24
26
 
25
- await ifWe(lc, `lists default branch main for new repository`, async () => {
27
+ await ifWeMight(lc, `lists default branch main for new repository`, async () => {
26
28
  const testDir = path.join(process.cwd(), '.test_branch_checkout_1');
27
29
  const vcsFolderName = '.vcsgib_test_bc_1';
28
30
  try {
@@ -46,7 +48,7 @@ await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () =
46
48
  }
47
49
  });
48
50
 
49
- await ifWe(lc, `creates new branch from active branch root item and rejects duplicates`, async () => {
51
+ await ifWeMight(lc, `creates new branch from active branch root item and rejects duplicates`, async () => {
50
52
  const testDir = path.join(process.cwd(), '.test_branch_checkout_2');
51
53
  const vcsFolderName = '.vcsgib_test_bc_2';
52
54
  try {
@@ -110,7 +112,7 @@ await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () =
110
112
  }
111
113
  });
112
114
 
113
- await ifWe(lc, `switches active branch with checkout and projects branch snapshot onto worktree subfolder`, async () => {
115
+ await ifWeMight(lc, `switches active branch with checkout and projects branch snapshot onto worktree subfolder`, async () => {
114
116
  const testDir = path.join(process.cwd(), '.test_branch_checkout_3');
115
117
  const vcsFolderName = '.vcsgib_test_bc_3';
116
118
  try {
@@ -236,7 +238,7 @@ await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () =
236
238
  }
237
239
  });
238
240
 
239
- await ifWe(lc, `rejects checkout when branch does not exist or branchName missing`, async () => {
241
+ await ifWeMight(lc, `rejects checkout when branch does not exist or branchName missing`, async () => {
240
242
  const testDir = path.join(process.cwd(), '.test_branch_checkout_4');
241
243
  const vcsFolderName = '.vcsgib_test_bc_4';
242
244
  try {
@@ -278,7 +280,7 @@ await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () =
278
280
  }
279
281
  });
280
282
 
281
- await ifWe(lc, `lists branches silently on secondary branch without error noise`, async () => {
283
+ await ifWeMight(lc, `lists branches silently on secondary branch without error noise`, async () => {
282
284
  const testDir = path.join(process.cwd(), '.test_branch_checkout_secondary');
283
285
  const vcsFolderName = '.vcsgib_test_bc_sec';
284
286
  try {
@@ -319,4 +321,184 @@ await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () =
319
321
  await fs.rm(testDir, { recursive: true, force: true });
320
322
  }
321
323
  });
324
+
325
+ await ifWeMight(lc, `branches single file-root item and projects into file-branch worktree on checkout`, async () => {
326
+ const testDir = path.join(process.cwd(), '.test_branch_checkout_file_root');
327
+ const vcsFolderName = '.vcsgib_test_bc_file';
328
+ try {
329
+ await executeInitCmd({
330
+ targetDir: testDir,
331
+ vcsFolderName,
332
+ quiet: true,
333
+ });
334
+
335
+ // Create nested file structure src/utils/math.js and commit on main
336
+ const mathRelPath = 'src/utils/math.js';
337
+ await fs.mkdir(path.join(testDir, 'src', 'utils'), { recursive: true });
338
+ await fs.writeFile(path.join(testDir, mathRelPath), 'export function add(a, b) { return a + b; }', 'utf8');
339
+
340
+ await executeAddCmd({
341
+ paths: [mathRelPath],
342
+ targetDir: testDir,
343
+ vcsFolderName,
344
+ quiet: true,
345
+ });
346
+
347
+ await executeCommitCmd({
348
+ message: 'Add math.js',
349
+ targetDir: testDir,
350
+ vcsFolderName,
351
+ quiet: true,
352
+ });
353
+
354
+ // Execute branch command for single file math-opt targeting src/utils/math.js
355
+ const branchRes = await executeBranchCmd({
356
+ branchName: 'math-opt',
357
+ itemPath: mathRelPath,
358
+ targetDir: testDir,
359
+ vcsFolderName,
360
+ quiet: true,
361
+ });
362
+
363
+ iReckon(lc, branchRes.createdBranchName).willEqual('math-opt');
364
+
365
+ // Attempt branching uncommitted/non-existent item
366
+ let uncommittedErr = false;
367
+ try {
368
+ await executeBranchCmd({
369
+ branchName: 'invalid-file-branch',
370
+ itemPath: 'src/utils/non-existent.js',
371
+ targetDir: testDir,
372
+ vcsFolderName,
373
+ quiet: true,
374
+ });
375
+ } catch (err: any) {
376
+ uncommittedErr = true;
377
+ iReckon(lc, err.message).includes("not found in active branch snapshot");
378
+ }
379
+ iReckon(lc, uncommittedErr).isGonnaBeTrue();
380
+
381
+ // Execute checkout command for math-opt
382
+ const checkoutRes = await executeCheckoutCmd({
383
+ branchName: 'math-opt',
384
+ targetDir: testDir,
385
+ vcsFolderName,
386
+ quiet: true,
387
+ });
388
+
389
+ const expectedWorktree = path.join(testDir, 'b', 'math-opt');
390
+ iReckon(lc, checkoutRes.worktreePath).willEqual(expectedWorktree);
391
+
392
+ // Verify single file projected at expectedWorktree/math.js (my-repo/b/math-opt/math.js)
393
+ const projectedFilePath = path.join(expectedWorktree, 'math.js');
394
+ const fileExists = await fs.stat(projectedFilePath).then(s => s.isFile()).catch(() => false);
395
+ iReckon(lc, fileExists).isGonnaBeTrue();
396
+
397
+ const fileContent = await fs.readFile(projectedFilePath, 'utf8');
398
+ iReckon(lc, fileContent).willEqual('export function add(a, b) { return a + b; }');
399
+ } finally {
400
+ await fs.rm(testDir, { recursive: true, force: true });
401
+ }
402
+ });
403
+
404
+ await ifWeMight(lc, `performs vcs merge end-to-end between main and secondary branch`, async () => {
405
+ const testDir = path.join(process.cwd(), '.test_vcs_merge_e2e');
406
+ const vcsFolderName = '.vcsgib_test_merge_e2e';
407
+ try {
408
+ await executeInitCmd({
409
+ targetDir: testDir,
410
+ vcsFolderName,
411
+ quiet: true,
412
+ });
413
+
414
+ // 1. Initial commit on main
415
+ const initialContent = "line 1\nline 2\nline 3";
416
+ await fs.writeFile(path.join(testDir, 'app.ts'), initialContent, 'utf8');
417
+ await executeAddCmd({
418
+ paths: [path.join(testDir, 'app.ts')],
419
+ targetDir: testDir,
420
+ vcsFolderName,
421
+ quiet: true,
422
+ });
423
+ await executeCommitCmd({
424
+ message: 'Initial commit on main',
425
+ targetDir: testDir,
426
+ vcsFolderName,
427
+ quiet: true,
428
+ });
429
+
430
+ // 2. Create secondary branch feature-merge
431
+ await executeBranchCmd({
432
+ branchName: 'feature-merge',
433
+ targetDir: testDir,
434
+ vcsFolderName,
435
+ quiet: true,
436
+ });
437
+
438
+ // 3. Checkout feature-merge branch and make a commit (modify line 3)
439
+ await executeCheckoutCmd({
440
+ branchName: 'feature-merge',
441
+ targetDir: testDir,
442
+ vcsFolderName,
443
+ quiet: true,
444
+ });
445
+ const featureWorktree = path.join(testDir, 'b', 'feature-merge');
446
+ const featureContent = "line 1\nline 2\nline 3 modified by secondary";
447
+ await fs.writeFile(path.join(featureWorktree, 'app.ts'), featureContent, 'utf8');
448
+ await executeAddCmd({
449
+ paths: [path.join(featureWorktree, 'app.ts')],
450
+ targetDir: testDir,
451
+ vcsFolderName,
452
+ quiet: true,
453
+ });
454
+ await executeCommitCmd({
455
+ message: 'Update app.ts on feature-merge',
456
+ targetDir: testDir,
457
+ vcsFolderName,
458
+ quiet: true,
459
+ });
460
+
461
+ // 4. Checkout main branch and make a commit (modify line 1)
462
+ await executeCheckoutCmd({
463
+ branchName: 'main',
464
+ targetDir: testDir,
465
+ vcsFolderName,
466
+ quiet: true,
467
+ });
468
+ const mainContent = "line 1 modified by main\nline 2\nline 3";
469
+ await fs.writeFile(path.join(testDir, 'app.ts'), mainContent, 'utf8');
470
+ await executeAddCmd({
471
+ paths: [path.join(testDir, 'app.ts')],
472
+ targetDir: testDir,
473
+ vcsFolderName,
474
+ quiet: true,
475
+ });
476
+ await executeCommitCmd({
477
+ message: 'Update app.ts on main',
478
+ targetDir: testDir,
479
+ vcsFolderName,
480
+ quiet: true,
481
+ });
482
+
483
+ // 5. Execute vcs merge feature-merge into main
484
+ const mergeRes = await executeMergeCmd({
485
+ targetBranchName: 'feature-merge',
486
+ targetDir: testDir,
487
+ vcsFolderName,
488
+ quiet: true,
489
+ });
490
+
491
+ iReckon(lc, mergeRes.activeBranch).willEqual('main');
492
+ iReckon(lc, mergeRes.targetBranch).willEqual('feature-merge');
493
+
494
+ // 6. Verify projected file on physical worktree disk contains merged lines
495
+ const mergedAppTs = await fs.readFile(path.join(testDir, 'app.ts'), 'utf8');
496
+ iReckon(lc, mergedAppTs).includes('line 1 modified by main');
497
+ iReckon(lc, mergedAppTs).includes('line 2');
498
+ iReckon(lc, mergedAppTs).includes('line 3 modified by secondary');
499
+ } finally {
500
+ await fs.rm(testDir, { recursive: true, force: true });
501
+ }
502
+ });
322
503
  });
504
+
@@ -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,
@@ -122,7 +124,7 @@ export async function executeBranchCmd({
122
124
  const branches: BranchInfo[] = [];
123
125
  for (const [name, bNode] of branchMap.entries()) {
124
126
  const addr = getIbGibAddr({ ibGib: bNode });
125
- const rootItemAddr = bNode.rel8ns?.b2tfs_item?.at(0);
127
+ const rootItemAddr = bNode.rel8ns?.b2tfs_item?.at(-1);
126
128
  const isActive = name === activeBranchName;
127
129
  branches.push({
128
130
  name,
@@ -140,6 +142,7 @@ export async function executeBranchCmd({
140
142
  }
141
143
 
142
144
  return { branches };
145
+
143
146
  }
144
147
 
145
148
  // CREATE BRANCH
@@ -149,10 +152,10 @@ export async function executeBranchCmd({
149
152
 
150
153
  // Find active branch root item to fork from
151
154
  const activeBranchIbGib = branchMap.get(activeBranchName) || branchIbGibs.at(0);
152
- const rootItemAddr = activeBranchIbGib?.rel8ns?.b2tfs_item?.at(0);
155
+ const rootItemAddr = activeBranchIbGib?.rel8ns?.b2tfs_item?.at(-1);
153
156
 
154
157
  if (!rootItemAddr) {
155
- throw new Error(`Cannot find active branch root item to fork branch from (E: 8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d)`);
158
+ throw new Error(`Cannot find active branch root item to fork branch from (E: 8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c)`);
156
159
  }
157
160
 
158
161
  const rootItem = await getIbGibWithMetaspaceFallback({
@@ -165,6 +168,25 @@ export async function executeBranchCmd({
165
168
  throw new Error(`Root item node for address '${rootItemAddr}' not found in localSpace (E: 9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e)`);
166
169
  }
167
170
 
171
+ let targetRootItem: B2tFSItemIbGib_V1 = rootItem;
172
+ if (itemPath && itemPath.trim().length > 0) {
173
+ const normalizedItemPath = itemPath.trim().replace(/\\/g, '/').replace(/^\.\//, '');
174
+ const committedBranchItemsMap = new Map<string, B2tFSItemIbGib_V1>();
175
+ await executeBranchCmd_walkBranchGraphForItems({
176
+ item: rootItem,
177
+ space: activeBranchSpace,
178
+ metaspace,
179
+ currentRelPath: '',
180
+ outMap: committedBranchItemsMap,
181
+ });
182
+
183
+ const targetItemNode = committedBranchItemsMap.get(normalizedItemPath);
184
+ if (!targetItemNode) {
185
+ throw new Error(`Item '${itemPath}' not found in active branch snapshot (E: 8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c)`);
186
+ }
187
+ targetRootItem = targetItemNode;
188
+ }
189
+
168
190
  // Instantiate new branch space b2s_<newBranch>
169
191
  const { space: newBranchSpace, spaceId: newBranchSpaceId } = await getOrCreateBranchSpace({
170
192
  metaspace,
@@ -172,18 +194,18 @@ export async function executeBranchCmd({
172
194
  branchName: trimmedBranchName,
173
195
  });
174
196
 
175
- // Populate new branch space from live dependency graph of rootItem
197
+ // Populate new branch space from live dependency graph of targetRootItem
176
198
  await populateBranchSpaceFromGraph({
177
199
  sourceSpace: activeBranchSpace,
178
200
  targetBranchSpace: newBranchSpace,
179
- rootItemIbGib: rootItem,
201
+ rootItemIbGib: targetRootItem,
180
202
  metaspace,
181
203
  });
182
204
 
183
205
  // Create new B2tFSBranchIbGib_V1 branch node inside newBranchSpace
184
206
  const newBranchIbGib = await createB2tFSBranchIbGib({
185
207
  branchName: trimmedBranchName,
186
- rootItem,
208
+ rootItem: targetRootItem,
187
209
  space: newBranchSpace,
188
210
  metaspace,
189
211
  });
@@ -210,7 +232,7 @@ export async function executeBranchCmd({
210
232
  });
211
233
 
212
234
  if (!quiet) {
213
- console.log(`Created branch '${trimmedBranchName}' (I: 0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f)`);
235
+ console.log(`Created branch '${trimmedBranchName}' (I: 0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5e)`);
214
236
  }
215
237
 
216
238
  // Return updated branch list
@@ -241,3 +263,51 @@ export async function executeBranchCmd({
241
263
  }
242
264
  }
243
265
  }
266
+
267
+ // #region executeBranchCmd_walkBranchGraphForItems
268
+ async function executeBranchCmd_walkBranchGraphForItems({
269
+ item,
270
+ space,
271
+ metaspace,
272
+ currentRelPath,
273
+ outMap,
274
+ }: {
275
+ item: B2tFSItemIbGib_V1;
276
+ space: any;
277
+ metaspace?: any;
278
+ currentRelPath: string;
279
+ outMap: Map<string, B2tFSItemIbGib_V1>;
280
+ }): Promise<void> {
281
+ if (!item || !item.data) { return; }
282
+
283
+ const { fsType } = item.data;
284
+
285
+ if (currentRelPath) {
286
+ outMap.set(currentRelPath, item);
287
+ }
288
+
289
+ if (fsType === 'folder') {
290
+ const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
291
+ for (const childAddr of childAddrs) {
292
+ const childItem = await getIbGibWithMetaspaceFallback({
293
+ addr: childAddr,
294
+ space,
295
+ metaspace,
296
+ }) as B2tFSItemIbGib_V1;
297
+
298
+ if (childItem && childItem.data) {
299
+ const childRelPath = currentRelPath
300
+ ? `${currentRelPath}/${childItem.data.name}`
301
+ : childItem.data.name;
302
+ await executeBranchCmd_walkBranchGraphForItems({
303
+ item: childItem,
304
+ space,
305
+ metaspace,
306
+ currentRelPath: childRelPath,
307
+ outMap,
308
+ });
309
+ }
310
+ }
311
+ }
312
+ }
313
+ // #endregion executeBranchCmd_walkBranchGraphForItems
@@ -11,7 +11,8 @@ import fs from 'node:fs/promises';
11
11
  import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
12
12
  import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
13
13
  import { getIbGibsFromCache_fallbackToSpaces } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
14
- import { putInSpace } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
14
+ import { putInSpace, getLatestAddrs } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
15
+
15
16
 
16
17
  import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
17
18
  import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
@@ -49,7 +50,7 @@ export async function executeCheckoutCmd({
49
50
  const lc = `${lcFile}[${executeCheckoutCmd.name}]`;
50
51
  try {
51
52
  if (logalot) {
52
- console.log(`${lc} starting... (I: 3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c)`);
53
+ console.log(`${lc} starting... (I: 3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8e)`);
53
54
  }
54
55
 
55
56
  const trimmedBranchName = branchName?.trim();
@@ -68,7 +69,7 @@ export async function executeCheckoutCmd({
68
69
  try {
69
70
  await fs.access(vcsPath);
70
71
  } catch {
71
- throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e)`);
72
+ throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0e)`);
72
73
  }
73
74
 
74
75
  // 1. Fetch authoritative repository index from localSpace
@@ -100,21 +101,15 @@ export async function executeCheckoutCmd({
100
101
  }
101
102
 
102
103
  // Find target branch node
103
- const targetBranchIbGib = branchIbGibs.find(
104
+ const targetBranchIbGib = branchIbGibs.findLast(
104
105
  (b) => b?.data?.name === trimmedBranchName,
105
106
  );
106
107
 
108
+
107
109
  if (!targetBranchIbGib) {
108
110
  throw new Error(`Branch '${trimmedBranchName}' does not exist (E: 7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a)`);
109
111
  }
110
112
 
111
- const targetBranchAddr = getIbGibAddr({ ibGib: targetBranchIbGib });
112
- const rootItemAddr = targetBranchIbGib.rel8ns?.b2tfs_item?.at(0);
113
-
114
- if (!rootItemAddr) {
115
- throw new Error(`Branch '${trimmedBranchName}' does not have a valid root item address (E: 8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b)`);
116
- }
117
-
118
113
  // 3. Resolve target branch space b2s_<branchName>
119
114
  const targetBranchSpaceRes = await getOrCreateBranchSpace({
120
115
  metaspace,
@@ -123,6 +118,25 @@ export async function executeCheckoutCmd({
123
118
  });
124
119
  const targetBranchSpace = targetBranchSpaceRes.space;
125
120
 
121
+ const targetBranchAddr = getIbGibAddr({ ibGib: targetBranchIbGib });
122
+ const targetBranchTjpAddr = targetBranchIbGib.rel8ns?.tjp?.at(0) || targetBranchAddr;
123
+ const latestTargetBranchRes = await getLatestAddrs({ space: targetBranchSpace, addrs: [targetBranchTjpAddr] });
124
+ const latestTargetBranchAddr = (latestTargetBranchRes?.data as any)?.latestAddrs?.[0] || targetBranchAddr;
125
+
126
+
127
+ const latestTargetBranchIbGib = (await getIbGibWithMetaspaceFallback({
128
+ addr: latestTargetBranchAddr,
129
+ space: targetBranchSpace,
130
+ metaspace,
131
+ })) as B2tFSBranchIbGib_V1 || targetBranchIbGib;
132
+
133
+ const rootItemAddr = latestTargetBranchIbGib.rel8ns?.b2tfs_item?.at(-1);
134
+
135
+ if (!rootItemAddr) {
136
+ throw new Error(`Branch '${trimmedBranchName}' does not have a valid root item address (E: 8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b)`);
137
+ }
138
+
139
+
126
140
  await putInSpace({ space: targetBranchSpace, ibGib: targetBranchIbGib });
127
141
 
128
142
  // Update activeBranch in B2tFSIndexIbGib_V1 in targetBranchSpace and localSpace
@@ -152,7 +166,9 @@ export async function executeCheckoutCmd({
152
166
 
153
167
  await fs.mkdir(worktreePath, { recursive: true });
154
168
 
155
- // Retrieve root item and project child items via writeB2tFSItemToFS
169
+ // Retrieve root item and project branch snapshot onto worktree subfolder:
170
+ // If rootItem is a single file, write it directly to worktreePath (my-repo/b/<branchName>/<fileName>).
171
+ // If rootItem is a folder, write child items into worktreePath.
156
172
  const rootItem = await getIbGibWithMetaspaceFallback({
157
173
  addr: rootItemAddr,
158
174
  space: targetBranchSpace,
@@ -160,21 +176,30 @@ export async function executeCheckoutCmd({
160
176
  }) as B2tFSItemIbGib_V1;
161
177
 
162
178
  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,
179
+ if (rootItem.data?.fsType === 'file') {
180
+ await writeB2tFSItemToFS({
181
+ item: rootItem,
182
+ destPath: worktreePath,
167
183
  space: targetBranchSpace,
168
184
  metaspace,
169
- }) as B2tFSItemIbGib_V1;
170
-
171
- if (childItem) {
172
- await writeB2tFSItemToFS({
173
- item: childItem,
174
- destPath: worktreePath,
185
+ });
186
+ } else {
187
+ const childAddrs = rootItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
188
+ for (const childAddr of childAddrs) {
189
+ const childItem = await getIbGibWithMetaspaceFallback({
190
+ addr: childAddr,
175
191
  space: targetBranchSpace,
176
192
  metaspace,
177
- });
193
+ }) as B2tFSItemIbGib_V1;
194
+
195
+ if (childItem) {
196
+ await writeB2tFSItemToFS({
197
+ item: childItem,
198
+ destPath: worktreePath,
199
+ space: targetBranchSpace,
200
+ metaspace,
201
+ });
202
+ }
178
203
  }
179
204
  }
180
205
  }