@ibgib/vcs-gib 0.0.23 → 0.0.26
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 +19 -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 +5 -2
- 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 +149 -33
- 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 +236 -38
- 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 +50 -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/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 +89 -0
- package/dist/engine/status/status-helper.d.mts.map +1 -0
- package/dist/engine/status/status-helper.mjs +285 -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 +5 -2
- package/src/cli/commands/commit-cmd.mts +165 -42
- 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 +288 -41
- package/src/cli/commands/status-cmd.mts +57 -256
- package/src/cli/vcs-cli-main.mts +25 -4
- 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 +380 -0
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -7,30 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import fs from 'node:fs/promises';
|
|
10
|
-
import { Dirent } from 'node:fs';
|
|
11
|
-
import crypto from 'node:crypto';
|
|
12
10
|
|
|
13
11
|
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
14
|
-
import { getIbGibsFromCache_fallbackToSpaces } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
|
|
15
|
-
|
|
16
12
|
import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
|
|
17
|
-
import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
|
|
18
|
-
import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
|
|
19
|
-
import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
|
|
20
|
-
import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
|
|
21
|
-
import { isPathFiltered, parseFilterFileContent } from '../../engine/filter/glob-helper.mjs';
|
|
22
|
-
import { getB2tFSIndexIbGib } from '../../engine/index/index-helper.mjs';
|
|
23
|
-
import { B2tFSStagedItemRecord } from '../../engine/index/index-types.mjs';
|
|
24
13
|
import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
|
|
14
|
+
import { getRepoStatus, VcsStatusItem, PendingCommentInfo } from '../../engine/status/status-helper.mjs';
|
|
25
15
|
|
|
26
16
|
const lcFile = `[status-cmd]`;
|
|
27
17
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
28
18
|
|
|
29
|
-
export
|
|
30
|
-
path: string;
|
|
31
|
-
fsType: 'file' | 'folder';
|
|
32
|
-
status: 'untracked' | 'modified' | 'deleted' | 'staged';
|
|
33
|
-
}
|
|
19
|
+
export { VcsStatusItem, PendingCommentInfo };
|
|
34
20
|
|
|
35
21
|
export interface StatusCmdResult {
|
|
36
22
|
branchName: string;
|
|
@@ -38,18 +24,21 @@ export interface StatusCmdResult {
|
|
|
38
24
|
modified: VcsStatusItem[];
|
|
39
25
|
deleted: VcsStatusItem[];
|
|
40
26
|
untracked: VcsStatusItem[];
|
|
27
|
+
pendingComments: PendingCommentInfo[];
|
|
41
28
|
}
|
|
42
29
|
|
|
43
30
|
export interface StatusCmdOpts {
|
|
44
31
|
targetDir?: string;
|
|
45
32
|
vcsFolderName?: string;
|
|
46
33
|
quiet?: boolean;
|
|
34
|
+
comments?: boolean;
|
|
47
35
|
}
|
|
48
36
|
|
|
49
37
|
export async function executeStatusCmd({
|
|
50
38
|
targetDir = process.cwd(),
|
|
51
39
|
vcsFolderName = '.vcsgib',
|
|
52
40
|
quiet = false,
|
|
41
|
+
comments = false,
|
|
53
42
|
}: StatusCmdOpts = {}): Promise<StatusCmdResult> {
|
|
54
43
|
const lc = `${lcFile}[${executeStatusCmd.name}]`;
|
|
55
44
|
|
|
@@ -72,170 +61,88 @@ export async function executeStatusCmd({
|
|
|
72
61
|
throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 4d5e6f7a8b9c10d11e12f13a14b15c6d)`);
|
|
73
62
|
}
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
const indexIbGib = await getB2tFSIndexIbGib({ space: localSpace, vcsPath });
|
|
77
|
-
const branchName = indexIbGib?.data?.activeBranch || 'main';
|
|
78
|
-
|
|
79
|
-
const activeWorktreeDir = branchName === 'main' ? repoRoot : path.join(repoRoot, 'b', branchName);
|
|
80
|
-
|
|
81
|
-
// Target active branch space b2s_<activeBranch>
|
|
82
|
-
const { space: activeBranchSpace } = await getOrCreateBranchSpace({
|
|
64
|
+
const statusRes = await getRepoStatus({
|
|
83
65
|
metaspace,
|
|
66
|
+
localSpace,
|
|
84
67
|
vcsPath,
|
|
85
|
-
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// 2. Extract staged items directly from in-band B2tFSIndexIbGib_V1 data
|
|
89
|
-
const stagedMap = new Map<string, B2tFSStagedItemRecord>();
|
|
90
|
-
const indexStagedPaths = indexIbGib?.data?.stagedPaths || {};
|
|
91
|
-
for (const [relPath, itemRec] of Object.entries(indexStagedPaths)) {
|
|
92
|
-
stagedMap.set(relPath, itemRec);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 3. Find active branch root item address from in-band branch graph node
|
|
96
|
-
let rootItemAddr: string | undefined;
|
|
97
|
-
const branchAddrs = [
|
|
98
|
-
...(indexIbGib?.rel8ns?.branch || []),
|
|
99
|
-
...(indexIbGib?.rel8ns?.branch_tjp || []),
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
if (branchAddrs.length > 0) {
|
|
103
|
-
let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
|
|
104
|
-
for (const bAddr of branchAddrs) {
|
|
105
|
-
const bIbGib = await getIbGibWithMetaspaceFallback({
|
|
106
|
-
addr: bAddr,
|
|
107
|
-
space: activeBranchSpace,
|
|
108
|
-
metaspace,
|
|
109
|
-
});
|
|
110
|
-
if (bIbGib) {
|
|
111
|
-
branchIbGibs.push(bIbGib as B2tFSBranchIbGib_V1);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const activeBranchIbGib = branchIbGibs.find(
|
|
116
|
-
(b) => (b as B2tFSBranchIbGib_V1)?.data?.name === branchName,
|
|
117
|
-
) as B2tFSBranchIbGib_V1 || (branchIbGibs.at(0) as B2tFSBranchIbGib_V1);
|
|
118
|
-
|
|
119
|
-
rootItemAddr = activeBranchIbGib?.rel8ns?.b2tfs_item?.at(0);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Read .ibgibignore patterns
|
|
123
|
-
let filterPatterns: string[] = [];
|
|
124
|
-
const ignorePath = path.join(activeWorktreeDir, '.ibgibignore');
|
|
125
|
-
try {
|
|
126
|
-
const ignoreContent = await fs.readFile(ignorePath, 'utf8');
|
|
127
|
-
filterPatterns = parseFilterFileContent(ignoreContent);
|
|
128
|
-
} catch {
|
|
129
|
-
// Ignore file missing is fine
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 4. Walk active branch graph items
|
|
133
|
-
const branchItemsMap = new Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder' }>();
|
|
134
|
-
|
|
135
|
-
if (rootItemAddr) {
|
|
136
|
-
const rootItem = await getIbGibWithMetaspaceFallback({
|
|
137
|
-
addr: rootItemAddr,
|
|
138
|
-
space: activeBranchSpace,
|
|
139
|
-
metaspace,
|
|
140
|
-
}) as B2tFSItemIbGib_V1;
|
|
141
|
-
|
|
142
|
-
if (rootItem) {
|
|
143
|
-
await statusCmd_walkBranchGraph({
|
|
144
|
-
item: rootItem,
|
|
145
|
-
space: activeBranchSpace,
|
|
146
|
-
metaspace,
|
|
147
|
-
currentRelPath: '',
|
|
148
|
-
outMap: branchItemsMap,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// 5. Scan physical workspace
|
|
154
|
-
const physicalItemsMap = new Map<string, { relPath: string; fullPath: string; isDirectory: boolean; fileHash?: string }>();
|
|
155
|
-
await statusCmd_scanPhysicalWorkspace({
|
|
156
|
-
dirPath: activeWorktreeDir,
|
|
157
|
-
baseDir: activeWorktreeDir,
|
|
68
|
+
repoRoot,
|
|
158
69
|
vcsFolderName,
|
|
159
|
-
filterPatterns,
|
|
160
|
-
outMap: physicalItemsMap,
|
|
161
70
|
});
|
|
162
71
|
|
|
163
|
-
|
|
164
|
-
const staged: VcsStatusItem[] = [];
|
|
165
|
-
const modified: VcsStatusItem[] = [];
|
|
166
|
-
const deleted: VcsStatusItem[] = [];
|
|
167
|
-
const untracked: VcsStatusItem[] = [];
|
|
168
|
-
|
|
169
|
-
// Add staged items
|
|
170
|
-
for (const [relPath, stagedRec] of stagedMap.entries()) {
|
|
171
|
-
staged.push({
|
|
172
|
-
path: relPath,
|
|
173
|
-
fsType: stagedRec.fsType || 'file',
|
|
174
|
-
status: 'staged',
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Compare physical items against branch items and staged
|
|
179
|
-
for (const [relPath, physItem] of physicalItemsMap.entries()) {
|
|
180
|
-
if (stagedMap.has(relPath)) {
|
|
181
|
-
continue; // Staged item already reported
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const branchItem = branchItemsMap.get(relPath);
|
|
185
|
-
if (!branchItem) {
|
|
186
|
-
// Not in active branch snapshot and not staged => untracked
|
|
187
|
-
untracked.push({
|
|
188
|
-
path: relPath,
|
|
189
|
-
fsType: physItem.isDirectory ? 'folder' : 'file',
|
|
190
|
-
status: 'untracked',
|
|
191
|
-
});
|
|
192
|
-
} else {
|
|
193
|
-
// Tracked in branch -> check if modified
|
|
194
|
-
if (!physItem.isDirectory && physItem.fileHash && branchItem.fileHash && physItem.fileHash !== branchItem.fileHash) {
|
|
195
|
-
modified.push({
|
|
196
|
-
path: relPath,
|
|
197
|
-
fsType: 'file',
|
|
198
|
-
status: 'modified',
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
72
|
+
const { branchName, staged, modified, deleted, untracked, pendingComments } = statusRes;
|
|
203
73
|
|
|
204
|
-
|
|
205
|
-
for (const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
path: relPath,
|
|
210
|
-
fsType: branchItem.fsType,
|
|
211
|
-
status: 'deleted',
|
|
212
|
-
});
|
|
213
|
-
}
|
|
74
|
+
const commentsByTarget = new Map<string, PendingCommentInfo[]>();
|
|
75
|
+
for (const c of pendingComments) {
|
|
76
|
+
const list = commentsByTarget.get(c.targetRelPath) || [];
|
|
77
|
+
list.push(c);
|
|
78
|
+
commentsByTarget.set(c.targetRelPath, list);
|
|
214
79
|
}
|
|
215
80
|
|
|
216
81
|
if (!quiet) {
|
|
217
82
|
console.log(`On branch ${branchName} (I: 9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b)`);
|
|
83
|
+
|
|
218
84
|
if (staged.length > 0) {
|
|
219
85
|
console.log('\nChanges to be committed:');
|
|
220
86
|
for (const item of staged) {
|
|
87
|
+
const itemComments = commentsByTarget.get(item.path) || [];
|
|
221
88
|
console.log(` staged: ${item.path}`);
|
|
89
|
+
if (itemComments.length === 0) {
|
|
90
|
+
console.log(` (hint: no comment file found for ${item.path} - run 'vcs gmsg')`);
|
|
91
|
+
} else if (comments) {
|
|
92
|
+
for (const comm of itemComments) {
|
|
93
|
+
console.log(` [comment: ${comm.commentRelPath}]`);
|
|
94
|
+
const lines = comm.content.trim().split('\n');
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
console.log(` ${line}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
222
100
|
}
|
|
223
101
|
}
|
|
102
|
+
|
|
224
103
|
if (modified.length > 0 || deleted.length > 0) {
|
|
225
104
|
console.log('\nChanges not staged for commit:');
|
|
226
105
|
for (const item of modified) {
|
|
106
|
+
const itemComments = commentsByTarget.get(item.path) || [];
|
|
227
107
|
console.log(` modified: ${item.path}`);
|
|
108
|
+
if (comments && itemComments.length > 0) {
|
|
109
|
+
for (const comm of itemComments) {
|
|
110
|
+
console.log(` [comment: ${comm.commentRelPath}]`);
|
|
111
|
+
const lines = comm.content.trim().split('\n');
|
|
112
|
+
for (const line of lines) {
|
|
113
|
+
console.log(` ${line}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
228
117
|
}
|
|
229
118
|
for (const item of deleted) {
|
|
230
119
|
console.log(` deleted: ${item.path}`);
|
|
231
120
|
}
|
|
232
121
|
}
|
|
122
|
+
|
|
233
123
|
if (untracked.length > 0) {
|
|
234
124
|
console.log('\nUntracked files:');
|
|
235
125
|
for (const item of untracked) {
|
|
236
126
|
console.log(` untracked:${item.path}`);
|
|
237
127
|
}
|
|
238
128
|
}
|
|
129
|
+
|
|
130
|
+
// Pending folder-level comments
|
|
131
|
+
const folderComments = pendingComments.filter(c => c.targetType === 'folder');
|
|
132
|
+
if (folderComments.length > 0) {
|
|
133
|
+
console.log('\nPending folder comments:');
|
|
134
|
+
for (const fc of folderComments) {
|
|
135
|
+
const folderLabel = fc.targetRelPath ? fc.targetRelPath : '(root)';
|
|
136
|
+
console.log(` folder: ${folderLabel} [${fc.commentRelPath}]`);
|
|
137
|
+
if (comments) {
|
|
138
|
+
const lines = fc.content.trim().split('\n');
|
|
139
|
+
for (const line of lines) {
|
|
140
|
+
console.log(` ${line}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
239
146
|
if (staged.length === 0 && modified.length === 0 && deleted.length === 0 && untracked.length === 0) {
|
|
240
147
|
console.log('nothing to commit, working tree clean');
|
|
241
148
|
}
|
|
@@ -247,6 +154,7 @@ export async function executeStatusCmd({
|
|
|
247
154
|
modified,
|
|
248
155
|
deleted,
|
|
249
156
|
untracked,
|
|
157
|
+
pendingComments,
|
|
250
158
|
};
|
|
251
159
|
} catch (error) {
|
|
252
160
|
console.error(`${lc} ${extractErrorMsg(error)} (E: 5e6f7a8b9c10d11e12f13a14b15c6d7e)`);
|
|
@@ -257,110 +165,3 @@ export async function executeStatusCmd({
|
|
|
257
165
|
}
|
|
258
166
|
}
|
|
259
167
|
}
|
|
260
|
-
|
|
261
|
-
// #region statusCmd_walkBranchGraph
|
|
262
|
-
async function statusCmd_walkBranchGraph({
|
|
263
|
-
item,
|
|
264
|
-
space,
|
|
265
|
-
metaspace,
|
|
266
|
-
currentRelPath,
|
|
267
|
-
outMap,
|
|
268
|
-
}: {
|
|
269
|
-
item: B2tFSItemIbGib_V1;
|
|
270
|
-
space: any;
|
|
271
|
-
metaspace?: any;
|
|
272
|
-
currentRelPath: string;
|
|
273
|
-
outMap: Map<string, { relPath: string; fileHash?: string; fsType: 'file' | 'folder' }>;
|
|
274
|
-
}): Promise<void> {
|
|
275
|
-
if (!item || !item.data) { return; }
|
|
276
|
-
|
|
277
|
-
const { fsType, fileHash } = item.data;
|
|
278
|
-
if (currentRelPath) {
|
|
279
|
-
outMap.set(currentRelPath, { relPath: currentRelPath, fileHash, fsType });
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (fsType === 'folder') {
|
|
283
|
-
const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
|
|
284
|
-
for (const childAddr of childAddrs) {
|
|
285
|
-
const childItem = await getIbGibWithMetaspaceFallback({
|
|
286
|
-
addr: childAddr,
|
|
287
|
-
space,
|
|
288
|
-
metaspace,
|
|
289
|
-
}) as B2tFSItemIbGib_V1;
|
|
290
|
-
if (childItem && childItem.data) {
|
|
291
|
-
const childRelPath = currentRelPath
|
|
292
|
-
? `${currentRelPath}/${childItem.data.name}`
|
|
293
|
-
: childItem.data.name;
|
|
294
|
-
await statusCmd_walkBranchGraph({
|
|
295
|
-
item: childItem,
|
|
296
|
-
space,
|
|
297
|
-
metaspace,
|
|
298
|
-
currentRelPath: childRelPath,
|
|
299
|
-
outMap,
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
// #endregion statusCmd_walkBranchGraph
|
|
306
|
-
|
|
307
|
-
// #region statusCmd_scanPhysicalWorkspace
|
|
308
|
-
async function statusCmd_scanPhysicalWorkspace({
|
|
309
|
-
dirPath,
|
|
310
|
-
baseDir,
|
|
311
|
-
vcsFolderName,
|
|
312
|
-
filterPatterns,
|
|
313
|
-
outMap,
|
|
314
|
-
}: {
|
|
315
|
-
dirPath: string;
|
|
316
|
-
baseDir: string;
|
|
317
|
-
vcsFolderName: string;
|
|
318
|
-
filterPatterns: string[];
|
|
319
|
-
outMap: Map<string, { relPath: string; fullPath: string; isDirectory: boolean; fileHash?: string }>;
|
|
320
|
-
}): Promise<void> {
|
|
321
|
-
let entries: Dirent[] = [];
|
|
322
|
-
try {
|
|
323
|
-
entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
324
|
-
} catch {
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
for (const entry of entries) {
|
|
329
|
-
const fullEntryPath = path.join(dirPath, entry.name);
|
|
330
|
-
const relPath = path.relative(baseDir, fullEntryPath).replace(/\\/g, '/');
|
|
331
|
-
|
|
332
|
-
// Ignore vcs storage folder, .git, and worktrees folder b
|
|
333
|
-
if (
|
|
334
|
-
relPath === vcsFolderName || relPath.startsWith(vcsFolderName + '/') ||
|
|
335
|
-
relPath === '.git' || relPath.startsWith('.git/') ||
|
|
336
|
-
relPath === 'b' || relPath.startsWith('b/')
|
|
337
|
-
) {
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (isPathFiltered({ relativePath: relPath, isDirectory: entry.isDirectory(), filterPatterns })) {
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (entry.isDirectory()) {
|
|
346
|
-
outMap.set(relPath, { relPath, fullPath: fullEntryPath, isDirectory: true });
|
|
347
|
-
await statusCmd_scanPhysicalWorkspace({
|
|
348
|
-
dirPath: fullEntryPath,
|
|
349
|
-
baseDir,
|
|
350
|
-
vcsFolderName,
|
|
351
|
-
filterPatterns,
|
|
352
|
-
outMap,
|
|
353
|
-
});
|
|
354
|
-
} else if (entry.isFile()) {
|
|
355
|
-
let fileHash: string | undefined;
|
|
356
|
-
try {
|
|
357
|
-
const fileData = await fs.readFile(fullEntryPath);
|
|
358
|
-
fileHash = crypto.createHash('sha256').update(fileData).digest('hex');
|
|
359
|
-
} catch {
|
|
360
|
-
// File unreadable
|
|
361
|
-
}
|
|
362
|
-
outMap.set(relPath, { relPath, fullPath: fullEntryPath, isDirectory: false, fileHash });
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
// #endregion statusCmd_scanPhysicalWorkspace
|
package/src/cli/vcs-cli-main.mts
CHANGED
|
@@ -20,6 +20,7 @@ import { executeCheckoutCmd } from './commands/checkout-cmd.mjs';
|
|
|
20
20
|
import { executeMergeCmd } from './commands/merge-cmd.mjs';
|
|
21
21
|
import { executeResetCmd } from './commands/reset-cmd.mjs';
|
|
22
22
|
import { executeRestoreCmd } from './commands/restore-cmd.mjs';
|
|
23
|
+
import { executeGenerateCommentCmd } from './commands/generate-comment-cmd.mjs';
|
|
23
24
|
import { AUTO_GENERATED_VERSION } from '../AUTO-GENERATED-version.mjs';
|
|
24
25
|
import { GLOBAL_LOG_A_LOT } from '../vcs-gib-constants.mjs';
|
|
25
26
|
|
|
@@ -39,7 +40,8 @@ Usage:
|
|
|
39
40
|
|
|
40
41
|
Commands:
|
|
41
42
|
init [dir] Initialize a new ibgib repository
|
|
42
|
-
status Show repository working tree status
|
|
43
|
+
status Show repository working tree status (-c, --comments for comment text)
|
|
44
|
+
generate-comment Generate .ib.<file>.md metadata comment templates (synonyms: gmsg, gcomm, gen-msg, gen-comment)
|
|
43
45
|
add <path...> Stage file/folder changes (-a, --all to stage all)
|
|
44
46
|
commit -m "message" Record snapshot changes to the repository
|
|
45
47
|
log Show commit logs
|
|
@@ -54,6 +56,7 @@ Flags:
|
|
|
54
56
|
-v, --version Show version information
|
|
55
57
|
-m <message> Commit message
|
|
56
58
|
-b <branch> Target branch name
|
|
59
|
+
-c, --comments Show full comment content in status
|
|
57
60
|
-a, --all Stage all tracked/untracked files (for add/reset)
|
|
58
61
|
-s, --staged Unstage specified files from index (for restore)
|
|
59
62
|
-w, --worktree Revert physical working tree files (for restore)
|
|
@@ -91,7 +94,20 @@ export async function main({
|
|
|
91
94
|
}
|
|
92
95
|
case VCS_SUBCOMMANDS.STATUS: {
|
|
93
96
|
const targetDir = parsed.positionals[0] || process.cwd();
|
|
94
|
-
await executeStatusCmd({
|
|
97
|
+
await executeStatusCmd({
|
|
98
|
+
targetDir,
|
|
99
|
+
comments: parsed.flags.comments,
|
|
100
|
+
});
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case VCS_SUBCOMMANDS.GENERATE_COMMENT:
|
|
104
|
+
case VCS_SUBCOMMANDS.GEN_COMMENT:
|
|
105
|
+
case VCS_SUBCOMMANDS.GEN_MSG:
|
|
106
|
+
case VCS_SUBCOMMANDS.GCOMM:
|
|
107
|
+
case VCS_SUBCOMMANDS.GMSG: {
|
|
108
|
+
await executeGenerateCommentCmd({
|
|
109
|
+
paths: parsed.positionals,
|
|
110
|
+
});
|
|
95
111
|
break;
|
|
96
112
|
}
|
|
97
113
|
case VCS_SUBCOMMANDS.ADD: {
|
|
@@ -108,8 +124,13 @@ export async function main({
|
|
|
108
124
|
break;
|
|
109
125
|
}
|
|
110
126
|
case VCS_SUBCOMMANDS.LOG: {
|
|
111
|
-
const targetDir =
|
|
112
|
-
|
|
127
|
+
const targetDir = process.cwd();
|
|
128
|
+
const filePath = parsed.positionals[0];
|
|
129
|
+
await executeLogCmd({
|
|
130
|
+
targetDir,
|
|
131
|
+
filePath,
|
|
132
|
+
allComments: parsed.flags.all,
|
|
133
|
+
});
|
|
113
134
|
break;
|
|
114
135
|
}
|
|
115
136
|
case VCS_SUBCOMMANDS.BRANCH: {
|
|
@@ -321,6 +321,9 @@ export async function updateB2tFSIndexIbGib({
|
|
|
321
321
|
|
|
322
322
|
const newIndexIbGib = res.newIbGib as B2tFSIndexIbGib_V1;
|
|
323
323
|
await persistTransformResult({ resTransform: res, space });
|
|
324
|
+
if (metaspace.zeroSpace && metaspace.zeroSpace !== space) {
|
|
325
|
+
await persistTransformResult({ resTransform: res, space: metaspace.zeroSpace });
|
|
326
|
+
}
|
|
324
327
|
await metaspace.registerNewIbGib({ ibGib: newIndexIbGib });
|
|
325
328
|
|
|
326
329
|
if (vcsPath) {
|
|
@@ -242,8 +242,11 @@ function getSpacesArray(metaAny: any): IbGibSpaceAny[] {
|
|
|
242
242
|
async function fetchFromSpaceSilently(spaceToQuery: IbGibSpaceAny, addrToGet: string): Promise<any> {
|
|
243
243
|
try {
|
|
244
244
|
const resGet = await getFromSpace({ space: spaceToQuery, addrs: [addrToGet] });
|
|
245
|
-
if (resGet
|
|
246
|
-
|
|
245
|
+
if (resGet) {
|
|
246
|
+
const found = resGet.ibGibs?.find(x => !!x) || (resGet.rawResultIbGib as any)?.ibGibs?.find((x: any) => !!x);
|
|
247
|
+
if (found) {
|
|
248
|
+
return found;
|
|
249
|
+
}
|
|
247
250
|
}
|
|
248
251
|
} catch {
|
|
249
252
|
// Suppress missing address error
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module b2tfs-3way-graft-strategy
|
|
3
3
|
*
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* ⚠️ CRITICAL GRAFT STRATEGY GUIDANCE WARNING ⚠️
|
|
6
|
+
* BEFORE MAKING ANY CHANGES TO THIS FILE, YOU MUST CONSULT AND CAREFULLY READ:
|
|
7
|
+
* libs/core-gib/src/sync/GRAFT_STRATEGY_GUIDANCE.md
|
|
8
|
+
*
|
|
9
|
+
* Graft strategies must strictly fulfill the `GraftTimelinesResult` contract,
|
|
10
|
+
* ensure timeline `n` monotonicity (using Math.max over base/orphan branch frames),
|
|
11
|
+
* and include ALL newly referenced ancillary dependency nodes (comments, binary
|
|
12
|
+
* stones, DNAs) in `ancillaryIbGibsCreated` so that the destination branch
|
|
13
|
+
* space is completely self-contained.
|
|
14
|
+
* ============================================================================
|
|
15
|
+
*
|
|
4
16
|
* 3-way text line diff graft strategy for B2tFS file item timelines.
|
|
5
17
|
* Reconciles diverging file content via 3-way line diff (LCA, Base, Orphan)
|
|
6
18
|
* and inserts Git conflict markers when line edits collide.
|
|
@@ -13,14 +25,15 @@
|
|
|
13
25
|
import { extractErrorMsg, hash } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
14
26
|
import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
15
27
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
16
|
-
import { getGib } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
|
|
28
|
+
import { getGib, isDna } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
|
|
17
29
|
import { rel8 } from '@ibgib/ts-gib/dist/V1/transforms/rel8.mjs';
|
|
18
30
|
|
|
19
31
|
import { GraftMergeStrategyBase_V1 } from '@ibgib/core-gib/dist/sync/graft-info/graft-merge-strategy-base-v1.mjs';
|
|
20
32
|
import { GraftContext, GraftStrategy, GraftTimelinesResult } from '@ibgib/core-gib/dist/sync/graft-info/graft-info-types.mjs';
|
|
21
33
|
import { FlatIbGibGraph } from '@ibgib/core-gib/dist/common/other/graph-types.mjs';
|
|
22
34
|
import { getFromSpace, persistTransformResult, registerNewIbGib, putInSpace } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
|
|
23
|
-
import { getBinIb, getIbGibsFromCache_fallbackToSpaces } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
|
|
35
|
+
import { getBinIb, getIbGibsFromCache_fallbackToSpaces, splitPerTjpAndOrDna, getTimelinesGroupedByTjp } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
|
|
36
|
+
import { getDependencyGraph } from '@ibgib/core-gib/dist/common/other/graph-helper.mjs';
|
|
24
37
|
import { createGraftInfo } from '@ibgib/core-gib/dist/sync/graft-info/graft-info-helpers.mjs';
|
|
25
38
|
import { GRAFT_INFO_REL8N_NAME } from '@ibgib/core-gib/dist/sync/graft-info/graft-info-constants.mjs';
|
|
26
39
|
import { BinIbGib_V1 } from '@ibgib/core-gib/dist/common/bin/bin-types.mjs';
|
|
@@ -151,6 +164,12 @@ export function perform3WayTextLineMerge({
|
|
|
151
164
|
};
|
|
152
165
|
}
|
|
153
166
|
|
|
167
|
+
/**
|
|
168
|
+
* 3-way text line diff graft strategy for B2tFS file item timelines.
|
|
169
|
+
*
|
|
170
|
+
* ⚠️ **IMPORTANT**: Before making ANY changes to this class or its methods, consult:
|
|
171
|
+
* `libs/core-gib/src/sync/GRAFT_STRATEGY_GUIDANCE.md`.
|
|
172
|
+
*/
|
|
154
173
|
export class B2tFS3WayGraftStrategy extends GraftMergeStrategyBase_V1 implements GraftStrategy {
|
|
155
174
|
readonly strategyId: string = 'B2tFS3WayGraftStrategy';
|
|
156
175
|
private lc = '[B2tFS3WayGraftStrategy]';
|
|
@@ -211,6 +230,16 @@ export class B2tFS3WayGraftStrategy extends GraftMergeStrategyBase_V1 implements
|
|
|
211
230
|
}
|
|
212
231
|
}
|
|
213
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Executes 3-way line diff text graft across LCA, Base, and Orphan file items.
|
|
235
|
+
*
|
|
236
|
+
* ⚠️ **CRITICAL CONTRACT**: Before modifying this method, review:
|
|
237
|
+
* `libs/core-gib/src/sync/GRAFT_STRATEGY_GUIDANCE.md`
|
|
238
|
+
* Ensure:
|
|
239
|
+
* 1. Monotonic `n` counter (`highestDataN = Math.max(...)`)
|
|
240
|
+
* 2. Full dependency graph inclusion in `ancillaryIbGibsCreated` (comments, bin stones, DNAs)
|
|
241
|
+
* 3. Self-contained destination branch space
|
|
242
|
+
*/
|
|
214
243
|
async graft(context: GraftContext): Promise<GraftTimelinesResult | undefined> {
|
|
215
244
|
const lc = `${lcFile}[${this.strategyId}][${this.graft.name}]`;
|
|
216
245
|
try {
|
|
@@ -360,16 +389,67 @@ export class B2tFS3WayGraftStrategy extends GraftMergeStrategyBase_V1 implements
|
|
|
360
389
|
};
|
|
361
390
|
const baseItemDataN = fnGetN(baseItem);
|
|
362
391
|
const orphanItemDataN = fnGetN(orphanItem);
|
|
363
|
-
const
|
|
392
|
+
const highestDataN = Math.max(baseItemDataN, orphanItemDataN);
|
|
393
|
+
|
|
394
|
+
const lcaCommentAddrs = lcaItem.rel8ns?.comment || [];
|
|
395
|
+
const lcaCommentAddrsSet = new Set(lcaCommentAddrs);
|
|
396
|
+
const mergedCommentAddrsSinceLCA_base = (baseItem.rel8ns?.comment || []).filter(addr => !lcaCommentAddrsSet.has(addr));
|
|
397
|
+
const mergedCommentAddrsSinceLCA_orphan = (orphanItem.rel8ns?.comment || []).filter(addr => !lcaCommentAddrsSet.has(addr));
|
|
398
|
+
const newCommentAddrs = Array.from(new Set([...mergedCommentAddrsSinceLCA_base, ...mergedCommentAddrsSinceLCA_orphan]));
|
|
399
|
+
const allMergedCommentAddrs = Array.from(new Set([...lcaCommentAddrs, ...newCommentAddrs]));
|
|
400
|
+
|
|
401
|
+
const commentDepIbGibs: IbGib_V1[] = [];
|
|
402
|
+
if (newCommentAddrs.length > 0) {
|
|
403
|
+
for (const cAddr of newCommentAddrs) {
|
|
404
|
+
const cIb = await this.fetchIbGib({ addr: cAddr, space, readCache_graph: ibGibReadCache });
|
|
405
|
+
if (!cIb) {
|
|
406
|
+
throw new Error(`(UNEXPECTED) Comment ibgib for address '${cAddr}' not found in cache or space (E: a1b2c3d4e5f67a8b9c0d1e2f3a4b5c6d)`);
|
|
407
|
+
}
|
|
408
|
+
commentDepIbGibs.push(cIb);
|
|
409
|
+
const cDepGraph = await getDependencyGraph({
|
|
410
|
+
ibGib: cIb,
|
|
411
|
+
space,
|
|
412
|
+
ibGibs: ibGibReadCache ? Object.values(ibGibReadCache) : undefined,
|
|
413
|
+
});
|
|
414
|
+
for (const depNode of Object.values(cDepGraph)) {
|
|
415
|
+
const depAddr = getIbGibAddr({ ibGib: depNode });
|
|
416
|
+
if (!commentDepIbGibs.some(x => getIbGibAddr({ ibGib: x }) === depAddr)) {
|
|
417
|
+
commentDepIbGibs.push(depNode);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const { mapWithTjp_YesDna, mapWithoutTjps } = splitPerTjpAndOrDna({ ibGibs: commentDepIbGibs });
|
|
423
|
+
|
|
424
|
+
// Put DNA and non-timeline stones into space with isDna flag
|
|
425
|
+
for (const dnaOrStone of Object.values(mapWithoutTjps)) {
|
|
426
|
+
if (space) {
|
|
427
|
+
await putInSpace({ ibGib: dnaOrStone, space, isDna: isDna({ ibGib: dnaOrStone }) });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Register timeline frames in order
|
|
432
|
+
const timelinesByTjp = getTimelinesGroupedByTjp({ ibGibs: Object.values(mapWithTjp_YesDna) });
|
|
433
|
+
for (const tjpAddr of Object.keys(timelinesByTjp)) {
|
|
434
|
+
const timeline = timelinesByTjp[tjpAddr];
|
|
435
|
+
for (const frame of timeline) {
|
|
436
|
+
if (space) {
|
|
437
|
+
await putInSpace({ ibGib: frame, space });
|
|
438
|
+
await registerNewIbGib({ ibGib: frame, space });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
364
443
|
|
|
365
444
|
const evolveRes = await evolveB2tFSItemIbGib({
|
|
366
445
|
existingItem: baseItem,
|
|
367
446
|
dataToUpdate: {
|
|
368
447
|
fileHash,
|
|
369
|
-
n:
|
|
448
|
+
n: highestDataN + 1,
|
|
370
449
|
},
|
|
371
450
|
rel8nsToUpdate: {
|
|
372
451
|
[BINARY_REL8N_NAME]: [binAddr],
|
|
452
|
+
...(allMergedCommentAddrs.length > 0 ? { comment: allMergedCommentAddrs } : {}),
|
|
373
453
|
},
|
|
374
454
|
});
|
|
375
455
|
|
|
@@ -438,7 +518,7 @@ export class B2tFS3WayGraftStrategy extends GraftMergeStrategyBase_V1 implements
|
|
|
438
518
|
dnasReplayed: [],
|
|
439
519
|
dnasCreatedInGraft: newGraftDnaIbGibs,
|
|
440
520
|
intermediateIbGibsCreatedInGraft: newGraftedIntermediateIbGibs_ordered,
|
|
441
|
-
ancillaryIbGibsCreated: [binIbGib],
|
|
521
|
+
ancillaryIbGibsCreated: [binIbGib, ...commentDepIbGibs],
|
|
442
522
|
newGraftTip: finalGraftTip,
|
|
443
523
|
graftInfoCreated: graftInfo,
|
|
444
524
|
};
|
|
@@ -462,17 +542,16 @@ export class B2tFS3WayGraftStrategy extends GraftMergeStrategyBase_V1 implements
|
|
|
462
542
|
readCache_graph?: FlatIbGibGraph;
|
|
463
543
|
}): Promise<IbGib_V1 | undefined> {
|
|
464
544
|
if (!addr) { return undefined; }
|
|
465
|
-
const cleanAddr = addr.includes('^') ? addr.split('^')[0] : addr;
|
|
466
545
|
if (readCache_graph) {
|
|
467
|
-
if (readCache_graph[cleanAddr]) { return readCache_graph[cleanAddr]; }
|
|
468
546
|
if (readCache_graph[addr]) { return readCache_graph[addr]; }
|
|
469
|
-
const
|
|
547
|
+
const gib = addr.includes('^') ? addr.substring(addr.lastIndexOf('^') + 1) : addr;
|
|
548
|
+
const matchingKey = Object.keys(readCache_graph).find(k => k === addr || (gib && k.endsWith('^' + gib)));
|
|
470
549
|
if (matchingKey) { return readCache_graph[matchingKey]; }
|
|
471
550
|
}
|
|
472
551
|
try {
|
|
473
552
|
const res = await getFromSpace({ space, addrs: [addr] });
|
|
474
|
-
if (res
|
|
475
|
-
const found = res.ibGibs.find((x: any) => !!x);
|
|
553
|
+
if (res) {
|
|
554
|
+
const found = res.ibGibs?.find((x: any) => !!x) || (res.rawResultIbGib as any)?.ibGibs?.find((x: any) => !!x);
|
|
476
555
|
if (found) { return found; }
|
|
477
556
|
}
|
|
478
557
|
} catch {
|