@ibgib/vcs-gib 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/VCS_CLI_USER_GUIDE.md +198 -0
- package/dist/AUTO-GENERATED-version.d.mts +6 -0
- package/dist/AUTO-GENERATED-version.d.mts.map +1 -0
- package/dist/AUTO-GENERATED-version.mjs +6 -0
- package/dist/AUTO-GENERATED-version.mjs.map +1 -0
- package/dist/assumptions.respec.d.mts +2 -0
- package/dist/assumptions.respec.d.mts.map +1 -0
- package/dist/assumptions.respec.mjs +41 -0
- package/dist/assumptions.respec.mjs.map +1 -0
- package/dist/bootstrap/node-bootstrap.d.mts +35 -0
- package/dist/bootstrap/node-bootstrap.d.mts.map +1 -0
- package/dist/bootstrap/node-bootstrap.mjs +165 -0
- package/dist/bootstrap/node-bootstrap.mjs.map +1 -0
- package/dist/bootstrap/node-bootstrap.respec.node.d.mts +7 -0
- package/dist/bootstrap/node-bootstrap.respec.node.d.mts.map +1 -0
- package/dist/bootstrap/node-bootstrap.respec.node.mjs +28 -0
- package/dist/bootstrap/node-bootstrap.respec.node.mjs.map +1 -0
- package/dist/cli/cli-arg-parser.d.mts +27 -0
- package/dist/cli/cli-arg-parser.d.mts.map +1 -0
- package/dist/cli/cli-arg-parser.mjs +96 -0
- package/dist/cli/cli-arg-parser.mjs.map +1 -0
- package/dist/cli/cli-arg-parser.respec.node.d.mts +7 -0
- package/dist/cli/cli-arg-parser.respec.node.d.mts.map +1 -0
- package/dist/cli/cli-arg-parser.respec.node.mjs +35 -0
- package/dist/cli/cli-arg-parser.respec.node.mjs.map +1 -0
- package/dist/cli/cli-constants.d.mts +26 -0
- package/dist/cli/cli-constants.d.mts.map +1 -0
- package/dist/cli/cli-constants.mjs +67 -0
- package/dist/cli/cli-constants.mjs.map +1 -0
- package/dist/cli/cli-e2e.respec.node.d.mts +8 -0
- package/dist/cli/cli-e2e.respec.node.d.mts.map +1 -0
- package/dist/cli/cli-e2e.respec.node.mjs +263 -0
- package/dist/cli/cli-e2e.respec.node.mjs.map +1 -0
- package/dist/cli/commands/add-cmd.d.mts +20 -0
- package/dist/cli/commands/add-cmd.d.mts.map +1 -0
- package/dist/cli/commands/add-cmd.mjs +333 -0
- package/dist/cli/commands/add-cmd.mjs.map +1 -0
- package/dist/cli/commands/branch-checkout-cmd.respec.node.d.mts +7 -0
- package/dist/cli/commands/branch-checkout-cmd.respec.node.d.mts.map +1 -0
- package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs +288 -0
- package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs.map +1 -0
- package/dist/cli/commands/branch-cmd.d.mts +25 -0
- package/dist/cli/commands/branch-cmd.d.mts.map +1 -0
- package/dist/cli/commands/branch-cmd.mjs +187 -0
- package/dist/cli/commands/branch-cmd.mjs.map +1 -0
- package/dist/cli/commands/checkout-cmd.d.mts +21 -0
- package/dist/cli/commands/checkout-cmd.d.mts.map +1 -0
- package/dist/cli/commands/checkout-cmd.mjs +154 -0
- package/dist/cli/commands/checkout-cmd.mjs.map +1 -0
- package/dist/cli/commands/commit-cmd.d.mts +23 -0
- package/dist/cli/commands/commit-cmd.d.mts.map +1 -0
- package/dist/cli/commands/commit-cmd.mjs +326 -0
- package/dist/cli/commands/commit-cmd.mjs.map +1 -0
- package/dist/cli/commands/commit-log-cmd.respec.node.d.mts +7 -0
- package/dist/cli/commands/commit-log-cmd.respec.node.d.mts.map +1 -0
- package/dist/cli/commands/commit-log-cmd.respec.node.mjs +251 -0
- package/dist/cli/commands/commit-log-cmd.respec.node.mjs.map +1 -0
- package/dist/cli/commands/init-cmd.d.mts +20 -0
- package/dist/cli/commands/init-cmd.d.mts.map +1 -0
- package/dist/cli/commands/init-cmd.mjs +119 -0
- package/dist/cli/commands/init-cmd.mjs.map +1 -0
- package/dist/cli/commands/init-cmd.respec.node.d.mts +7 -0
- package/dist/cli/commands/init-cmd.respec.node.d.mts.map +1 -0
- package/dist/cli/commands/init-cmd.respec.node.mjs +40 -0
- package/dist/cli/commands/init-cmd.respec.node.mjs.map +1 -0
- package/dist/cli/commands/log-cmd.d.mts +27 -0
- package/dist/cli/commands/log-cmd.d.mts.map +1 -0
- package/dist/cli/commands/log-cmd.mjs +146 -0
- package/dist/cli/commands/log-cmd.mjs.map +1 -0
- package/dist/cli/commands/status-add-cmd.respec.node.d.mts +7 -0
- package/dist/cli/commands/status-add-cmd.respec.node.d.mts.map +1 -0
- package/dist/cli/commands/status-add-cmd.respec.node.mjs +215 -0
- package/dist/cli/commands/status-add-cmd.respec.node.mjs.map +1 -0
- package/dist/cli/commands/status-cmd.d.mts +25 -0
- package/dist/cli/commands/status-cmd.d.mts.map +1 -0
- package/dist/cli/commands/status-cmd.mjs +286 -0
- package/dist/cli/commands/status-cmd.mjs.map +1 -0
- package/dist/cli/vcs-cli-main.d.mts +14 -0
- package/dist/cli/vcs-cli-main.d.mts.map +1 -0
- package/dist/cli/vcs-cli-main.mjs +136 -0
- package/dist/cli/vcs-cli-main.mjs.map +1 -0
- package/dist/engine/branch/branch-graph-helper.d.mts +19 -0
- package/dist/engine/branch/branch-graph-helper.d.mts.map +1 -0
- package/dist/engine/branch/branch-graph-helper.mjs +49 -0
- package/dist/engine/branch/branch-graph-helper.mjs.map +1 -0
- package/dist/engine/branch/branch-helper.d.mts +26 -0
- package/dist/engine/branch/branch-helper.d.mts.map +1 -0
- package/dist/engine/branch/branch-helper.mjs +57 -0
- package/dist/engine/branch/branch-helper.mjs.map +1 -0
- package/dist/engine/branch/branch-space-helper.d.mts +22 -0
- package/dist/engine/branch/branch-space-helper.d.mts.map +1 -0
- package/dist/engine/branch/branch-space-helper.mjs +77 -0
- package/dist/engine/branch/branch-space-helper.mjs.map +1 -0
- package/dist/engine/branch/branch-space.respec.node.d.mts +8 -0
- package/dist/engine/branch/branch-space.respec.node.d.mts.map +1 -0
- package/dist/engine/branch/branch-space.respec.node.mjs +138 -0
- package/dist/engine/branch/branch-space.respec.node.mjs.map +1 -0
- package/dist/engine/branch/branch-types.d.mts +26 -0
- package/dist/engine/branch/branch-types.d.mts.map +1 -0
- package/dist/engine/branch/branch-types.mjs +7 -0
- package/dist/engine/branch/branch-types.mjs.map +1 -0
- package/dist/engine/diff/line-diff-helper.d.mts +76 -0
- package/dist/engine/diff/line-diff-helper.d.mts.map +1 -0
- package/dist/engine/diff/line-diff-helper.mjs +144 -0
- package/dist/engine/diff/line-diff-helper.mjs.map +1 -0
- package/dist/engine/diff/line-diff-helper.respec.node.d.mts +7 -0
- package/dist/engine/diff/line-diff-helper.respec.node.d.mts.map +1 -0
- package/dist/engine/diff/line-diff-helper.respec.node.mjs +66 -0
- package/dist/engine/diff/line-diff-helper.respec.node.mjs.map +1 -0
- package/dist/engine/filter/glob-helper.d.mts +51 -0
- package/dist/engine/filter/glob-helper.d.mts.map +1 -0
- package/dist/engine/filter/glob-helper.mjs +132 -0
- package/dist/engine/filter/glob-helper.mjs.map +1 -0
- package/dist/engine/filter/glob-helper.respec.node.d.mts +7 -0
- package/dist/engine/filter/glob-helper.respec.node.d.mts.map +1 -0
- package/dist/engine/filter/glob-helper.respec.node.mjs +78 -0
- package/dist/engine/filter/glob-helper.respec.node.mjs.map +1 -0
- package/dist/engine/index/index-helper.d.mts +78 -0
- package/dist/engine/index/index-helper.d.mts.map +1 -0
- package/dist/engine/index/index-helper.mjs +289 -0
- package/dist/engine/index/index-helper.mjs.map +1 -0
- package/dist/engine/index/index-types.d.mts +63 -0
- package/dist/engine/index/index-types.d.mts.map +1 -0
- package/dist/engine/index/index-types.mjs +7 -0
- package/dist/engine/index/index-types.mjs.map +1 -0
- package/dist/engine/item/item-atom-helper.d.mts +20 -0
- package/dist/engine/item/item-atom-helper.d.mts.map +1 -0
- package/dist/engine/item/item-atom-helper.mjs +30 -0
- package/dist/engine/item/item-atom-helper.mjs.map +1 -0
- package/dist/engine/item/item-helper.d.mts +37 -0
- package/dist/engine/item/item-helper.d.mts.map +1 -0
- package/dist/engine/item/item-helper.mjs +194 -0
- package/dist/engine/item/item-helper.mjs.map +1 -0
- package/dist/engine/item/item-types.d.mts +127 -0
- package/dist/engine/item/item-types.d.mts.map +1 -0
- package/dist/engine/item/item-types.mjs +16 -0
- package/dist/engine/item/item-types.mjs.map +1 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -0
- package/dist/respec-gib.node.d.mts +7 -0
- package/dist/respec-gib.node.d.mts.map +1 -0
- package/dist/respec-gib.node.mjs +11 -0
- package/dist/respec-gib.node.mjs.map +1 -0
- package/dist/vcs-gib-constants.d.mts +7 -0
- package/dist/vcs-gib-constants.d.mts.map +1 -0
- package/dist/vcs-gib-constants.mjs +7 -0
- package/dist/vcs-gib-constants.mjs.map +1 -0
- package/dist/vcs-gib.respec.node.d.mts +8 -0
- package/dist/vcs-gib.respec.node.d.mts.map +1 -0
- package/dist/vcs-gib.respec.node.mjs +82 -0
- package/dist/vcs-gib.respec.node.mjs.map +1 -0
- package/generate-version-file.js +20 -0
- package/package.json +28 -0
- package/src/AUTO-GENERATED-version.mts +5 -0
- package/src/assumptions.respec.mts +50 -0
- package/src/bootstrap/node-bootstrap.mts +198 -0
- package/src/bootstrap/node-bootstrap.respec.node.mts +35 -0
- package/src/cli/cli-arg-parser.mts +119 -0
- package/src/cli/cli-arg-parser.respec.node.mts +45 -0
- package/src/cli/cli-constants.mts +77 -0
- package/src/cli/cli-e2e.respec.node.mts +318 -0
- package/src/cli/commands/add-cmd.mts +415 -0
- package/src/cli/commands/branch-checkout-cmd.respec.node.mts +322 -0
- package/src/cli/commands/branch-cmd.mts +243 -0
- package/src/cli/commands/checkout-cmd.mts +202 -0
- package/src/cli/commands/commit-cmd.mts +421 -0
- package/src/cli/commands/commit-log-cmd.respec.node.mts +276 -0
- package/src/cli/commands/init-cmd.mts +152 -0
- package/src/cli/commands/init-cmd.respec.node.mts +49 -0
- package/src/cli/commands/log-cmd.mts +197 -0
- package/src/cli/commands/status-add-cmd.respec.node.mts +246 -0
- package/src/cli/commands/status-cmd.mts +366 -0
- package/src/cli/vcs-cli-main.mts +153 -0
- package/src/engine/branch/branch-graph-helper.mts +69 -0
- package/src/engine/branch/branch-helper.mts +79 -0
- package/src/engine/branch/branch-space-helper.mts +104 -0
- package/src/engine/branch/branch-space.respec.node.mts +158 -0
- package/src/engine/branch/branch-types.mts +29 -0
- package/src/engine/diff/line-diff-helper.mts +210 -0
- package/src/engine/diff/line-diff-helper.respec.node.mts +84 -0
- package/src/engine/filter/glob-helper.mts +165 -0
- package/src/engine/filter/glob-helper.respec.node.mts +94 -0
- package/src/engine/index/index-helper.mts +378 -0
- package/src/engine/index/index-types.mts +66 -0
- package/src/engine/item/item-atom-helper.mts +30 -0
- package/src/engine/item/item-helper.mts +239 -0
- package/src/engine/item/item-types.mts +136 -0
- package/src/index.mts +7 -0
- package/src/respec-gib.node.mts +12 -0
- package/src/vcs-gib-constants.mts +7 -0
- package/src/vcs-gib.respec.node.mts +96 -0
- package/test_runs/001-hello-node/20260728_090100_0001-flow-core-main.md +39 -0
- package/test_runs/001-hello-node/20260728_090200_0003-flow-branching-worktree.md +54 -0
- package/test_runs/001-hello-node/20260728_090300_0002-flow-selective-add.md +45 -0
- package/test_runs/001-hello-node/20260728_142100_0007-flow-edge-cases.md +44 -0
- package/test_runs/001-hello-node/20260728_142400_0005-flow-hardcoded-ignores.md +36 -0
- package/tools/pack-for-test.mjs +73 -0
- package/tsconfig.json +18 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module branch-checkout-cmd.respec
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for `vcs branch` and `vcs checkout` command handlers & worktree projections.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import fs from 'node:fs/promises';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
respecfully, ifWe, iReckon
|
|
12
|
+
} from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
13
|
+
|
|
14
|
+
import { executeInitCmd } from './init-cmd.mjs';
|
|
15
|
+
import { executeAddCmd } from './add-cmd.mjs';
|
|
16
|
+
import { executeCommitCmd } from './commit-cmd.mjs';
|
|
17
|
+
import { executeBranchCmd } from './branch-cmd.mjs';
|
|
18
|
+
import { executeCheckoutCmd } from './checkout-cmd.mjs';
|
|
19
|
+
import { executeStatusCmd } from './status-cmd.mjs';
|
|
20
|
+
|
|
21
|
+
const lc = `[apps/vcs-gib/src/cli/commands/branch-checkout-cmd.respec.node.mts]`;
|
|
22
|
+
|
|
23
|
+
await respecfully(lc, `vcs branch and vcs checkout Command Handlers`, async () => {
|
|
24
|
+
|
|
25
|
+
await ifWe(lc, `lists default branch main for new repository`, async () => {
|
|
26
|
+
const testDir = path.join(process.cwd(), '.test_branch_checkout_1');
|
|
27
|
+
const vcsFolderName = '.vcsgib_test_bc_1';
|
|
28
|
+
try {
|
|
29
|
+
await executeInitCmd({
|
|
30
|
+
targetDir: testDir,
|
|
31
|
+
vcsFolderName,
|
|
32
|
+
quiet: true,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const branchRes = await executeBranchCmd({
|
|
36
|
+
targetDir: testDir,
|
|
37
|
+
vcsFolderName,
|
|
38
|
+
quiet: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
iReckon(lc, branchRes.branches.length).willEqual(1);
|
|
42
|
+
iReckon(lc, branchRes.branches[0].name).willEqual('main');
|
|
43
|
+
iReckon(lc, branchRes.branches[0].isActive).isGonnaBeTrue();
|
|
44
|
+
} finally {
|
|
45
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await ifWe(lc, `creates new branch from active branch root item and rejects duplicates`, async () => {
|
|
50
|
+
const testDir = path.join(process.cwd(), '.test_branch_checkout_2');
|
|
51
|
+
const vcsFolderName = '.vcsgib_test_bc_2';
|
|
52
|
+
try {
|
|
53
|
+
await executeInitCmd({
|
|
54
|
+
targetDir: testDir,
|
|
55
|
+
vcsFolderName,
|
|
56
|
+
quiet: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Create feature branch
|
|
60
|
+
const createRes = await executeBranchCmd({
|
|
61
|
+
branchName: 'feature-auth',
|
|
62
|
+
targetDir: testDir,
|
|
63
|
+
vcsFolderName,
|
|
64
|
+
quiet: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
iReckon(lc, createRes.createdBranchName).willEqual('feature-auth');
|
|
68
|
+
iReckon(lc, Boolean(createRes.createdBranchAddr)).isGonnaBeTrue();
|
|
69
|
+
|
|
70
|
+
// List branches
|
|
71
|
+
const listRes = await executeBranchCmd({
|
|
72
|
+
targetDir: testDir,
|
|
73
|
+
vcsFolderName,
|
|
74
|
+
quiet: true,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
iReckon(lc, listRes.branches.length).willEqual(2);
|
|
78
|
+
const featureBranch = listRes.branches.find(b => b.name === 'feature-auth');
|
|
79
|
+
iReckon(lc, Boolean(featureBranch)).isGonnaBeTrue();
|
|
80
|
+
iReckon(lc, featureBranch?.isActive).isGonnaBeFalse();
|
|
81
|
+
|
|
82
|
+
// Verify dedicated branch space subfolders exist in .vcsgib
|
|
83
|
+
const mainSpaceDir = path.join(testDir, vcsFolderName, 'ibgib', 'b2s_main');
|
|
84
|
+
const featureSpaceDir = path.join(testDir, vcsFolderName, 'ibgib', 'b2s_feature-auth');
|
|
85
|
+
const mainSpaceExists = await fs.stat(mainSpaceDir).then(s => s.isDirectory()).catch(() =>
|
|
86
|
+
fs.stat(path.join(testDir, vcsFolderName, 'b2s_main')).then(s => s.isDirectory()).catch(() => false)
|
|
87
|
+
);
|
|
88
|
+
const featureSpaceExists = await fs.stat(featureSpaceDir).then(s => s.isDirectory()).catch(() =>
|
|
89
|
+
fs.stat(path.join(testDir, vcsFolderName, 'b2s_feature-auth')).then(s => s.isDirectory()).catch(() => false)
|
|
90
|
+
);
|
|
91
|
+
iReckon(lc, mainSpaceExists).isGonnaBeTrue();
|
|
92
|
+
iReckon(lc, featureSpaceExists).isGonnaBeTrue();
|
|
93
|
+
|
|
94
|
+
// Duplicate branch attempt
|
|
95
|
+
let errorThrown = false;
|
|
96
|
+
try {
|
|
97
|
+
await executeBranchCmd({
|
|
98
|
+
branchName: 'feature-auth',
|
|
99
|
+
targetDir: testDir,
|
|
100
|
+
vcsFolderName,
|
|
101
|
+
quiet: true,
|
|
102
|
+
});
|
|
103
|
+
} catch (err: any) {
|
|
104
|
+
errorThrown = true;
|
|
105
|
+
iReckon(lc, err.message).includes("already exists");
|
|
106
|
+
}
|
|
107
|
+
iReckon(lc, errorThrown).isGonnaBeTrue();
|
|
108
|
+
} finally {
|
|
109
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await ifWe(lc, `switches active branch with checkout and projects branch snapshot onto worktree subfolder`, async () => {
|
|
114
|
+
const testDir = path.join(process.cwd(), '.test_branch_checkout_3');
|
|
115
|
+
const vcsFolderName = '.vcsgib_test_bc_3';
|
|
116
|
+
try {
|
|
117
|
+
await executeInitCmd({
|
|
118
|
+
targetDir: testDir,
|
|
119
|
+
vcsFolderName,
|
|
120
|
+
quiet: true,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Commit a file on main
|
|
124
|
+
await fs.writeFile(path.join(testDir, 'app.ts'), 'console.log("Main");', 'utf8');
|
|
125
|
+
await executeAddCmd({
|
|
126
|
+
paths: ['app.ts'],
|
|
127
|
+
targetDir: testDir,
|
|
128
|
+
vcsFolderName,
|
|
129
|
+
quiet: true,
|
|
130
|
+
});
|
|
131
|
+
await executeCommitCmd({
|
|
132
|
+
message: 'Add app.ts on main',
|
|
133
|
+
targetDir: testDir,
|
|
134
|
+
vcsFolderName,
|
|
135
|
+
quiet: true,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Create branch feature-ui
|
|
139
|
+
await executeBranchCmd({
|
|
140
|
+
branchName: 'feature-ui',
|
|
141
|
+
targetDir: testDir,
|
|
142
|
+
vcsFolderName,
|
|
143
|
+
quiet: true,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Checkout feature-ui
|
|
147
|
+
const checkoutRes = await executeCheckoutCmd({
|
|
148
|
+
branchName: 'feature-ui',
|
|
149
|
+
targetDir: testDir,
|
|
150
|
+
vcsFolderName,
|
|
151
|
+
quiet: true,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
iReckon(lc, checkoutRes.previousBranch).willEqual('main');
|
|
155
|
+
iReckon(lc, checkoutRes.currentBranch).willEqual('feature-ui');
|
|
156
|
+
|
|
157
|
+
const expectedWorktree = path.join(testDir, 'b', 'feature-ui');
|
|
158
|
+
iReckon(lc, checkoutRes.worktreePath).willEqual(expectedWorktree);
|
|
159
|
+
|
|
160
|
+
// Verify project file exists in worktree path
|
|
161
|
+
const projectedFile = path.join(expectedWorktree, 'app.ts');
|
|
162
|
+
const fileContent = await fs.readFile(projectedFile, 'utf8');
|
|
163
|
+
iReckon(lc, fileContent).willEqual('console.log("Main");');
|
|
164
|
+
|
|
165
|
+
// Verify active branch in status
|
|
166
|
+
const statusRes = await executeStatusCmd({
|
|
167
|
+
targetDir: testDir,
|
|
168
|
+
vcsFolderName,
|
|
169
|
+
quiet: true,
|
|
170
|
+
});
|
|
171
|
+
iReckon(lc, statusRes.branchName).willEqual('feature-ui');
|
|
172
|
+
|
|
173
|
+
// Verify branch list displays ALL branches while on feature-ui
|
|
174
|
+
const branchListOnFeature = await executeBranchCmd({
|
|
175
|
+
targetDir: testDir,
|
|
176
|
+
vcsFolderName,
|
|
177
|
+
quiet: true,
|
|
178
|
+
});
|
|
179
|
+
iReckon(lc, branchListOnFeature.branches.length).willEqual(2);
|
|
180
|
+
iReckon(lc, branchListOnFeature.branches.map(b => b.name)).includes('main');
|
|
181
|
+
iReckon(lc, branchListOnFeature.branches.map(b => b.name)).includes('feature-ui');
|
|
182
|
+
|
|
183
|
+
// Make a commit on feature-ui branch
|
|
184
|
+
await fs.writeFile(path.join(expectedWorktree, 'ui.ts'), 'export const ui = 1;', 'utf8');
|
|
185
|
+
await executeAddCmd({
|
|
186
|
+
paths: ['ui.ts'],
|
|
187
|
+
targetDir: expectedWorktree,
|
|
188
|
+
vcsFolderName,
|
|
189
|
+
quiet: true,
|
|
190
|
+
});
|
|
191
|
+
await executeCommitCmd({
|
|
192
|
+
message: 'Add ui.ts on feature-ui',
|
|
193
|
+
targetDir: expectedWorktree,
|
|
194
|
+
vcsFolderName,
|
|
195
|
+
quiet: true,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Verify branch list after commit on feature-ui still displays ALL branches
|
|
199
|
+
const branchListAfterCommit = await executeBranchCmd({
|
|
200
|
+
targetDir: expectedWorktree,
|
|
201
|
+
vcsFolderName,
|
|
202
|
+
quiet: true,
|
|
203
|
+
});
|
|
204
|
+
iReckon(lc, branchListAfterCommit.branches.length).willEqual(2);
|
|
205
|
+
|
|
206
|
+
// Switch back to main
|
|
207
|
+
const switchBackRes = await executeCheckoutCmd({
|
|
208
|
+
branchName: 'main',
|
|
209
|
+
targetDir: expectedWorktree,
|
|
210
|
+
vcsFolderName,
|
|
211
|
+
quiet: true,
|
|
212
|
+
});
|
|
213
|
+
iReckon(lc, switchBackRes.currentBranch).willEqual('main');
|
|
214
|
+
iReckon(lc, switchBackRes.worktreePath).willEqual(testDir);
|
|
215
|
+
|
|
216
|
+
// Verify branch list displays ALL branches while on main after feature-ui commit
|
|
217
|
+
const branchListOnMain = await executeBranchCmd({
|
|
218
|
+
targetDir: testDir,
|
|
219
|
+
vcsFolderName,
|
|
220
|
+
quiet: true,
|
|
221
|
+
});
|
|
222
|
+
iReckon(lc, branchListOnMain.branches.length).willEqual(2);
|
|
223
|
+
iReckon(lc, branchListOnMain.branches.map(b => b.name)).includes('main');
|
|
224
|
+
iReckon(lc, branchListOnMain.branches.map(b => b.name)).includes('feature-ui');
|
|
225
|
+
|
|
226
|
+
// Switch back to feature-ui seamlessly
|
|
227
|
+
const switchAgainRes = await executeCheckoutCmd({
|
|
228
|
+
branchName: 'feature-ui',
|
|
229
|
+
targetDir: testDir,
|
|
230
|
+
vcsFolderName,
|
|
231
|
+
quiet: true,
|
|
232
|
+
});
|
|
233
|
+
iReckon(lc, switchAgainRes.currentBranch).willEqual('feature-ui');
|
|
234
|
+
} finally {
|
|
235
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await ifWe(lc, `rejects checkout when branch does not exist or branchName missing`, async () => {
|
|
240
|
+
const testDir = path.join(process.cwd(), '.test_branch_checkout_4');
|
|
241
|
+
const vcsFolderName = '.vcsgib_test_bc_4';
|
|
242
|
+
try {
|
|
243
|
+
await executeInitCmd({
|
|
244
|
+
targetDir: testDir,
|
|
245
|
+
vcsFolderName,
|
|
246
|
+
quiet: true,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
let err1 = false;
|
|
250
|
+
try {
|
|
251
|
+
await executeCheckoutCmd({
|
|
252
|
+
branchName: '',
|
|
253
|
+
targetDir: testDir,
|
|
254
|
+
vcsFolderName,
|
|
255
|
+
quiet: true,
|
|
256
|
+
});
|
|
257
|
+
} catch (err: any) {
|
|
258
|
+
err1 = true;
|
|
259
|
+
iReckon(lc, err.message).includes('Branch name is required');
|
|
260
|
+
}
|
|
261
|
+
iReckon(lc, err1).isGonnaBeTrue();
|
|
262
|
+
|
|
263
|
+
let err2 = false;
|
|
264
|
+
try {
|
|
265
|
+
await executeCheckoutCmd({
|
|
266
|
+
branchName: 'non-existent-branch',
|
|
267
|
+
targetDir: testDir,
|
|
268
|
+
vcsFolderName,
|
|
269
|
+
quiet: true,
|
|
270
|
+
});
|
|
271
|
+
} catch (err: any) {
|
|
272
|
+
err2 = true;
|
|
273
|
+
iReckon(lc, err.message).includes("does not exist");
|
|
274
|
+
}
|
|
275
|
+
iReckon(lc, err2).isGonnaBeTrue();
|
|
276
|
+
} finally {
|
|
277
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
await ifWe(lc, `lists branches silently on secondary branch without error noise`, async () => {
|
|
282
|
+
const testDir = path.join(process.cwd(), '.test_branch_checkout_secondary');
|
|
283
|
+
const vcsFolderName = '.vcsgib_test_bc_sec';
|
|
284
|
+
try {
|
|
285
|
+
await executeInitCmd({
|
|
286
|
+
targetDir: testDir,
|
|
287
|
+
vcsFolderName,
|
|
288
|
+
quiet: true,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Create and checkout secondary branch feature-math
|
|
292
|
+
await executeBranchCmd({
|
|
293
|
+
branchName: 'feature-math',
|
|
294
|
+
targetDir: testDir,
|
|
295
|
+
vcsFolderName,
|
|
296
|
+
quiet: true,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
await executeCheckoutCmd({
|
|
300
|
+
branchName: 'feature-math',
|
|
301
|
+
targetDir: testDir,
|
|
302
|
+
vcsFolderName,
|
|
303
|
+
quiet: true,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Run branch list while on secondary branch
|
|
307
|
+
const listRes = await executeBranchCmd({
|
|
308
|
+
targetDir: testDir,
|
|
309
|
+
vcsFolderName,
|
|
310
|
+
quiet: true,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
iReckon(lc, listRes.branches.length).willEqual(2);
|
|
314
|
+
const mathBranch = listRes.branches.find(b => b.name === 'feature-math');
|
|
315
|
+
const mainBranch = listRes.branches.find(b => b.name === 'main');
|
|
316
|
+
iReckon(lc, mathBranch?.isActive).isGonnaBeTrue();
|
|
317
|
+
iReckon(lc, mainBranch?.isActive).isGonnaBeFalse();
|
|
318
|
+
} finally {
|
|
319
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
});
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module branch-cmd
|
|
3
|
+
*
|
|
4
|
+
* Command handler for `vcs branch [branchName]`.
|
|
5
|
+
* Lists registered branches or creates a new B2tFSBranchIbGib_V1 node registered in B2tFSIndexIbGib_V1.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import fs from 'node:fs/promises';
|
|
10
|
+
|
|
11
|
+
import { extractErrorMsg, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
12
|
+
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
13
|
+
import { getIbGibsFromCache_fallbackToSpaces } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
|
|
14
|
+
import { putInSpace } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
|
|
15
|
+
|
|
16
|
+
import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
|
|
17
|
+
import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
|
|
18
|
+
import { createB2tFSBranchIbGib } from '../../engine/branch/branch-helper.mjs';
|
|
19
|
+
import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
|
|
20
|
+
import { populateBranchSpaceFromGraph } from '../../engine/branch/branch-graph-helper.mjs';
|
|
21
|
+
import { B2tFSItemIbGib_V1 } from '../../engine/item/item-types.mjs';
|
|
22
|
+
import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
|
|
23
|
+
import { getB2tFSIndexIbGib, updateB2tFSIndexIbGib, getSpaceQualifiedIbGibAddr } from '../../engine/index/index-helper.mjs';
|
|
24
|
+
import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
|
|
25
|
+
|
|
26
|
+
const lcFile = `[branch-cmd]`;
|
|
27
|
+
const logalot = GLOBAL_LOG_A_LOT;
|
|
28
|
+
|
|
29
|
+
export interface BranchCmdOpts {
|
|
30
|
+
branchName?: string;
|
|
31
|
+
targetDir?: string;
|
|
32
|
+
vcsFolderName?: string;
|
|
33
|
+
quiet?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BranchInfo {
|
|
37
|
+
name: string;
|
|
38
|
+
isActive: boolean;
|
|
39
|
+
addr: string;
|
|
40
|
+
rootItemAddr?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface BranchCmdResult {
|
|
44
|
+
branches: BranchInfo[];
|
|
45
|
+
createdBranchName?: string;
|
|
46
|
+
createdBranchAddr?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function executeBranchCmd({
|
|
50
|
+
branchName,
|
|
51
|
+
targetDir = process.cwd(),
|
|
52
|
+
vcsFolderName = '.vcsgib',
|
|
53
|
+
quiet = false,
|
|
54
|
+
}: BranchCmdOpts = {}): Promise<BranchCmdResult> {
|
|
55
|
+
const lc = `${lcFile}[${executeBranchCmd.name}]`;
|
|
56
|
+
try {
|
|
57
|
+
if (logalot) {
|
|
58
|
+
console.log(`${lc} starting... (I: 4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f)`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const absTargetDir = path.resolve(targetDir);
|
|
62
|
+
|
|
63
|
+
const { metaspace, localSpace, vcsPath } = await bootstrapVcsMetaspace({
|
|
64
|
+
repoPath: absTargetDir,
|
|
65
|
+
vcsFolderName,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Verify repo exists
|
|
69
|
+
try {
|
|
70
|
+
await fs.access(vcsPath);
|
|
71
|
+
} catch {
|
|
72
|
+
throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a)`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 1. Fetch authoritative repository index from localSpace
|
|
76
|
+
const indexIbGib = await getB2tFSIndexIbGib({ space: localSpace, vcsPath });
|
|
77
|
+
if (!indexIbGib) {
|
|
78
|
+
throw new Error(`Repository index B2tFSIndexIbGib_V1 not found in localSpace (E: 6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b)`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const activeBranchName = indexIbGib.data?.activeBranch || 'main';
|
|
82
|
+
|
|
83
|
+
// Resolve active branch space b2s_<activeBranch>
|
|
84
|
+
const { space: activeBranchSpace } = await getOrCreateBranchSpace({
|
|
85
|
+
metaspace,
|
|
86
|
+
vcsPath,
|
|
87
|
+
branchName: activeBranchName,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// 2. Fetch all registered branch ibgibs
|
|
91
|
+
const branchAddrs = [
|
|
92
|
+
...(indexIbGib.rel8ns?.branch || []),
|
|
93
|
+
...(indexIbGib.rel8ns?.branch_tjp || []),
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
|
|
97
|
+
if (branchAddrs.length > 0) {
|
|
98
|
+
for (const bAddr of branchAddrs) {
|
|
99
|
+
const bIbGib = await getIbGibWithMetaspaceFallback({
|
|
100
|
+
addr: bAddr,
|
|
101
|
+
space: activeBranchSpace,
|
|
102
|
+
metaspace,
|
|
103
|
+
});
|
|
104
|
+
if (bIbGib) {
|
|
105
|
+
branchIbGibs.push(bIbGib as B2tFSBranchIbGib_V1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Deduplicate branch nodes by branch name
|
|
111
|
+
const branchMap = new Map<string, B2tFSBranchIbGib_V1>();
|
|
112
|
+
for (const b of branchIbGibs) {
|
|
113
|
+
if (b?.data?.name) {
|
|
114
|
+
branchMap.set(b.data.name, b);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const trimmedBranchName = branchName?.trim();
|
|
119
|
+
|
|
120
|
+
if (!trimmedBranchName) {
|
|
121
|
+
// LIST BRANCHES
|
|
122
|
+
const branches: BranchInfo[] = [];
|
|
123
|
+
for (const [name, bNode] of branchMap.entries()) {
|
|
124
|
+
const addr = getIbGibAddr({ ibGib: bNode });
|
|
125
|
+
const rootItemAddr = bNode.rel8ns?.b2tfs_item?.at(0);
|
|
126
|
+
const isActive = name === activeBranchName;
|
|
127
|
+
branches.push({
|
|
128
|
+
name,
|
|
129
|
+
isActive,
|
|
130
|
+
addr,
|
|
131
|
+
rootItemAddr,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!quiet) {
|
|
136
|
+
for (const b of branches) {
|
|
137
|
+
const prefix = b.isActive ? '* ' : ' ';
|
|
138
|
+
console.log(`${prefix}${b.name}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { branches };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// CREATE BRANCH
|
|
146
|
+
if (branchMap.has(trimmedBranchName)) {
|
|
147
|
+
throw new Error(`Branch '${trimmedBranchName}' already exists (E: 7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c)`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Find active branch root item to fork from
|
|
151
|
+
const activeBranchIbGib = branchMap.get(activeBranchName) || branchIbGibs.at(0);
|
|
152
|
+
const rootItemAddr = activeBranchIbGib?.rel8ns?.b2tfs_item?.at(0);
|
|
153
|
+
|
|
154
|
+
if (!rootItemAddr) {
|
|
155
|
+
throw new Error(`Cannot find active branch root item to fork branch from (E: 8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d)`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const rootItem = await getIbGibWithMetaspaceFallback({
|
|
159
|
+
addr: rootItemAddr,
|
|
160
|
+
space: activeBranchSpace,
|
|
161
|
+
metaspace,
|
|
162
|
+
}) as B2tFSItemIbGib_V1;
|
|
163
|
+
|
|
164
|
+
if (!rootItem) {
|
|
165
|
+
throw new Error(`Root item node for address '${rootItemAddr}' not found in localSpace (E: 9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e)`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Instantiate new branch space b2s_<newBranch>
|
|
169
|
+
const { space: newBranchSpace, spaceId: newBranchSpaceId } = await getOrCreateBranchSpace({
|
|
170
|
+
metaspace,
|
|
171
|
+
vcsPath,
|
|
172
|
+
branchName: trimmedBranchName,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Populate new branch space from live dependency graph of rootItem
|
|
176
|
+
await populateBranchSpaceFromGraph({
|
|
177
|
+
sourceSpace: activeBranchSpace,
|
|
178
|
+
targetBranchSpace: newBranchSpace,
|
|
179
|
+
rootItemIbGib: rootItem,
|
|
180
|
+
metaspace,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Create new B2tFSBranchIbGib_V1 branch node inside newBranchSpace
|
|
184
|
+
const newBranchIbGib = await createB2tFSBranchIbGib({
|
|
185
|
+
branchName: trimmedBranchName,
|
|
186
|
+
rootItem,
|
|
187
|
+
space: newBranchSpace,
|
|
188
|
+
metaspace,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Put in activeBranchSpace so active index can reference it
|
|
192
|
+
await putInSpace({ space: activeBranchSpace, ibGib: newBranchIbGib });
|
|
193
|
+
|
|
194
|
+
const newBranchAddr = getIbGibAddr({ ibGib: newBranchIbGib });
|
|
195
|
+
const sqNewBranchAddr = getSpaceQualifiedIbGibAddr({
|
|
196
|
+
spaceId: newBranchSpaceId,
|
|
197
|
+
ibGibAddr: newBranchAddr,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Register new branch node in B2tFSIndexIbGib_V1 using space-qualified address
|
|
201
|
+
await updateB2tFSIndexIbGib({
|
|
202
|
+
space: activeBranchSpace,
|
|
203
|
+
metaspace,
|
|
204
|
+
indexIbGib,
|
|
205
|
+
rel8nsToUpdate: {
|
|
206
|
+
branch: [sqNewBranchAddr],
|
|
207
|
+
branch_tjp: [sqNewBranchAddr],
|
|
208
|
+
},
|
|
209
|
+
vcsPath,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (!quiet) {
|
|
213
|
+
console.log(`Created branch '${trimmedBranchName}' (I: 0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f)`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Return updated branch list
|
|
217
|
+
branchMap.set(trimmedBranchName, newBranchIbGib);
|
|
218
|
+
const branches: BranchInfo[] = [];
|
|
219
|
+
for (const [name, bNode] of branchMap.entries()) {
|
|
220
|
+
const addr = getIbGibAddr({ ibGib: bNode });
|
|
221
|
+
const itemAddr = bNode.rel8ns?.b2tfs_item?.at(0);
|
|
222
|
+
branches.push({
|
|
223
|
+
name,
|
|
224
|
+
isActive: name === activeBranchName,
|
|
225
|
+
addr,
|
|
226
|
+
rootItemAddr: itemAddr,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
branches,
|
|
232
|
+
createdBranchName: trimmedBranchName,
|
|
233
|
+
createdBranchAddr: newBranchAddr,
|
|
234
|
+
};
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`${lc} ${extractErrorMsg(error)} (E: 1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a)`);
|
|
237
|
+
throw error;
|
|
238
|
+
} finally {
|
|
239
|
+
if (logalot) {
|
|
240
|
+
console.log(`${lc} complete. (I: 2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b)`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|