@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
|
@@ -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,13 @@ 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';
|
|
18
|
+
import { resolveContextualBranchName } from '../../engine/branch/branch-helper.mjs';
|
|
19
19
|
import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
|
|
20
|
+
import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
|
|
20
21
|
import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
|
|
21
22
|
import { getB2tFSIndexIbGib, extractBranchNameFromAddr } from '../../engine/index/index-helper.mjs';
|
|
22
23
|
import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
|
|
@@ -25,29 +26,36 @@ const lcFile = `[log-cmd]`;
|
|
|
25
26
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
26
27
|
|
|
27
28
|
export interface VcsLogItem {
|
|
28
|
-
branchAddr
|
|
29
|
+
branchAddr?: string;
|
|
29
30
|
commitAddr: string;
|
|
30
31
|
commentAddr?: string;
|
|
31
32
|
message: string;
|
|
32
33
|
timestamp: string;
|
|
33
34
|
author: string;
|
|
34
35
|
rootItemAddr?: string;
|
|
36
|
+
committedPaths?: string[];
|
|
37
|
+
itemComments?: Array<{ path: string; name?: string; message: string }>;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
export interface LogCmdOpts {
|
|
38
41
|
targetDir?: string;
|
|
39
42
|
vcsFolderName?: string;
|
|
43
|
+
filePath?: string;
|
|
44
|
+
allComments?: boolean;
|
|
40
45
|
quiet?: boolean;
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
export interface LogCmdResult {
|
|
44
49
|
branchName: string;
|
|
50
|
+
targetPath?: string;
|
|
45
51
|
commits: VcsLogItem[];
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
export async function executeLogCmd({
|
|
49
55
|
targetDir = process.cwd(),
|
|
50
56
|
vcsFolderName = '.vcsgib',
|
|
57
|
+
filePath,
|
|
58
|
+
allComments = false,
|
|
51
59
|
quiet = false,
|
|
52
60
|
}: LogCmdOpts = {}): Promise<LogCmdResult> {
|
|
53
61
|
const lc = `${lcFile}[${executeLogCmd.name}]`;
|
|
@@ -77,7 +85,11 @@ export async function executeLogCmd({
|
|
|
77
85
|
throw new Error(`Repository index B2tFSIndexIbGib_V1 not found in localSpace (E: 3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a)`);
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
const branchName =
|
|
88
|
+
const branchName = resolveContextualBranchName({
|
|
89
|
+
targetDir: absTargetDir,
|
|
90
|
+
repoRoot,
|
|
91
|
+
indexIbGib,
|
|
92
|
+
});
|
|
81
93
|
|
|
82
94
|
// Target active branch space b2s_<activeBranch>
|
|
83
95
|
const { space: activeSpace } = await getOrCreateBranchSpace({
|
|
@@ -87,45 +99,122 @@ export async function executeLogCmd({
|
|
|
87
99
|
});
|
|
88
100
|
|
|
89
101
|
// 2. Fetch active branch node address matching active branchName
|
|
90
|
-
const branchAddrs = [
|
|
91
|
-
...(indexIbGib.rel8ns?.branch || []),
|
|
92
|
-
...(indexIbGib.rel8ns?.branch_tjp || []),
|
|
93
|
-
];
|
|
102
|
+
const branchAddrs = indexIbGib.rel8ns?.branch || indexIbGib.rel8ns?.branch_tjp || [];
|
|
94
103
|
|
|
95
104
|
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) {
|
|
105
|
+
if (branchAddrs.length > 0) {
|
|
106
|
+
let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
|
|
107
|
+
for (const bAddr of branchAddrs) {
|
|
109
108
|
const bIbGib = await getIbGibWithMetaspaceFallback({
|
|
110
|
-
addr,
|
|
109
|
+
addr: bAddr,
|
|
111
110
|
space: activeSpace,
|
|
112
111
|
metaspace,
|
|
113
|
-
})
|
|
114
|
-
if (bIbGib
|
|
115
|
-
|
|
116
|
-
break;
|
|
112
|
+
});
|
|
113
|
+
if (bIbGib) {
|
|
114
|
+
branchIbGibs.push(bIbGib as B2tFSBranchIbGib_V1);
|
|
117
115
|
}
|
|
118
116
|
}
|
|
117
|
+
|
|
118
|
+
const matchingBranches = branchIbGibs.filter(b => b?.data?.name === branchName);
|
|
119
|
+
const activeBranchIbGib = matchingBranches.sort((a, b) => (b.data?.n || 0) - (a.data?.n || 0))[0]
|
|
120
|
+
|| branchIbGibs.at(-1);
|
|
121
|
+
|
|
122
|
+
if (activeBranchIbGib) {
|
|
123
|
+
currBranchAddr = getIbGibAddr({ ibGib: activeBranchIbGib });
|
|
124
|
+
}
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
const commits: VcsLogItem[] = [];
|
|
128
|
+
|
|
129
|
+
// #region File-Specific Log Mode (vcs log <filePath>)
|
|
130
|
+
if (filePath) {
|
|
131
|
+
const normalizedRelPath = path.relative(repoRoot, path.resolve(absTargetDir, filePath)).replace(/\\/g, '/');
|
|
132
|
+
|
|
133
|
+
// Find current root item
|
|
134
|
+
const branchIbGib = currBranchAddr
|
|
135
|
+
? await getIbGibWithMetaspaceFallback({ addr: currBranchAddr, space: activeSpace, metaspace }) as B2tFSBranchIbGib_V1
|
|
136
|
+
: undefined;
|
|
137
|
+
const rootItemAddr = branchIbGib?.rel8ns?.b2tFS_item?.at(-1) || branchIbGib?.rel8ns?.b2tfs_item?.at(-1);
|
|
138
|
+
|
|
139
|
+
if (!rootItemAddr) {
|
|
140
|
+
throw new Error(`No root item found on branch ${branchName} (E: 4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b)`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const targetItem = await logCmd_findItemByPath({
|
|
144
|
+
rootItemAddr,
|
|
145
|
+
targetRelPath: normalizedRelPath,
|
|
146
|
+
space: activeSpace,
|
|
147
|
+
metaspace,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (!targetItem) {
|
|
151
|
+
throw new Error(`Path '${normalizedRelPath}' not found in active branch snapshot (E: 5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c)`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Walk target item timeline history backward
|
|
155
|
+
const visitedItemAddrs = new Set<string>();
|
|
156
|
+
let currItem: B2tFSItemIbGib_V1 | undefined = targetItem;
|
|
157
|
+
|
|
158
|
+
while (currItem) {
|
|
159
|
+
const itemAddr = getIbGibAddr({ ibGib: currItem });
|
|
160
|
+
if (visitedItemAddrs.has(itemAddr)) { break; }
|
|
161
|
+
visitedItemAddrs.add(itemAddr);
|
|
162
|
+
|
|
163
|
+
const commentAddrs = currItem.rel8ns?.comment || [];
|
|
164
|
+
for (const cAddr of commentAddrs) {
|
|
165
|
+
const commentIbGib = await getIbGibWithMetaspaceFallback({
|
|
166
|
+
addr: cAddr,
|
|
167
|
+
space: activeSpace,
|
|
168
|
+
metaspace,
|
|
169
|
+
}) as CommentIbGib_V1;
|
|
170
|
+
|
|
171
|
+
if (commentIbGib) {
|
|
172
|
+
commits.push({
|
|
173
|
+
commitAddr: itemAddr,
|
|
174
|
+
commentAddr: cAddr,
|
|
175
|
+
message: commentIbGib.data?.text || '',
|
|
176
|
+
timestamp: commentIbGib.data?.textTimestamp || commentIbGib.data?.timestamp || '',
|
|
177
|
+
author: 'vcs-user',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const pastAddr = currItem.rel8ns?.past?.at(0) || currItem.rel8ns?.ancestor?.at(0);
|
|
183
|
+
if (pastAddr) {
|
|
184
|
+
currItem = await getIbGibWithMetaspaceFallback({
|
|
185
|
+
addr: pastAddr,
|
|
186
|
+
space: activeSpace,
|
|
187
|
+
metaspace,
|
|
188
|
+
}) as B2tFSItemIbGib_V1;
|
|
189
|
+
} else {
|
|
190
|
+
currItem = undefined;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!quiet) {
|
|
195
|
+
console.log(`File: ${normalizedRelPath} (branch: ${branchName}) (I: 6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d)`);
|
|
196
|
+
if (commits.length === 0) {
|
|
197
|
+
console.log('No comments found for this file.');
|
|
198
|
+
} else {
|
|
199
|
+
for (const commit of commits) {
|
|
200
|
+
console.log(`\nitem ${commit.commitAddr}`);
|
|
201
|
+
if (commit.timestamp) { console.log(`Date: ${commit.timestamp}`); }
|
|
202
|
+
console.log(`\n ${commit.message}\n`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
branchName,
|
|
209
|
+
targetPath: normalizedRelPath,
|
|
210
|
+
commits,
|
|
211
|
+
};
|
|
124
212
|
}
|
|
213
|
+
// #endregion File-Specific Log Mode
|
|
214
|
+
|
|
215
|
+
// #region Standard Branch Log Mode
|
|
125
216
|
const visitedAddrs = new Set<string>();
|
|
126
|
-
const commits: VcsLogItem[] = [];
|
|
127
217
|
|
|
128
|
-
// 3. Walk timeline history backward
|
|
129
218
|
while (currBranchAddr && !visitedAddrs.has(currBranchAddr)) {
|
|
130
219
|
visitedAddrs.add(currBranchAddr);
|
|
131
220
|
|
|
@@ -139,28 +228,68 @@ export async function executeLogCmd({
|
|
|
139
228
|
break;
|
|
140
229
|
}
|
|
141
230
|
|
|
142
|
-
const
|
|
143
|
-
|
|
231
|
+
const rootItemAddr = branchIbGib.rel8ns?.b2tFS_item?.at(-1) || branchIbGib.rel8ns?.b2tfs_item?.at(-1);
|
|
232
|
+
|
|
233
|
+
let rootItem: B2tFSItemIbGib_V1 | undefined;
|
|
234
|
+
if (rootItemAddr) {
|
|
235
|
+
rootItem = await getIbGibWithMetaspaceFallback({
|
|
236
|
+
addr: rootItemAddr,
|
|
237
|
+
space: activeSpace,
|
|
238
|
+
metaspace,
|
|
239
|
+
}) as B2tFSItemIbGib_V1;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let commentAddrs = [
|
|
243
|
+
...(branchIbGib.rel8ns?.comment || []),
|
|
244
|
+
...(rootItem?.rel8ns?.comment || []),
|
|
245
|
+
];
|
|
246
|
+
// Deduplicate
|
|
247
|
+
commentAddrs = Array.from(new Set(commentAddrs));
|
|
144
248
|
|
|
145
|
-
|
|
249
|
+
let message = '';
|
|
250
|
+
let timestamp = branchIbGib.data?.committedAt || '';
|
|
251
|
+
// If branch has a direct comment, use that first, otherwise use the last added comment on root item
|
|
252
|
+
let primaryCommentAddr = branchIbGib.rel8ns?.comment?.at(-1) || rootItem?.rel8ns?.comment?.at(-1) || commentAddrs.at(0);
|
|
253
|
+
|
|
254
|
+
if (primaryCommentAddr) {
|
|
146
255
|
const commentIbGib = await getIbGibWithMetaspaceFallback({
|
|
147
|
-
addr:
|
|
256
|
+
addr: primaryCommentAddr,
|
|
148
257
|
space: activeSpace,
|
|
149
258
|
metaspace,
|
|
150
259
|
}) as CommentIbGib_V1;
|
|
151
260
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
261
|
+
if (commentIbGib?.data?.text) {
|
|
262
|
+
message = commentIbGib.data.text;
|
|
263
|
+
}
|
|
264
|
+
if (commentIbGib?.data?.textTimestamp || commentIbGib?.data?.timestamp) {
|
|
265
|
+
timestamp = commentIbGib.data.textTimestamp || commentIbGib.data.timestamp || timestamp;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const committedPaths = branchIbGib.data?.committedPaths || [];
|
|
270
|
+
const itemComments: Array<{ path: string; name?: string; message: string }> = [];
|
|
155
271
|
|
|
272
|
+
if (allComments && rootItem) {
|
|
273
|
+
await logCmd_collectAllItemComments({
|
|
274
|
+
item: rootItem,
|
|
275
|
+
space: activeSpace,
|
|
276
|
+
metaspace,
|
|
277
|
+
currentRelPath: '',
|
|
278
|
+
outComments: itemComments,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (message || primaryCommentAddr || (committedPaths && committedPaths.length > 0)) {
|
|
156
283
|
commits.push({
|
|
157
284
|
branchAddr: currBranchAddr,
|
|
158
|
-
commitAddr:
|
|
159
|
-
commentAddr,
|
|
160
|
-
message,
|
|
285
|
+
commitAddr: currBranchAddr,
|
|
286
|
+
commentAddr: primaryCommentAddr,
|
|
287
|
+
message: message || '(no commit message)',
|
|
161
288
|
timestamp,
|
|
162
|
-
author,
|
|
289
|
+
author: 'vcs-user',
|
|
163
290
|
rootItemAddr,
|
|
291
|
+
committedPaths,
|
|
292
|
+
itemComments: itemComments.length > 0 ? itemComments : undefined,
|
|
164
293
|
});
|
|
165
294
|
}
|
|
166
295
|
|
|
@@ -178,6 +307,16 @@ export async function executeLogCmd({
|
|
|
178
307
|
console.log(`Author: ${commit.author}`);
|
|
179
308
|
console.log(`Date: ${commit.timestamp}`);
|
|
180
309
|
console.log(`\n ${commit.message}\n`);
|
|
310
|
+
if (commit.committedPaths && commit.committedPaths.length > 0) {
|
|
311
|
+
console.log(`Changed: ${commit.committedPaths.join(', ')}`);
|
|
312
|
+
}
|
|
313
|
+
if (commit.itemComments && commit.itemComments.length > 0) {
|
|
314
|
+
console.log('Item Comments:');
|
|
315
|
+
for (const ic of commit.itemComments) {
|
|
316
|
+
const label = ic.name ? `${ic.path} (${ic.name})` : ic.path;
|
|
317
|
+
console.log(` * [${label}]: ${ic.message}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
181
320
|
}
|
|
182
321
|
}
|
|
183
322
|
}
|
|
@@ -186,6 +325,7 @@ export async function executeLogCmd({
|
|
|
186
325
|
branchName,
|
|
187
326
|
commits,
|
|
188
327
|
};
|
|
328
|
+
// #endregion Standard Branch Log Mode
|
|
189
329
|
} catch (error) {
|
|
190
330
|
console.error(`${lc} ${extractErrorMsg(error)} (E: 5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c)`);
|
|
191
331
|
throw error;
|
|
@@ -195,3 +335,115 @@ export async function executeLogCmd({
|
|
|
195
335
|
}
|
|
196
336
|
}
|
|
197
337
|
}
|
|
338
|
+
|
|
339
|
+
async function logCmd_findItemByPath({
|
|
340
|
+
rootItemAddr,
|
|
341
|
+
targetRelPath,
|
|
342
|
+
space,
|
|
343
|
+
metaspace,
|
|
344
|
+
}: {
|
|
345
|
+
rootItemAddr: string;
|
|
346
|
+
targetRelPath: string;
|
|
347
|
+
space: any;
|
|
348
|
+
metaspace?: any;
|
|
349
|
+
}): Promise<B2tFSItemIbGib_V1 | undefined> {
|
|
350
|
+
const rootItem = await getIbGibWithMetaspaceFallback({
|
|
351
|
+
addr: rootItemAddr,
|
|
352
|
+
space,
|
|
353
|
+
metaspace,
|
|
354
|
+
}) as B2tFSItemIbGib_V1;
|
|
355
|
+
|
|
356
|
+
if (!rootItem) { return undefined; }
|
|
357
|
+
if (!targetRelPath || targetRelPath === '.' || targetRelPath === '') {
|
|
358
|
+
return rootItem;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const segments = targetRelPath.split('/');
|
|
362
|
+
let currentItem: B2tFSItemIbGib_V1 = rootItem;
|
|
363
|
+
|
|
364
|
+
for (const segment of segments) {
|
|
365
|
+
if (currentItem.data?.fsType !== 'folder') {
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const childAddrs = currentItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
|
|
370
|
+
let nextItem: B2tFSItemIbGib_V1 | undefined;
|
|
371
|
+
|
|
372
|
+
for (const cAddr of childAddrs) {
|
|
373
|
+
const child = await getIbGibWithMetaspaceFallback({
|
|
374
|
+
addr: cAddr,
|
|
375
|
+
space,
|
|
376
|
+
metaspace,
|
|
377
|
+
}) as B2tFSItemIbGib_V1;
|
|
378
|
+
|
|
379
|
+
if (child && child.data?.name === segment) {
|
|
380
|
+
nextItem = child;
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (!nextItem) {
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
currentItem = nextItem;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return currentItem;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function logCmd_collectAllItemComments({
|
|
395
|
+
item,
|
|
396
|
+
space,
|
|
397
|
+
metaspace,
|
|
398
|
+
currentRelPath,
|
|
399
|
+
outComments,
|
|
400
|
+
}: {
|
|
401
|
+
item: B2tFSItemIbGib_V1;
|
|
402
|
+
space: any;
|
|
403
|
+
metaspace?: any;
|
|
404
|
+
currentRelPath: string;
|
|
405
|
+
outComments: Array<{ path: string; name?: string; message: string }>;
|
|
406
|
+
}): Promise<void> {
|
|
407
|
+
if (!item || !item.data) { return; }
|
|
408
|
+
|
|
409
|
+
const itemComments = item.rel8ns?.comment || [];
|
|
410
|
+
for (const cAddr of itemComments) {
|
|
411
|
+
const commentIbGib = await getIbGibWithMetaspaceFallback({
|
|
412
|
+
addr: cAddr,
|
|
413
|
+
space,
|
|
414
|
+
metaspace,
|
|
415
|
+
}) as CommentIbGib_V1;
|
|
416
|
+
|
|
417
|
+
if (commentIbGib && commentIbGib.data?.text) {
|
|
418
|
+
outComments.push({
|
|
419
|
+
path: currentRelPath || '(root)',
|
|
420
|
+
name: (commentIbGib.data as any)?.name,
|
|
421
|
+
message: commentIbGib.data.text,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (item.data.fsType === 'folder') {
|
|
427
|
+
const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
|
|
428
|
+
for (const childAddr of childAddrs) {
|
|
429
|
+
const childItem = await getIbGibWithMetaspaceFallback({
|
|
430
|
+
addr: childAddr,
|
|
431
|
+
space,
|
|
432
|
+
metaspace,
|
|
433
|
+
}) as B2tFSItemIbGib_V1;
|
|
434
|
+
|
|
435
|
+
if (childItem && childItem.data) {
|
|
436
|
+
const childRel = currentRelPath
|
|
437
|
+
? `${currentRelPath}/${childItem.data.name}`
|
|
438
|
+
: childItem.data.name;
|
|
439
|
+
await logCmd_collectAllItemComments({
|
|
440
|
+
item: childItem,
|
|
441
|
+
space,
|
|
442
|
+
metaspace,
|
|
443
|
+
currentRelPath: childRel,
|
|
444
|
+
outComments,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|