@ibgib/vcs-gib 0.0.17 → 0.0.18

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 (143) hide show
  1. package/dist/AUTO-GENERATED-version.d.mts +1 -1
  2. package/dist/AUTO-GENERATED-version.mjs +1 -1
  3. package/dist/bootstrap/node-bootstrap.d.mts.map +1 -1
  4. package/dist/bootstrap/node-bootstrap.mjs +3 -0
  5. package/dist/bootstrap/node-bootstrap.mjs.map +1 -1
  6. package/dist/bootstrap/{node-bootstrap.respec.node.d.mts → node-bootstrap.respec.d.mts} +1 -1
  7. package/dist/bootstrap/node-bootstrap.respec.d.mts.map +1 -0
  8. package/dist/bootstrap/{node-bootstrap.respec.node.mjs → node-bootstrap.respec.mjs} +1 -1
  9. package/dist/bootstrap/node-bootstrap.respec.mjs.map +1 -0
  10. package/dist/cli/{cli-arg-parser.respec.node.d.mts → cli-arg-parser.respec.d.mts} +1 -1
  11. package/dist/cli/cli-arg-parser.respec.d.mts.map +1 -0
  12. package/dist/cli/{cli-arg-parser.respec.node.mjs → cli-arg-parser.respec.mjs} +1 -1
  13. package/dist/cli/cli-arg-parser.respec.mjs.map +1 -0
  14. package/dist/cli/cli-constants.d.mts +1 -0
  15. package/dist/cli/cli-constants.d.mts.map +1 -1
  16. package/dist/cli/cli-constants.mjs +1 -0
  17. package/dist/cli/cli-constants.mjs.map +1 -1
  18. package/dist/cli/{cli-e2e.respec.node.d.mts → cli-e2e.respec.d.mts} +1 -1
  19. package/dist/cli/cli-e2e.respec.d.mts.map +1 -0
  20. package/dist/cli/{cli-e2e.respec.node.mjs → cli-e2e.respec.mjs} +1 -1
  21. package/dist/cli/cli-e2e.respec.mjs.map +1 -0
  22. package/dist/cli/commands/add-cmd.d.mts.map +1 -1
  23. package/dist/cli/commands/add-cmd.mjs +2 -2
  24. package/dist/cli/commands/add-cmd.mjs.map +1 -1
  25. package/dist/cli/commands/{branch-checkout-cmd.respec.node.d.mts → branch-checkout-cmd.respec.d.mts} +1 -1
  26. package/dist/cli/commands/branch-checkout-cmd.respec.d.mts.map +1 -0
  27. package/dist/cli/commands/{branch-checkout-cmd.respec.node.mjs → branch-checkout-cmd.respec.mjs} +103 -9
  28. package/dist/cli/commands/branch-checkout-cmd.respec.mjs.map +1 -0
  29. package/dist/cli/commands/branch-cmd.d.mts.map +1 -1
  30. package/dist/cli/commands/branch-cmd.mjs +2 -2
  31. package/dist/cli/commands/branch-cmd.mjs.map +1 -1
  32. package/dist/cli/commands/checkout-cmd.d.mts.map +1 -1
  33. package/dist/cli/commands/checkout-cmd.mjs +15 -7
  34. package/dist/cli/commands/checkout-cmd.mjs.map +1 -1
  35. package/dist/cli/commands/commit-cmd.d.mts.map +1 -1
  36. package/dist/cli/commands/commit-cmd.mjs +2 -2
  37. package/dist/cli/commands/commit-cmd.mjs.map +1 -1
  38. package/dist/cli/commands/{commit-log-cmd.respec.node.d.mts → commit-log-cmd.respec.d.mts} +1 -1
  39. package/dist/cli/commands/commit-log-cmd.respec.d.mts.map +1 -0
  40. package/dist/cli/commands/{commit-log-cmd.respec.node.mjs → commit-log-cmd.respec.mjs} +1 -1
  41. package/dist/cli/commands/commit-log-cmd.respec.mjs.map +1 -0
  42. package/dist/cli/commands/{init-cmd.respec.node.d.mts → init-cmd.respec.d.mts} +1 -1
  43. package/dist/cli/commands/init-cmd.respec.d.mts.map +1 -0
  44. package/dist/cli/commands/{init-cmd.respec.node.mjs → init-cmd.respec.mjs} +1 -1
  45. package/dist/cli/commands/init-cmd.respec.mjs.map +1 -0
  46. package/dist/cli/commands/merge-cmd.d.mts +23 -0
  47. package/dist/cli/commands/merge-cmd.d.mts.map +1 -0
  48. package/dist/cli/commands/merge-cmd.mjs +224 -0
  49. package/dist/cli/commands/merge-cmd.mjs.map +1 -0
  50. package/dist/cli/commands/{reset-restore-cmd.respec.node.d.mts → reset-restore-cmd.respec.d.mts} +1 -1
  51. package/dist/cli/commands/reset-restore-cmd.respec.d.mts.map +1 -0
  52. package/dist/cli/commands/{reset-restore-cmd.respec.node.mjs → reset-restore-cmd.respec.mjs} +1 -1
  53. package/dist/cli/commands/reset-restore-cmd.respec.mjs.map +1 -0
  54. package/dist/cli/commands/{status-add-cmd.respec.node.d.mts → status-add-cmd.respec.d.mts} +1 -1
  55. package/dist/cli/commands/status-add-cmd.respec.d.mts.map +1 -0
  56. package/dist/cli/commands/{status-add-cmd.respec.node.mjs → status-add-cmd.respec.mjs} +1 -1
  57. package/dist/cli/commands/status-add-cmd.respec.mjs.map +1 -0
  58. package/dist/cli/vcs-cli-main.d.mts.map +1 -1
  59. package/dist/cli/vcs-cli-main.mjs +7 -0
  60. package/dist/cli/vcs-cli-main.mjs.map +1 -1
  61. package/dist/engine/branch/{branch-space.respec.node.d.mts → branch-space.respec.d.mts} +1 -1
  62. package/dist/engine/branch/branch-space.respec.d.mts.map +1 -0
  63. package/dist/engine/branch/{branch-space.respec.node.mjs → branch-space.respec.mjs} +1 -1
  64. package/dist/engine/branch/branch-space.respec.mjs.map +1 -0
  65. package/dist/engine/diff/{line-diff-helper.respec.node.d.mts → line-diff-helper.respec.d.mts} +1 -1
  66. package/dist/engine/diff/line-diff-helper.respec.d.mts.map +1 -0
  67. package/dist/engine/diff/{line-diff-helper.respec.node.mjs → line-diff-helper.respec.mjs} +1 -1
  68. package/dist/engine/diff/line-diff-helper.respec.mjs.map +1 -0
  69. package/dist/engine/filter/{glob-helper.respec.node.d.mts → glob-helper.respec.d.mts} +1 -1
  70. package/dist/engine/filter/glob-helper.respec.d.mts.map +1 -0
  71. package/dist/engine/filter/{glob-helper.respec.node.mjs → glob-helper.respec.mjs} +1 -1
  72. package/dist/engine/filter/glob-helper.respec.mjs.map +1 -0
  73. package/dist/engine/item/{item.respec.node.d.mts → item.respec.d.mts} +1 -1
  74. package/dist/engine/item/item.respec.d.mts.map +1 -0
  75. package/dist/engine/item/{item.respec.node.mjs → item.respec.mjs} +1 -1
  76. package/dist/engine/item/item.respec.mjs.map +1 -0
  77. package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts +38 -0
  78. package/dist/engine/merge/b2tfs-3way-merge-strategy.d.mts.map +1 -0
  79. package/dist/engine/merge/b2tfs-3way-merge-strategy.mjs +593 -0
  80. package/dist/engine/merge/b2tfs-3way-merge-strategy.mjs.map +1 -0
  81. package/dist/engine/merge/b2tfs-merge-strategy.respec.d.mts +7 -0
  82. package/dist/engine/merge/b2tfs-merge-strategy.respec.d.mts.map +1 -0
  83. package/dist/engine/merge/b2tfs-merge-strategy.respec.mjs +335 -0
  84. package/dist/engine/merge/b2tfs-merge-strategy.respec.mjs.map +1 -0
  85. package/dist/respec-gib.node.mjs +1 -1
  86. package/dist/respec-gib.node.mjs.map +1 -1
  87. package/dist/{vcs-gib.respec.node.d.mts → vcs-gib.respec.d.mts} +1 -1
  88. package/dist/vcs-gib.respec.d.mts.map +1 -0
  89. package/dist/{vcs-gib.respec.node.mjs → vcs-gib.respec.mjs} +1 -1
  90. package/dist/vcs-gib.respec.mjs.map +1 -0
  91. package/package.json +3 -3
  92. package/src/AUTO-GENERATED-version.mts +1 -1
  93. package/src/bootstrap/node-bootstrap.mts +4 -0
  94. package/src/cli/cli-constants.mts +1 -0
  95. package/src/cli/commands/add-cmd.mts +5 -3
  96. package/src/cli/commands/{branch-checkout-cmd.respec.node.mts → branch-checkout-cmd.respec.mts} +112 -9
  97. package/src/cli/commands/branch-cmd.mts +3 -2
  98. package/src/cli/commands/checkout-cmd.mts +23 -9
  99. package/src/cli/commands/commit-cmd.mts +5 -3
  100. package/src/cli/commands/merge-cmd.mts +289 -0
  101. package/src/cli/vcs-cli-main.mts +7 -0
  102. package/src/engine/merge/b2tfs-3way-merge-strategy.mts +725 -0
  103. package/src/engine/merge/b2tfs-merge-strategy.respec.mts +438 -0
  104. package/src/respec-gib.node.mts +2 -2
  105. package/tsconfig.tsbuildinfo +1 -1
  106. package/dist/bootstrap/node-bootstrap.respec.node.d.mts.map +0 -1
  107. package/dist/bootstrap/node-bootstrap.respec.node.mjs.map +0 -1
  108. package/dist/cli/cli-arg-parser.respec.node.d.mts.map +0 -1
  109. package/dist/cli/cli-arg-parser.respec.node.mjs.map +0 -1
  110. package/dist/cli/cli-e2e.respec.node.d.mts.map +0 -1
  111. package/dist/cli/cli-e2e.respec.node.mjs.map +0 -1
  112. package/dist/cli/commands/branch-checkout-cmd.respec.node.d.mts.map +0 -1
  113. package/dist/cli/commands/branch-checkout-cmd.respec.node.mjs.map +0 -1
  114. package/dist/cli/commands/commit-log-cmd.respec.node.d.mts.map +0 -1
  115. package/dist/cli/commands/commit-log-cmd.respec.node.mjs.map +0 -1
  116. package/dist/cli/commands/init-cmd.respec.node.d.mts.map +0 -1
  117. package/dist/cli/commands/init-cmd.respec.node.mjs.map +0 -1
  118. package/dist/cli/commands/reset-restore-cmd.respec.node.d.mts.map +0 -1
  119. package/dist/cli/commands/reset-restore-cmd.respec.node.mjs.map +0 -1
  120. package/dist/cli/commands/status-add-cmd.respec.node.d.mts.map +0 -1
  121. package/dist/cli/commands/status-add-cmd.respec.node.mjs.map +0 -1
  122. package/dist/engine/branch/branch-space.respec.node.d.mts.map +0 -1
  123. package/dist/engine/branch/branch-space.respec.node.mjs.map +0 -1
  124. package/dist/engine/diff/line-diff-helper.respec.node.d.mts.map +0 -1
  125. package/dist/engine/diff/line-diff-helper.respec.node.mjs.map +0 -1
  126. package/dist/engine/filter/glob-helper.respec.node.d.mts.map +0 -1
  127. package/dist/engine/filter/glob-helper.respec.node.mjs.map +0 -1
  128. package/dist/engine/item/item.respec.node.d.mts.map +0 -1
  129. package/dist/engine/item/item.respec.node.mjs.map +0 -1
  130. package/dist/vcs-gib.respec.node.d.mts.map +0 -1
  131. package/dist/vcs-gib.respec.node.mjs.map +0 -1
  132. /package/src/bootstrap/{node-bootstrap.respec.node.mts → node-bootstrap.respec.mts} +0 -0
  133. /package/src/cli/{cli-arg-parser.respec.node.mts → cli-arg-parser.respec.mts} +0 -0
  134. /package/src/cli/{cli-e2e.respec.node.mts → cli-e2e.respec.mts} +0 -0
  135. /package/src/cli/commands/{commit-log-cmd.respec.node.mts → commit-log-cmd.respec.mts} +0 -0
  136. /package/src/cli/commands/{init-cmd.respec.node.mts → init-cmd.respec.mts} +0 -0
  137. /package/src/cli/commands/{reset-restore-cmd.respec.node.mts → reset-restore-cmd.respec.mts} +0 -0
  138. /package/src/cli/commands/{status-add-cmd.respec.node.mts → status-add-cmd.respec.mts} +0 -0
  139. /package/src/engine/branch/{branch-space.respec.node.mts → branch-space.respec.mts} +0 -0
  140. /package/src/engine/diff/{line-diff-helper.respec.node.mts → line-diff-helper.respec.mts} +0 -0
  141. /package/src/engine/filter/{glob-helper.respec.node.mts → glob-helper.respec.mts} +0 -0
  142. /package/src/engine/item/{item.respec.node.mts → item.respec.mts} +0 -0
  143. /package/src/{vcs-gib.respec.node.mts → vcs-gib.respec.mts} +0 -0
@@ -0,0 +1,725 @@
1
+ /**
2
+ * @module b2tfs-3way-merge-strategy
3
+ *
4
+ * Extensible Graft Merge Strategy for 3-way B2tFS filesystem snapshot trees and text files.
5
+ */
6
+
7
+ import crypto from 'node:crypto';
8
+
9
+ import { extractErrorMsg, hash } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
10
+ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
11
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
12
+ import { getGib } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
13
+ import { rel8 } from '@ibgib/ts-gib/dist/V1/transforms/rel8.mjs';
14
+ import { IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
15
+
16
+ import { GraftMergeStrategyBase_V1 } from '@ibgib/core-gib/dist/sync/graft-info/graft-merge-strategy-base-v1.mjs';
17
+ import { GraftContext, GraftPreference, GraftStrategy, GraftTimelinesResult, GraftMergeContext, GraftMergeResult } from '@ibgib/core-gib/dist/sync/graft-info/graft-info-types.mjs';
18
+ import { FlatIbGibGraph } from '@ibgib/core-gib/dist/common/other/graph-types.mjs';
19
+
20
+
21
+ import { getFromSpace, persistTransformResult, registerNewIbGib, putInSpace } from '@ibgib/core-gib/dist/witness/space/space-helper.mjs';
22
+ import { getBinIb, getIbGibsFromCache_fallbackToSpaces, getTimelinesGroupedByTjp, getTimestampInfo, splitPerTjpAndOrDna } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
23
+
24
+ import { applyTransforms, getUniqueDnas } from '@ibgib/core-gib/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs';
25
+ import { createGraftInfo } from '@ibgib/core-gib/dist/sync/graft-info/graft-info-helpers.mjs';
26
+ import { GRAFT_INFO_REL8N_NAME } from '@ibgib/core-gib/dist/sync/graft-info/graft-info-constants.mjs';
27
+ import { BinIbGib_V1 } from '@ibgib/core-gib/dist/common/bin/bin-types.mjs';
28
+ import { SyncConflictStrategy } from '@ibgib/core-gib/dist/sync/sync-constants.mjs';
29
+
30
+ import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
31
+ import { isBinaryBuffer, decodeToLines, computeLineLcsDiff } from '../diff/line-diff-helper.mjs';
32
+ import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME, BINARY_REL8N_NAME } from '../item/item-types.mjs';
33
+ import { evolveB2tFSItemIbGib } from '../item/item-helper.mjs';
34
+ import { IbGibSpaceAny } from '@ibgib/core-gib/dist/witness/space/space-base-v1.mjs';
35
+
36
+ const lcFile = `[b2tfs-3way-merge-strategy]`;
37
+ const logalot = GLOBAL_LOG_A_LOT;
38
+
39
+ /**
40
+ * 3-way text line diff merge result.
41
+ */
42
+ export interface Text3WayMergeResult {
43
+ mergedText: string;
44
+ hasConflict: boolean;
45
+ }
46
+
47
+ /**
48
+ * Performs 3-way line diff merging across LCA, Base, and Orphan text lines.
49
+ * Inserts Git conflict markers (`<<<<<<< HEAD`, `=======`, `>>>>>>>`) when overlapping
50
+ * line changes conflict.
51
+ */
52
+ export function perform3WayTextLineMerge({
53
+ textLCA,
54
+ textBase,
55
+ textOrphan,
56
+ }: {
57
+ textLCA: string;
58
+ textBase: string;
59
+ textOrphan: string;
60
+ }): Text3WayMergeResult {
61
+ console.log(`[DEBUG 3WAY] textLCA: ${JSON.stringify(textLCA)}`);
62
+ console.log(`[DEBUG 3WAY] textBase: ${JSON.stringify(textBase)}`);
63
+ console.log(`[DEBUG 3WAY] textOrphan: ${JSON.stringify(textOrphan)}`);
64
+ if (textBase === textOrphan) {
65
+ return { mergedText: textBase, hasConflict: false };
66
+ }
67
+ if (textBase === textLCA) {
68
+ return { mergedText: textOrphan, hasConflict: false };
69
+ }
70
+ if (textOrphan === textLCA) {
71
+ return { mergedText: textBase, hasConflict: false };
72
+ }
73
+
74
+
75
+ const linesLCA = textLCA.split(/\r?\n/);
76
+ const linesBase = textBase.split(/\r?\n/);
77
+ const linesOrphan = textOrphan.split(/\r?\n/);
78
+
79
+ const chunksBase = computeLineLcsDiff(linesLCA, linesBase);
80
+ const chunksOrphan = computeLineLcsDiff(linesLCA, linesOrphan);
81
+
82
+ if (chunksBase.length === 0) {
83
+ return { mergedText: textOrphan, hasConflict: false };
84
+ }
85
+ if (chunksOrphan.length === 0) {
86
+ return { mergedText: textBase, hasConflict: false };
87
+ }
88
+
89
+ let hasConflict = false;
90
+ const outputLines: string[] = [];
91
+
92
+ let lcaIdx = 0;
93
+ let baseChunkIdx = 0;
94
+ let orphanChunkIdx = 0;
95
+
96
+ while (lcaIdx < linesLCA.length || baseChunkIdx < chunksBase.length || orphanChunkIdx < chunksOrphan.length) {
97
+ const nextBaseChunk = chunksBase[baseChunkIdx];
98
+ const nextOrphanChunk = chunksOrphan[orphanChunkIdx];
99
+
100
+ const baseAtLca = nextBaseChunk && nextBaseChunk.old_i === lcaIdx;
101
+ const orphanAtLca = nextOrphanChunk && nextOrphanChunk.old_i === lcaIdx;
102
+
103
+ if (baseAtLca && orphanAtLca) {
104
+ const bLines = nextBaseChunk.newLines;
105
+ const oLines = nextOrphanChunk.newLines;
106
+ const bOldLen = nextBaseChunk.oldLines.length;
107
+ const oOldLen = nextOrphanChunk.oldLines.length;
108
+
109
+ if (bLines.join('\n') === oLines.join('\n') && bOldLen === oOldLen) {
110
+ outputLines.push(...bLines);
111
+ } else {
112
+ hasConflict = true;
113
+ outputLines.push('<<<<<<< HEAD');
114
+ outputLines.push(...bLines);
115
+ outputLines.push('=======');
116
+ outputLines.push(...oLines);
117
+ outputLines.push('>>>>>>>');
118
+ }
119
+
120
+ const consumedLca = Math.max(bOldLen, oOldLen, 1);
121
+ lcaIdx += consumedLca;
122
+ baseChunkIdx++;
123
+ orphanChunkIdx++;
124
+ } else if (baseAtLca) {
125
+ outputLines.push(...nextBaseChunk.newLines);
126
+ const consumedLca = Math.max(nextBaseChunk.oldLines.length, 1);
127
+ lcaIdx += consumedLca;
128
+ baseChunkIdx++;
129
+ } else if (orphanAtLca) {
130
+ outputLines.push(...nextOrphanChunk.newLines);
131
+ const consumedLca = Math.max(nextOrphanChunk.oldLines.length, 1);
132
+ lcaIdx += consumedLca;
133
+ orphanChunkIdx++;
134
+ } else {
135
+ if (lcaIdx < linesLCA.length) {
136
+ outputLines.push(linesLCA[lcaIdx]);
137
+ lcaIdx++;
138
+ } else {
139
+ if (nextBaseChunk && baseChunkIdx < chunksBase.length) {
140
+ outputLines.push(...nextBaseChunk.newLines);
141
+ baseChunkIdx++;
142
+ }
143
+ if (nextOrphanChunk && orphanChunkIdx < chunksOrphan.length) {
144
+ outputLines.push(...nextOrphanChunk.newLines);
145
+ orphanChunkIdx++;
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ return {
152
+ mergedText: outputLines.join('\n'),
153
+ hasConflict,
154
+ };
155
+ }
156
+
157
+ export class B2tFS3WayGraftStrategy extends GraftMergeStrategyBase_V1 implements GraftStrategy {
158
+ readonly strategyId: string = 'B2tFS3WayGraftStrategy';
159
+ private lc = '[B2tFS3WayGraftStrategy]';
160
+
161
+ private isB2tFSNode(ibGib?: IbGib_V1): boolean {
162
+ if (!ibGib || !ibGib.ib) { return false; }
163
+ return ibGib.ib.startsWith('b2tfs_item') ||
164
+ ibGib.ib.startsWith('b2tfs_branch') ||
165
+ ibGib.data?.classname === 'B2tFSItemIbGib_V1' ||
166
+ ibGib.data?.classname === 'B2tFSBranchIbGib_V1';
167
+ }
168
+
169
+ async canGraft(context: GraftContext): Promise<boolean> {
170
+ const lc = `${this.lc}[${this.canGraft.name}]`;
171
+ try {
172
+ const { conflictStrategy, timelineAddrsA, timelineAddrsB, ibGibReadCache, space } = context;
173
+ if (conflictStrategy === SyncConflictStrategy.optimisticWithLCS || (conflictStrategy as string) === 'b2tfs3WayMerge') {
174
+ return true;
175
+ }
176
+ const tipAddrA = timelineAddrsA.at(-1);
177
+ const tipAddrB = timelineAddrsB.at(-1);
178
+ if (!tipAddrA || !tipAddrB) { return false; }
179
+
180
+ const [tipA, tipB] = await getIbGibsFromCache_fallbackToSpaces({
181
+ addrs: [tipAddrA, tipAddrB],
182
+ readCache_graph: ibGibReadCache,
183
+ space,
184
+ });
185
+
186
+ return this.isB2tFSNode(tipA) || this.isB2tFSNode(tipB);
187
+ } catch (error) {
188
+ console.error(`${lc} ${extractErrorMsg(error)} (E: 8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e)`);
189
+ return false;
190
+ }
191
+ }
192
+
193
+ async graft(context: GraftContext): Promise<GraftTimelinesResult> {
194
+ const lc = `${lcFile}[${this.strategyId}][${this.graft.name}]`;
195
+ try {
196
+ if (logalot) {
197
+ console.log(`${lc} starting 3-way B2tFS snapshot graft... (I: 91a82b3c4d5e6f7a8b9c0d1e2f3a4b5c)`);
198
+ }
199
+
200
+ const {
201
+ conflictStrategy,
202
+ timelineAddrsA,
203
+ timelineAddrsB,
204
+ latestCommonFrameAddr,
205
+ ibGibReadCache,
206
+ space,
207
+ graftPreference,
208
+ } = context;
209
+
210
+
211
+ // 1. Fetch branch slices since LCA (timeline A = Sender, timeline B = Receiver)
212
+ const indexLCA_A = timelineAddrsA.lastIndexOf(latestCommonFrameAddr);
213
+ const addrsA_sinceLCA = timelineAddrsA.slice(indexLCA_A + 1);
214
+ const branchA_sender_sinceLCA = await getIbGibsFromCache_fallbackToSpaces({
215
+ readCache_graph: ibGibReadCache,
216
+ addrs: addrsA_sinceLCA,
217
+ space,
218
+ });
219
+ const tipA_sender = branchA_sender_sinceLCA.at(-1)!;
220
+
221
+ const indexLCA_B = timelineAddrsB.lastIndexOf(latestCommonFrameAddr);
222
+ const addrsB_sinceLCA = timelineAddrsB.slice(indexLCA_B + 1);
223
+ const branchB_receiver_sinceLCA = await getIbGibsFromCache_fallbackToSpaces({
224
+ readCache_graph: ibGibReadCache,
225
+ addrs: addrsB_sinceLCA,
226
+ space,
227
+ });
228
+ const tipB_receiver = branchB_receiver_sinceLCA.at(-1)!;
229
+
230
+ // 2. Base & Orphan tip determination (directional preference) via DRY base helper
231
+ const {
232
+ baseTip,
233
+ baseBranch,
234
+ orphanTip,
235
+ orphanBranch,
236
+ } = this.resolveBaseAndOrphan({
237
+ graftPreference,
238
+ tipA_sender,
239
+ branchA_sender_sinceLCA,
240
+ tipB_receiver,
241
+ branchB_receiver_sinceLCA,
242
+ });
243
+
244
+
245
+
246
+ const baseTipAddr = getIbGibAddr({ ibGib: baseTip });
247
+ const baseBranchAddrs = baseBranch.map(x => getIbGibAddr({ ibGib: x }));
248
+ const orphanTipAddr = getIbGibAddr({ ibGib: orphanTip });
249
+ const orphanBranchAddrs = orphanBranch.map(x => getIbGibAddr({ ibGib: x }));
250
+
251
+ // 3. Replay orphan branch transforms onto base tip
252
+ const [latestCommonFrameIbGib] = await getIbGibsFromCache_fallbackToSpaces({
253
+ addrs: [latestCommonFrameAddr],
254
+ space,
255
+ });
256
+
257
+ const dnaAddrsAlreadyAccountedFor: IbGibAddr[] = (latestCommonFrameIbGib?.rel8ns?.dna ?? []).concat();
258
+ const dnaAddrsToApply: IbGibAddr[] = [];
259
+ for (const orphanFrame of orphanBranch) {
260
+ const orphanDnaAddrsToApply = getUniqueDnas({
261
+ base: dnaAddrsAlreadyAccountedFor,
262
+ extended: orphanFrame.rel8ns?.dna ?? [],
263
+ throwIfExtendedDoesntContainBase: true,
264
+ });
265
+ orphanDnaAddrsToApply.forEach(x => dnaAddrsAlreadyAccountedFor.push(x));
266
+ orphanDnaAddrsToApply.forEach(x => dnaAddrsToApply.push(x));
267
+ }
268
+
269
+ const dnaIbGibsToApply = await getIbGibsFromCache_fallbackToSpaces({
270
+ addrs: dnaAddrsToApply,
271
+ readCache_graph: ibGibReadCache,
272
+ space,
273
+ });
274
+
275
+ const createdIbGibs_Running: IbGib_V1[] = [];
276
+ const newGraftedIntermediateIbGibs_ordered: IbGib_V1[] = [];
277
+ const newGraftDnaIbGibs: IbGib_V1[] = [];
278
+
279
+ let currentGraftTip = await applyTransforms({
280
+ src: baseTip,
281
+ dnaAddrsToApplyToStoreVersion_MUTATES_IN_PLACE: dnaAddrsToApply.concat(),
282
+ allLocalIbGibs: dnaIbGibsToApply,
283
+ createdIbGibs_Running,
284
+ });
285
+
286
+ if (space && createdIbGibs_Running.length > 0) {
287
+ await putInSpace({ space, ibGibs: createdIbGibs_Running });
288
+ const { mapWithTjp_YesDna: newGraftedIbGibsMap } = splitPerTjpAndOrDna({ ibGibs: createdIbGibs_Running });
289
+ const newGraftedIbGibs_orderNotGuaranteed = Object.values(newGraftedIbGibsMap);
290
+ const newGraftedIbGibsByTjp = getTimelinesGroupedByTjp({ ibGibs: newGraftedIbGibs_orderNotGuaranteed });
291
+ if (Object.keys(newGraftedIbGibsByTjp).length > 0) {
292
+ Object.values(newGraftedIbGibsByTjp)[0].forEach(x => newGraftedIntermediateIbGibs_ordered.push(x));
293
+ for (const item of newGraftedIntermediateIbGibs_ordered) {
294
+ await registerNewIbGib({ ibGib: item, space });
295
+ }
296
+ }
297
+ }
298
+
299
+ // 4. Perform 3-way filesystem tree merge across lcaItem, baseItem, orphanItem
300
+ const lcaNode = await this.fetchIbGib({ addr: latestCommonFrameAddr, space, readCache_graph: ibGibReadCache });
301
+ const lcaItem = await this.resolveRootItemNode({ nodeOrAddr: lcaNode, space, readCache_graph: ibGibReadCache });
302
+ const baseItem = await this.resolveRootItemNode({ nodeOrAddr: baseTip, space, readCache_graph: ibGibReadCache });
303
+ const orphanItem = await this.resolveRootItemNode({ nodeOrAddr: orphanTip, space, readCache_graph: ibGibReadCache });
304
+
305
+ let mergedRootItem: B2tFSItemIbGib_V1 | undefined;
306
+ if (lcaItem || baseItem || orphanItem) {
307
+ mergedRootItem = await this.mergeTreeNodes({
308
+ lcaItem,
309
+ baseItem,
310
+ orphanItem,
311
+ space,
312
+ readCache_graph: ibGibReadCache,
313
+ newGraftDnaIbGibs,
314
+ newGraftedIntermediateIbGibs_ordered,
315
+ });
316
+ }
317
+
318
+
319
+ // 5. Evolve currentGraftTip if root item changed
320
+ if (mergedRootItem) {
321
+ const mergedRootAddr = getIbGibAddr({ ibGib: mergedRootItem });
322
+ const currentRootAddr = baseItem ? getIbGibAddr({ ibGib: baseItem }) : undefined;
323
+
324
+ if (currentGraftTip.ib?.startsWith('b2tfs_branch') || currentGraftTip.data?.classname === 'B2tFSBranchIbGib_V1') {
325
+ if (mergedRootAddr !== currentRootAddr) {
326
+ const resRel8Branch = await rel8({
327
+ src: currentGraftTip,
328
+ rel8nsToAddByAddr: {
329
+ b2tfs_item: [mergedRootAddr],
330
+ },
331
+ dna: true,
332
+ nCounter: true,
333
+ });
334
+
335
+ const newGraftTip = await this.applyTransformAndRegister({
336
+ resTransform: resRel8Branch,
337
+ space,
338
+ newGraftDnaIbGibs,
339
+ newGraftedIntermediateIbGibs_ordered,
340
+ });
341
+
342
+ currentGraftTip = newGraftTip;
343
+ }
344
+ } else if (currentGraftTip.ib?.startsWith('b2tfs_item') || currentGraftTip.data?.classname === 'B2tFSItemIbGib_V1') {
345
+ if (mergedRootAddr !== getIbGibAddr({ ibGib: currentGraftTip })) {
346
+ currentGraftTip = mergedRootItem;
347
+ }
348
+ }
349
+ }
350
+
351
+ // 6. Attach GraftInfo frame
352
+ const graftInfo = await createGraftInfo({
353
+ conflictStrategy,
354
+ details: `Replayed ${dnaAddrsToApply.length} transforms from orphan (${orphanTipAddr}) onto base (${baseTipAddr}) with 3-way B2tFS tree merge.`,
355
+ addlGraftDetails: undefined,
356
+ latestCommonFrameAddr,
357
+ baseAddrsSinceLCA: baseBranchAddrs,
358
+ orphanAddrsSinceLCA: orphanBranchAddrs,
359
+ dnaAddrsApplied: dnaAddrsToApply,
360
+ });
361
+
362
+ if (space) {
363
+ await putInSpace({ ibGib: graftInfo, space });
364
+ }
365
+ const graftInfoAddr = getIbGibAddr({ ibGib: graftInfo });
366
+ const resRel8GraftInfo = await rel8({
367
+ src: currentGraftTip,
368
+ rel8nsToAddByAddr: {
369
+ [GRAFT_INFO_REL8N_NAME]: [graftInfoAddr],
370
+ },
371
+ dna: true,
372
+ nCounter: !!currentGraftTip.data?.n,
373
+ });
374
+
375
+ for (const dna of resRel8GraftInfo.dnas ?? []) {
376
+ newGraftDnaIbGibs.push(dna);
377
+ }
378
+ if (space) {
379
+ await persistTransformResult({ resTransform: resRel8GraftInfo, space });
380
+ await registerNewIbGib({ ibGib: resRel8GraftInfo.newIbGib, space });
381
+ }
382
+
383
+ const finalGraftTip = resRel8GraftInfo.newIbGib;
384
+
385
+ return {
386
+ dnasReplayed: dnaIbGibsToApply,
387
+ dnasCreatedInGraft: newGraftDnaIbGibs,
388
+ intermediateIbGibsCreatedInGraft: newGraftedIntermediateIbGibs_ordered,
389
+ newGraftTip: finalGraftTip,
390
+ graftInfoCreated: graftInfo,
391
+ };
392
+ } catch (error) {
393
+ console.error(`${lc} ${extractErrorMsg(error)} (E: 7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e)`);
394
+ throw error;
395
+ } finally {
396
+ if (logalot) {
397
+ console.log(`${lc} complete. (I: 6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f)`);
398
+ }
399
+ }
400
+ }
401
+
402
+ private async fetchIbGib({
403
+ addr,
404
+ space,
405
+ readCache_graph,
406
+ }: {
407
+ addr?: string;
408
+ space?: IbGibSpaceAny;
409
+ readCache_graph?: FlatIbGibGraph;
410
+ }): Promise<IbGib_V1 | undefined> {
411
+ if (!addr) { return undefined; }
412
+ const cleanAddr = addr.includes('^') ? addr.split('^')[0] : addr;
413
+ if (readCache_graph) {
414
+ if (readCache_graph[cleanAddr]) { return readCache_graph[cleanAddr]; }
415
+ if (readCache_graph[addr]) { return readCache_graph[addr]; }
416
+ const matchingKey = Object.keys(readCache_graph).find(k => k === cleanAddr || k.startsWith(cleanAddr + '^') || (k.includes('^') && k.split('^')[0] === cleanAddr));
417
+ if (matchingKey) { return readCache_graph[matchingKey]; }
418
+ }
419
+ if (!space) { return undefined; }
420
+ try {
421
+ const res = await getFromSpace({ space, addrs: [addr, cleanAddr] });
422
+ if (res && res.success && res.ibGibs && res.ibGibs.length > 0) {
423
+ const found = res.ibGibs.find((x: any) => !!x);
424
+ if (found) { return found; }
425
+ }
426
+ } catch {
427
+ // Suppress fetch error if address not found
428
+ }
429
+ return undefined;
430
+ }
431
+
432
+
433
+ private async resolveRootItemNode({
434
+ nodeOrAddr,
435
+ space,
436
+ readCache_graph,
437
+ }: {
438
+ nodeOrAddr: IbGib_V1 | string | undefined;
439
+ space?: any;
440
+ readCache_graph?: FlatIbGibGraph;
441
+ }): Promise<B2tFSItemIbGib_V1 | undefined> {
442
+ if (!nodeOrAddr) { return undefined; }
443
+ let node: IbGib_V1 | undefined;
444
+ if (typeof nodeOrAddr === 'string') {
445
+ node = await this.fetchIbGib({ addr: nodeOrAddr, space, readCache_graph });
446
+ } else {
447
+ node = nodeOrAddr;
448
+ }
449
+ if (!node) { return undefined; }
450
+
451
+ if (node.ib?.startsWith('b2tfs_branch') || node.data?.classname === 'B2tFSBranchIbGib_V1') {
452
+ const rootItemAddr = node.rel8ns?.b2tfs_item?.at(-1);
453
+ if (rootItemAddr) {
454
+ const rootNode = await this.fetchIbGib({ addr: rootItemAddr, space, readCache_graph });
455
+ return rootNode as B2tFSItemIbGib_V1;
456
+ }
457
+ return undefined;
458
+ }
459
+
460
+ if (node.ib?.startsWith('b2tfs_item') || node.data?.classname === 'B2tFSItemIbGib_V1') {
461
+ return node as B2tFSItemIbGib_V1;
462
+ }
463
+
464
+ return undefined;
465
+ }
466
+
467
+ private async getFilePayloadBuffer({
468
+ fileItem,
469
+ space,
470
+ readCache_graph,
471
+ }: {
472
+ fileItem?: B2tFSItemIbGib_V1;
473
+ space?: any;
474
+ readCache_graph?: FlatIbGibGraph;
475
+ }): Promise<{ buffer: Uint8Array; binIbGib?: IbGib_V1 } | undefined> {
476
+ if (!fileItem || !fileItem.rel8ns) { return undefined; }
477
+ const binAddrs = fileItem.rel8ns[BINARY_REL8N_NAME] || [];
478
+ const latestBinAddr = binAddrs.at(-1);
479
+ if (!latestBinAddr) { return undefined; }
480
+
481
+ const binIbGib = await this.fetchIbGib({ addr: latestBinAddr, space, readCache_graph });
482
+ if (!binIbGib || binIbGib.data === undefined) { return undefined; }
483
+
484
+ let fileData: Uint8Array;
485
+ const dataObj = binIbGib.data as any;
486
+ if (dataObj && typeof dataObj === 'object' && dataObj._dataType === 'Uint8Array_Base64' && typeof dataObj.value === 'string') {
487
+ fileData = Buffer.from(dataObj.value, 'base64');
488
+ } else if (typeof dataObj === 'string') {
489
+ fileData = Buffer.from(dataObj, 'utf8');
490
+ } else if (dataObj instanceof Uint8Array || Buffer.isBuffer(dataObj)) {
491
+ fileData = dataObj as Uint8Array;
492
+ } else if (typeof dataObj === 'object' && dataObj !== null) {
493
+ if (typeof dataObj.text === 'string') {
494
+ fileData = Buffer.from(dataObj.text, 'utf8');
495
+ } else if (typeof dataObj.data === 'string') {
496
+ fileData = Buffer.from(dataObj.data, 'utf8');
497
+ } else {
498
+ const vals = Object.values(dataObj as object);
499
+ fileData = new Uint8Array(vals as number[]);
500
+ }
501
+ } else {
502
+ fileData = new Uint8Array(0);
503
+ }
504
+
505
+
506
+ return { buffer: fileData, binIbGib };
507
+ }
508
+
509
+ private async getChildItemsMap({
510
+ folderItem,
511
+ space,
512
+ readCache_graph,
513
+ }: {
514
+ folderItem?: B2tFSItemIbGib_V1;
515
+ space?: any;
516
+ readCache_graph?: FlatIbGibGraph;
517
+ }): Promise<Map<string, B2tFSItemIbGib_V1>> {
518
+ const map = new Map<string, B2tFSItemIbGib_V1>();
519
+ if (!folderItem || !folderItem.rel8ns) { return map; }
520
+ const childAddrs = folderItem.rel8ns[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
521
+ for (const addr of childAddrs) {
522
+ const childNode = await this.fetchIbGib({ addr, space, readCache_graph }) as B2tFSItemIbGib_V1;
523
+ if (childNode && childNode.data?.name) {
524
+ map.set(childNode.data.name, childNode);
525
+ }
526
+ }
527
+ return map;
528
+ }
529
+
530
+ private async mergeTreeNodes({
531
+ lcaItem,
532
+ baseItem,
533
+ orphanItem,
534
+ space,
535
+ readCache_graph,
536
+ newGraftDnaIbGibs,
537
+ newGraftedIntermediateIbGibs_ordered,
538
+ }: {
539
+ lcaItem?: B2tFSItemIbGib_V1;
540
+ baseItem?: B2tFSItemIbGib_V1;
541
+ orphanItem?: B2tFSItemIbGib_V1;
542
+ space?: any;
543
+ readCache_graph?: FlatIbGibGraph;
544
+ newGraftDnaIbGibs: IbGib_V1[];
545
+ newGraftedIntermediateIbGibs_ordered: IbGib_V1[];
546
+ }): Promise<B2tFSItemIbGib_V1 | undefined> {
547
+ const lc = `${lcFile}[${this.mergeTreeNodes.name}]`;
548
+
549
+ const baseAddr = baseItem ? getIbGibAddr({ ibGib: baseItem }) : undefined;
550
+ const orphanAddr = orphanItem ? getIbGibAddr({ ibGib: orphanItem }) : undefined;
551
+
552
+ const lcaAddr = lcaItem ? getIbGibAddr({ ibGib: lcaItem }) : undefined;
553
+
554
+ if (baseAddr && orphanAddr && baseAddr === orphanAddr) {
555
+ return baseItem;
556
+ }
557
+
558
+ if (lcaAddr && baseAddr && baseAddr === lcaAddr && orphanItem) {
559
+ return orphanItem;
560
+ }
561
+
562
+ if (lcaAddr && orphanAddr && orphanAddr === lcaAddr && baseItem) {
563
+ return baseItem;
564
+ }
565
+
566
+ if (!baseItem && !orphanItem) {
567
+ return undefined;
568
+ }
569
+
570
+ if (baseItem && !orphanItem) {
571
+ if (lcaAddr && baseAddr === lcaAddr) {
572
+ return undefined;
573
+ }
574
+ return baseItem;
575
+ }
576
+
577
+ if (orphanItem && !baseItem) {
578
+ if (lcaAddr && orphanAddr === lcaAddr) {
579
+ return undefined;
580
+ }
581
+ return orphanItem;
582
+ }
583
+
584
+ if (!baseItem || !orphanItem) {
585
+ throw new Error(`(UNEXPECTED) baseItem or orphanItem missing in 3-way merge branch (E: 5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a)`);
586
+ }
587
+
588
+ const fsType = baseItem.data?.fsType || orphanItem.data?.fsType;
589
+
590
+ if (fsType === 'file') {
591
+ const lcaPayload = await this.getFilePayloadBuffer({ fileItem: lcaItem, space, readCache_graph });
592
+ const basePayload = await this.getFilePayloadBuffer({ fileItem: baseItem, space, readCache_graph });
593
+ const orphanPayload = await this.getFilePayloadBuffer({ fileItem: orphanItem, space, readCache_graph });
594
+
595
+ const isBaseBin = basePayload ? isBinaryBuffer(basePayload.buffer) : false;
596
+ const isOrphanBin = orphanPayload ? isBinaryBuffer(orphanPayload.buffer) : false;
597
+ const isLcaBin = lcaPayload ? isBinaryBuffer(lcaPayload.buffer) : false;
598
+
599
+ if (isBaseBin || isOrphanBin || isLcaBin) {
600
+ if (logalot) {
601
+ console.log(`${lc} Non-text binary file detected (${baseItem.data?.name}). Skipping 3-way line diff. (I: 4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b)`);
602
+ }
603
+ return baseItem;
604
+ }
605
+
606
+ const textLCA = lcaPayload ? decodeToLines(lcaPayload.buffer).join('\n') : '';
607
+ const textBase = basePayload ? decodeToLines(basePayload.buffer).join('\n') : '';
608
+ const textOrphan = orphanPayload ? decodeToLines(orphanPayload.buffer).join('\n') : '';
609
+
610
+ console.log(`${lc} [MERGE DIAGNOSTIC] file: ${baseItem.data?.name}`);
611
+ console.log(`${lc} [MERGE DIAGNOSTIC] textLCA:\n${textLCA}`);
612
+ console.log(`${lc} [MERGE DIAGNOSTIC] textBase:\n${textBase}`);
613
+ console.log(`${lc} [MERGE DIAGNOSTIC] textOrphan:\n${textOrphan}`);
614
+
615
+ const { mergedText } = perform3WayTextLineMerge({ textLCA, textBase, textOrphan });
616
+ console.log(`${lc} [MERGE DIAGNOSTIC] mergedText:\n${mergedText}`);
617
+
618
+ const dataBuffer = Buffer.from(mergedText, 'utf8');
619
+ const fileHash = await hash({ s: mergedText });
620
+
621
+
622
+ const fileExt = baseItem.data?.fileExt || (baseItem.data?.name?.includes('.') ? baseItem.data.name.split('.').pop() || 'txt' : 'txt');
623
+ const binIb = getBinIb({
624
+ binHash: fileHash,
625
+ binExt: fileExt,
626
+ binEncoding: 'utf8',
627
+ });
628
+
629
+ const binIbGib: BinIbGib_V1 = {
630
+ ib: binIb,
631
+ data: dataBuffer,
632
+ };
633
+ binIbGib.gib = await getGib({ ibGib: binIbGib, hasTjp: false });
634
+
635
+ const binAddr = getIbGibAddr({ ibGib: binIbGib });
636
+ if (space) {
637
+ await putInSpace({ ibGib: binIbGib, space });
638
+ await registerNewIbGib({ ibGib: binIbGib, space });
639
+ }
640
+
641
+ const evolveRes = await evolveB2tFSItemIbGib({
642
+ existingItem: baseItem,
643
+ dataToUpdate: {
644
+ fileHash,
645
+ },
646
+ rel8nsToUpdate: {
647
+ [BINARY_REL8N_NAME]: [binAddr],
648
+ },
649
+ });
650
+
651
+ if (evolveRes.dnas) { newGraftDnaIbGibs.push(...evolveRes.dnas); }
652
+ if (evolveRes.newIbGib) { newGraftedIntermediateIbGibs_ordered.push(evolveRes.newIbGib); }
653
+ if (space) {
654
+ await persistTransformResult({ resTransform: evolveRes, space });
655
+ await registerNewIbGib({ ibGib: evolveRes.newIbGib, space });
656
+ }
657
+
658
+ return evolveRes.newIbGib as B2tFSItemIbGib_V1;
659
+ } else if (fsType === 'folder') {
660
+ const lcaChildrenMap = await this.getChildItemsMap({ folderItem: lcaItem, space, readCache_graph });
661
+ const baseChildrenMap = await this.getChildItemsMap({ folderItem: baseItem, space, readCache_graph });
662
+ const orphanChildrenMap = await this.getChildItemsMap({ folderItem: orphanItem, space, readCache_graph });
663
+
664
+ const allNames = new Set([
665
+ ...lcaChildrenMap.keys(),
666
+ ...baseChildrenMap.keys(),
667
+ ...orphanChildrenMap.keys(),
668
+ ]);
669
+
670
+ const mergedChildAddrs: string[] = [];
671
+
672
+ for (const name of allNames) {
673
+ const childLCA = lcaChildrenMap.get(name);
674
+ const childBase = baseChildrenMap.get(name);
675
+ const childOrphan = orphanChildrenMap.get(name);
676
+
677
+ const mergedChild = await this.mergeTreeNodes({
678
+ lcaItem: childLCA,
679
+ baseItem: childBase,
680
+ orphanItem: childOrphan,
681
+ space,
682
+ readCache_graph,
683
+ newGraftDnaIbGibs,
684
+ newGraftedIntermediateIbGibs_ordered,
685
+ });
686
+
687
+
688
+ if (mergedChild) {
689
+ mergedChildAddrs.push(getIbGibAddr({ ibGib: mergedChild }));
690
+ }
691
+ }
692
+
693
+ const currentChildAddrs = baseItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
694
+ const addrsChanged = mergedChildAddrs.length !== currentChildAddrs.length ||
695
+ mergedChildAddrs.some((addr, idx) => addr !== currentChildAddrs[idx]);
696
+
697
+ if (!addrsChanged) {
698
+ return baseItem;
699
+ }
700
+
701
+ const evolveRes = await evolveB2tFSItemIbGib({
702
+ existingItem: baseItem,
703
+ rel8nsToUpdate: {
704
+ [B2TFS_CHILD_ITEM_REL8N_NAME]: mergedChildAddrs,
705
+ },
706
+ rel8nsToRemove: {
707
+ [B2TFS_CHILD_ITEM_REL8N_NAME]: baseItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [],
708
+ },
709
+ });
710
+
711
+ if (evolveRes.dnas) { newGraftDnaIbGibs.push(...evolveRes.dnas); }
712
+ if (evolveRes.newIbGib) { newGraftedIntermediateIbGibs_ordered.push(evolveRes.newIbGib); }
713
+ if (space) {
714
+ await persistTransformResult({ resTransform: evolveRes, space });
715
+ await registerNewIbGib({ ibGib: evolveRes.newIbGib, space });
716
+ }
717
+
718
+ return evolveRes.newIbGib as B2tFSItemIbGib_V1;
719
+ }
720
+
721
+ return baseItem;
722
+ }
723
+ }
724
+
725
+ export const B2tFSItem3WayMergeStrategy = B2tFS3WayGraftStrategy;