@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.
Files changed (93) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/VCS_CLI_USER_GUIDE.md +94 -89
  3. package/dist/AUTO-GENERATED-version.d.mts +1 -1
  4. package/dist/AUTO-GENERATED-version.mjs +1 -1
  5. package/dist/bootstrap/node-bootstrap.mjs +1 -1
  6. package/dist/cli/cli-arg-parser.d.mts +1 -0
  7. package/dist/cli/cli-arg-parser.d.mts.map +1 -1
  8. package/dist/cli/cli-arg-parser.mjs +5 -1
  9. package/dist/cli/cli-arg-parser.mjs.map +1 -1
  10. package/dist/cli/cli-constants.d.mts +6 -0
  11. package/dist/cli/cli-constants.d.mts.map +1 -1
  12. package/dist/cli/cli-constants.mjs +12 -0
  13. package/dist/cli/cli-constants.mjs.map +1 -1
  14. package/dist/cli/commands/add-cmd.d.mts.map +1 -1
  15. package/dist/cli/commands/add-cmd.mjs +5 -2
  16. package/dist/cli/commands/add-cmd.mjs.map +1 -1
  17. package/dist/cli/commands/commit-cmd.d.mts +5 -4
  18. package/dist/cli/commands/commit-cmd.d.mts.map +1 -1
  19. package/dist/cli/commands/commit-cmd.mjs +149 -33
  20. package/dist/cli/commands/commit-cmd.mjs.map +1 -1
  21. package/dist/cli/commands/generate-comment-cmd.d.mts +19 -0
  22. package/dist/cli/commands/generate-comment-cmd.d.mts.map +1 -0
  23. package/dist/cli/commands/generate-comment-cmd.mjs +140 -0
  24. package/dist/cli/commands/generate-comment-cmd.mjs.map +1 -0
  25. package/dist/cli/commands/log-cmd.d.mts +12 -3
  26. package/dist/cli/commands/log-cmd.d.mts.map +1 -1
  27. package/dist/cli/commands/log-cmd.mjs +236 -38
  28. package/dist/cli/commands/log-cmd.mjs.map +1 -1
  29. package/dist/cli/commands/merge-cmd.d.mts +21 -8
  30. package/dist/cli/commands/merge-cmd.d.mts.map +1 -1
  31. package/dist/cli/commands/merge-cmd.mjs +138 -70
  32. package/dist/cli/commands/merge-cmd.mjs.map +1 -1
  33. package/dist/cli/commands/status-cmd.d.mts +5 -6
  34. package/dist/cli/commands/status-cmd.d.mts.map +1 -1
  35. package/dist/cli/commands/status-cmd.mjs +50 -205
  36. package/dist/cli/commands/status-cmd.mjs.map +1 -1
  37. package/dist/cli/vcs-cli-main.d.mts.map +1 -1
  38. package/dist/cli/vcs-cli-main.mjs +27 -6
  39. package/dist/cli/vcs-cli-main.mjs.map +1 -1
  40. package/dist/engine/index/index-helper.d.mts.map +1 -1
  41. package/dist/engine/index/index-helper.mjs +3 -0
  42. package/dist/engine/index/index-helper.mjs.map +1 -1
  43. package/dist/engine/item/item-helper.d.mts +31 -1
  44. package/dist/engine/item/item-helper.d.mts.map +1 -1
  45. package/dist/engine/item/item-helper.mjs +44 -2
  46. package/dist/engine/item/item-helper.mjs.map +1 -1
  47. package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts +74 -0
  48. package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts.map +1 -0
  49. package/dist/engine/merge/{b2tfs-3way-merge-strategy.mjs → b2tfs-3way-graft-strategy.mjs} +133 -304
  50. package/dist/engine/merge/b2tfs-3way-graft-strategy.mjs.map +1 -0
  51. package/dist/engine/merge/{b2tfs-3way-merge-types.d.mts → b2tfs-3way-graft-types.d.mts} +2 -9
  52. package/dist/engine/merge/b2tfs-3way-graft-types.d.mts.map +1 -0
  53. package/dist/engine/merge/b2tfs-3way-graft-types.mjs +2 -0
  54. package/dist/engine/merge/b2tfs-3way-graft-types.mjs.map +1 -0
  55. package/dist/engine/status/status-helper.d.mts +89 -0
  56. package/dist/engine/status/status-helper.d.mts.map +1 -0
  57. package/dist/engine/status/status-helper.mjs +285 -0
  58. package/dist/engine/status/status-helper.mjs.map +1 -0
  59. package/dist/test/test-helpers.d.mts +51 -0
  60. package/dist/test/test-helpers.d.mts.map +1 -0
  61. package/dist/test/test-helpers.mjs +135 -0
  62. package/dist/test/test-helpers.mjs.map +1 -0
  63. package/package.json +4 -4
  64. package/src/AUTO-GENERATED-version.mts +1 -1
  65. package/src/bootstrap/node-bootstrap.mts +1 -1
  66. package/src/cli/cli-arg-parser.mts +5 -0
  67. package/src/cli/cli-constants.mts +13 -0
  68. package/src/cli/commands/add-cmd.mts +5 -2
  69. package/src/cli/commands/branch-checkout-cmd.respec.log +8 -101
  70. package/src/cli/commands/branch-checkout-cmd.respec.mts +11 -10
  71. package/src/cli/commands/commit-cmd.mts +165 -42
  72. package/src/cli/commands/commit-log-cmd.respec.mts +216 -3
  73. package/src/cli/commands/generate-comment-cmd.mts +171 -0
  74. package/src/cli/commands/log-cmd.mts +288 -41
  75. package/src/cli/commands/merge-cmd.mts +154 -76
  76. package/src/cli/commands/status-cmd.mts +57 -256
  77. package/src/cli/vcs-cli-main.mts +28 -7
  78. package/src/engine/index/index-helper.mts +3 -0
  79. package/src/engine/item/item-helper.mts +76 -3
  80. package/src/engine/merge/{b2tfs-3way-merge-strategy.mts → b2tfs-3way-graft-strategy.mts} +152 -374
  81. package/src/engine/merge/{b2tfs-3way-merge-strategy.respec.mts → b2tfs-3way-graft-strategy.respec.mts} +27 -122
  82. package/src/engine/merge/{b2tfs-3way-merge-types.mts → b2tfs-3way-graft-types.mts} +34 -42
  83. package/src/engine/merge/b2tfs-sync-comments.respec.mts +399 -0
  84. package/src/engine/status/status-helper.mts +380 -0
  85. package/src/test/test-helpers.mts +168 -0
  86. package/tsconfig.test.tsbuildinfo +1 -1
  87. package/tsconfig.tsbuildinfo +1 -1
  88. package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts +0 -48
  89. package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts.map +0 -1
  90. package/dist/engine/merge/b2tfs-3way-merge-strategy.mjs.map +0 -1
  91. package/dist/engine/merge/b2tfs-3way-merge-types.d.mts.map +0 -1
  92. package/dist/engine/merge/b2tfs-3way-merge-types.mjs +0 -2
  93. package/dist/engine/merge/b2tfs-3way-merge-types.mjs.map +0 -1
@@ -2,8 +2,16 @@
2
2
  * @module merge-cmd
3
3
  *
4
4
  * Command handler for `vcs merge <branchName>`.
5
- * Merges target branch into active branch using SyncSagaCoordinator sync and 3-way graft merge strategy.
6
- * Projects merged branch snapshot onto active branch physical worktree path.
5
+ * Merges the incoming branch into the base (active/current) branch using
6
+ * SyncSagaCoordinator sync and 3-way graft merge strategy.
7
+ * Projects merged branch snapshot onto the base branch physical worktree path.
8
+ *
9
+ * ## Terminology (matches standard git usage)
10
+ *
11
+ * * **base** branch / space — the branch currently checked out, the one being
12
+ * merged INTO (analogous to `ours` / `HEAD` in git)
13
+ * * **incoming** branch / space — the branch whose changes are being merged IN
14
+ * (analogous to `theirs` / the topic branch in git)
7
15
  */
8
16
 
9
17
  import path from 'node:path';
@@ -11,7 +19,8 @@ import fs from 'node:fs/promises';
11
19
 
12
20
  import { clone, extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
13
21
  import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
14
- import { putInSpace, getLatestAddrs, } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
22
+ import { putInSpace, getLatestAddrs, persistTransformResult, registerNewIbGib } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
23
+ import { rel8 } from '@ibgib/ts-gib/dist/V1/transforms/rel8.mjs';
15
24
  import { SyncSagaCoordinator } from '@ibgib/core-gib/dist/sync/sync-saga-coordinator.mjs';
16
25
  import { SyncPeerInnerspace_V1 } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
17
26
  import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
@@ -23,43 +32,52 @@ import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
23
32
  import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
24
33
  import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
25
34
  import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
26
- import { writeB2tFSItemToFS, getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
35
+ import { writeB2tFSItemToFS, getIbGibWithMetaspaceFallback, reifyB2tFSHierarchy } from '../../engine/item/item-helper.mjs';
27
36
  import { getB2tFSIndexIbGib, updateB2tFSIndexIbGib } from '../../engine/index/index-helper.mjs';
28
37
  import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
38
+ import { logBranchState, logItemState, logCheckpoint, logAddr, logAddrs } from '../../test/test-helpers.mjs';
39
+
29
40
 
30
41
  const lcFile = `[merge-cmd]`;
31
42
  const logalot = GLOBAL_LOG_A_LOT;
32
43
 
33
44
  export interface MergeCmdOpts {
34
- targetBranchName?: string;
45
+ /** The incoming branch name — the branch whose changes will be merged in. */
46
+ incomingBranchName?: string;
35
47
  targetDir?: string;
36
48
  vcsFolderName?: string;
37
49
  quiet?: boolean;
50
+ /** When true, emit diagnostic checkpoint logging (useful in tests). */
51
+ diagLog?: boolean;
38
52
  }
39
53
 
40
54
  export interface MergeCmdResult {
41
- activeBranch: string;
42
- targetBranch: string;
43
- activeSpaceId: string;
44
- targetSpaceId: string;
55
+ /** The base (current/active) branch name — merged into. */
56
+ baseBranch: string;
57
+ /** The incoming branch name — merged from. */
58
+ incomingBranch: string;
59
+ baseSpaceId: string;
60
+ incomingSpaceId: string;
45
61
  mergedBranchAddr?: string;
46
62
  worktreePath: string;
47
63
  }
48
64
 
49
65
  export async function executeMergeCmd({
50
- targetBranchName,
66
+ incomingBranchName,
51
67
  targetDir = process.cwd(),
52
68
  vcsFolderName = '.vcsgib',
53
69
  quiet = false,
70
+ diagLog = false,
54
71
  }: MergeCmdOpts = {}): Promise<MergeCmdResult> {
55
72
  const lc = `${lcFile}[${executeMergeCmd.name}]`;
73
+ const diag = (label: string) => { if (diagLog) logCheckpoint(`merge-cmd: ${label}`); };
56
74
  try {
57
75
  if (logalot) {
58
76
  console.log(`${lc} starting... (I: 4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f)`);
59
77
  }
60
78
 
61
- const trimmedTargetBranch = targetBranchName?.trim();
62
- if (!trimmedTargetBranch) {
79
+ const trimmedIncomingBranch = incomingBranchName?.trim();
80
+ if (!trimmedIncomingBranch) {
63
81
  throw new Error(`Branch name is required for merge (E: 5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a)`);
64
82
  }
65
83
 
@@ -83,10 +101,10 @@ export async function executeMergeCmd({
83
101
  throw new Error(`Repository index B2tFSIndexIbGib_V1 not found in localSpace (E: 7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c)`);
84
102
  }
85
103
 
86
- const activeBranchName = indexIbGib.data?.activeBranch || 'main';
104
+ const baseBranchName = indexIbGib.data?.activeBranch || 'main';
87
105
 
88
- if (activeBranchName === trimmedTargetBranch) {
89
- throw new Error(`Cannot merge branch '${trimmedTargetBranch}' into itself (E: 8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d)`);
106
+ if (baseBranchName === trimmedIncomingBranch) {
107
+ throw new Error(`Cannot merge branch '${trimmedIncomingBranch}' into itself (E: 8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d)`);
90
108
  }
91
109
 
92
110
  // 2. Fetch all registered branch ibgibs
@@ -109,72 +127,79 @@ export async function executeMergeCmd({
109
127
  }
110
128
  }
111
129
 
112
- // Resolve active branch node & target branch node
113
- const activeBranchIbGib = branchIbGibs.findLast(
114
- (b) => b?.data?.name === activeBranchName,
130
+ // Resolve base branch node & incoming branch node
131
+ const baseBranchIbGib = branchIbGibs.findLast(
132
+ (b) => b?.data?.name === baseBranchName,
115
133
  );
116
- if (!activeBranchIbGib) {
117
- throw new Error(`Active branch '${activeBranchName}' not found in repository (E: 9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e)`);
134
+ if (!baseBranchIbGib) {
135
+ throw new Error(`Base branch '${baseBranchName}' not found in repository (E: 9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e)`);
118
136
  }
119
137
 
120
- const targetBranchIbGib = branchIbGibs.findLast(
121
- (b) => b?.data?.name === trimmedTargetBranch,
138
+ const incomingBranchIbGib = branchIbGibs.findLast(
139
+ (b) => b?.data?.name === trimmedIncomingBranch,
122
140
  );
123
141
 
124
- if (!targetBranchIbGib) {
125
- throw new Error(`Branch '${trimmedTargetBranch}' does not exist (E: 0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f)`);
142
+ if (!incomingBranchIbGib) {
143
+ throw new Error(`Branch '${trimmedIncomingBranch}' does not exist (E: 0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f)`);
126
144
  }
127
145
 
128
- // 3. Resolve active branch space & target branch space
129
- const activeSpaceRes = await getOrCreateBranchSpace({
146
+ // 3. Resolve base branch space & incoming branch space
147
+ const baseSpaceRes = await getOrCreateBranchSpace({
130
148
  metaspace,
131
149
  vcsPath,
132
- branchName: activeBranchName,
150
+ branchName: baseBranchName,
133
151
  });
134
- const activeSpace = activeSpaceRes.space;
152
+ const baseSpace = baseSpaceRes.space;
135
153
 
136
- const targetSpaceRes = await getOrCreateBranchSpace({
154
+ const incomingSpaceRes = await getOrCreateBranchSpace({
137
155
  metaspace,
138
156
  vcsPath,
139
- branchName: trimmedTargetBranch,
157
+ branchName: trimmedIncomingBranch,
140
158
  });
141
- const targetSpace = targetSpaceRes.space;
159
+ const incomingSpace = incomingSpaceRes.space;
142
160
 
143
- await putInSpace({ space: activeSpace, ibGib: activeBranchIbGib });
144
- await putInSpace({ space: targetSpace, ibGib: targetBranchIbGib });
161
+ await putInSpace({ space: baseSpace, ibGib: baseBranchIbGib });
162
+ await putInSpace({ space: incomingSpace, ibGib: incomingBranchIbGib });
145
163
 
146
- // Resolve latest branch tip inside targetSpace (in case commits occurred on target branch)
147
- const targetBranchTjpAddr = targetBranchIbGib.rel8ns?.tjp?.at(0) || getIbGibAddr({ ibGib: targetBranchIbGib });
148
- const latestTargetBranchRes = await getLatestAddrs({ space: targetSpace, addrs: [targetBranchTjpAddr] });
149
- const latestTargetBranchAddr = (latestTargetBranchRes?.data as any)?.latestAddrs?.[0] || getIbGibAddr({ ibGib: targetBranchIbGib });
164
+ // Resolve latest branch tip inside incomingSpace (in case commits occurred on incoming branch)
165
+ const incomingBranchTjpAddr = incomingBranchIbGib.rel8ns?.tjp?.at(0) || getIbGibAddr({ ibGib: incomingBranchIbGib });
166
+ const latestIncomingBranchRes = await getLatestAddrs({ space: incomingSpace, addrs: [incomingBranchTjpAddr] });
167
+ const latestIncomingBranchAddr = (latestIncomingBranchRes?.data as any)?.latestAddrs?.[0] || getIbGibAddr({ ibGib: incomingBranchIbGib });
150
168
 
151
- const latestTargetBranchIbGib = (await getIbGibWithMetaspaceFallback({
152
- addr: latestTargetBranchAddr,
153
- space: targetSpace,
169
+ const latestIncomingBranchIbGib = (await getIbGibWithMetaspaceFallback({
170
+ addr: latestIncomingBranchAddr,
171
+ space: incomingSpace,
154
172
  metaspace,
155
- })) as B2tFSBranchIbGib_V1 || targetBranchIbGib;
173
+ })) as B2tFSBranchIbGib_V1 || incomingBranchIbGib;
156
174
 
157
- const targetRootAddr = latestTargetBranchIbGib.rel8ns?.b2tfs_item?.at(-1);
175
+ const incomingRootAddr = latestIncomingBranchIbGib.rel8ns?.b2tfs_item?.at(-1);
158
176
 
159
- if (!targetRootAddr) {
160
- throw new Error(`Target branch '${trimmedTargetBranch}' has no valid root item address (E: 3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e)`);
177
+ if (!incomingRootAddr) {
178
+ throw new Error(`Incoming branch '${trimmedIncomingBranch}' has no valid root item address (E: 3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e)`);
161
179
  }
162
- const targetRootItem = await getIbGibWithMetaspaceFallback({
163
- addr: targetRootAddr,
164
- space: targetSpace,
180
+ const incomingRootItem = await getIbGibWithMetaspaceFallback({
181
+ addr: incomingRootAddr,
182
+ space: incomingSpace,
165
183
  metaspace,
166
184
  });
167
- if (!targetRootItem) {
168
- throw new Error(`Target root item '${targetRootAddr}' not found in target space (E: 4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f)`);
185
+ if (!incomingRootItem) {
186
+ throw new Error(`Incoming root item '${incomingRootAddr}' not found in incoming space (E: 4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f)`);
187
+ }
188
+
189
+ if (diagLog) {
190
+ diag('resolving branches & spaces');
191
+ await logBranchState({ label: 'baseBranch (main)', branchIbGib: baseBranchIbGib, space: baseSpace });
192
+ await logBranchState({ label: 'incomingBranch (feature-merge)', branchIbGib: incomingBranchIbGib, space: incomingSpace });
193
+ await logItemState({ label: 'incomingRootItem', itemIbGib: incomingRootItem, space: incomingSpace });
169
194
  }
170
195
 
171
- // 4. Setup SyncPeerInnerspace and SyncSagaCoordinator syncing target branch space into active branch space
196
+ // 4. Setup SyncPeerInnerspace and SyncSagaCoordinator syncing incoming branch space into base branch space
172
197
  const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
173
198
  await peer.initialized;
174
199
  await peer.initializeOpts({
175
200
  localMetaspace: metaspace,
176
- localSpace: targetSpace,
177
- receiverSpace: activeSpace,
201
+ localSpace: incomingSpace,
202
+ receiverSpace: baseSpace,
178
203
  receiverCoordinator: new SyncSagaCoordinator(),
179
204
  receiverMetaspace: metaspace,
180
205
  });
@@ -182,31 +207,74 @@ export async function executeMergeCmd({
182
207
  const senderCoordinator = new SyncSagaCoordinator();
183
208
  const syncSaga = await senderCoordinator.sync({
184
209
  peer,
185
- localSpace: targetSpace,
210
+ localSpace: incomingSpace,
186
211
  metaspace,
187
- domainIbGibs: [targetRootItem],
212
+ domainIbGibs: [incomingRootItem],
188
213
  conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
189
214
  graftPreference: GraftPreference.receiver,
190
215
  });
191
216
 
192
217
  await syncSaga.done;
193
218
 
194
- // 5. Query activeSpace for updated active branch node & root item evolved natively by sync graft strategy
195
- const activeBranchTjpAddr = activeBranchIbGib.rel8ns?.tjp?.at(0) || getIbGibAddr({ ibGib: activeBranchIbGib });
196
- const latestBranchRes = await getLatestAddrs({ space: activeSpace, addrs: [activeBranchTjpAddr] });
197
- const mergedBranchAddr = (latestBranchRes?.data as any)?.latestAddrs?.[0] || getIbGibAddr({ ibGib: activeBranchIbGib });
219
+ // 5. Reify the base branch filesystem hierarchy.
220
+ // After sync, any child timelines (files or subfolders) that were merged/advanced
221
+ // in baseSpace will have newer tips. reifyB2tFSHierarchy bubbles all updated child tips
222
+ // up through the folder DAG, evolving parents in-place and returning the newly reified root item.
223
+ const currentBaseRootItemAddr = baseBranchIbGib.rel8ns?.b2tfs_item?.at(-1);
224
+ if (!currentBaseRootItemAddr) {
225
+ throw new Error(`Base branch '${baseBranchName}' has no root item address (E: 6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d)`);
226
+ }
198
227
 
199
- const mergedBranchIbGib = (await getIbGibWithMetaspaceFallback({
200
- addr: mergedBranchAddr,
201
- space: activeSpace,
228
+ const reifyRes = await reifyB2tFSHierarchy({
229
+ rootItemAddr: currentBaseRootItemAddr,
230
+ space: baseSpace,
202
231
  metaspace,
203
- })) as B2tFSBranchIbGib_V1 || activeBranchIbGib;
232
+ });
233
+
234
+ const reifiedRootItem = reifyRes.reifiedRootItem;
235
+ const reifiedRootAddr = getIbGibAddr({ ibGib: reifiedRootItem });
236
+
237
+ if (diagLog) {
238
+ diag('after reification');
239
+ logAddr('currentBaseRootItemAddr', currentBaseRootItemAddr);
240
+ logAddr('reifiedRootAddr', reifiedRootAddr);
241
+ console.log(`[merge-cmd] reification hasChanges:`, reifyRes.hasChanges);
242
+ console.log(`[merge-cmd] reifiedAddrsMap:`, JSON.stringify(reifyRes.reifiedAddrsMap));
243
+ }
204
244
 
205
- const mergedRootItemAddr = mergedBranchIbGib.rel8ns?.b2tfs_item?.at(-1);
245
+ // 5b. Evolve the base branch ibgib to point at the reified root item tip.
246
+ // The sync engine does not update branch ibgibs — branches live in
247
+ // their own space and are never domain ibgibs in the sync. We advance
248
+ // the branch pointer to the reified root item.
249
+ let mergedBranchAddr: string = getIbGibAddr({ ibGib: baseBranchIbGib });
250
+ if (reifyRes.hasChanges && reifiedRootAddr !== currentBaseRootItemAddr) {
251
+ if (diagLog) {
252
+ console.log(`[merge-cmd] Evolving base branch: root item changing from ${currentBaseRootItemAddr} -> ${reifiedRootAddr}`);
253
+ }
254
+ const resRel8Branch = await rel8({
255
+ src: baseBranchIbGib,
256
+ rel8nsToReplaceByAddr: {
257
+ b2tfs_item: [{ oldAddr: currentBaseRootItemAddr, newAddr: reifiedRootAddr }],
258
+ },
259
+ dna: true,
260
+ nCounter: true,
261
+ });
262
+ const evolvedBranchIbGib = resRel8Branch.newIbGib;
263
+ await persistTransformResult({ resTransform: resRel8Branch, space: baseSpace });
264
+ await registerNewIbGib({ ibGib: evolvedBranchIbGib, space: baseSpace });
265
+ mergedBranchAddr = getIbGibAddr({ ibGib: evolvedBranchIbGib });
266
+ if (diagLog) {
267
+ console.log(`[merge-cmd] Base branch evolved to addr: ${mergedBranchAddr}`);
268
+ }
269
+ } else {
270
+ if (diagLog) {
271
+ console.log(`[merge-cmd] Base branch root item unchanged (${currentBaseRootItemAddr})`);
272
+ }
273
+ }
206
274
 
207
275
  // Update indexIbGib to reference new merged branch node
208
276
  await updateB2tFSIndexIbGib({
209
- space: activeSpace,
277
+ space: baseSpace,
210
278
  metaspace,
211
279
  indexIbGib,
212
280
  rel8nsToUpdate: {
@@ -215,28 +283,38 @@ export async function executeMergeCmd({
215
283
  vcsPath,
216
284
  });
217
285
 
286
+ const mergedRootItemAddr = reifiedRootAddr;
287
+
288
+ if (diagLog) {
289
+ diag('projecting onto disk');
290
+ logAddr('mergedRootItemAddr', mergedRootItemAddr);
291
+ }
218
292
 
219
293
  // 6. Project merged branch snapshot files onto physical worktree disk
220
- const isDefaultBranch = activeBranchName === 'main';
294
+ const isDefaultBranch = baseBranchName === 'main';
221
295
  const worktreePath = isDefaultBranch
222
296
  ? repoRoot
223
- : path.join(repoRoot, 'b', activeBranchName);
297
+ : path.join(repoRoot, 'b', baseBranchName);
224
298
 
225
299
  await fs.mkdir(worktreePath, { recursive: true });
226
300
 
227
301
  if (mergedRootItemAddr) {
228
302
  const mergedRootItem = await getIbGibWithMetaspaceFallback({
229
303
  addr: mergedRootItemAddr,
230
- space: activeSpace,
304
+ space: baseSpace,
231
305
  metaspace,
232
306
  }) as B2tFSItemIbGib_V1;
233
307
 
308
+ if (diagLog) {
309
+ await logItemState({ label: 'mergedRootItem to write', itemIbGib: mergedRootItem, space: baseSpace });
310
+ }
311
+
234
312
  if (mergedRootItem) {
235
313
  if (mergedRootItem.data?.fsType === 'file') {
236
314
  await writeB2tFSItemToFS({
237
315
  item: mergedRootItem,
238
316
  destPath: worktreePath,
239
- space: activeSpace,
317
+ space: baseSpace,
240
318
  metaspace,
241
319
  });
242
320
  } else {
@@ -244,7 +322,7 @@ export async function executeMergeCmd({
244
322
  for (const childAddr of childAddrs) {
245
323
  const childItem = await getIbGibWithMetaspaceFallback({
246
324
  addr: childAddr,
247
- space: activeSpace,
325
+ space: baseSpace,
248
326
  metaspace,
249
327
  }) as B2tFSItemIbGib_V1;
250
328
 
@@ -252,7 +330,7 @@ export async function executeMergeCmd({
252
330
  await writeB2tFSItemToFS({
253
331
  item: childItem,
254
332
  destPath: worktreePath,
255
- space: activeSpace,
333
+ space: baseSpace,
256
334
  metaspace,
257
335
  });
258
336
  }
@@ -262,16 +340,16 @@ export async function executeMergeCmd({
262
340
  }
263
341
 
264
342
  if (!quiet) {
265
- console.log(`Successfully merged branch '${trimmedTargetBranch}' into '${activeBranchName}' (I: 1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a)`);
343
+ console.log(`Successfully merged branch '${trimmedIncomingBranch}' into '${baseBranchName}' (I: 1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a)`);
266
344
  console.log(` Worktree projection: ${worktreePath}`);
267
345
  }
268
346
 
269
347
  return {
270
- activeBranch: activeBranchName,
271
- targetBranch: trimmedTargetBranch,
272
- activeSpaceId: activeSpaceRes.spaceId,
273
- targetSpaceId: targetSpaceRes.spaceId,
274
- mergedBranchAddr: getIbGibAddr({ ibGib: mergedBranchIbGib }),
348
+ baseBranch: baseBranchName,
349
+ incomingBranch: trimmedIncomingBranch,
350
+ baseSpaceId: baseSpaceRes.spaceId,
351
+ incomingSpaceId: incomingSpaceRes.spaceId,
352
+ mergedBranchAddr,
275
353
  worktreePath,
276
354
  };
277
355
  } catch (error) {