amalgm 0.1.221 → 0.1.234
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/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/state/attachments.js +7 -1
- package/runtime/scripts/amalgm-mcp/state/shared-rest.js +39 -2
- package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +180 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +47 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +28 -24
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +152 -12
- package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +6 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +105 -67
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +183 -23
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +99 -206
- package/runtime/scripts/amalgm-mcp/workspace/card.js +30 -9
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +3 -2
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +194 -20
- package/runtime/scripts/amalgm-mcp/workspace/git.js +23 -5
- package/runtime/scripts/amalgm-mcp/workspace/merge.js +24 -186
- package/runtime/scripts/amalgm-mcp/workspace/objects.js +13 -1
- package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +323 -0
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +115 -101
- package/runtime/scripts/amalgm-mcp/workspace/transition.js +7 -7
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +106 -137
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The reducer reads the single rail-ordered wall and derives exactly:
|
|
7
7
|
* - one winning card (highest official card version), and
|
|
8
|
-
* - one checkpoint tree (checkpoint transitions
|
|
8
|
+
* - one checkpoint tree (checkpoint transitions applied in official order).
|
|
9
9
|
*
|
|
10
10
|
* The materializer has no peer branches. It obtains the winning commit,
|
|
11
11
|
* moves to that exact card when safe, advances the one held checkpoint tree
|
|
@@ -30,6 +30,7 @@ const {
|
|
|
30
30
|
const {
|
|
31
31
|
CHECKPOINT_CONTRACT,
|
|
32
32
|
checkpointValueReady,
|
|
33
|
+
readCheckpoint,
|
|
33
34
|
readCheckpointValue,
|
|
34
35
|
repoKeyOfCard,
|
|
35
36
|
revisionOf,
|
|
@@ -80,20 +81,40 @@ function readRepoState(repoKey) {
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
function
|
|
84
|
+
function repoStateIdentity(state) {
|
|
85
|
+
if (!state) return null;
|
|
86
|
+
const { indexDiff, worktreeDiff, updatedAt, ...identity } = state;
|
|
87
|
+
return JSON.stringify(identity);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function persistRepoState(repoKey, state) {
|
|
84
91
|
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
85
92
|
const next = { repoKey, ...state, updatedAt: new Date().toISOString() };
|
|
93
|
+
const previous = readRepoState(repoKey);
|
|
94
|
+
if (repoStateIdentity(previous) === repoStateIdentity(next)) {
|
|
95
|
+
return { state: previous, changed: false };
|
|
96
|
+
}
|
|
86
97
|
fs.writeFileSync(stateFile(repoKey), `${JSON.stringify(next, null, 2)}\n`);
|
|
87
|
-
return next;
|
|
98
|
+
return { state: next, changed: true };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function repoStateEventValue(state) {
|
|
102
|
+
const { indexDiff, worktreeDiff, ...value } = state;
|
|
103
|
+
return value;
|
|
88
104
|
}
|
|
89
105
|
|
|
90
106
|
function validVersion(value) {
|
|
91
107
|
return Number.isInteger(value) && value > 0;
|
|
92
108
|
}
|
|
93
109
|
|
|
110
|
+
function preferBundleObjects(held, candidate) {
|
|
111
|
+
if (held?.objects?.kind === 'bundle' || candidate?.objects?.kind !== 'bundle') return held;
|
|
112
|
+
return { ...held, objects: candidate.objects };
|
|
113
|
+
}
|
|
114
|
+
|
|
94
115
|
/**
|
|
95
116
|
* Pure wall reduction. Machine identity never chooses a winner. Checkpoint
|
|
96
|
-
* transitions remain in the wall until the next card
|
|
117
|
+
* transitions remain in the wall until the next card includes them in a
|
|
97
118
|
* commit, so reducing current state never forgets an earlier edit.
|
|
98
119
|
*/
|
|
99
120
|
function reduceWall(entries) {
|
|
@@ -121,11 +142,25 @@ function reduceWall(entries) {
|
|
|
121
142
|
}
|
|
122
143
|
if (!repo.card || card.positionVersion > repo.card.positionVersion) {
|
|
123
144
|
repo.card = { ...card, writer: wallEntry.machine };
|
|
145
|
+
} else if (
|
|
146
|
+
card.positionVersion === repo.card.positionVersion
|
|
147
|
+
&& card.positionMutationId === repo.card.positionMutationId
|
|
148
|
+
&& card.head?.oid === repo.card.head?.oid
|
|
149
|
+
) {
|
|
150
|
+
repo.card = preferBundleObjects(repo.card, card);
|
|
124
151
|
}
|
|
125
152
|
for (const ref of card.branches || []) {
|
|
126
153
|
if (!ref?.name || !validVersion(ref.version)) continue;
|
|
127
154
|
const held = repo.branchMap.get(ref.name);
|
|
128
|
-
if (!held || ref.version > held.version)
|
|
155
|
+
if (!held || ref.version > held.version) {
|
|
156
|
+
repo.branchMap.set(ref.name, ref);
|
|
157
|
+
} else if (
|
|
158
|
+
ref.version === held.version
|
|
159
|
+
&& ref.mutationId === held.mutationId
|
|
160
|
+
&& ref.oid === held.oid
|
|
161
|
+
) {
|
|
162
|
+
repo.branchMap.set(ref.name, preferBundleObjects(held, ref));
|
|
163
|
+
}
|
|
129
164
|
}
|
|
130
165
|
for (const checkpoint of card.checkpoints || []) {
|
|
131
166
|
if (
|
|
@@ -293,9 +328,7 @@ async function moveToWinningCard(localCard, targetCard) {
|
|
|
293
328
|
&& targetCard.head?.oid
|
|
294
329
|
&& current.head.branch === targetCard.head?.branch
|
|
295
330
|
&& await isAncestor(localCard.path, targetCard.head.oid, current.head.oid);
|
|
296
|
-
if (pendingCard) {
|
|
297
|
-
return { state: 'ready', moved: false, pendingCard: true, conflicts: [] };
|
|
298
|
-
}
|
|
331
|
+
if (pendingCard) return { state: 'ready', moved: false, pendingCard: true };
|
|
299
332
|
|
|
300
333
|
const objects = await obtainCardObjects(localCard, targetCard);
|
|
301
334
|
if (objects.state !== 'ready') return { state: 'waiting', reason: objects.state };
|
|
@@ -340,25 +373,14 @@ async function moveToWinningCard(localCard, targetCard) {
|
|
|
340
373
|
});
|
|
341
374
|
}
|
|
342
375
|
|
|
343
|
-
const commonTree = await merge.treeOf(localCard.path, commonOid);
|
|
344
376
|
const winningTree = await merge.treeOf(localCard.path, targetCard.head.oid);
|
|
345
377
|
const oldTree = await merge.treeOf(localCard.path, current.head.oid);
|
|
346
|
-
const committed = forward || current.head.oid === targetCard.head.oid
|
|
347
|
-
? { tree: winningTree, conflicts: [] }
|
|
348
|
-
: await merge.reduceTree(
|
|
349
|
-
localCard.path,
|
|
350
|
-
winningTree,
|
|
351
|
-
commonTree,
|
|
352
|
-
oldTree,
|
|
353
|
-
{ label: `parked-${current.head.oid.slice(0, 12)}` },
|
|
354
|
-
);
|
|
355
378
|
const oldIndexTree = await merge.treeOfPatch(localCard.path, current.head.oid, movingIndexDiff);
|
|
356
379
|
const staged = await merge.reduceTree(
|
|
357
380
|
localCard.path,
|
|
358
|
-
|
|
381
|
+
winningTree,
|
|
359
382
|
oldTree,
|
|
360
383
|
oldIndexTree,
|
|
361
|
-
{ label: `staged-${current.head.oid.slice(0, 12)}` },
|
|
362
384
|
);
|
|
363
385
|
const oldWorktree = await merge.treeOfPatch(localCard.path, oldIndexTree, movingWorktreeDiff);
|
|
364
386
|
const working = await merge.reduceTree(
|
|
@@ -366,13 +388,7 @@ async function moveToWinningCard(localCard, targetCard) {
|
|
|
366
388
|
staged.tree,
|
|
367
389
|
oldIndexTree,
|
|
368
390
|
oldWorktree,
|
|
369
|
-
{ label: `working-${current.head.oid.slice(0, 12)}` },
|
|
370
391
|
);
|
|
371
|
-
const conflicts = [
|
|
372
|
-
...committed.conflicts,
|
|
373
|
-
...staged.conflicts,
|
|
374
|
-
...working.conflicts,
|
|
375
|
-
];
|
|
376
392
|
|
|
377
393
|
const rollbackDir = path.join(STATE_DIR, 'card-moves');
|
|
378
394
|
fs.mkdirSync(rollbackDir, { recursive: true });
|
|
@@ -398,7 +414,6 @@ async function moveToWinningCard(localCard, targetCard) {
|
|
|
398
414
|
state: 'ready',
|
|
399
415
|
moved: true,
|
|
400
416
|
parked,
|
|
401
|
-
conflicts: [...conflicts, ...applied.conflicts],
|
|
402
417
|
};
|
|
403
418
|
}
|
|
404
419
|
|
|
@@ -446,6 +461,7 @@ async function materializeBranches(localCard, plan, heldBranches = []) {
|
|
|
446
461
|
pending.push({ name: ref.name, oid: currentOid });
|
|
447
462
|
continue;
|
|
448
463
|
}
|
|
464
|
+
if (currentOid === ref.oid) continue;
|
|
449
465
|
if (ref.oid === null) {
|
|
450
466
|
if (current.has(ref.name)) {
|
|
451
467
|
await transition.deleteBranchRef(localCard.path, ref.name);
|
|
@@ -484,7 +500,6 @@ async function reduceCheckpoint(localCard, plan) {
|
|
|
484
500
|
const baseTree = await merge.treeOf(localCard.path, baseOid);
|
|
485
501
|
let indexTree = baseTree;
|
|
486
502
|
let worktreeTree = baseTree;
|
|
487
|
-
const conflicts = [];
|
|
488
503
|
if (plan.checkpointBaseConflict) {
|
|
489
504
|
return { state: 'waiting', reason: 'checkpoint-base-disagrees' };
|
|
490
505
|
}
|
|
@@ -501,32 +516,6 @@ async function reduceCheckpoint(localCard, plan) {
|
|
|
501
516
|
);
|
|
502
517
|
}
|
|
503
518
|
|
|
504
|
-
for (const entry of plan.parked || []) {
|
|
505
|
-
const commonOid = await mergeBase(localCard.path, baseOid, entry.oid);
|
|
506
|
-
if (!commonOid) return { state: 'waiting', reason: `parked-unrelated-${entry.oid}` };
|
|
507
|
-
const commonTree = await merge.treeOf(localCard.path, commonOid);
|
|
508
|
-
const parkedTree = await merge.treeOf(localCard.path, entry.oid);
|
|
509
|
-
const label = `parked-${entry.version || entry.oid.slice(0, 12)}`;
|
|
510
|
-
const reducedIndex = await merge.reduceTree(
|
|
511
|
-
localCard.path,
|
|
512
|
-
indexTree,
|
|
513
|
-
commonTree,
|
|
514
|
-
parkedTree,
|
|
515
|
-
{ label },
|
|
516
|
-
);
|
|
517
|
-
indexTree = reducedIndex.tree;
|
|
518
|
-
conflicts.push(...reducedIndex.conflicts.map((item) => ({ ...item, layer: 'index' })));
|
|
519
|
-
const reducedWorktree = await merge.reduceTree(
|
|
520
|
-
localCard.path,
|
|
521
|
-
worktreeTree,
|
|
522
|
-
commonTree,
|
|
523
|
-
parkedTree,
|
|
524
|
-
{ label },
|
|
525
|
-
);
|
|
526
|
-
worktreeTree = reducedWorktree.tree;
|
|
527
|
-
conflicts.push(...reducedWorktree.conflicts.map((item) => ({ ...item, layer: 'worktree' })));
|
|
528
|
-
}
|
|
529
|
-
|
|
530
519
|
for (const operation of plan.transitions) {
|
|
531
520
|
const from = readCheckpointValue(baseOid, operation.from);
|
|
532
521
|
const to = readCheckpointValue(baseOid, operation.to);
|
|
@@ -536,11 +525,13 @@ async function reduceCheckpoint(localCard, plan) {
|
|
|
536
525
|
}
|
|
537
526
|
const fromIndexTree = await merge.treeOfPatch(localCard.path, baseOid, from.indexDiff);
|
|
538
527
|
const toIndexTree = await merge.treeOfPatch(localCard.path, baseOid, to.indexDiff);
|
|
539
|
-
const reducedIndex = await merge.reduceTree(
|
|
540
|
-
|
|
541
|
-
|
|
528
|
+
const reducedIndex = await merge.reduceTree(
|
|
529
|
+
localCard.path,
|
|
530
|
+
indexTree,
|
|
531
|
+
fromIndexTree,
|
|
532
|
+
toIndexTree,
|
|
533
|
+
);
|
|
542
534
|
indexTree = reducedIndex.tree;
|
|
543
|
-
conflicts.push(...reducedIndex.conflicts.map((item) => ({ ...item, layer: 'index' })));
|
|
544
535
|
|
|
545
536
|
const fromWorktreeTree = await merge.treeOfPatch(localCard.path, fromIndexTree, from.worktreeDiff);
|
|
546
537
|
const toWorktreeTree = await merge.treeOfPatch(localCard.path, toIndexTree, to.worktreeDiff);
|
|
@@ -549,10 +540,8 @@ async function reduceCheckpoint(localCard, plan) {
|
|
|
549
540
|
worktreeTree,
|
|
550
541
|
fromWorktreeTree,
|
|
551
542
|
toWorktreeTree,
|
|
552
|
-
{ label: String(operation.version) },
|
|
553
543
|
);
|
|
554
544
|
worktreeTree = reducedWorktree.tree;
|
|
555
|
-
conflicts.push(...reducedWorktree.conflicts.map((item) => ({ ...item, layer: 'worktree' })));
|
|
556
545
|
}
|
|
557
546
|
|
|
558
547
|
const indexDiff = await merge.diffOfTree(localCard.path, baseOid, indexTree);
|
|
@@ -564,26 +553,32 @@ async function reduceCheckpoint(localCard, plan) {
|
|
|
564
553
|
indexDiff,
|
|
565
554
|
worktreeDiff,
|
|
566
555
|
checkpointRevision: revisionOf(baseOid, indexDiff, worktreeDiff),
|
|
567
|
-
conflicts,
|
|
568
556
|
};
|
|
569
557
|
}
|
|
570
558
|
|
|
571
|
-
function captureIsOnRail(plan, captured, localMachine = machineId()) {
|
|
559
|
+
function captureIsOnRail(plan, captured, localMachine = machineId(), acknowledged = null) {
|
|
572
560
|
if (
|
|
573
561
|
!captured
|
|
574
562
|
|| captured.indexDiff === null
|
|
575
563
|
|| captured.baseOid !== plan.card.head.oid
|
|
576
564
|
) return false;
|
|
565
|
+
const revision = revisionOf(
|
|
566
|
+
captured.baseOid,
|
|
567
|
+
captured.indexDiff,
|
|
568
|
+
captured.worktreeDiff,
|
|
569
|
+
);
|
|
577
570
|
const latest = [...plan.transitions]
|
|
578
571
|
.reverse()
|
|
579
572
|
.find((operation) => operation.machine === localMachine);
|
|
580
573
|
return Boolean(
|
|
581
|
-
latest
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
captured.
|
|
586
|
-
|
|
574
|
+
(latest && latest.to?.revision === revision)
|
|
575
|
+
|| (
|
|
576
|
+
Number.isInteger(acknowledged?.version)
|
|
577
|
+
&& acknowledged.version > 0
|
|
578
|
+
&& acknowledged.baseOid === captured.baseOid
|
|
579
|
+
&& acknowledged.branch === plan.card.head.branch
|
|
580
|
+
&& acknowledged.to?.revision === revision
|
|
581
|
+
)
|
|
587
582
|
);
|
|
588
583
|
}
|
|
589
584
|
|
|
@@ -605,9 +600,31 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
605
600
|
const planParked = new Set((plan.parked || []).map((entry) => entry.oid));
|
|
606
601
|
const requiredParkedWaiting = parkedWaiting.filter((entry) => planParked.has(entry.oid));
|
|
607
602
|
if (requiredParkedWaiting.length > 0) {
|
|
603
|
+
// Parking is a local write. Remember it before waiting on an inbound
|
|
604
|
+
// bundle, so the next pass cannot replace unnumbered local work.
|
|
605
|
+
const persisted = persistRepoState(plan.repoKey, {
|
|
606
|
+
...(previous || {}),
|
|
607
|
+
path: localCard.path,
|
|
608
|
+
replica: localCard.replica,
|
|
609
|
+
parked,
|
|
610
|
+
parkedWaiting,
|
|
611
|
+
materialized: false,
|
|
612
|
+
});
|
|
613
|
+
if (persisted.changed) {
|
|
614
|
+
appendStateEvent({
|
|
615
|
+
resource: 'workspace-reducer',
|
|
616
|
+
op: previous ? 'update' : 'insert',
|
|
617
|
+
id: localCard.replica,
|
|
618
|
+
value: repoStateEventValue(persisted.state),
|
|
619
|
+
source: 'workspace-reducer',
|
|
620
|
+
});
|
|
621
|
+
}
|
|
608
622
|
return { state: 'waiting', reason: `parked-${requiredParkedWaiting[0].state}` };
|
|
609
623
|
}
|
|
610
624
|
const branchResult = await materializeBranches(localCard, plan, previous?.branches);
|
|
625
|
+
if (branchResult.waiting.length > 0) {
|
|
626
|
+
return { state: 'waiting', reason: `branch-${branchResult.waiting[0].state}` };
|
|
627
|
+
}
|
|
611
628
|
const reduced = await reduceCheckpoint(localCard, plan);
|
|
612
629
|
if (reduced.state !== 'ready') return reduced;
|
|
613
630
|
|
|
@@ -636,23 +653,19 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
636
653
|
let heldIndex;
|
|
637
654
|
let targetWorktree;
|
|
638
655
|
let targetIndex;
|
|
639
|
-
const pendingConflicts = [];
|
|
640
|
-
|
|
641
656
|
if (cardResult.pendingCard) {
|
|
642
657
|
const pendingBaseTree = await merge.treeOf(localCard.path, localCard.head.oid);
|
|
643
658
|
const rebasedIndex = await merge.reduceTree(
|
|
644
659
|
localCard.path,
|
|
645
|
-
pendingBaseTree,
|
|
646
|
-
previousHeldIndex,
|
|
647
660
|
reduced.indexTree,
|
|
648
|
-
|
|
661
|
+
previousHeldIndex,
|
|
662
|
+
pendingBaseTree,
|
|
649
663
|
);
|
|
650
664
|
const rebasedWorktree = await merge.reduceTree(
|
|
651
665
|
localCard.path,
|
|
652
|
-
pendingBaseTree,
|
|
653
|
-
previousHeldWorktree,
|
|
654
666
|
reduced.worktreeTree,
|
|
655
|
-
|
|
667
|
+
previousHeldWorktree,
|
|
668
|
+
pendingBaseTree,
|
|
656
669
|
);
|
|
657
670
|
heldIndex = pendingBaseTree;
|
|
658
671
|
heldWorktree = pendingBaseTree;
|
|
@@ -664,13 +677,20 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
664
677
|
stateCardMutationId = null;
|
|
665
678
|
stateIndexDiff = await merge.diffOfTree(localCard.path, stateBaseOid, targetIndex);
|
|
666
679
|
stateWorktreeDiff = await merge.diffOfTree(localCard.path, targetIndex, targetWorktree);
|
|
667
|
-
pendingConflicts.push(
|
|
668
|
-
...rebasedIndex.conflicts.map((item) => ({ ...item, layer: 'pending-card-index' })),
|
|
669
|
-
...rebasedWorktree.conflicts.map((item) => ({ ...item, layer: 'pending-card-worktree' })),
|
|
670
|
-
);
|
|
671
680
|
} else {
|
|
672
681
|
const captured = await captureCheckpoint(localCard.path);
|
|
673
|
-
const
|
|
682
|
+
const wall = require('./wall');
|
|
683
|
+
const localCheckpoint = readCheckpoint(machineId(), plan.repoKey);
|
|
684
|
+
const project = wall.projectForRepo(plan.repoKey);
|
|
685
|
+
const acknowledged = localCheckpoint && {
|
|
686
|
+
...localCheckpoint,
|
|
687
|
+
version: wall.mutationVersion(
|
|
688
|
+
project?.resourceId,
|
|
689
|
+
localCheckpoint.mutationId,
|
|
690
|
+
plan.repoKey,
|
|
691
|
+
),
|
|
692
|
+
};
|
|
693
|
+
const represented = captureIsOnRail(plan, captured, machineId(), acknowledged);
|
|
674
694
|
const capturedIndex = represented
|
|
675
695
|
? await merge.treeOfPatch(localCard.path, plan.card.head.oid, captured.indexDiff)
|
|
676
696
|
: null;
|
|
@@ -686,17 +706,16 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
686
706
|
const currentIndex = await merge.indexTree(localCard.path);
|
|
687
707
|
const reducedLocalIndex = await merge.reduceTree(
|
|
688
708
|
localCard.path,
|
|
689
|
-
currentIndex,
|
|
690
|
-
heldIndex,
|
|
691
709
|
targetIndex,
|
|
692
|
-
|
|
710
|
+
heldIndex,
|
|
711
|
+
currentIndex,
|
|
693
712
|
);
|
|
694
713
|
const applied = await merge.advance(localCard.path, heldWorktree, targetWorktree, {
|
|
695
714
|
label: `checkpoint-${reduced.checkpointRevision || 'clean'}`,
|
|
696
715
|
});
|
|
697
716
|
const indexResult = await advanceIndex(localCard.path, currentIndex, reducedLocalIndex.tree);
|
|
698
717
|
if (indexResult.state !== 'ready') return indexResult;
|
|
699
|
-
const
|
|
718
|
+
const persisted = persistRepoState(plan.repoKey, {
|
|
700
719
|
path: localCard.path,
|
|
701
720
|
replica: localCard.replica,
|
|
702
721
|
branch: stateBranch,
|
|
@@ -715,26 +734,21 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
715
734
|
heldIndexTree: targetIndex,
|
|
716
735
|
parked,
|
|
717
736
|
parkedWaiting: [...parkedWaiting, ...branchResult.waiting],
|
|
718
|
-
conflicts: [
|
|
719
|
-
...(cardResult.conflicts || []),
|
|
720
|
-
...reduced.conflicts,
|
|
721
|
-
...pendingConflicts,
|
|
722
|
-
...reducedLocalIndex.conflicts.map((item) => ({ ...item, layer: 'index-materialize' })),
|
|
723
|
-
...applied.conflicts,
|
|
724
|
-
],
|
|
725
737
|
materialized: applied.heldTree === targetWorktree
|
|
726
738
|
&& reducedLocalIndex.tree === targetIndex,
|
|
727
739
|
});
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
740
|
+
if (persisted.changed) {
|
|
741
|
+
appendStateEvent({
|
|
742
|
+
resource: 'workspace-reducer',
|
|
743
|
+
op: previous ? 'update' : 'insert',
|
|
744
|
+
id: localCard.replica,
|
|
745
|
+
value: repoStateEventValue(persisted.state),
|
|
746
|
+
source: 'workspace-reducer',
|
|
747
|
+
});
|
|
748
|
+
}
|
|
735
749
|
return {
|
|
736
|
-
state:
|
|
737
|
-
reason:
|
|
750
|
+
state: 'in-sync',
|
|
751
|
+
reason: null,
|
|
738
752
|
moved: cardResult.moved || branchResult.moved || applied.applied.length > 0,
|
|
739
753
|
};
|
|
740
754
|
}
|
|
@@ -337,13 +337,13 @@ function setIndexTree(worktreeRoot, treeOid) {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
/**
|
|
340
|
-
*
|
|
341
|
-
*
|
|
340
|
+
* Apply one ordered file write set to a working tree. Each entry is
|
|
341
|
+
* { path, content, mode } (content a
|
|
342
342
|
* Buffer, mode '100644' | '100755' | '120000') or { path, content: null }
|
|
343
343
|
* for a deletion. Paths are confined to the worktree; the whole set lands
|
|
344
344
|
* as one journal line.
|
|
345
345
|
*/
|
|
346
|
-
function
|
|
346
|
+
function applyFiles(worktreeRoot, writes, label) {
|
|
347
347
|
const root = path.resolve(worktreeRoot);
|
|
348
348
|
const written = [];
|
|
349
349
|
const removed = [];
|
|
@@ -368,20 +368,20 @@ function applyMergedFiles(worktreeRoot, writes, label) {
|
|
|
368
368
|
}
|
|
369
369
|
written.push(write.path);
|
|
370
370
|
}
|
|
371
|
-
journal({ action: '
|
|
371
|
+
journal({ action: 'apply-files', worktree: root, label, written, removed, ok: true });
|
|
372
372
|
} catch (error) {
|
|
373
373
|
journal({
|
|
374
|
-
action: '
|
|
374
|
+
action: 'apply-files', worktree: root, label, written, removed, ok: false,
|
|
375
375
|
error: String(error.message).slice(0, 500),
|
|
376
376
|
});
|
|
377
|
-
throw new TransitionError('
|
|
377
|
+
throw new TransitionError('apply-files', root, String(error.message));
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
module.exports = {
|
|
382
382
|
TransitionError,
|
|
383
383
|
applyIndexPatch,
|
|
384
|
-
|
|
384
|
+
applyFiles,
|
|
385
385
|
applyPatch,
|
|
386
386
|
checkoutInitialCard,
|
|
387
387
|
createBundle,
|