@ibgib/vcs-gib 0.0.21 → 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/bootstrap/node-bootstrap.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/merge-cmd.d.mts +21 -8
- package/dist/cli/commands/merge-cmd.d.mts.map +1 -1
- package/dist/cli/commands/merge-cmd.mjs +138 -70
- package/dist/cli/commands/merge-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 +27 -6
- 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 +31 -1
- package/dist/engine/item/item-helper.d.mts.map +1 -1
- package/dist/engine/item/item-helper.mjs +44 -2
- package/dist/engine/item/item-helper.mjs.map +1 -1
- package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts +74 -0
- package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts.map +1 -0
- package/dist/engine/merge/{b2tfs-3way-merge-strategy.mjs → b2tfs-3way-graft-strategy.mjs} +133 -304
- package/dist/engine/merge/b2tfs-3way-graft-strategy.mjs.map +1 -0
- package/dist/engine/merge/{b2tfs-3way-merge-types.d.mts → b2tfs-3way-graft-types.d.mts} +2 -9
- package/dist/engine/merge/b2tfs-3way-graft-types.d.mts.map +1 -0
- package/dist/engine/merge/b2tfs-3way-graft-types.mjs +2 -0
- package/dist/engine/merge/b2tfs-3way-graft-types.mjs.map +1 -0
- 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/dist/test/test-helpers.d.mts +51 -0
- package/dist/test/test-helpers.d.mts.map +1 -0
- package/dist/test/test-helpers.mjs +135 -0
- package/dist/test/test-helpers.mjs.map +1 -0
- package/package.json +4 -4
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/bootstrap/node-bootstrap.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/branch-checkout-cmd.respec.log +8 -101
- package/src/cli/commands/branch-checkout-cmd.respec.mts +11 -10
- 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/merge-cmd.mts +154 -76
- package/src/cli/commands/status-cmd.mts +57 -256
- package/src/cli/vcs-cli-main.mts +28 -7
- package/src/engine/index/index-helper.mts +3 -0
- package/src/engine/item/item-helper.mts +76 -3
- package/src/engine/merge/{b2tfs-3way-merge-strategy.mts → b2tfs-3way-graft-strategy.mts} +152 -374
- package/src/engine/merge/{b2tfs-3way-merge-strategy.respec.mts → b2tfs-3way-graft-strategy.respec.mts} +27 -122
- package/src/engine/merge/{b2tfs-3way-merge-types.mts → b2tfs-3way-graft-types.mts} +34 -42
- package/src/engine/merge/b2tfs-sync-comments.respec.mts +399 -0
- package/src/engine/status/status-helper.mts +380 -0
- package/src/test/test-helpers.mts +168 -0
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts +0 -48
- package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts.map +0 -1
- package/dist/engine/merge/b2tfs-3way-merge-strategy.mjs.map +0 -1
- package/dist/engine/merge/b2tfs-3way-merge-types.d.mts.map +0 -1
- package/dist/engine/merge/b2tfs-3way-merge-types.mjs +0 -2
- package/dist/engine/merge/b2tfs-3way-merge-types.mjs.map +0 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module generate-comment-cmd
|
|
3
|
+
*
|
|
4
|
+
* Command handler for `vcs generate-comment` (synonyms: `gen-comment`, `gen-msg`, `gcomm`, `gmsg`).
|
|
5
|
+
* Scans repository status or target paths and creates `.ib.<file>.md` or `.ib.<topic>.md` companion metadata files.
|
|
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 { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
|
|
13
|
+
import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
|
|
14
|
+
import { getRepoStatus } from '../../engine/status/status-helper.mjs';
|
|
15
|
+
|
|
16
|
+
const lcFile = `[generate-comment-cmd]`;
|
|
17
|
+
const logalot = GLOBAL_LOG_A_LOT;
|
|
18
|
+
|
|
19
|
+
export interface GenerateCommentCmdOpts {
|
|
20
|
+
targetDir?: string;
|
|
21
|
+
vcsFolderName?: string;
|
|
22
|
+
paths?: string[];
|
|
23
|
+
folderTopic?: string;
|
|
24
|
+
quiet?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface GenerateCommentCmdResult {
|
|
28
|
+
createdFiles: string[];
|
|
29
|
+
skippedFiles: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function executeGenerateCommentCmd({
|
|
33
|
+
targetDir = process.cwd(),
|
|
34
|
+
vcsFolderName = '.vcsgib',
|
|
35
|
+
paths = [],
|
|
36
|
+
folderTopic = 'notes',
|
|
37
|
+
quiet = false,
|
|
38
|
+
}: GenerateCommentCmdOpts = {}): Promise<GenerateCommentCmdResult> {
|
|
39
|
+
const lc = `${lcFile}[${executeGenerateCommentCmd.name}]`;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
if (logalot) {
|
|
43
|
+
console.log(`${lc} starting executeGenerateCommentCmd (I: 8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e)`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const absTargetDir = path.resolve(targetDir);
|
|
47
|
+
|
|
48
|
+
const { metaspace, localSpace, vcsPath, repoRoot } = await bootstrapVcsMetaspace({
|
|
49
|
+
repoPath: absTargetDir,
|
|
50
|
+
vcsFolderName,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Verify repo exists
|
|
54
|
+
try {
|
|
55
|
+
await fs.access(vcsPath);
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f)`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const statusRes = await getRepoStatus({
|
|
61
|
+
metaspace,
|
|
62
|
+
localSpace,
|
|
63
|
+
vcsPath,
|
|
64
|
+
repoRoot,
|
|
65
|
+
vcsFolderName,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const activeWorktreeDir = statusRes.activeWorktreeDir;
|
|
69
|
+
const targetRelPaths: string[] = [];
|
|
70
|
+
|
|
71
|
+
if (paths.length > 0) {
|
|
72
|
+
for (const p of paths) {
|
|
73
|
+
const absP = path.resolve(activeWorktreeDir, p);
|
|
74
|
+
const relP = path.relative(activeWorktreeDir, absP).replace(/\\/g, '/');
|
|
75
|
+
if (relP && !targetRelPaths.includes(relP)) {
|
|
76
|
+
targetRelPaths.push(relP);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
// Auto-detect from staged and modified items
|
|
81
|
+
for (const s of statusRes.staged) {
|
|
82
|
+
if (!targetRelPaths.includes(s.path)) {
|
|
83
|
+
targetRelPaths.push(s.path);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const m of statusRes.modified) {
|
|
87
|
+
if (!targetRelPaths.includes(m.path)) {
|
|
88
|
+
targetRelPaths.push(m.path);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const u of statusRes.untracked) {
|
|
92
|
+
if (!targetRelPaths.includes(u.path)) {
|
|
93
|
+
targetRelPaths.push(u.path);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const createdFiles: string[] = [];
|
|
99
|
+
const skippedFiles: string[] = [];
|
|
100
|
+
|
|
101
|
+
for (const relP of targetRelPaths) {
|
|
102
|
+
const absP = path.join(activeWorktreeDir, relP);
|
|
103
|
+
|
|
104
|
+
let isDirectory = false;
|
|
105
|
+
try {
|
|
106
|
+
const stat = await fs.stat(absP);
|
|
107
|
+
isDirectory = stat.isDirectory();
|
|
108
|
+
} catch {
|
|
109
|
+
// If path doesn't exist yet on disk, guess based on extension
|
|
110
|
+
isDirectory = !path.extname(relP);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let commentFileName: string;
|
|
114
|
+
let commentDirPath: string;
|
|
115
|
+
|
|
116
|
+
if (isDirectory) {
|
|
117
|
+
commentDirPath = absP;
|
|
118
|
+
commentFileName = `.ib.${folderTopic}.md`;
|
|
119
|
+
} else {
|
|
120
|
+
commentDirPath = path.dirname(absP);
|
|
121
|
+
const baseName = path.basename(absP);
|
|
122
|
+
commentFileName = `.ib.${baseName}.md`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const commentFullPath = path.join(commentDirPath, commentFileName);
|
|
126
|
+
const commentRelPath = path.relative(activeWorktreeDir, commentFullPath).replace(/\\/g, '/');
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
await fs.access(commentFullPath);
|
|
130
|
+
// Already exists
|
|
131
|
+
skippedFiles.push(commentRelPath);
|
|
132
|
+
} catch {
|
|
133
|
+
// Does not exist -> create boilerplate template
|
|
134
|
+
const headerTarget = relP ? `\`${relP}\`` : 'root';
|
|
135
|
+
const boilerplate = `# ${headerTarget}\n\n(Enter contemporaneous notes/comments here)\n`;
|
|
136
|
+
await fs.mkdir(commentDirPath, { recursive: true });
|
|
137
|
+
await fs.writeFile(commentFullPath, boilerplate, 'utf8');
|
|
138
|
+
createdFiles.push(commentRelPath);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!quiet) {
|
|
143
|
+
if (createdFiles.length > 0) {
|
|
144
|
+
console.log(`Generated ${createdFiles.length} comment file(s):`);
|
|
145
|
+
for (const f of createdFiles) {
|
|
146
|
+
console.log(` created: ${f}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (skippedFiles.length > 0) {
|
|
150
|
+
for (const f of skippedFiles) {
|
|
151
|
+
console.log(` already exists: ${f}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (createdFiles.length === 0 && skippedFiles.length === 0) {
|
|
155
|
+
console.log(`No modified or staged files found to generate comments for.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
createdFiles,
|
|
161
|
+
skippedFiles,
|
|
162
|
+
};
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error(`${lc} ${extractErrorMsg(error)} (E: 0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a)`);
|
|
165
|
+
throw error;
|
|
166
|
+
} finally {
|
|
167
|
+
if (logalot) {
|
|
168
|
+
console.log(`${lc} complete. (I: 1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b)`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Command handler for `vcs log`.
|
|
5
5
|
* Reads active branch timeline from B2tFSIndexIbGib_V1 in localSpace,
|
|
6
|
-
* walks branch timeline history backward, and displays formatted commit history.
|
|
6
|
+
* walks branch or item timeline history backward, and displays formatted commit and comment history.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import path from 'node:path';
|
|
@@ -11,12 +11,12 @@ import fs from 'node:fs/promises';
|
|
|
11
11
|
|
|
12
12
|
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
13
13
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
14
|
-
import { getIbGibsFromCache_fallbackToSpaces } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
|
|
15
14
|
import { CommentIbGib_V1 } from '@ibgib/core-gib/dist/common/comment/comment-types.mjs';
|
|
16
15
|
|
|
17
16
|
import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
|
|
18
17
|
import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
|
|
19
18
|
import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
|
|
19
|
+
import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
|
|
20
20
|
import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
|
|
21
21
|
import { getB2tFSIndexIbGib, extractBranchNameFromAddr } from '../../engine/index/index-helper.mjs';
|
|
22
22
|
import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
|
|
@@ -25,29 +25,36 @@ const lcFile = `[log-cmd]`;
|
|
|
25
25
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
26
26
|
|
|
27
27
|
export interface VcsLogItem {
|
|
28
|
-
branchAddr
|
|
28
|
+
branchAddr?: string;
|
|
29
29
|
commitAddr: string;
|
|
30
30
|
commentAddr?: string;
|
|
31
31
|
message: string;
|
|
32
32
|
timestamp: string;
|
|
33
33
|
author: string;
|
|
34
34
|
rootItemAddr?: string;
|
|
35
|
+
committedPaths?: string[];
|
|
36
|
+
itemComments?: Array<{ path: string; name?: string; message: string }>;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
export interface LogCmdOpts {
|
|
38
40
|
targetDir?: string;
|
|
39
41
|
vcsFolderName?: string;
|
|
42
|
+
filePath?: string;
|
|
43
|
+
allComments?: boolean;
|
|
40
44
|
quiet?: boolean;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
export interface LogCmdResult {
|
|
44
48
|
branchName: string;
|
|
49
|
+
targetPath?: string;
|
|
45
50
|
commits: VcsLogItem[];
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
export async function executeLogCmd({
|
|
49
54
|
targetDir = process.cwd(),
|
|
50
55
|
vcsFolderName = '.vcsgib',
|
|
56
|
+
filePath,
|
|
57
|
+
allComments = false,
|
|
51
58
|
quiet = false,
|
|
52
59
|
}: LogCmdOpts = {}): Promise<LogCmdResult> {
|
|
53
60
|
const lc = `${lcFile}[${executeLogCmd.name}]`;
|
|
@@ -87,45 +94,122 @@ export async function executeLogCmd({
|
|
|
87
94
|
});
|
|
88
95
|
|
|
89
96
|
// 2. Fetch active branch node address matching active branchName
|
|
90
|
-
const branchAddrs = [
|
|
91
|
-
...(indexIbGib.rel8ns?.branch || []),
|
|
92
|
-
...(indexIbGib.rel8ns?.branch_tjp || []),
|
|
93
|
-
];
|
|
97
|
+
const branchAddrs = indexIbGib.rel8ns?.branch || indexIbGib.rel8ns?.branch_tjp || [];
|
|
94
98
|
|
|
95
99
|
let currBranchAddr: string | undefined;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const parsedName = extractBranchNameFromAddr(addr);
|
|
100
|
-
if (parsedName === branchName) {
|
|
101
|
-
currBranchAddr = addr;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Fallback: fetch branch nodes via getIbGibWithMetaspaceFallback to match b.data?.name === branchName
|
|
107
|
-
if (!currBranchAddr) {
|
|
108
|
-
for (const addr of branchAddrs) {
|
|
100
|
+
if (branchAddrs.length > 0) {
|
|
101
|
+
let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
|
|
102
|
+
for (const bAddr of branchAddrs) {
|
|
109
103
|
const bIbGib = await getIbGibWithMetaspaceFallback({
|
|
110
|
-
addr,
|
|
104
|
+
addr: bAddr,
|
|
111
105
|
space: activeSpace,
|
|
112
106
|
metaspace,
|
|
113
|
-
})
|
|
114
|
-
if (bIbGib
|
|
115
|
-
|
|
116
|
-
break;
|
|
107
|
+
});
|
|
108
|
+
if (bIbGib) {
|
|
109
|
+
branchIbGibs.push(bIbGib as B2tFSBranchIbGib_V1);
|
|
117
110
|
}
|
|
118
111
|
}
|
|
112
|
+
|
|
113
|
+
const matchingBranches = branchIbGibs.filter(b => b?.data?.name === branchName);
|
|
114
|
+
const activeBranchIbGib = matchingBranches.sort((a, b) => (b.data?.n || 0) - (a.data?.n || 0))[0]
|
|
115
|
+
|| branchIbGibs.at(-1);
|
|
116
|
+
|
|
117
|
+
if (activeBranchIbGib) {
|
|
118
|
+
currBranchAddr = getIbGibAddr({ ibGib: activeBranchIbGib });
|
|
119
|
+
}
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
const commits: VcsLogItem[] = [];
|
|
123
|
+
|
|
124
|
+
// #region File-Specific Log Mode (vcs log <filePath>)
|
|
125
|
+
if (filePath) {
|
|
126
|
+
const normalizedRelPath = path.relative(repoRoot, path.resolve(absTargetDir, filePath)).replace(/\\/g, '/');
|
|
127
|
+
|
|
128
|
+
// Find current root item
|
|
129
|
+
const branchIbGib = currBranchAddr
|
|
130
|
+
? await getIbGibWithMetaspaceFallback({ addr: currBranchAddr, space: activeSpace, metaspace }) as B2tFSBranchIbGib_V1
|
|
131
|
+
: undefined;
|
|
132
|
+
const rootItemAddr = branchIbGib?.rel8ns?.b2tFS_item?.at(-1) || branchIbGib?.rel8ns?.b2tfs_item?.at(-1);
|
|
133
|
+
|
|
134
|
+
if (!rootItemAddr) {
|
|
135
|
+
throw new Error(`No root item found on branch ${branchName} (E: 4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b)`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const targetItem = await logCmd_findItemByPath({
|
|
139
|
+
rootItemAddr,
|
|
140
|
+
targetRelPath: normalizedRelPath,
|
|
141
|
+
space: activeSpace,
|
|
142
|
+
metaspace,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (!targetItem) {
|
|
146
|
+
throw new Error(`Path '${normalizedRelPath}' not found in active branch snapshot (E: 5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c)`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Walk target item timeline history backward
|
|
150
|
+
const visitedItemAddrs = new Set<string>();
|
|
151
|
+
let currItem: B2tFSItemIbGib_V1 | undefined = targetItem;
|
|
152
|
+
|
|
153
|
+
while (currItem) {
|
|
154
|
+
const itemAddr = getIbGibAddr({ ibGib: currItem });
|
|
155
|
+
if (visitedItemAddrs.has(itemAddr)) { break; }
|
|
156
|
+
visitedItemAddrs.add(itemAddr);
|
|
157
|
+
|
|
158
|
+
const commentAddrs = currItem.rel8ns?.comment || [];
|
|
159
|
+
for (const cAddr of commentAddrs) {
|
|
160
|
+
const commentIbGib = await getIbGibWithMetaspaceFallback({
|
|
161
|
+
addr: cAddr,
|
|
162
|
+
space: activeSpace,
|
|
163
|
+
metaspace,
|
|
164
|
+
}) as CommentIbGib_V1;
|
|
165
|
+
|
|
166
|
+
if (commentIbGib) {
|
|
167
|
+
commits.push({
|
|
168
|
+
commitAddr: itemAddr,
|
|
169
|
+
commentAddr: cAddr,
|
|
170
|
+
message: commentIbGib.data?.text || '',
|
|
171
|
+
timestamp: commentIbGib.data?.textTimestamp || commentIbGib.data?.timestamp || '',
|
|
172
|
+
author: 'vcs-user',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const pastAddr = currItem.rel8ns?.past?.at(0) || currItem.rel8ns?.ancestor?.at(0);
|
|
178
|
+
if (pastAddr) {
|
|
179
|
+
currItem = await getIbGibWithMetaspaceFallback({
|
|
180
|
+
addr: pastAddr,
|
|
181
|
+
space: activeSpace,
|
|
182
|
+
metaspace,
|
|
183
|
+
}) as B2tFSItemIbGib_V1;
|
|
184
|
+
} else {
|
|
185
|
+
currItem = undefined;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!quiet) {
|
|
190
|
+
console.log(`File: ${normalizedRelPath} (branch: ${branchName}) (I: 6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d)`);
|
|
191
|
+
if (commits.length === 0) {
|
|
192
|
+
console.log('No comments found for this file.');
|
|
193
|
+
} else {
|
|
194
|
+
for (const commit of commits) {
|
|
195
|
+
console.log(`\nitem ${commit.commitAddr}`);
|
|
196
|
+
if (commit.timestamp) { console.log(`Date: ${commit.timestamp}`); }
|
|
197
|
+
console.log(`\n ${commit.message}\n`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
branchName,
|
|
204
|
+
targetPath: normalizedRelPath,
|
|
205
|
+
commits,
|
|
206
|
+
};
|
|
124
207
|
}
|
|
208
|
+
// #endregion File-Specific Log Mode
|
|
209
|
+
|
|
210
|
+
// #region Standard Branch Log Mode
|
|
125
211
|
const visitedAddrs = new Set<string>();
|
|
126
|
-
const commits: VcsLogItem[] = [];
|
|
127
212
|
|
|
128
|
-
// 3. Walk timeline history backward
|
|
129
213
|
while (currBranchAddr && !visitedAddrs.has(currBranchAddr)) {
|
|
130
214
|
visitedAddrs.add(currBranchAddr);
|
|
131
215
|
|
|
@@ -139,28 +223,68 @@ export async function executeLogCmd({
|
|
|
139
223
|
break;
|
|
140
224
|
}
|
|
141
225
|
|
|
142
|
-
const
|
|
143
|
-
|
|
226
|
+
const rootItemAddr = branchIbGib.rel8ns?.b2tFS_item?.at(-1) || branchIbGib.rel8ns?.b2tfs_item?.at(-1);
|
|
227
|
+
|
|
228
|
+
let rootItem: B2tFSItemIbGib_V1 | undefined;
|
|
229
|
+
if (rootItemAddr) {
|
|
230
|
+
rootItem = await getIbGibWithMetaspaceFallback({
|
|
231
|
+
addr: rootItemAddr,
|
|
232
|
+
space: activeSpace,
|
|
233
|
+
metaspace,
|
|
234
|
+
}) as B2tFSItemIbGib_V1;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let commentAddrs = [
|
|
238
|
+
...(branchIbGib.rel8ns?.comment || []),
|
|
239
|
+
...(rootItem?.rel8ns?.comment || []),
|
|
240
|
+
];
|
|
241
|
+
// Deduplicate
|
|
242
|
+
commentAddrs = Array.from(new Set(commentAddrs));
|
|
243
|
+
|
|
244
|
+
let message = '';
|
|
245
|
+
let timestamp = branchIbGib.data?.committedAt || '';
|
|
246
|
+
// If branch has a direct comment, use that first, otherwise use the last added comment on root item
|
|
247
|
+
let primaryCommentAddr = branchIbGib.rel8ns?.comment?.at(-1) || rootItem?.rel8ns?.comment?.at(-1) || commentAddrs.at(0);
|
|
144
248
|
|
|
145
|
-
if (
|
|
249
|
+
if (primaryCommentAddr) {
|
|
146
250
|
const commentIbGib = await getIbGibWithMetaspaceFallback({
|
|
147
|
-
addr:
|
|
251
|
+
addr: primaryCommentAddr,
|
|
148
252
|
space: activeSpace,
|
|
149
253
|
metaspace,
|
|
150
254
|
}) as CommentIbGib_V1;
|
|
151
255
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
256
|
+
if (commentIbGib?.data?.text) {
|
|
257
|
+
message = commentIbGib.data.text;
|
|
258
|
+
}
|
|
259
|
+
if (commentIbGib?.data?.textTimestamp || commentIbGib?.data?.timestamp) {
|
|
260
|
+
timestamp = commentIbGib.data.textTimestamp || commentIbGib.data.timestamp || timestamp;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const committedPaths = branchIbGib.data?.committedPaths || [];
|
|
265
|
+
const itemComments: Array<{ path: string; name?: string; message: string }> = [];
|
|
266
|
+
|
|
267
|
+
if (allComments && rootItem) {
|
|
268
|
+
await logCmd_collectAllItemComments({
|
|
269
|
+
item: rootItem,
|
|
270
|
+
space: activeSpace,
|
|
271
|
+
metaspace,
|
|
272
|
+
currentRelPath: '',
|
|
273
|
+
outComments: itemComments,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
155
276
|
|
|
277
|
+
if (message || primaryCommentAddr || (committedPaths && committedPaths.length > 0)) {
|
|
156
278
|
commits.push({
|
|
157
279
|
branchAddr: currBranchAddr,
|
|
158
|
-
commitAddr:
|
|
159
|
-
commentAddr,
|
|
160
|
-
message,
|
|
280
|
+
commitAddr: currBranchAddr,
|
|
281
|
+
commentAddr: primaryCommentAddr,
|
|
282
|
+
message: message || '(no commit message)',
|
|
161
283
|
timestamp,
|
|
162
|
-
author,
|
|
284
|
+
author: 'vcs-user',
|
|
163
285
|
rootItemAddr,
|
|
286
|
+
committedPaths,
|
|
287
|
+
itemComments: itemComments.length > 0 ? itemComments : undefined,
|
|
164
288
|
});
|
|
165
289
|
}
|
|
166
290
|
|
|
@@ -178,6 +302,16 @@ export async function executeLogCmd({
|
|
|
178
302
|
console.log(`Author: ${commit.author}`);
|
|
179
303
|
console.log(`Date: ${commit.timestamp}`);
|
|
180
304
|
console.log(`\n ${commit.message}\n`);
|
|
305
|
+
if (commit.committedPaths && commit.committedPaths.length > 0) {
|
|
306
|
+
console.log(`Changed: ${commit.committedPaths.join(', ')}`);
|
|
307
|
+
}
|
|
308
|
+
if (commit.itemComments && commit.itemComments.length > 0) {
|
|
309
|
+
console.log('Item Comments:');
|
|
310
|
+
for (const ic of commit.itemComments) {
|
|
311
|
+
const label = ic.name ? `${ic.path} (${ic.name})` : ic.path;
|
|
312
|
+
console.log(` * [${label}]: ${ic.message}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
181
315
|
}
|
|
182
316
|
}
|
|
183
317
|
}
|
|
@@ -186,6 +320,7 @@ export async function executeLogCmd({
|
|
|
186
320
|
branchName,
|
|
187
321
|
commits,
|
|
188
322
|
};
|
|
323
|
+
// #endregion Standard Branch Log Mode
|
|
189
324
|
} catch (error) {
|
|
190
325
|
console.error(`${lc} ${extractErrorMsg(error)} (E: 5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c)`);
|
|
191
326
|
throw error;
|
|
@@ -195,3 +330,115 @@ export async function executeLogCmd({
|
|
|
195
330
|
}
|
|
196
331
|
}
|
|
197
332
|
}
|
|
333
|
+
|
|
334
|
+
async function logCmd_findItemByPath({
|
|
335
|
+
rootItemAddr,
|
|
336
|
+
targetRelPath,
|
|
337
|
+
space,
|
|
338
|
+
metaspace,
|
|
339
|
+
}: {
|
|
340
|
+
rootItemAddr: string;
|
|
341
|
+
targetRelPath: string;
|
|
342
|
+
space: any;
|
|
343
|
+
metaspace?: any;
|
|
344
|
+
}): Promise<B2tFSItemIbGib_V1 | undefined> {
|
|
345
|
+
const rootItem = await getIbGibWithMetaspaceFallback({
|
|
346
|
+
addr: rootItemAddr,
|
|
347
|
+
space,
|
|
348
|
+
metaspace,
|
|
349
|
+
}) as B2tFSItemIbGib_V1;
|
|
350
|
+
|
|
351
|
+
if (!rootItem) { return undefined; }
|
|
352
|
+
if (!targetRelPath || targetRelPath === '.' || targetRelPath === '') {
|
|
353
|
+
return rootItem;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const segments = targetRelPath.split('/');
|
|
357
|
+
let currentItem: B2tFSItemIbGib_V1 = rootItem;
|
|
358
|
+
|
|
359
|
+
for (const segment of segments) {
|
|
360
|
+
if (currentItem.data?.fsType !== 'folder') {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const childAddrs = currentItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
|
|
365
|
+
let nextItem: B2tFSItemIbGib_V1 | undefined;
|
|
366
|
+
|
|
367
|
+
for (const cAddr of childAddrs) {
|
|
368
|
+
const child = await getIbGibWithMetaspaceFallback({
|
|
369
|
+
addr: cAddr,
|
|
370
|
+
space,
|
|
371
|
+
metaspace,
|
|
372
|
+
}) as B2tFSItemIbGib_V1;
|
|
373
|
+
|
|
374
|
+
if (child && child.data?.name === segment) {
|
|
375
|
+
nextItem = child;
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (!nextItem) {
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
383
|
+
currentItem = nextItem;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return currentItem;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function logCmd_collectAllItemComments({
|
|
390
|
+
item,
|
|
391
|
+
space,
|
|
392
|
+
metaspace,
|
|
393
|
+
currentRelPath,
|
|
394
|
+
outComments,
|
|
395
|
+
}: {
|
|
396
|
+
item: B2tFSItemIbGib_V1;
|
|
397
|
+
space: any;
|
|
398
|
+
metaspace?: any;
|
|
399
|
+
currentRelPath: string;
|
|
400
|
+
outComments: Array<{ path: string; name?: string; message: string }>;
|
|
401
|
+
}): Promise<void> {
|
|
402
|
+
if (!item || !item.data) { return; }
|
|
403
|
+
|
|
404
|
+
const itemComments = item.rel8ns?.comment || [];
|
|
405
|
+
for (const cAddr of itemComments) {
|
|
406
|
+
const commentIbGib = await getIbGibWithMetaspaceFallback({
|
|
407
|
+
addr: cAddr,
|
|
408
|
+
space,
|
|
409
|
+
metaspace,
|
|
410
|
+
}) as CommentIbGib_V1;
|
|
411
|
+
|
|
412
|
+
if (commentIbGib && commentIbGib.data?.text) {
|
|
413
|
+
outComments.push({
|
|
414
|
+
path: currentRelPath || '(root)',
|
|
415
|
+
name: (commentIbGib.data as any)?.name,
|
|
416
|
+
message: commentIbGib.data.text,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (item.data.fsType === 'folder') {
|
|
422
|
+
const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
|
|
423
|
+
for (const childAddr of childAddrs) {
|
|
424
|
+
const childItem = await getIbGibWithMetaspaceFallback({
|
|
425
|
+
addr: childAddr,
|
|
426
|
+
space,
|
|
427
|
+
metaspace,
|
|
428
|
+
}) as B2tFSItemIbGib_V1;
|
|
429
|
+
|
|
430
|
+
if (childItem && childItem.data) {
|
|
431
|
+
const childRel = currentRelPath
|
|
432
|
+
? `${currentRelPath}/${childItem.data.name}`
|
|
433
|
+
: childItem.data.name;
|
|
434
|
+
await logCmd_collectAllItemComments({
|
|
435
|
+
item: childItem,
|
|
436
|
+
space,
|
|
437
|
+
metaspace,
|
|
438
|
+
currentRelPath: childRel,
|
|
439
|
+
outComments,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|