amalgm 0.1.218 → 0.1.219
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
CHANGED
|
@@ -685,6 +685,51 @@ test('a local commit made after the verified card snapshot is never parked or ov
|
|
|
685
685
|
);
|
|
686
686
|
});
|
|
687
687
|
|
|
688
|
+
test('a pending descendant commit catches up without becoming a losing commit', async () => {
|
|
689
|
+
const { repo, oid: baseOid } = await makeRepo('pending-descendant-card');
|
|
690
|
+
const firstPatch = await patchFor(repo, { 'a.txt': 'before commit\n' });
|
|
691
|
+
const secondPatch = await patchFor(repo, {
|
|
692
|
+
'a.txt': 'before commit\n',
|
|
693
|
+
'b.txt': 'after commit\n',
|
|
694
|
+
});
|
|
695
|
+
const winningCard = card(repo, baseOid, 1);
|
|
696
|
+
const before = {
|
|
697
|
+
repoKey: 'root:test',
|
|
698
|
+
card: winningCard,
|
|
699
|
+
transitions: [transition('machine-peer', 2, baseOid, '', firstPatch)],
|
|
700
|
+
parked: [],
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
await materializeRepo(winningCard, before, 2);
|
|
704
|
+
await git(repo, 'add', '.');
|
|
705
|
+
await git(repo, 'commit', '-m', 'local pending card');
|
|
706
|
+
const pendingOid = (await git(repo, 'rev-parse', 'HEAD')).trim();
|
|
707
|
+
|
|
708
|
+
const pendingCard = card(repo, pendingOid, null);
|
|
709
|
+
const caughtUp = await materializeRepo(pendingCard, {
|
|
710
|
+
...before,
|
|
711
|
+
transitions: [
|
|
712
|
+
...before.transitions,
|
|
713
|
+
transition('machine-peer', 3, baseOid, firstPatch, secondPatch),
|
|
714
|
+
],
|
|
715
|
+
}, 3);
|
|
716
|
+
|
|
717
|
+
assert.equal(caughtUp.state, 'in-sync');
|
|
718
|
+
assert.equal((await git(repo, 'rev-parse', 'HEAD')).trim(), pendingOid);
|
|
719
|
+
assert.equal(fs.readFileSync(path.join(repo, 'a.txt'), 'utf8'), 'before commit\n');
|
|
720
|
+
assert.equal(fs.readFileSync(path.join(repo, 'b.txt'), 'utf8'), 'after commit\n');
|
|
721
|
+
const staged = (await git(repo, 'diff', '--cached', '--name-only')).trim();
|
|
722
|
+
const status = (await git(repo, 'status', '--short')).trim();
|
|
723
|
+
assert.equal(staged, 'b.txt', status);
|
|
724
|
+
await assert.rejects(
|
|
725
|
+
git(repo, 'rev-parse', `refs/amalgm/parked/${pendingOid}`),
|
|
726
|
+
);
|
|
727
|
+
const state = readRepoState('root:test');
|
|
728
|
+
assert.equal(state.baseOid, pendingOid);
|
|
729
|
+
assert.equal(state.appliedThrough, 3);
|
|
730
|
+
assert.equal(state.cardVersion, null);
|
|
731
|
+
});
|
|
732
|
+
|
|
688
733
|
test('an empty index advance cannot overwrite a commit that just finished', async () => {
|
|
689
734
|
const { repo } = await makeRepo('index-commit-race');
|
|
690
735
|
const observedIndex = (await git(repo, 'write-tree')).trim();
|
|
@@ -148,6 +148,15 @@ test('only a value different from both the materialized baseline and the rail is
|
|
|
148
148
|
branches: [{ name: 'main', oid: oidC }],
|
|
149
149
|
};
|
|
150
150
|
assert.equal(hasLocalContribution(local, remoteAdvance, baseline), false);
|
|
151
|
+
assert.equal(hasLocalContribution({
|
|
152
|
+
...local,
|
|
153
|
+
head: { branch: 'main', oid: oidB, detached: false },
|
|
154
|
+
refs: [{ name: 'main', oid: oidB }],
|
|
155
|
+
}, remoteAdvance, {
|
|
156
|
+
...baseline,
|
|
157
|
+
baseOid: oidB,
|
|
158
|
+
pendingCard: true,
|
|
159
|
+
}), true);
|
|
151
160
|
assert.equal(hasLocalContribution(local, remoteAdvance, baseline, {
|
|
152
161
|
checkpoint: 'repo-checkpoint@8',
|
|
153
162
|
machine: 'machine-a',
|
|
@@ -288,6 +288,14 @@ async function moveToWinningCard(localCard, targetCard) {
|
|
|
288
288
|
&& current.head.branch === targetCard.head?.branch
|
|
289
289
|
) return { state: 'ready', moved: false };
|
|
290
290
|
|
|
291
|
+
const pendingCard = current.head.oid
|
|
292
|
+
&& targetCard.head?.oid
|
|
293
|
+
&& current.head.branch === targetCard.head?.branch
|
|
294
|
+
&& await isAncestor(localCard.path, targetCard.head.oid, current.head.oid);
|
|
295
|
+
if (pendingCard) {
|
|
296
|
+
return { state: 'ready', moved: false, pendingCard: true, conflicts: [] };
|
|
297
|
+
}
|
|
298
|
+
|
|
291
299
|
const objects = await obtainCardObjects(localCard, targetCard);
|
|
292
300
|
if (objects.state !== 'ready') return { state: 'waiting', reason: objects.state };
|
|
293
301
|
|
|
@@ -594,38 +602,87 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
594
602
|
const reduced = await reduceCheckpoint(localCard, plan);
|
|
595
603
|
if (reduced.state !== 'ready') return reduced;
|
|
596
604
|
|
|
597
|
-
const
|
|
598
|
-
const
|
|
599
|
-
const represented = captureIsOnRail(plan, captured);
|
|
600
|
-
const capturedIndex = represented
|
|
601
|
-
? await merge.treeOfPatch(localCard.path, plan.card.head.oid, captured.indexDiff)
|
|
602
|
-
: null;
|
|
603
|
-
const capturedWorktree = represented
|
|
604
|
-
? await merge.treeOfPatch(localCard.path, capturedIndex, captured.worktreeDiff)
|
|
605
|
-
: null;
|
|
606
|
-
const heldWorktree = capturedWorktree || (
|
|
605
|
+
const sharedBaseTree = await merge.treeOf(localCard.path, plan.card.head.oid);
|
|
606
|
+
const previousHeldWorktree = (
|
|
607
607
|
previous?.baseOid === plan.card.head.oid
|
|
608
608
|
&& previous.heldTree
|
|
609
609
|
&& await merge.hasTree(localCard.path, previous.heldTree)
|
|
610
610
|
? previous.heldTree
|
|
611
|
-
:
|
|
611
|
+
: sharedBaseTree
|
|
612
612
|
);
|
|
613
|
-
const
|
|
613
|
+
const previousHeldIndex = (
|
|
614
614
|
previous?.baseOid === plan.card.head.oid
|
|
615
615
|
&& previous.heldIndexTree
|
|
616
616
|
&& await merge.hasTree(localCard.path, previous.heldIndexTree)
|
|
617
617
|
? previous.heldIndexTree
|
|
618
|
-
:
|
|
618
|
+
: sharedBaseTree
|
|
619
619
|
);
|
|
620
|
+
let stateBaseOid = plan.card.head.oid;
|
|
621
|
+
let stateBranch = plan.card.head.branch;
|
|
622
|
+
let stateCardVersion = plan.card.positionVersion;
|
|
623
|
+
let stateCardMutationId = plan.card.positionMutationId;
|
|
624
|
+
let stateIndexDiff = reduced.indexDiff;
|
|
625
|
+
let stateWorktreeDiff = reduced.worktreeDiff;
|
|
626
|
+
let heldWorktree;
|
|
627
|
+
let heldIndex;
|
|
628
|
+
let targetWorktree;
|
|
629
|
+
let targetIndex;
|
|
630
|
+
const pendingConflicts = [];
|
|
631
|
+
|
|
632
|
+
if (cardResult.pendingCard) {
|
|
633
|
+
const pendingBaseTree = await merge.treeOf(localCard.path, localCard.head.oid);
|
|
634
|
+
const rebasedIndex = await merge.reduceTree(
|
|
635
|
+
localCard.path,
|
|
636
|
+
pendingBaseTree,
|
|
637
|
+
previousHeldIndex,
|
|
638
|
+
reduced.indexTree,
|
|
639
|
+
{ label: `pending-card-index-${appliedThrough ?? 'local'}` },
|
|
640
|
+
);
|
|
641
|
+
const rebasedWorktree = await merge.reduceTree(
|
|
642
|
+
localCard.path,
|
|
643
|
+
pendingBaseTree,
|
|
644
|
+
previousHeldWorktree,
|
|
645
|
+
reduced.worktreeTree,
|
|
646
|
+
{ label: `pending-card-worktree-${appliedThrough ?? 'local'}` },
|
|
647
|
+
);
|
|
648
|
+
heldIndex = pendingBaseTree;
|
|
649
|
+
heldWorktree = pendingBaseTree;
|
|
650
|
+
targetIndex = rebasedIndex.tree;
|
|
651
|
+
targetWorktree = rebasedWorktree.tree;
|
|
652
|
+
stateBaseOid = localCard.head.oid;
|
|
653
|
+
stateBranch = localCard.head.branch;
|
|
654
|
+
stateCardVersion = null;
|
|
655
|
+
stateCardMutationId = null;
|
|
656
|
+
stateIndexDiff = await merge.diffOfTree(localCard.path, stateBaseOid, targetIndex);
|
|
657
|
+
stateWorktreeDiff = await merge.diffOfTree(localCard.path, targetIndex, targetWorktree);
|
|
658
|
+
pendingConflicts.push(
|
|
659
|
+
...rebasedIndex.conflicts.map((item) => ({ ...item, layer: 'pending-card-index' })),
|
|
660
|
+
...rebasedWorktree.conflicts.map((item) => ({ ...item, layer: 'pending-card-worktree' })),
|
|
661
|
+
);
|
|
662
|
+
} else {
|
|
663
|
+
const captured = await captureCheckpoint(localCard.path);
|
|
664
|
+
const represented = captureIsOnRail(plan, captured);
|
|
665
|
+
const capturedIndex = represented
|
|
666
|
+
? await merge.treeOfPatch(localCard.path, plan.card.head.oid, captured.indexDiff)
|
|
667
|
+
: null;
|
|
668
|
+
const capturedWorktree = represented
|
|
669
|
+
? await merge.treeOfPatch(localCard.path, capturedIndex, captured.worktreeDiff)
|
|
670
|
+
: null;
|
|
671
|
+
heldWorktree = capturedWorktree || previousHeldWorktree;
|
|
672
|
+
heldIndex = capturedIndex || previousHeldIndex;
|
|
673
|
+
targetWorktree = reduced.worktreeTree;
|
|
674
|
+
targetIndex = reduced.indexTree;
|
|
675
|
+
}
|
|
676
|
+
|
|
620
677
|
const currentIndex = await merge.indexTree(localCard.path);
|
|
621
678
|
const reducedLocalIndex = await merge.reduceTree(
|
|
622
679
|
localCard.path,
|
|
623
680
|
currentIndex,
|
|
624
681
|
heldIndex,
|
|
625
|
-
|
|
682
|
+
targetIndex,
|
|
626
683
|
{ label: `index-${reduced.checkpointRevision || 'clean'}` },
|
|
627
684
|
);
|
|
628
|
-
const applied = await merge.advance(localCard.path, heldWorktree,
|
|
685
|
+
const applied = await merge.advance(localCard.path, heldWorktree, targetWorktree, {
|
|
629
686
|
label: `checkpoint-${reduced.checkpointRevision || 'clean'}`,
|
|
630
687
|
});
|
|
631
688
|
const indexResult = await advanceIndex(localCard.path, currentIndex, reducedLocalIndex.tree);
|
|
@@ -633,28 +690,30 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
633
690
|
const state = writeRepoState(plan.repoKey, {
|
|
634
691
|
path: localCard.path,
|
|
635
692
|
replica: localCard.replica,
|
|
636
|
-
branch:
|
|
637
|
-
baseOid:
|
|
638
|
-
cardVersion:
|
|
639
|
-
cardMutationId:
|
|
693
|
+
branch: stateBranch,
|
|
694
|
+
baseOid: stateBaseOid,
|
|
695
|
+
cardVersion: stateCardVersion,
|
|
696
|
+
cardMutationId: stateCardMutationId,
|
|
697
|
+
pendingCard: Boolean(cardResult.pendingCard),
|
|
640
698
|
appliedThrough,
|
|
641
|
-
checkpointRevision:
|
|
642
|
-
indexDiff:
|
|
643
|
-
worktreeDiff:
|
|
699
|
+
checkpointRevision: revisionOf(stateBaseOid, stateIndexDiff, stateWorktreeDiff),
|
|
700
|
+
indexDiff: stateIndexDiff,
|
|
701
|
+
worktreeDiff: stateWorktreeDiff,
|
|
644
702
|
checkpointVersions: plan.transitions.map((entry) => entry.version),
|
|
645
703
|
branches: plan.branches || [],
|
|
646
704
|
heldTree: applied.heldTree,
|
|
647
|
-
heldIndexTree:
|
|
705
|
+
heldIndexTree: targetIndex,
|
|
648
706
|
parked,
|
|
649
707
|
parkedWaiting: [...parkedWaiting, ...branchResult.waiting],
|
|
650
708
|
conflicts: [
|
|
651
709
|
...(cardResult.conflicts || []),
|
|
652
710
|
...reduced.conflicts,
|
|
711
|
+
...pendingConflicts,
|
|
653
712
|
...reducedLocalIndex.conflicts.map((item) => ({ ...item, layer: 'index-materialize' })),
|
|
654
713
|
...applied.conflicts,
|
|
655
714
|
],
|
|
656
|
-
materialized: applied.heldTree ===
|
|
657
|
-
&& reducedLocalIndex.tree ===
|
|
715
|
+
materialized: applied.heldTree === targetWorktree
|
|
716
|
+
&& reducedLocalIndex.tree === targetIndex,
|
|
658
717
|
});
|
|
659
718
|
appendStateEvent({
|
|
660
719
|
resource: 'workspace-reducer',
|
|
@@ -252,7 +252,8 @@ function hasLocalContribution(card, plan, baseline, checkpoint = null, previousC
|
|
|
252
252
|
const onRail = samePosition(card, plan.card);
|
|
253
253
|
const atBaseline = baseline
|
|
254
254
|
&& card.head?.branch === baseline.branch
|
|
255
|
-
&& card.head?.oid === baseline.baseOid
|
|
255
|
+
&& card.head?.oid === baseline.baseOid
|
|
256
|
+
&& !baseline.pendingCard;
|
|
256
257
|
const alreadyPublished = validVersion(previousCard?.positionVersion)
|
|
257
258
|
&& samePosition(card, previousCard);
|
|
258
259
|
const reversesPublishedSharedPosition = previousCard
|