@ibgib/vcs-gib 0.0.23 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/VCS_CLI_USER_GUIDE.md +94 -89
- package/dist/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/cli/cli-arg-parser.d.mts +1 -0
- package/dist/cli/cli-arg-parser.d.mts.map +1 -1
- package/dist/cli/cli-arg-parser.mjs +5 -1
- package/dist/cli/cli-arg-parser.mjs.map +1 -1
- package/dist/cli/cli-constants.d.mts +6 -0
- package/dist/cli/cli-constants.d.mts.map +1 -1
- package/dist/cli/cli-constants.mjs +12 -0
- package/dist/cli/cli-constants.mjs.map +1 -1
- package/dist/cli/commands/add-cmd.d.mts.map +1 -1
- package/dist/cli/commands/add-cmd.mjs +11 -3
- package/dist/cli/commands/add-cmd.mjs.map +1 -1
- package/dist/cli/commands/commit-cmd.d.mts +5 -4
- package/dist/cli/commands/commit-cmd.d.mts.map +1 -1
- package/dist/cli/commands/commit-cmd.mjs +155 -35
- package/dist/cli/commands/commit-cmd.mjs.map +1 -1
- package/dist/cli/commands/generate-comment-cmd.d.mts +19 -0
- package/dist/cli/commands/generate-comment-cmd.d.mts.map +1 -0
- package/dist/cli/commands/generate-comment-cmd.mjs +140 -0
- package/dist/cli/commands/generate-comment-cmd.mjs.map +1 -0
- package/dist/cli/commands/log-cmd.d.mts +12 -3
- package/dist/cli/commands/log-cmd.d.mts.map +1 -1
- package/dist/cli/commands/log-cmd.mjs +242 -39
- package/dist/cli/commands/log-cmd.mjs.map +1 -1
- package/dist/cli/commands/status-cmd.d.mts +5 -6
- package/dist/cli/commands/status-cmd.d.mts.map +1 -1
- package/dist/cli/commands/status-cmd.mjs +51 -205
- package/dist/cli/commands/status-cmd.mjs.map +1 -1
- package/dist/cli/vcs-cli-main.d.mts.map +1 -1
- package/dist/cli/vcs-cli-main.mjs +25 -4
- package/dist/cli/vcs-cli-main.mjs.map +1 -1
- package/dist/engine/branch/branch-helper.d.mts +14 -0
- package/dist/engine/branch/branch-helper.d.mts.map +1 -1
- package/dist/engine/branch/branch-helper.mjs +21 -0
- package/dist/engine/branch/branch-helper.mjs.map +1 -1
- package/dist/engine/index/index-helper.d.mts.map +1 -1
- package/dist/engine/index/index-helper.mjs +3 -0
- package/dist/engine/index/index-helper.mjs.map +1 -1
- package/dist/engine/item/item-helper.d.mts.map +1 -1
- package/dist/engine/item/item-helper.mjs +5 -2
- package/dist/engine/item/item-helper.mjs.map +1 -1
- package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts +28 -0
- package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts.map +1 -1
- package/dist/engine/merge/b2tfs-3way-graft-strategy.mjs +84 -12
- package/dist/engine/merge/b2tfs-3way-graft-strategy.mjs.map +1 -1
- package/dist/engine/status/status-helper.d.mts +90 -0
- package/dist/engine/status/status-helper.d.mts.map +1 -0
- package/dist/engine/status/status-helper.mjs +286 -0
- package/dist/engine/status/status-helper.mjs.map +1 -0
- package/package.json +3 -3
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/cli/cli-arg-parser.mts +5 -0
- package/src/cli/cli-constants.mts +13 -0
- package/src/cli/commands/add-cmd.mts +11 -3
- package/src/cli/commands/branch-checkout-cmd.respec.mts +54 -0
- package/src/cli/commands/commit-cmd.mts +171 -44
- package/src/cli/commands/commit-log-cmd.respec.mts +216 -3
- package/src/cli/commands/generate-comment-cmd.mts +171 -0
- package/src/cli/commands/log-cmd.mts +294 -42
- package/src/cli/commands/status-cmd.mts +58 -256
- package/src/cli/vcs-cli-main.mts +25 -4
- package/src/engine/branch/branch-helper.mts +33 -0
- package/src/engine/index/index-helper.mts +3 -0
- package/src/engine/item/item-helper.mts +5 -2
- package/src/engine/merge/b2tfs-3way-graft-strategy.mts +89 -10
- package/src/engine/merge/b2tfs-sync-comments.respec.mts +399 -0
- package/src/engine/status/status-helper.mts +383 -0
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* @module commit-cmd
|
|
3
3
|
*
|
|
4
4
|
* Command handler for `vcs commit -m "message"`.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Ingests staged items and co-located .ib.*.md comment metadata files,
|
|
6
|
+
* attaches comments to corresponding file and folder b2tfs_items,
|
|
7
|
+
* attaches overall commit message to root b2tfs_item, evolves root item,
|
|
8
|
+
* advances B2tFSBranchIbGib_V1 with committedPaths, and clears index staging area.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import path from 'node:path';
|
|
11
12
|
import fs from 'node:fs/promises';
|
|
13
|
+
import { Dirent } from 'node:fs';
|
|
12
14
|
import crypto from 'node:crypto';
|
|
13
15
|
|
|
14
16
|
import { getUUID, extractErrorMsg, delay } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
@@ -16,19 +18,18 @@ import { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
|
|
|
16
18
|
import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
|
|
17
19
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
18
20
|
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
21
|
import { createCommentIbGib } from '@ibgib/core-gib/dist/common/comment/comment-helper.mjs';
|
|
21
22
|
import { CommentIbGib_V1 } from '@ibgib/core-gib/dist/common/comment/comment-types.mjs';
|
|
22
23
|
|
|
23
24
|
import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
|
|
24
25
|
import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
|
|
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
27
|
import { getIbGibWithMetaspaceFallback, createB2tFSItemIbGib, evolveB2tFSItemIbGib } from '../../engine/item/item-helper.mjs';
|
|
28
|
-
import { getB2tFSBranchSpaceName } from '../../engine/branch/branch-helper.mjs';
|
|
28
|
+
import { getB2tFSBranchSpaceName, resolveContextualBranchName } 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 { isCommentMetadataFilename, extractCommentTopicName } from '../../engine/status/status-helper.mjs';
|
|
32
33
|
import { TransformResult } from '@ibgib/ts-gib/dist/types.mjs';
|
|
33
34
|
import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
34
35
|
import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
|
|
@@ -45,7 +46,7 @@ export interface CommitCmdOpts {
|
|
|
45
46
|
|
|
46
47
|
export interface CommitCmdResult {
|
|
47
48
|
commitAddr: string;
|
|
48
|
-
commentAddr
|
|
49
|
+
commentAddr?: string;
|
|
49
50
|
branchName: string;
|
|
50
51
|
committedPaths: string[];
|
|
51
52
|
rootItemAddr: string;
|
|
@@ -78,18 +79,18 @@ export async function executeCommitCmd({
|
|
|
78
79
|
throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d)`);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
// Validate message
|
|
82
|
-
if (!message || message.trim().length === 0) {
|
|
83
|
-
throw new Error(`Commit message is required (E: 2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e)`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
82
|
// Fetch authoritative repository index from localSpace
|
|
87
83
|
const indexIbGib = await getB2tFSIndexIbGib({ space: localSpace, vcsPath });
|
|
88
84
|
if (!indexIbGib) {
|
|
89
85
|
throw new Error(`Repository index B2tFSIndexIbGib_V1 not found in localSpace (E: 3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f)`);
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
const branchName =
|
|
88
|
+
const branchName = resolveContextualBranchName({
|
|
89
|
+
targetDir: absTargetDir,
|
|
90
|
+
repoRoot,
|
|
91
|
+
indexIbGib,
|
|
92
|
+
});
|
|
93
|
+
const activeWorktreeDir = branchName === 'main' ? repoRoot : path.join(repoRoot, 'b', branchName);
|
|
93
94
|
|
|
94
95
|
// Target active branch space b2s_<activeBranch>
|
|
95
96
|
const { space: activeSpace } = await getOrCreateBranchSpace({
|
|
@@ -98,24 +99,22 @@ export async function executeCommitCmd({
|
|
|
98
99
|
branchName,
|
|
99
100
|
});
|
|
100
101
|
|
|
101
|
-
//
|
|
102
|
-
const stagedPathsMap = indexIbGib.data?.stagedPaths || {};
|
|
103
|
-
const stagedAddrs = indexIbGib.data?.staged_item || [];
|
|
102
|
+
// Verify there are staged items
|
|
103
|
+
const stagedPathsMap: { [relPath: string]: B2tFSStagedItemRecord } = { ...(indexIbGib.data?.stagedPaths || {}) };
|
|
104
104
|
const committedPaths = Object.keys(stagedPathsMap);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
throw new Error(`Nothing to commit (no staged items) (E: 4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a)`);
|
|
105
|
+
if (committedPaths.length === 0) {
|
|
106
|
+
throw new Error(`Nothing to commit (no staged items found in index) (E: 4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a)`);
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
//
|
|
109
|
+
// Find active branch node
|
|
110
|
+
let currentBranchIbGib: B2tFSBranchIbGib_V1 | undefined;
|
|
111
|
+
let currentBranchAddr: string | undefined;
|
|
112
|
+
|
|
111
113
|
const branchAddrs = [
|
|
112
114
|
...(indexIbGib.rel8ns?.branch || []),
|
|
113
115
|
...(indexIbGib.rel8ns?.branch_tjp || []),
|
|
114
116
|
];
|
|
115
117
|
|
|
116
|
-
let currentBranchIbGib: B2tFSBranchIbGib_V1 | undefined;
|
|
117
|
-
let currentBranchAddr: string | undefined;
|
|
118
|
-
|
|
119
118
|
if (branchAddrs.length > 0) {
|
|
120
119
|
let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
|
|
121
120
|
for (const bAddr of branchAddrs) {
|
|
@@ -133,35 +132,54 @@ export async function executeCommitCmd({
|
|
|
133
132
|
(b) => (b as B2tFSBranchIbGib_V1)?.data?.name === branchName,
|
|
134
133
|
) as B2tFSBranchIbGib_V1 || (branchIbGibs.at(-1) as B2tFSBranchIbGib_V1);
|
|
135
134
|
|
|
136
|
-
|
|
137
135
|
if (currentBranchIbGib) {
|
|
138
136
|
currentBranchAddr = getIbGibAddr({ ibGib: currentBranchIbGib });
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
139
|
|
|
142
|
-
const currentRootAddr = currentBranchIbGib?.rel8ns?.b2tfs_item?.at(-1);
|
|
143
|
-
|
|
140
|
+
const currentRootAddr = currentBranchIbGib?.rel8ns?.b2tFS_item?.at(-1) || currentBranchIbGib?.rel8ns?.b2tfs_item?.at(-1);
|
|
144
141
|
|
|
145
|
-
// 1.
|
|
146
|
-
const commentRes = await createCommentIbGib({
|
|
147
|
-
text: message.trim(),
|
|
148
|
-
saveInSpace: true,
|
|
149
|
-
space: activeSpace,
|
|
150
|
-
});
|
|
151
|
-
const commentIbGib = commentRes.newIbGib as CommentIbGib_V1;
|
|
152
|
-
const commentAddr = getIbGibAddr({ ibGib: commentIbGib });
|
|
153
|
-
await metaspace.registerNewIbGib({ ibGib: commentIbGib, space: activeSpace });
|
|
154
|
-
|
|
155
|
-
// 2. Evolve root folder item graph with staged items in activeSpace
|
|
142
|
+
// 1. Evolve root item graph with staged items and ingest co-located .ib.*.md comment files
|
|
156
143
|
const repoName = path.basename(repoRoot) || 'root';
|
|
157
|
-
|
|
144
|
+
let { newRootItem, newRootAddr, ingestedCommentCount } = await commitCmd_buildEvolvedRootItem({
|
|
158
145
|
currentRootAddr,
|
|
159
146
|
stagedPathsMap,
|
|
160
147
|
space: activeSpace,
|
|
161
148
|
metaspace,
|
|
162
149
|
repoName,
|
|
150
|
+
worktreeDir: activeWorktreeDir,
|
|
163
151
|
});
|
|
164
152
|
|
|
153
|
+
if ((!message || message.trim().length === 0) && ingestedCommentCount === 0) {
|
|
154
|
+
throw new Error(`Commit message is required (-m "message" or companion comment files) (E: 2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e)`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 2. If CLI message is provided, attach CommentIbGib to the root b2tfs item
|
|
158
|
+
let commentAddr: string | undefined;
|
|
159
|
+
if (message && message.trim().length > 0) {
|
|
160
|
+
const commentRes = await createCommentIbGib({
|
|
161
|
+
text: message.trim(),
|
|
162
|
+
saveInSpace: true,
|
|
163
|
+
space: activeSpace,
|
|
164
|
+
});
|
|
165
|
+
const commentIbGib = commentRes.newIbGib as CommentIbGib_V1;
|
|
166
|
+
commentAddr = getIbGibAddr({ ibGib: commentIbGib });
|
|
167
|
+
await metaspace.registerNewIbGib({ ibGib: commentIbGib, space: activeSpace });
|
|
168
|
+
|
|
169
|
+
const existingComments = newRootItem.rel8ns?.comment || [];
|
|
170
|
+
const evolveRootRes = await evolveB2tFSItemIbGib({
|
|
171
|
+
existingItem: newRootItem,
|
|
172
|
+
rel8nsToUpdate: {
|
|
173
|
+
comment: [...existingComments, commentAddr],
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
await persistTransformResult({ resTransform: evolveRootRes, space: activeSpace });
|
|
177
|
+
await metaspace.registerNewIbGib({ ibGib: evolveRootRes.newIbGib, space: activeSpace });
|
|
178
|
+
|
|
179
|
+
newRootItem = evolveRootRes.newIbGib as B2tFSItemIbGib_V1;
|
|
180
|
+
newRootAddr = getIbGibAddr({ ibGib: newRootItem });
|
|
181
|
+
}
|
|
182
|
+
|
|
165
183
|
// 3. Advance B2tFSBranchIbGib_V1 node inside activeSpace
|
|
166
184
|
const branchUuid = currentBranchIbGib?.data?.uuid || (await getUUID());
|
|
167
185
|
const newBranchRes = await Factory_V1.firstGen({
|
|
@@ -173,10 +191,11 @@ export async function executeCommitCmd({
|
|
|
173
191
|
uuid: branchUuid,
|
|
174
192
|
classname: 'B2tFSBranchIbGib_V1',
|
|
175
193
|
committedAt: new Date().toISOString(),
|
|
194
|
+
committedPaths,
|
|
176
195
|
},
|
|
177
196
|
rel8ns: {
|
|
178
197
|
b2tfs_item: [newRootAddr],
|
|
179
|
-
comment: [commentAddr],
|
|
198
|
+
...(commentAddr ? { comment: [commentAddr] } : {}),
|
|
180
199
|
},
|
|
181
200
|
});
|
|
182
201
|
|
|
@@ -212,7 +231,8 @@ export async function executeCommitCmd({
|
|
|
212
231
|
|
|
213
232
|
if (!quiet) {
|
|
214
233
|
const shortGib = newBranchIbGib.gib ? newBranchIbGib.gib.slice(0, 7) : 'commit';
|
|
215
|
-
|
|
234
|
+
const logMsg = message ? message.trim() : '(committed)';
|
|
235
|
+
console.log(`[${branchName} ${shortGib}] ${logMsg} (I: 5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b)`);
|
|
216
236
|
console.log(` ${committedPaths.length} file(s) changed`);
|
|
217
237
|
}
|
|
218
238
|
|
|
@@ -241,13 +261,16 @@ async function commitCmd_buildEvolvedRootItem({
|
|
|
241
261
|
space,
|
|
242
262
|
metaspace,
|
|
243
263
|
repoName,
|
|
264
|
+
worktreeDir,
|
|
244
265
|
}: {
|
|
245
266
|
currentRootAddr?: string;
|
|
246
267
|
stagedPathsMap: { [relPath: string]: B2tFSStagedItemRecord };
|
|
247
268
|
space: any;
|
|
248
269
|
metaspace: any;
|
|
249
270
|
repoName: string;
|
|
250
|
-
|
|
271
|
+
worktreeDir: string;
|
|
272
|
+
}): Promise<{ newRootItem: B2tFSItemIbGib_V1; newRootAddr: string; ingestedCommentCount: number }> {
|
|
273
|
+
let ingestedCommentCount = 0;
|
|
251
274
|
const itemsByRelPath = new Map<string, { itemAddr: string; fsType: 'file' | 'folder'; item?: B2tFSItemIbGib_V1 }>();
|
|
252
275
|
|
|
253
276
|
if (currentRootAddr) {
|
|
@@ -276,6 +299,63 @@ async function commitCmd_buildEvolvedRootItem({
|
|
|
276
299
|
});
|
|
277
300
|
}
|
|
278
301
|
|
|
302
|
+
// 1. Ingest co-located .ib.<filename>.md comment files for staged files
|
|
303
|
+
for (const [relPath, itemRec] of itemsByRelPath.entries()) {
|
|
304
|
+
if (itemRec.fsType !== 'file') { continue; }
|
|
305
|
+
|
|
306
|
+
const dir = path.dirname(relPath);
|
|
307
|
+
const base = path.basename(relPath);
|
|
308
|
+
const commentDir = dir === '.' ? worktreeDir : path.join(worktreeDir, dir);
|
|
309
|
+
const commentFullPath = path.join(commentDir, `.ib.${base}.md`);
|
|
310
|
+
|
|
311
|
+
let commentText = '';
|
|
312
|
+
try {
|
|
313
|
+
commentText = await fs.readFile(commentFullPath, 'utf8');
|
|
314
|
+
} catch {
|
|
315
|
+
// No comment file for this file
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (commentText.trim().length > 0) {
|
|
320
|
+
const commentRes = await createCommentIbGib({
|
|
321
|
+
text: commentText.trim(),
|
|
322
|
+
saveInSpace: true,
|
|
323
|
+
space,
|
|
324
|
+
});
|
|
325
|
+
const commentIbGib = commentRes.newIbGib as CommentIbGib_V1;
|
|
326
|
+
const commentAddr = getIbGibAddr({ ibGib: commentIbGib });
|
|
327
|
+
await metaspace.registerNewIbGib({ ibGib: commentIbGib, space });
|
|
328
|
+
ingestedCommentCount++;
|
|
329
|
+
|
|
330
|
+
const currentItem = itemRec.item || (await getIbGibWithMetaspaceFallback({
|
|
331
|
+
addr: itemRec.itemAddr,
|
|
332
|
+
space,
|
|
333
|
+
metaspace,
|
|
334
|
+
}) as B2tFSItemIbGib_V1);
|
|
335
|
+
|
|
336
|
+
if (currentItem) {
|
|
337
|
+
const existingComments = currentItem.rel8ns?.comment || [];
|
|
338
|
+
const evolvedItemRes = await evolveB2tFSItemIbGib({
|
|
339
|
+
existingItem: currentItem,
|
|
340
|
+
rel8nsToUpdate: {
|
|
341
|
+
comment: [...existingComments, commentAddr],
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
await persistTransformResult({ resTransform: evolvedItemRes, space });
|
|
345
|
+
await metaspace.registerNewIbGib({ ibGib: evolvedItemRes.newIbGib, space });
|
|
346
|
+
|
|
347
|
+
itemRec.itemAddr = getIbGibAddr({ ibGib: evolvedItemRes.newIbGib });
|
|
348
|
+
itemRec.item = evolvedItemRes.newIbGib as B2tFSItemIbGib_V1;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
await fs.unlink(commentFullPath);
|
|
353
|
+
} catch {
|
|
354
|
+
// Delete failed / ignore
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
279
359
|
// Ensure parent folder entries exist for all items
|
|
280
360
|
const allRelPaths = Array.from(itemsByRelPath.keys());
|
|
281
361
|
for (const relPath of allRelPaths) {
|
|
@@ -331,6 +411,46 @@ async function commitCmd_buildEvolvedRootItem({
|
|
|
331
411
|
}
|
|
332
412
|
}
|
|
333
413
|
|
|
414
|
+
// Check for folder-level .ib.<topic>.md files in this folder
|
|
415
|
+
const folderDiskPath = folderRelPath ? path.join(worktreeDir, folderRelPath) : worktreeDir;
|
|
416
|
+
const folderCommentAddrs: string[] = [];
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
const dirents = await fs.readdir(folderDiskPath, { withFileTypes: true });
|
|
420
|
+
for (const dirent of dirents) {
|
|
421
|
+
if (dirent.isFile() && isCommentMetadataFilename(dirent.name)) {
|
|
422
|
+
const topicName = extractCommentTopicName(dirent.name);
|
|
423
|
+
const isCoLocatedFileComment = itemsByRelPath.has(folderRelPath ? `${folderRelPath}/${topicName}` : topicName);
|
|
424
|
+
|
|
425
|
+
if (!isCoLocatedFileComment) {
|
|
426
|
+
const fullCommentPath = path.join(folderDiskPath, dirent.name);
|
|
427
|
+
const commentContent = await fs.readFile(fullCommentPath, 'utf8');
|
|
428
|
+
|
|
429
|
+
if (commentContent.trim().length > 0) {
|
|
430
|
+
const commentRes = await createCommentIbGib({
|
|
431
|
+
text: commentContent.trim(),
|
|
432
|
+
saveInSpace: true,
|
|
433
|
+
space,
|
|
434
|
+
});
|
|
435
|
+
const commentIbGib = commentRes.newIbGib as CommentIbGib_V1;
|
|
436
|
+
const cAddr = getIbGibAddr({ ibGib: commentIbGib });
|
|
437
|
+
await metaspace.registerNewIbGib({ ibGib: commentIbGib, space });
|
|
438
|
+
folderCommentAddrs.push(cAddr);
|
|
439
|
+
ingestedCommentCount++;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
await fs.unlink(fullCommentPath);
|
|
444
|
+
} catch {
|
|
445
|
+
// ignore delete error
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
} catch {
|
|
451
|
+
// Folder may not exist on disk
|
|
452
|
+
}
|
|
453
|
+
|
|
334
454
|
const folderName = folderRelPath ? path.basename(folderRelPath) : repoName;
|
|
335
455
|
const nameHash = crypto.createHash('sha256').update(folderName).digest('hex');
|
|
336
456
|
|
|
@@ -341,13 +461,17 @@ async function commitCmd_buildEvolvedRootItem({
|
|
|
341
461
|
if (existingFolderRec && existingFolderRec.item) {
|
|
342
462
|
const oldChildAddrs = existingFolderRec.item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
|
|
343
463
|
const toRemoveChildAddrs = oldChildAddrs.filter(a => !childAddrs.includes(a));
|
|
464
|
+
const existingComments = existingFolderRec.item.rel8ns?.comment || [];
|
|
344
465
|
|
|
345
466
|
folderRes = await evolveB2tFSItemIbGib({
|
|
346
467
|
existingItem: existingFolderRec.item,
|
|
347
468
|
dataToUpdate: {
|
|
348
469
|
nameHash,
|
|
349
470
|
},
|
|
350
|
-
rel8nsToUpdate:
|
|
471
|
+
rel8nsToUpdate: {
|
|
472
|
+
...(childAddrs.length > 0 ? { [B2TFS_CHILD_ITEM_REL8N_NAME]: childAddrs } : {}),
|
|
473
|
+
...(folderCommentAddrs.length > 0 ? { comment: [...existingComments, ...folderCommentAddrs] } : {}),
|
|
474
|
+
},
|
|
351
475
|
rel8nsToRemove: toRemoveChildAddrs.length > 0 ? { [B2TFS_CHILD_ITEM_REL8N_NAME]: toRemoveChildAddrs } : undefined,
|
|
352
476
|
});
|
|
353
477
|
} else {
|
|
@@ -357,7 +481,10 @@ async function commitCmd_buildEvolvedRootItem({
|
|
|
357
481
|
data: {
|
|
358
482
|
nameHash,
|
|
359
483
|
},
|
|
360
|
-
rel8ns:
|
|
484
|
+
rel8ns: {
|
|
485
|
+
...(childAddrs.length > 0 ? { [B2TFS_CHILD_ITEM_REL8N_NAME]: childAddrs } : {}),
|
|
486
|
+
...(folderCommentAddrs.length > 0 ? { comment: folderCommentAddrs } : {}),
|
|
487
|
+
},
|
|
361
488
|
});
|
|
362
489
|
}
|
|
363
490
|
|
|
@@ -374,7 +501,7 @@ async function commitCmd_buildEvolvedRootItem({
|
|
|
374
501
|
}
|
|
375
502
|
}
|
|
376
503
|
|
|
377
|
-
return { newRootItem: rootFolderItem!, newRootAddr: rootFolderAddr };
|
|
504
|
+
return { newRootItem: rootFolderItem!, newRootAddr: rootFolderAddr, ingestedCommentCount };
|
|
378
505
|
}
|
|
379
506
|
|
|
380
507
|
function commitCmd_getParentRelPath(relPath: string): string {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module commit-log-cmd.respec
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Unit tests for `vcs commit` and `vcs log` command handlers.
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -8,9 +8,11 @@ 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, ifWeMight, iReckon
|
|
12
12
|
} from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
13
13
|
|
|
14
|
+
import { getDependencyGraph } from '@ibgib/core-gib/dist/common/other/graph-helper.mjs';
|
|
15
|
+
import { getFromSpace } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
|
|
14
16
|
import { executeInitCmd } from './init-cmd.mjs';
|
|
15
17
|
import { executeAddCmd } from './add-cmd.mjs';
|
|
16
18
|
import { executeStatusCmd } from './status-cmd.mjs';
|
|
@@ -18,8 +20,12 @@ import { executeCommitCmd } from './commit-cmd.mjs';
|
|
|
18
20
|
import { executeLogCmd } from './log-cmd.mjs';
|
|
19
21
|
import { executeBranchCmd } from './branch-cmd.mjs';
|
|
20
22
|
import { executeCheckoutCmd } from './checkout-cmd.mjs';
|
|
23
|
+
import { executeMergeCmd } from './merge-cmd.mjs';
|
|
24
|
+
import { executeGenerateCommentCmd } from './generate-comment-cmd.mjs';
|
|
21
25
|
import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
|
|
22
26
|
import { getB2tFSIndexIbGib } from '../../engine/index/index-helper.mjs';
|
|
27
|
+
import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
|
|
28
|
+
import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
|
|
23
29
|
|
|
24
30
|
const lc = `[apps/vcs-gib/src/cli/commands/commit-log-cmd.respec.node.mts]`;
|
|
25
31
|
|
|
@@ -267,8 +273,215 @@ await respecfully(lc, `vcs commit and vcs log Command Handlers`, async () => {
|
|
|
267
273
|
});
|
|
268
274
|
|
|
269
275
|
iReckon(lc, logRes.branchName).willEqual('feature-log');
|
|
270
|
-
iReckon(lc, logRes.commits.length).willEqual(
|
|
276
|
+
iReckon(lc, logRes.commits.length).willEqual(2);
|
|
271
277
|
iReckon(lc, logRes.commits[0].message).willEqual('Feature branch commit');
|
|
278
|
+
iReckon(lc, logRes.commits[1].message).willEqual('Main branch commit');
|
|
279
|
+
} finally {
|
|
280
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
await ifWe(lc, `generates companion comment files, displays in status, ingests on commit, and projects in file log`, async () => {
|
|
285
|
+
const testDir = path.join(process.cwd(), '.test_commit_log_companion');
|
|
286
|
+
const vcsFolderName = '.vcsgib_test_commit_companion';
|
|
287
|
+
try {
|
|
288
|
+
await executeInitCmd({
|
|
289
|
+
targetDir: testDir,
|
|
290
|
+
vcsFolderName,
|
|
291
|
+
quiet: true,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const filePath = path.join(testDir, 'app.ts');
|
|
295
|
+
await fs.writeFile(filePath, 'console.log("hello world");', 'utf8');
|
|
296
|
+
|
|
297
|
+
// 1. Test generate-comment
|
|
298
|
+
const genRes = await executeGenerateCommentCmd({
|
|
299
|
+
targetDir: testDir,
|
|
300
|
+
vcsFolderName,
|
|
301
|
+
paths: ['app.ts'],
|
|
302
|
+
quiet: true,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
iReckon(lc, genRes.createdFiles.length).willEqual(1);
|
|
306
|
+
iReckon(lc, genRes.createdFiles[0]).includes('.ib.app.ts.md');
|
|
307
|
+
|
|
308
|
+
const companionPath = path.join(testDir, '.ib.app.ts.md');
|
|
309
|
+
let companionExists = false;
|
|
310
|
+
try {
|
|
311
|
+
await fs.access(companionPath);
|
|
312
|
+
companionExists = true;
|
|
313
|
+
} catch { }
|
|
314
|
+
iReckon(lc, companionExists).isGonnaBeTrue();
|
|
315
|
+
|
|
316
|
+
// Write custom notes into companion file
|
|
317
|
+
await fs.writeFile(companionPath, 'feat: initial implementation of app.ts\n* added console log', 'utf8');
|
|
318
|
+
|
|
319
|
+
// Write folder-level notes
|
|
320
|
+
const folderCompanionPath = path.join(testDir, '.ib.architecture.md');
|
|
321
|
+
await fs.writeFile(folderCompanionPath, 'Arch overview: client-side entrypoint', 'utf8');
|
|
322
|
+
|
|
323
|
+
// Stage app.ts
|
|
324
|
+
await executeAddCmd({
|
|
325
|
+
paths: ['app.ts'],
|
|
326
|
+
targetDir: testDir,
|
|
327
|
+
vcsFolderName,
|
|
328
|
+
quiet: true,
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// 2. Test status with comments
|
|
332
|
+
const statusRes = await executeStatusCmd({
|
|
333
|
+
targetDir: testDir,
|
|
334
|
+
vcsFolderName,
|
|
335
|
+
comments: true,
|
|
336
|
+
quiet: true,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
iReckon(lc, statusRes.pendingComments.length).willEqual(2);
|
|
340
|
+
const fileComment = statusRes.pendingComments.find(c => c.targetType === 'file');
|
|
341
|
+
iReckon(lc, Boolean(fileComment)).isGonnaBeTrue();
|
|
342
|
+
iReckon(lc, fileComment?.content).includes('initial implementation of app.ts');
|
|
343
|
+
|
|
344
|
+
// 3. Commit staged files and notes
|
|
345
|
+
const commitRes = await executeCommitCmd({
|
|
346
|
+
message: 'Initial root commit',
|
|
347
|
+
targetDir: testDir,
|
|
348
|
+
vcsFolderName,
|
|
349
|
+
quiet: true,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
iReckon(lc, commitRes.committedPaths).includes('app.ts');
|
|
353
|
+
|
|
354
|
+
// Verify companion files were consumed and removed
|
|
355
|
+
let companionStillExists = false;
|
|
356
|
+
try {
|
|
357
|
+
await fs.access(companionPath);
|
|
358
|
+
companionStillExists = true;
|
|
359
|
+
} catch { }
|
|
360
|
+
iReckon(lc, companionStillExists).isGonnaBeFalse();
|
|
361
|
+
|
|
362
|
+
let folderCompanionStillExists = false;
|
|
363
|
+
try {
|
|
364
|
+
await fs.access(folderCompanionPath);
|
|
365
|
+
folderCompanionStillExists = true;
|
|
366
|
+
} catch { }
|
|
367
|
+
iReckon(lc, folderCompanionStillExists).isGonnaBeFalse();
|
|
368
|
+
|
|
369
|
+
// 4. Test file-specific log (vcs log app.ts)
|
|
370
|
+
const fileLogRes = await executeLogCmd({
|
|
371
|
+
targetDir: testDir,
|
|
372
|
+
vcsFolderName,
|
|
373
|
+
filePath: 'app.ts',
|
|
374
|
+
quiet: true,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
iReckon(lc, fileLogRes.commits.length >= 1).isGonnaBeTrue();
|
|
378
|
+
iReckon(lc, fileLogRes.commits[0].message).includes('initial implementation of app.ts');
|
|
379
|
+
|
|
380
|
+
// 5. Test root log with allComments
|
|
381
|
+
const rootLogRes = await executeLogCmd({
|
|
382
|
+
targetDir: testDir,
|
|
383
|
+
vcsFolderName,
|
|
384
|
+
allComments: true,
|
|
385
|
+
quiet: true,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
iReckon(lc, rootLogRes.commits.length).willEqual(1);
|
|
389
|
+
iReckon(lc, rootLogRes.commits[0].message).willEqual('Initial root commit');
|
|
390
|
+
iReckon(lc, rootLogRes.commits[0].committedPaths).includes('app.ts');
|
|
391
|
+
} finally {
|
|
392
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
await ifWe(lc, `preserves and unions file comments across 3-way branch merges`, async () => {
|
|
397
|
+
const testDir = path.join(process.cwd(), '.test_commit_log_merge_comments');
|
|
398
|
+
const vcsFolderName = '.vcsgib_test_commit_merge_comments';
|
|
399
|
+
try {
|
|
400
|
+
await executeInitCmd({
|
|
401
|
+
targetDir: testDir,
|
|
402
|
+
vcsFolderName,
|
|
403
|
+
quiet: true,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Base commit on main
|
|
407
|
+
const appFile = path.join(testDir, 'app.ts');
|
|
408
|
+
await fs.writeFile(appFile, 'const a = 1;\nconst b = 2;\n', 'utf8');
|
|
409
|
+
await executeAddCmd({ paths: ['app.ts'], targetDir: testDir, vcsFolderName, quiet: true });
|
|
410
|
+
await executeCommitCmd({ message: 'Base commit on main', targetDir: testDir, vcsFolderName, quiet: true });
|
|
411
|
+
|
|
412
|
+
// Create feature branch
|
|
413
|
+
await executeBranchCmd({ branchName: 'feature-comments', targetDir: testDir, vcsFolderName, quiet: true });
|
|
414
|
+
const checkoutRes = await executeCheckoutCmd({ branchName: 'feature-comments', targetDir: testDir, vcsFolderName, quiet: true });
|
|
415
|
+
|
|
416
|
+
// On feature branch: modify app.ts and attach companion note
|
|
417
|
+
const featureAppFile = path.join(checkoutRes.worktreePath, 'app.ts');
|
|
418
|
+
await fs.writeFile(featureAppFile, 'const a = 1;\nconst b = 200;\n', 'utf8');
|
|
419
|
+
const featureNoteFile = path.join(checkoutRes.worktreePath, '.ib.app.ts.md');
|
|
420
|
+
await fs.writeFile(featureNoteFile, 'feat(feature-comments): updated b to 200', 'utf8');
|
|
421
|
+
|
|
422
|
+
await executeAddCmd({ paths: ['app.ts'], targetDir: checkoutRes.worktreePath, vcsFolderName, quiet: true });
|
|
423
|
+
await executeCommitCmd({ message: 'Feature branch commit', targetDir: checkoutRes.worktreePath, vcsFolderName, quiet: true });
|
|
424
|
+
|
|
425
|
+
// Switch back to main branch
|
|
426
|
+
await executeCheckoutCmd({ branchName: 'main', targetDir: testDir, vcsFolderName, quiet: true });
|
|
427
|
+
|
|
428
|
+
// On main branch: modify app.ts and attach companion note
|
|
429
|
+
await fs.writeFile(appFile, 'const a = 100;\nconst b = 2;\n', 'utf8');
|
|
430
|
+
const mainNoteFile = path.join(testDir, '.ib.app.ts.md');
|
|
431
|
+
await fs.writeFile(mainNoteFile, 'feat(main): updated a to 100', 'utf8');
|
|
432
|
+
|
|
433
|
+
await executeAddCmd({ paths: ['app.ts'], targetDir: testDir, vcsFolderName, quiet: true });
|
|
434
|
+
await executeCommitCmd({ message: 'Main branch update', targetDir: testDir, vcsFolderName, quiet: true });
|
|
435
|
+
|
|
436
|
+
// Merge feature-comments into main
|
|
437
|
+
await executeMergeCmd({ incomingBranchName: 'feature-comments', targetDir: testDir, vcsFolderName, quiet: true });
|
|
438
|
+
|
|
439
|
+
// Check file log on main: both comments should be present!
|
|
440
|
+
const mergedFileLog = await executeLogCmd({
|
|
441
|
+
targetDir: testDir,
|
|
442
|
+
vcsFolderName,
|
|
443
|
+
filePath: 'app.ts',
|
|
444
|
+
quiet: true,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const allMessages = mergedFileLog.commits.map(c => c.message).join(' -- ');
|
|
448
|
+
iReckon(lc, allMessages).includes('updated b to 200');
|
|
449
|
+
iReckon(lc, allMessages).includes('updated a to 100');
|
|
450
|
+
|
|
451
|
+
// Verify getDependencyGraph works 100% within main branch space alone (no metaspace fallback)
|
|
452
|
+
const { metaspace, vcsPath } = await bootstrapVcsMetaspace({
|
|
453
|
+
repoPath: testDir,
|
|
454
|
+
vcsFolderName,
|
|
455
|
+
});
|
|
456
|
+
const { space: mainSpace } = await getOrCreateBranchSpace({
|
|
457
|
+
metaspace,
|
|
458
|
+
vcsPath,
|
|
459
|
+
branchName: 'main',
|
|
460
|
+
});
|
|
461
|
+
const indexIbGib = await getB2tFSIndexIbGib({ space: metaspace.zeroSpace, vcsPath });
|
|
462
|
+
const mainBranchAddr = indexIbGib?.rel8ns?.branch?.at(-1) || indexIbGib?.rel8ns?.branch_tjp?.at(-1);
|
|
463
|
+
const mainBranch = await getIbGibWithMetaspaceFallback({ addr: mainBranchAddr!, space: mainSpace, metaspace });
|
|
464
|
+
const mainRootAddr = mainBranch?.rel8ns?.b2tfs_item?.at(-1);
|
|
465
|
+
const mainRoot = await getIbGibWithMetaspaceFallback({ addr: mainRootAddr!, space: mainSpace, metaspace });
|
|
466
|
+
let appItem: any;
|
|
467
|
+
for (const childAddr of mainRoot?.rel8ns?.b2tfs_child || []) {
|
|
468
|
+
const child = await getIbGibWithMetaspaceFallback({ addr: childAddr, space: mainSpace, metaspace });
|
|
469
|
+
if (child?.data?.name === 'app.ts') {
|
|
470
|
+
appItem = child;
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
iReckon(lc, appItem).isGonnaBeTruthy();
|
|
475
|
+
|
|
476
|
+
// Ensure getDependencyGraph runs strictly within mainSpace with NO metaspace fallback
|
|
477
|
+
const depGraph = await getDependencyGraph({
|
|
478
|
+
ibGib: appItem,
|
|
479
|
+
space: mainSpace,
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
const graphValues = Object.values(depGraph);
|
|
483
|
+
const foundCommentB = graphValues.some(x => (x.data as any)?.text === 'feat(feature-comments): updated b to 200');
|
|
484
|
+
iReckon(lc, foundCommentB).isGonnaBeTruthy();
|
|
272
485
|
} finally {
|
|
273
486
|
await fs.rm(testDir, { recursive: true, force: true });
|
|
274
487
|
}
|