@ibgib/vcs-gib 0.0.6

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 (202) hide show
  1. package/README.md +94 -0
  2. package/VCS_CLI_USER_GUIDE.md +198 -0
  3. package/dist/AUTO-GENERATED-version.d.mts +6 -0
  4. package/dist/AUTO-GENERATED-version.d.mts.map +1 -0
  5. package/dist/AUTO-GENERATED-version.mjs +6 -0
  6. package/dist/AUTO-GENERATED-version.mjs.map +1 -0
  7. package/dist/assumptions.respec.d.mts +2 -0
  8. package/dist/assumptions.respec.d.mts.map +1 -0
  9. package/dist/assumptions.respec.mjs +41 -0
  10. package/dist/assumptions.respec.mjs.map +1 -0
  11. package/dist/bootstrap/node-bootstrap.d.mts +35 -0
  12. package/dist/bootstrap/node-bootstrap.d.mts.map +1 -0
  13. package/dist/bootstrap/node-bootstrap.mjs +165 -0
  14. package/dist/bootstrap/node-bootstrap.mjs.map +1 -0
  15. package/dist/bootstrap/node-bootstrap.respec.node.d.mts +7 -0
  16. package/dist/bootstrap/node-bootstrap.respec.node.d.mts.map +1 -0
  17. package/dist/bootstrap/node-bootstrap.respec.node.mjs +28 -0
  18. package/dist/bootstrap/node-bootstrap.respec.node.mjs.map +1 -0
  19. package/dist/cli/cli-arg-parser.d.mts +27 -0
  20. package/dist/cli/cli-arg-parser.d.mts.map +1 -0
  21. package/dist/cli/cli-arg-parser.mjs +96 -0
  22. package/dist/cli/cli-arg-parser.mjs.map +1 -0
  23. package/dist/cli/cli-arg-parser.respec.node.d.mts +7 -0
  24. package/dist/cli/cli-arg-parser.respec.node.d.mts.map +1 -0
  25. package/dist/cli/cli-arg-parser.respec.node.mjs +35 -0
  26. package/dist/cli/cli-arg-parser.respec.node.mjs.map +1 -0
  27. package/dist/cli/cli-constants.d.mts +26 -0
  28. package/dist/cli/cli-constants.d.mts.map +1 -0
  29. package/dist/cli/cli-constants.mjs +67 -0
  30. package/dist/cli/cli-constants.mjs.map +1 -0
  31. package/dist/cli/cli-e2e.respec.node.d.mts +8 -0
  32. package/dist/cli/cli-e2e.respec.node.d.mts.map +1 -0
  33. package/dist/cli/cli-e2e.respec.node.mjs +263 -0
  34. package/dist/cli/cli-e2e.respec.node.mjs.map +1 -0
  35. package/dist/cli/commands/add-cmd.d.mts +20 -0
  36. package/dist/cli/commands/add-cmd.d.mts.map +1 -0
  37. package/dist/cli/commands/add-cmd.mjs +333 -0
  38. package/dist/cli/commands/add-cmd.mjs.map +1 -0
  39. package/dist/cli/commands/branch-checkout-cmd.respec.node.d.mts +7 -0
  40. package/dist/cli/commands/branch-checkout-cmd.respec.node.d.mts.map +1 -0
  41. package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs +288 -0
  42. package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs.map +1 -0
  43. package/dist/cli/commands/branch-cmd.d.mts +25 -0
  44. package/dist/cli/commands/branch-cmd.d.mts.map +1 -0
  45. package/dist/cli/commands/branch-cmd.mjs +187 -0
  46. package/dist/cli/commands/branch-cmd.mjs.map +1 -0
  47. package/dist/cli/commands/checkout-cmd.d.mts +21 -0
  48. package/dist/cli/commands/checkout-cmd.d.mts.map +1 -0
  49. package/dist/cli/commands/checkout-cmd.mjs +154 -0
  50. package/dist/cli/commands/checkout-cmd.mjs.map +1 -0
  51. package/dist/cli/commands/commit-cmd.d.mts +23 -0
  52. package/dist/cli/commands/commit-cmd.d.mts.map +1 -0
  53. package/dist/cli/commands/commit-cmd.mjs +326 -0
  54. package/dist/cli/commands/commit-cmd.mjs.map +1 -0
  55. package/dist/cli/commands/commit-log-cmd.respec.node.d.mts +7 -0
  56. package/dist/cli/commands/commit-log-cmd.respec.node.d.mts.map +1 -0
  57. package/dist/cli/commands/commit-log-cmd.respec.node.mjs +251 -0
  58. package/dist/cli/commands/commit-log-cmd.respec.node.mjs.map +1 -0
  59. package/dist/cli/commands/init-cmd.d.mts +20 -0
  60. package/dist/cli/commands/init-cmd.d.mts.map +1 -0
  61. package/dist/cli/commands/init-cmd.mjs +119 -0
  62. package/dist/cli/commands/init-cmd.mjs.map +1 -0
  63. package/dist/cli/commands/init-cmd.respec.node.d.mts +7 -0
  64. package/dist/cli/commands/init-cmd.respec.node.d.mts.map +1 -0
  65. package/dist/cli/commands/init-cmd.respec.node.mjs +40 -0
  66. package/dist/cli/commands/init-cmd.respec.node.mjs.map +1 -0
  67. package/dist/cli/commands/log-cmd.d.mts +27 -0
  68. package/dist/cli/commands/log-cmd.d.mts.map +1 -0
  69. package/dist/cli/commands/log-cmd.mjs +146 -0
  70. package/dist/cli/commands/log-cmd.mjs.map +1 -0
  71. package/dist/cli/commands/status-add-cmd.respec.node.d.mts +7 -0
  72. package/dist/cli/commands/status-add-cmd.respec.node.d.mts.map +1 -0
  73. package/dist/cli/commands/status-add-cmd.respec.node.mjs +215 -0
  74. package/dist/cli/commands/status-add-cmd.respec.node.mjs.map +1 -0
  75. package/dist/cli/commands/status-cmd.d.mts +25 -0
  76. package/dist/cli/commands/status-cmd.d.mts.map +1 -0
  77. package/dist/cli/commands/status-cmd.mjs +286 -0
  78. package/dist/cli/commands/status-cmd.mjs.map +1 -0
  79. package/dist/cli/vcs-cli-main.d.mts +14 -0
  80. package/dist/cli/vcs-cli-main.d.mts.map +1 -0
  81. package/dist/cli/vcs-cli-main.mjs +136 -0
  82. package/dist/cli/vcs-cli-main.mjs.map +1 -0
  83. package/dist/engine/branch/branch-graph-helper.d.mts +19 -0
  84. package/dist/engine/branch/branch-graph-helper.d.mts.map +1 -0
  85. package/dist/engine/branch/branch-graph-helper.mjs +49 -0
  86. package/dist/engine/branch/branch-graph-helper.mjs.map +1 -0
  87. package/dist/engine/branch/branch-helper.d.mts +26 -0
  88. package/dist/engine/branch/branch-helper.d.mts.map +1 -0
  89. package/dist/engine/branch/branch-helper.mjs +57 -0
  90. package/dist/engine/branch/branch-helper.mjs.map +1 -0
  91. package/dist/engine/branch/branch-space-helper.d.mts +22 -0
  92. package/dist/engine/branch/branch-space-helper.d.mts.map +1 -0
  93. package/dist/engine/branch/branch-space-helper.mjs +77 -0
  94. package/dist/engine/branch/branch-space-helper.mjs.map +1 -0
  95. package/dist/engine/branch/branch-space.respec.node.d.mts +8 -0
  96. package/dist/engine/branch/branch-space.respec.node.d.mts.map +1 -0
  97. package/dist/engine/branch/branch-space.respec.node.mjs +138 -0
  98. package/dist/engine/branch/branch-space.respec.node.mjs.map +1 -0
  99. package/dist/engine/branch/branch-types.d.mts +26 -0
  100. package/dist/engine/branch/branch-types.d.mts.map +1 -0
  101. package/dist/engine/branch/branch-types.mjs +7 -0
  102. package/dist/engine/branch/branch-types.mjs.map +1 -0
  103. package/dist/engine/diff/line-diff-helper.d.mts +76 -0
  104. package/dist/engine/diff/line-diff-helper.d.mts.map +1 -0
  105. package/dist/engine/diff/line-diff-helper.mjs +144 -0
  106. package/dist/engine/diff/line-diff-helper.mjs.map +1 -0
  107. package/dist/engine/diff/line-diff-helper.respec.node.d.mts +7 -0
  108. package/dist/engine/diff/line-diff-helper.respec.node.d.mts.map +1 -0
  109. package/dist/engine/diff/line-diff-helper.respec.node.mjs +66 -0
  110. package/dist/engine/diff/line-diff-helper.respec.node.mjs.map +1 -0
  111. package/dist/engine/filter/glob-helper.d.mts +51 -0
  112. package/dist/engine/filter/glob-helper.d.mts.map +1 -0
  113. package/dist/engine/filter/glob-helper.mjs +132 -0
  114. package/dist/engine/filter/glob-helper.mjs.map +1 -0
  115. package/dist/engine/filter/glob-helper.respec.node.d.mts +7 -0
  116. package/dist/engine/filter/glob-helper.respec.node.d.mts.map +1 -0
  117. package/dist/engine/filter/glob-helper.respec.node.mjs +78 -0
  118. package/dist/engine/filter/glob-helper.respec.node.mjs.map +1 -0
  119. package/dist/engine/index/index-helper.d.mts +78 -0
  120. package/dist/engine/index/index-helper.d.mts.map +1 -0
  121. package/dist/engine/index/index-helper.mjs +289 -0
  122. package/dist/engine/index/index-helper.mjs.map +1 -0
  123. package/dist/engine/index/index-types.d.mts +63 -0
  124. package/dist/engine/index/index-types.d.mts.map +1 -0
  125. package/dist/engine/index/index-types.mjs +7 -0
  126. package/dist/engine/index/index-types.mjs.map +1 -0
  127. package/dist/engine/item/item-atom-helper.d.mts +20 -0
  128. package/dist/engine/item/item-atom-helper.d.mts.map +1 -0
  129. package/dist/engine/item/item-atom-helper.mjs +30 -0
  130. package/dist/engine/item/item-atom-helper.mjs.map +1 -0
  131. package/dist/engine/item/item-helper.d.mts +37 -0
  132. package/dist/engine/item/item-helper.d.mts.map +1 -0
  133. package/dist/engine/item/item-helper.mjs +194 -0
  134. package/dist/engine/item/item-helper.mjs.map +1 -0
  135. package/dist/engine/item/item-types.d.mts +127 -0
  136. package/dist/engine/item/item-types.d.mts.map +1 -0
  137. package/dist/engine/item/item-types.mjs +16 -0
  138. package/dist/engine/item/item-types.mjs.map +1 -0
  139. package/dist/index.d.mts +7 -0
  140. package/dist/index.d.mts.map +1 -0
  141. package/dist/index.mjs +7 -0
  142. package/dist/index.mjs.map +1 -0
  143. package/dist/respec-gib.node.d.mts +7 -0
  144. package/dist/respec-gib.node.d.mts.map +1 -0
  145. package/dist/respec-gib.node.mjs +11 -0
  146. package/dist/respec-gib.node.mjs.map +1 -0
  147. package/dist/vcs-gib-constants.d.mts +7 -0
  148. package/dist/vcs-gib-constants.d.mts.map +1 -0
  149. package/dist/vcs-gib-constants.mjs +7 -0
  150. package/dist/vcs-gib-constants.mjs.map +1 -0
  151. package/dist/vcs-gib.respec.node.d.mts +8 -0
  152. package/dist/vcs-gib.respec.node.d.mts.map +1 -0
  153. package/dist/vcs-gib.respec.node.mjs +82 -0
  154. package/dist/vcs-gib.respec.node.mjs.map +1 -0
  155. package/generate-version-file.js +20 -0
  156. package/package.json +28 -0
  157. package/src/AUTO-GENERATED-version.mts +5 -0
  158. package/src/assumptions.respec.mts +50 -0
  159. package/src/bootstrap/node-bootstrap.mts +198 -0
  160. package/src/bootstrap/node-bootstrap.respec.node.mts +35 -0
  161. package/src/cli/cli-arg-parser.mts +119 -0
  162. package/src/cli/cli-arg-parser.respec.node.mts +45 -0
  163. package/src/cli/cli-constants.mts +77 -0
  164. package/src/cli/cli-e2e.respec.node.mts +318 -0
  165. package/src/cli/commands/add-cmd.mts +415 -0
  166. package/src/cli/commands/branch-checkout-cmd.respec.node.mts +322 -0
  167. package/src/cli/commands/branch-cmd.mts +243 -0
  168. package/src/cli/commands/checkout-cmd.mts +202 -0
  169. package/src/cli/commands/commit-cmd.mts +421 -0
  170. package/src/cli/commands/commit-log-cmd.respec.node.mts +276 -0
  171. package/src/cli/commands/init-cmd.mts +152 -0
  172. package/src/cli/commands/init-cmd.respec.node.mts +49 -0
  173. package/src/cli/commands/log-cmd.mts +197 -0
  174. package/src/cli/commands/status-add-cmd.respec.node.mts +246 -0
  175. package/src/cli/commands/status-cmd.mts +366 -0
  176. package/src/cli/vcs-cli-main.mts +153 -0
  177. package/src/engine/branch/branch-graph-helper.mts +69 -0
  178. package/src/engine/branch/branch-helper.mts +79 -0
  179. package/src/engine/branch/branch-space-helper.mts +104 -0
  180. package/src/engine/branch/branch-space.respec.node.mts +158 -0
  181. package/src/engine/branch/branch-types.mts +29 -0
  182. package/src/engine/diff/line-diff-helper.mts +210 -0
  183. package/src/engine/diff/line-diff-helper.respec.node.mts +84 -0
  184. package/src/engine/filter/glob-helper.mts +165 -0
  185. package/src/engine/filter/glob-helper.respec.node.mts +94 -0
  186. package/src/engine/index/index-helper.mts +378 -0
  187. package/src/engine/index/index-types.mts +66 -0
  188. package/src/engine/item/item-atom-helper.mts +30 -0
  189. package/src/engine/item/item-helper.mts +239 -0
  190. package/src/engine/item/item-types.mts +136 -0
  191. package/src/index.mts +7 -0
  192. package/src/respec-gib.node.mts +12 -0
  193. package/src/vcs-gib-constants.mts +7 -0
  194. package/src/vcs-gib.respec.node.mts +96 -0
  195. package/test_runs/001-hello-node/20260728_090100_0001-flow-core-main.md +39 -0
  196. package/test_runs/001-hello-node/20260728_090200_0003-flow-branching-worktree.md +54 -0
  197. package/test_runs/001-hello-node/20260728_090300_0002-flow-selective-add.md +45 -0
  198. package/test_runs/001-hello-node/20260728_142100_0007-flow-edge-cases.md +44 -0
  199. package/test_runs/001-hello-node/20260728_142400_0005-flow-hardcoded-ignores.md +36 -0
  200. package/tools/pack-for-test.mjs +73 -0
  201. package/tsconfig.json +18 -0
  202. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * @module checkout-cmd
3
+ *
4
+ * Command handler for `vcs checkout <branchName>`.
5
+ * Switches activeBranch in B2tFSIndexIbGib_V1 in localSpace and projects branch snapshot onto worktree path.
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 { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
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';
15
+
16
+ import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
17
+ import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
18
+ import { getB2tFSBranchSpaceName } from '../../engine/branch/branch-helper.mjs';
19
+ import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
20
+ import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
21
+ import { writeB2tFSItemToFS, getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
22
+ import { getB2tFSIndexIbGib, updateB2tFSIndexIbGib } from '../../engine/index/index-helper.mjs';
23
+ import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
24
+
25
+ const lcFile = `[checkout-cmd]`;
26
+ const logalot = GLOBAL_LOG_A_LOT;
27
+
28
+ export interface CheckoutCmdOpts {
29
+ branchName?: string;
30
+ targetDir?: string;
31
+ vcsFolderName?: string;
32
+ quiet?: boolean;
33
+ }
34
+
35
+ export interface CheckoutCmdResult {
36
+ previousBranch: string;
37
+ currentBranch: string;
38
+ branchAddr: string;
39
+ rootItemAddr: string;
40
+ worktreePath: string;
41
+ }
42
+
43
+ export async function executeCheckoutCmd({
44
+ branchName,
45
+ targetDir = process.cwd(),
46
+ vcsFolderName = '.vcsgib',
47
+ quiet = false,
48
+ }: CheckoutCmdOpts = {}): Promise<CheckoutCmdResult> {
49
+ const lc = `${lcFile}[${executeCheckoutCmd.name}]`;
50
+ try {
51
+ if (logalot) {
52
+ console.log(`${lc} starting... (I: 3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c)`);
53
+ }
54
+
55
+ const trimmedBranchName = branchName?.trim();
56
+ if (!trimmedBranchName) {
57
+ throw new Error(`Branch name is required for checkout (E: 4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d)`);
58
+ }
59
+
60
+ const absTargetDir = path.resolve(targetDir);
61
+
62
+ const { metaspace, localSpace, repoRoot, vcsPath } = await bootstrapVcsMetaspace({
63
+ repoPath: absTargetDir,
64
+ vcsFolderName,
65
+ });
66
+
67
+ // Verify repo exists
68
+ try {
69
+ await fs.access(vcsPath);
70
+ } catch {
71
+ throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e)`);
72
+ }
73
+
74
+ // 1. Fetch authoritative repository index from localSpace
75
+ const indexIbGib = await getB2tFSIndexIbGib({ space: localSpace, vcsPath });
76
+ if (!indexIbGib) {
77
+ throw new Error(`Repository index B2tFSIndexIbGib_V1 not found in localSpace (E: 6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f)`);
78
+ }
79
+
80
+ const previousBranch = indexIbGib.data?.activeBranch || 'main';
81
+
82
+ // 2. Fetch all registered branch ibgibs
83
+ const branchAddrs = [
84
+ ...(indexIbGib.rel8ns?.branch || []),
85
+ ...(indexIbGib.rel8ns?.branch_tjp || []),
86
+ ];
87
+
88
+ let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
89
+ if (branchAddrs.length > 0) {
90
+ for (const bAddr of branchAddrs) {
91
+ const bIbGib = await getIbGibWithMetaspaceFallback({
92
+ addr: bAddr,
93
+ space: localSpace,
94
+ metaspace,
95
+ });
96
+ if (bIbGib) {
97
+ branchIbGibs.push(bIbGib as B2tFSBranchIbGib_V1);
98
+ }
99
+ }
100
+ }
101
+
102
+ // Find target branch node
103
+ const targetBranchIbGib = branchIbGibs.find(
104
+ (b) => b?.data?.name === trimmedBranchName,
105
+ );
106
+
107
+ if (!targetBranchIbGib) {
108
+ throw new Error(`Branch '${trimmedBranchName}' does not exist (E: 7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a)`);
109
+ }
110
+
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
+ // 3. Resolve target branch space b2s_<branchName>
119
+ const targetBranchSpaceRes = await getOrCreateBranchSpace({
120
+ metaspace,
121
+ vcsPath,
122
+ branchName: trimmedBranchName,
123
+ });
124
+ const targetBranchSpace = targetBranchSpaceRes.space;
125
+
126
+ await putInSpace({ space: targetBranchSpace, ibGib: targetBranchIbGib });
127
+
128
+ // Update activeBranch in B2tFSIndexIbGib_V1 in targetBranchSpace and localSpace
129
+ const updatedIndex = await updateB2tFSIndexIbGib({
130
+ space: targetBranchSpace,
131
+ metaspace,
132
+ indexIbGib,
133
+ dataToUpdate: {
134
+ activeBranch: trimmedBranchName,
135
+ activeSpaceId: getB2tFSBranchSpaceName(trimmedBranchName),
136
+ mySpaceId: targetBranchSpaceRes.spaceId,
137
+ },
138
+ vcsPath,
139
+ });
140
+
141
+ if (localSpace !== targetBranchSpace) {
142
+ await putInSpace({ space: localSpace, ibGib: updatedIndex });
143
+ }
144
+
145
+ // 4. Project branch snapshot onto worktree subfolder:
146
+ // 'main' -> repoRoot
147
+ // other branches -> repoRoot/b/<branch_name>/
148
+ const isDefaultBranch = trimmedBranchName === 'main';
149
+ const worktreePath = isDefaultBranch
150
+ ? repoRoot
151
+ : path.join(repoRoot, 'b', trimmedBranchName);
152
+
153
+ await fs.mkdir(worktreePath, { recursive: true });
154
+
155
+ // Retrieve root item and project child items via writeB2tFSItemToFS
156
+ const rootItem = await getIbGibWithMetaspaceFallback({
157
+ addr: rootItemAddr,
158
+ space: targetBranchSpace,
159
+ metaspace,
160
+ }) as B2tFSItemIbGib_V1;
161
+
162
+ 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,
167
+ space: targetBranchSpace,
168
+ metaspace,
169
+ }) as B2tFSItemIbGib_V1;
170
+
171
+ if (childItem) {
172
+ await writeB2tFSItemToFS({
173
+ item: childItem,
174
+ destPath: worktreePath,
175
+ space: targetBranchSpace,
176
+ metaspace,
177
+ });
178
+ }
179
+ }
180
+ }
181
+
182
+ if (!quiet) {
183
+ console.log(`Switched to branch '${trimmedBranchName}' (I: 9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c)`);
184
+ console.log(` Worktree projection: ${worktreePath}`);
185
+ }
186
+
187
+ return {
188
+ previousBranch,
189
+ currentBranch: trimmedBranchName,
190
+ branchAddr: targetBranchAddr,
191
+ rootItemAddr,
192
+ worktreePath,
193
+ };
194
+ } catch (error) {
195
+ console.error(`${lc} ${extractErrorMsg(error)} (E: 0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d)`);
196
+ throw error;
197
+ } finally {
198
+ if (logalot) {
199
+ console.log(`${lc} complete. (I: 1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e)`);
200
+ }
201
+ }
202
+ }
@@ -0,0 +1,421 @@
1
+ /**
2
+ * @module commit-cmd
3
+ *
4
+ * Command handler for `vcs commit -m "message"`.
5
+ * Validates staged items, evolves the B2tFS root folder item graph in localSpace,
6
+ * creates a CommentIbGib_V1 commit comment node, advances active B2tFSBranchIbGib_V1 pointer,
7
+ * clears staged items in B2tFSIndexIbGib_V1, and updates read-only index projection cache.
8
+ */
9
+
10
+ import path from 'node:path';
11
+ import fs from 'node:fs/promises';
12
+ import crypto from 'node:crypto';
13
+
14
+ import { getUUID, extractErrorMsg, delay } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
15
+ import { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
16
+ import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
17
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
18
+ import { putInSpace, persistTransformResult } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
19
+ import { getIbGibsFromCache_fallbackToSpaces } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
20
+ import { createCommentIbGib } from '@ibgib/core-gib/dist/common/comment/comment-helper.mjs';
21
+ import { CommentIbGib_V1 } from '@ibgib/core-gib/dist/common/comment/comment-types.mjs';
22
+
23
+ import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
24
+ import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
25
+ import { getB2tFSItemIb } from '../../engine/item/item-atom-helper.mjs';
26
+ import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
27
+ import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
28
+ import { getB2tFSBranchSpaceName } from '../../engine/branch/branch-helper.mjs';
29
+ import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
30
+ import { getB2tFSIndexIbGib, updateB2tFSIndexIbGib, getSpaceQualifiedIbGibAddr } from '../../engine/index/index-helper.mjs';
31
+ import { B2tFSStagedItemRecord } from '../../engine/index/index-types.mjs';
32
+ import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
33
+
34
+ const lcFile = `[commit-cmd]`;
35
+ const logalot = GLOBAL_LOG_A_LOT;
36
+
37
+ export interface CommitCmdOpts {
38
+ message?: string;
39
+ targetDir?: string;
40
+ vcsFolderName?: string;
41
+ quiet?: boolean;
42
+ }
43
+
44
+ export interface CommitCmdResult {
45
+ commitAddr: string;
46
+ commentAddr: string;
47
+ branchName: string;
48
+ committedPaths: string[];
49
+ rootItemAddr: string;
50
+ }
51
+
52
+ export async function executeCommitCmd({
53
+ message = '',
54
+ targetDir = process.cwd(),
55
+ vcsFolderName = '.vcsgib',
56
+ quiet = false,
57
+ }: CommitCmdOpts = {}): Promise<CommitCmdResult> {
58
+ const lc = `${lcFile}[${executeCommitCmd.name}]`;
59
+
60
+ try {
61
+ if (logalot) {
62
+ console.log(`${lc} starting executeCommitCmd (I: 9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d)`);
63
+ }
64
+
65
+ const absTargetDir = path.resolve(targetDir);
66
+
67
+ const { metaspace, localSpace, vcsPath, repoRoot } = await bootstrapVcsMetaspace({
68
+ repoPath: absTargetDir,
69
+ vcsFolderName,
70
+ });
71
+
72
+ // Verify repo exists
73
+ try {
74
+ await fs.access(vcsPath);
75
+ } catch {
76
+ throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d)`);
77
+ }
78
+
79
+ // Validate message
80
+ if (!message || message.trim().length === 0) {
81
+ throw new Error(`Commit message is required (E: 2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e)`);
82
+ }
83
+
84
+ // Fetch authoritative repository index from localSpace
85
+ const indexIbGib = await getB2tFSIndexIbGib({ space: localSpace, vcsPath });
86
+ if (!indexIbGib) {
87
+ throw new Error(`Repository index B2tFSIndexIbGib_V1 not found in localSpace (E: 3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f)`);
88
+ }
89
+
90
+ const branchName = indexIbGib.data?.activeBranch || 'main';
91
+
92
+ // Target active branch space b2s_<activeBranch>
93
+ const { space: activeSpace } = await getOrCreateBranchSpace({
94
+ metaspace,
95
+ vcsPath,
96
+ branchName,
97
+ });
98
+
99
+ // Validate staged items
100
+ const stagedPathsMap = indexIbGib.data?.stagedPaths || {};
101
+ const stagedAddrs = indexIbGib.data?.staged_item || [];
102
+ const committedPaths = Object.keys(stagedPathsMap);
103
+
104
+ if (committedPaths.length === 0 || stagedAddrs.length === 0) {
105
+ throw new Error(`Nothing to commit (no staged items) (E: 4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a)`);
106
+ }
107
+
108
+ // Fetch active branch node from activeSpace
109
+ const branchAddrs = [
110
+ ...(indexIbGib.rel8ns?.branch || []),
111
+ ...(indexIbGib.rel8ns?.branch_tjp || []),
112
+ ];
113
+
114
+ let currentBranchIbGib: B2tFSBranchIbGib_V1 | undefined;
115
+ let currentBranchAddr: string | undefined;
116
+
117
+ if (branchAddrs.length > 0) {
118
+ let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
119
+ for (const bAddr of branchAddrs) {
120
+ const bIbGib = await getIbGibWithMetaspaceFallback({
121
+ addr: bAddr,
122
+ space: activeSpace,
123
+ metaspace,
124
+ });
125
+ if (bIbGib) {
126
+ branchIbGibs.push(bIbGib as B2tFSBranchIbGib_V1);
127
+ }
128
+ }
129
+
130
+ currentBranchIbGib = branchIbGibs.find(
131
+ (b) => (b as B2tFSBranchIbGib_V1)?.data?.name === branchName,
132
+ ) as B2tFSBranchIbGib_V1 || (branchIbGibs.at(0) as B2tFSBranchIbGib_V1);
133
+
134
+ if (currentBranchIbGib) {
135
+ currentBranchAddr = getIbGibAddr({ ibGib: currentBranchIbGib });
136
+ }
137
+ }
138
+
139
+ const currentRootAddr = currentBranchIbGib?.rel8ns?.b2tfs_item?.at(0);
140
+
141
+ // 1. Create CommentIbGib_V1 commit comment node in activeSpace
142
+ const commentRes = await createCommentIbGib({
143
+ text: message.trim(),
144
+ saveInSpace: true,
145
+ space: activeSpace,
146
+ });
147
+ const commentIbGib = commentRes.newIbGib as CommentIbGib_V1;
148
+ const commentAddr = getIbGibAddr({ ibGib: commentIbGib });
149
+ await metaspace.registerNewIbGib({ ibGib: commentIbGib, space: activeSpace });
150
+
151
+ // 2. Evolve root folder item graph with staged items in activeSpace
152
+ const repoName = path.basename(repoRoot) || 'root';
153
+ const { newRootAddr } = await commitCmd_buildEvolvedRootItem({
154
+ currentRootAddr,
155
+ stagedPathsMap,
156
+ space: activeSpace,
157
+ metaspace,
158
+ repoName,
159
+ });
160
+
161
+ // 3. Advance B2tFSBranchIbGib_V1 node inside activeSpace
162
+ const branchUuid = currentBranchIbGib?.data?.uuid || (await getUUID());
163
+ const newBranchRes = await Factory_V1.firstGen({
164
+ parentIbGib: currentBranchIbGib || ROOT,
165
+ ib: `b2tfs_branch ${branchName} v1 ${branchUuid}`,
166
+ data: {
167
+ ...(currentBranchIbGib?.data || {}),
168
+ name: branchName,
169
+ uuid: branchUuid,
170
+ classname: 'B2tFSBranchIbGib_V1',
171
+ committedAt: new Date().toISOString(),
172
+ },
173
+ rel8ns: {
174
+ b2tfs_item: [newRootAddr],
175
+ comment: [commentAddr],
176
+ },
177
+ });
178
+
179
+ const newBranchIbGib = newBranchRes.newIbGib as B2tFSBranchIbGib_V1;
180
+ const newBranchAddr = getIbGibAddr({ ibGib: newBranchIbGib });
181
+ await persistTransformResult({ resTransform: newBranchRes, space: activeSpace });
182
+ await metaspace.registerNewIbGib({ ibGib: newBranchIbGib, space: activeSpace });
183
+
184
+ const sqBranchAddr = getSpaceQualifiedIbGibAddr({
185
+ spaceId: activeSpace.data?.spaceSubPath || activeSpace.data?.name || getB2tFSBranchSpaceName(branchName),
186
+ ibGibAddr: newBranchAddr,
187
+ });
188
+
189
+ // 4. Update B2tFSIndexIbGib_V1 in activeSpace & localSpace
190
+ const updatedIndex = await updateB2tFSIndexIbGib({
191
+ space: activeSpace,
192
+ metaspace,
193
+ indexIbGib,
194
+ dataToUpdate: {
195
+ stagedPaths: {},
196
+ staged_item: [],
197
+ },
198
+ rel8nsToUpdate: {
199
+ branch: [sqBranchAddr],
200
+ branch_tjp: [sqBranchAddr],
201
+ },
202
+ vcsPath,
203
+ });
204
+
205
+ if (localSpace !== activeSpace) {
206
+ await putInSpace({ space: localSpace, ibGib: updatedIndex });
207
+ }
208
+
209
+ if (!quiet) {
210
+ const shortGib = newBranchIbGib.gib ? newBranchIbGib.gib.slice(0, 7) : 'commit';
211
+ console.log(`[${branchName} ${shortGib}] ${message.trim()} (I: 5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b)`);
212
+ console.log(` ${committedPaths.length} file(s) changed`);
213
+ }
214
+
215
+ return {
216
+ commitAddr: newBranchAddr,
217
+ commentAddr,
218
+ branchName,
219
+ committedPaths,
220
+ rootItemAddr: newRootAddr,
221
+ };
222
+ } catch (error) {
223
+ console.error(`${lc} ${extractErrorMsg(error)} (E: 6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c)`);
224
+ throw error;
225
+ } finally {
226
+ await delay(50);
227
+ if (logalot) {
228
+ console.log(`${lc} complete. (I: 7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d)`);
229
+ }
230
+ }
231
+ }
232
+
233
+ // #region commitCmd_buildEvolvedRootItem
234
+ async function commitCmd_buildEvolvedRootItem({
235
+ currentRootAddr,
236
+ stagedPathsMap,
237
+ space,
238
+ metaspace,
239
+ repoName,
240
+ }: {
241
+ currentRootAddr?: string;
242
+ stagedPathsMap: { [relPath: string]: B2tFSStagedItemRecord };
243
+ space: any;
244
+ metaspace: any;
245
+ repoName: string;
246
+ }): Promise<{ newRootItem: B2tFSItemIbGib_V1; newRootAddr: string }> {
247
+ const itemsByRelPath = new Map<string, { itemAddr: string; fsType: 'file' | 'folder' }>();
248
+
249
+ if (currentRootAddr) {
250
+ const currentRootItem = await getIbGibWithMetaspaceFallback({
251
+ addr: currentRootAddr,
252
+ space,
253
+ metaspace,
254
+ }) as B2tFSItemIbGib_V1;
255
+
256
+ if (currentRootItem) {
257
+ await commitCmd_walkBranchGraph({
258
+ item: currentRootItem,
259
+ space,
260
+ metaspace,
261
+ currentRelPath: '',
262
+ outMap: itemsByRelPath,
263
+ });
264
+ }
265
+ }
266
+
267
+ // Overlay staged items
268
+ for (const [relPath, stagedRec] of Object.entries(stagedPathsMap)) {
269
+ itemsByRelPath.set(relPath, {
270
+ itemAddr: stagedRec.itemAddr,
271
+ fsType: stagedRec.fsType || 'file',
272
+ });
273
+ }
274
+
275
+ // Ensure parent folder entries exist for all items
276
+ const allRelPaths = Array.from(itemsByRelPath.keys());
277
+ for (const relPath of allRelPaths) {
278
+ const parts = relPath.split('/');
279
+ parts.pop(); // Remove file/folder name to get parent path
280
+ let currentFolderRel = '';
281
+ for (const part of parts) {
282
+ currentFolderRel = currentFolderRel ? `${currentFolderRel}/${part}` : part;
283
+ if (!itemsByRelPath.has(currentFolderRel)) {
284
+ itemsByRelPath.set(currentFolderRel, { itemAddr: '', fsType: 'folder' });
285
+ }
286
+ }
287
+ }
288
+
289
+ // Collect all folder paths (including root '')
290
+ const folderPathsSet = new Set<string>();
291
+ folderPathsSet.add('');
292
+ for (const [relPath, itemRec] of itemsByRelPath.entries()) {
293
+ if (itemRec.fsType === 'folder' || relPath === '') {
294
+ folderPathsSet.add(relPath);
295
+ }
296
+ }
297
+
298
+ // Sort folders deepest path first so children folders are built before parents
299
+ const sortedFolders = Array.from(folderPathsSet).sort((a, b) => {
300
+ const depthA = a ? a.split('/').length : 0;
301
+ const depthB = b ? b.split('/').length : 0;
302
+ return depthB - depthA;
303
+ });
304
+
305
+ const createdFolderAddrs = new Map<string, string>();
306
+ let rootFolderItem: B2tFSItemIbGib_V1 | undefined;
307
+ let rootFolderAddr: string = '';
308
+
309
+ for (const folderRelPath of sortedFolders) {
310
+ const childAddrs: string[] = [];
311
+ for (const [itemRelPath, itemRec] of itemsByRelPath.entries()) {
312
+ if (!itemRelPath || itemRelPath === folderRelPath) {
313
+ continue;
314
+ }
315
+ const parentRelPath = commitCmd_getParentRelPath(itemRelPath);
316
+ if (parentRelPath === folderRelPath) {
317
+ if (itemRec.fsType === 'folder') {
318
+ const folderAddr = createdFolderAddrs.get(itemRelPath);
319
+ if (folderAddr && !childAddrs.includes(folderAddr)) {
320
+ childAddrs.push(folderAddr);
321
+ }
322
+ } else {
323
+ if (itemRec.itemAddr && !childAddrs.includes(itemRec.itemAddr)) {
324
+ childAddrs.push(itemRec.itemAddr);
325
+ }
326
+ }
327
+ }
328
+ }
329
+
330
+ const folderName = folderRelPath ? path.basename(folderRelPath) : repoName;
331
+ const diffTxId = await getUUID();
332
+ const nameHash = crypto.createHash('sha256').update(folderName).digest('hex');
333
+
334
+ const rel8ns: Record<string, string[]> = {};
335
+ if (childAddrs.length > 0) {
336
+ rel8ns[B2TFS_CHILD_ITEM_REL8N_NAME] = childAddrs;
337
+ }
338
+
339
+ const folderRes = await Factory_V1.firstGen({
340
+ parentIbGib: ROOT,
341
+ ib: getB2tFSItemIb('folder', folderName, nameHash),
342
+ data: {
343
+ name: folderName,
344
+ nameHash,
345
+ fsType: 'folder',
346
+ diffTransactionId: diffTxId,
347
+ uuid: await getUUID(),
348
+ classname: 'B2tFSItemIbGib_V1',
349
+ },
350
+ rel8ns,
351
+ });
352
+
353
+ const folderItem = folderRes.newIbGib as B2tFSItemIbGib_V1;
354
+ const folderAddr = getIbGibAddr({ ibGib: folderItem });
355
+ await persistTransformResult({ resTransform: folderRes, space });
356
+ await metaspace.registerNewIbGib({ ibGib: folderItem, space });
357
+
358
+ createdFolderAddrs.set(folderRelPath, folderAddr);
359
+
360
+ if (folderRelPath === '') {
361
+ rootFolderItem = folderItem;
362
+ rootFolderAddr = folderAddr;
363
+ }
364
+ }
365
+
366
+ return { newRootItem: rootFolderItem!, newRootAddr: rootFolderAddr };
367
+ }
368
+
369
+ function commitCmd_getParentRelPath(relPath: string): string {
370
+ const parts = relPath.split('/');
371
+ parts.pop();
372
+ return parts.join('/');
373
+ }
374
+
375
+ async function commitCmd_walkBranchGraph({
376
+ item,
377
+ space,
378
+ metaspace,
379
+ currentRelPath,
380
+ outMap,
381
+ }: {
382
+ item: B2tFSItemIbGib_V1;
383
+ space: any;
384
+ metaspace?: any;
385
+ currentRelPath: string;
386
+ outMap: Map<string, { itemAddr: string; fsType: 'file' | 'folder' }>;
387
+ }): Promise<void> {
388
+ if (!item || !item.data) { return; }
389
+
390
+ const { fsType } = item.data;
391
+ const itemAddr = getIbGibAddr({ ibGib: item });
392
+
393
+ if (currentRelPath) {
394
+ outMap.set(currentRelPath, { itemAddr, fsType });
395
+ }
396
+
397
+ if (fsType === 'folder') {
398
+ const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
399
+ for (const childAddr of childAddrs) {
400
+ const childItem = await getIbGibWithMetaspaceFallback({
401
+ addr: childAddr,
402
+ space,
403
+ metaspace,
404
+ }) as B2tFSItemIbGib_V1;
405
+
406
+ if (childItem && childItem.data) {
407
+ const childRelPath = currentRelPath
408
+ ? `${currentRelPath}/${childItem.data.name}`
409
+ : childItem.data.name;
410
+ await commitCmd_walkBranchGraph({
411
+ item: childItem,
412
+ space,
413
+ metaspace,
414
+ currentRelPath: childRelPath,
415
+ outMap,
416
+ });
417
+ }
418
+ }
419
+ }
420
+ }
421
+ // #endregion commitCmd_buildEvolvedRootItem