amalgm 0.1.238 → 0.1.240
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/lib/shared-realtime-tunnel.js +22 -14
- package/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +68 -20
- package/runtime/scripts/amalgm-mcp/project-context/store.js +87 -19
- package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
- package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
- package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
- package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
- package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
- package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
- package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
- package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
- package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
- package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
- package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
- package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
- package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
- package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
- package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
- package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
- package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
- package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +81 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +114 -8
- package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
- package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
- package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
- package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
- package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
- package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
- package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
- package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
- package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +72 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
- package/runtime/scripts/amalgm-mcp/workspace/objects.js +119 -54
- package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
- package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
- package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
- package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
- package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
- package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
- package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
- package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
- package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
- package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +352 -497
|
@@ -6,6 +6,7 @@ const fs = require('fs');
|
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { execFile } = require('child_process');
|
|
9
|
+
const { EventEmitter } = require('events');
|
|
9
10
|
const { promisify } = require('util');
|
|
10
11
|
|
|
11
12
|
const execFileAsync = promisify(execFile);
|
|
@@ -23,6 +24,7 @@ const {
|
|
|
23
24
|
repoCardServiceStats,
|
|
24
25
|
startRepoCardService,
|
|
25
26
|
stopRepoCardService,
|
|
27
|
+
_private,
|
|
26
28
|
} = require('../workspace/cards');
|
|
27
29
|
|
|
28
30
|
test.after(() => {
|
|
@@ -316,7 +318,7 @@ test('coverage shift and deletion inside a single pass still reads as missing',
|
|
|
316
318
|
assert.equal(findCard(settled.cards, repo), undefined);
|
|
317
319
|
});
|
|
318
320
|
|
|
319
|
-
test('the live service hears a commit through the git-dir doorbell', async () => {
|
|
321
|
+
test('the live service hears a commit through the git-dir doorbell', async (t) => {
|
|
320
322
|
const repo = await makeRepo('projects/belled');
|
|
321
323
|
const projects = path.join(tempRoot, 'projects');
|
|
322
324
|
upsertWorkspace({ path: projects, name: 'Projects', source: 'computer' });
|
|
@@ -324,6 +326,13 @@ test('the live service hears a commit through the git-dir doorbell', async () =>
|
|
|
324
326
|
await refreshRepoCards();
|
|
325
327
|
const before = findCard(listRepoCards(), repo);
|
|
326
328
|
assert.equal(before.health, 'healthy');
|
|
329
|
+
// fs.watch reports resource exhaustion asynchronously on macOS.
|
|
330
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
331
|
+
if (!_private.watcherComplete(before.replica)) {
|
|
332
|
+
stopRepoCardService();
|
|
333
|
+
t.skip('the host refused part or all of the required fs.watch set');
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
327
336
|
|
|
328
337
|
fs.writeFileSync(path.join(repo, 'ding.txt'), 'ding\n');
|
|
329
338
|
await run(repo, 'add', '.');
|
|
@@ -341,7 +350,61 @@ test('the live service hears a commit through the git-dir doorbell', async () =>
|
|
|
341
350
|
assert.ok(repoCardServiceStats().doorbellRings >= 1);
|
|
342
351
|
});
|
|
343
352
|
|
|
344
|
-
test('
|
|
353
|
+
test('a doorbell rebuild releases its old watch set before opening the replacement', async () => {
|
|
354
|
+
const repo = await makeRepo('projects/bounded-doorbell');
|
|
355
|
+
const projects = path.join(tempRoot, 'projects');
|
|
356
|
+
upsertWorkspace({ path: projects, name: 'Projects', source: 'computer' });
|
|
357
|
+
stopRepoCardService();
|
|
358
|
+
await refreshRepoCards();
|
|
359
|
+
|
|
360
|
+
const originalWatch = fs.watch;
|
|
361
|
+
let openHandles = 0;
|
|
362
|
+
let maximumOpenHandles = 0;
|
|
363
|
+
let handleLimit = Number.POSITIVE_INFINITY;
|
|
364
|
+
fs.watch = () => {
|
|
365
|
+
if (openHandles >= handleLimit) {
|
|
366
|
+
const error = new Error('simulated watch-handle ceiling');
|
|
367
|
+
error.code = 'EMFILE';
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
openHandles += 1;
|
|
371
|
+
maximumOpenHandles = Math.max(maximumOpenHandles, openHandles);
|
|
372
|
+
const watcher = new EventEmitter();
|
|
373
|
+
let closed = false;
|
|
374
|
+
watcher.close = () => {
|
|
375
|
+
if (closed) return;
|
|
376
|
+
closed = true;
|
|
377
|
+
openHandles -= 1;
|
|
378
|
+
};
|
|
379
|
+
watcher.unref = () => watcher;
|
|
380
|
+
return watcher;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
startRepoCardService();
|
|
385
|
+
await refreshRepoCards();
|
|
386
|
+
const card = findCard(listRepoCards(), repo);
|
|
387
|
+
const initialHandles = _private.watcherCount(card.replica);
|
|
388
|
+
const initialTotal = openHandles;
|
|
389
|
+
assert.ok(initialHandles > 0);
|
|
390
|
+
assert.equal(_private.watcherComplete(card.replica), true);
|
|
391
|
+
assert.ok(initialTotal >= initialHandles);
|
|
392
|
+
|
|
393
|
+
handleLimit = initialTotal;
|
|
394
|
+
maximumOpenHandles = openHandles;
|
|
395
|
+
_private.rebuildDoorbell(card.replica, card);
|
|
396
|
+
|
|
397
|
+
assert.equal(_private.watcherComplete(card.replica), true);
|
|
398
|
+
assert.equal(openHandles, initialTotal);
|
|
399
|
+
assert.equal(maximumOpenHandles, initialTotal);
|
|
400
|
+
assert.equal(repoCardServiceStats().incompleteDoorbells, 0);
|
|
401
|
+
} finally {
|
|
402
|
+
stopRepoCardService();
|
|
403
|
+
fs.watch = originalWatch;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('the targeted doorbell path hears nested branch creation and deletion', async () => {
|
|
345
408
|
const repo = await makeRepo('projects/belled-refs');
|
|
346
409
|
const projects = path.join(tempRoot, 'projects');
|
|
347
410
|
upsertWorkspace({ path: projects, name: 'Projects', source: 'computer' });
|
|
@@ -351,6 +414,7 @@ test('the live service hears nested branch creation and deletion', async () => {
|
|
|
351
414
|
const before = findCard(listRepoCards(), repo);
|
|
352
415
|
|
|
353
416
|
await run(repo, 'branch', branch);
|
|
417
|
+
_private.ringDoorbell(before.replica);
|
|
354
418
|
let deadline = Date.now() + 5_000;
|
|
355
419
|
let created = findCard(listRepoCards(), repo);
|
|
356
420
|
while (!created.refs.some((ref) => ref.name === branch) && Date.now() < deadline) {
|
|
@@ -360,6 +424,7 @@ test('the live service hears nested branch creation and deletion', async () => {
|
|
|
360
424
|
assert.ok(created.refs.some((ref) => ref.name === branch));
|
|
361
425
|
|
|
362
426
|
await run(repo, 'branch', '-D', branch);
|
|
427
|
+
_private.ringDoorbell(before.replica);
|
|
363
428
|
deadline = Date.now() + 5_000;
|
|
364
429
|
let deleted = findCard(listRepoCards(), repo);
|
|
365
430
|
while (deleted.refs.some((ref) => ref.name === branch) && Date.now() < deadline) {
|
|
@@ -372,7 +437,7 @@ test('the live service hears nested branch creation and deletion', async () => {
|
|
|
372
437
|
assert.equal(deleted.generation, before.generation + 2);
|
|
373
438
|
});
|
|
374
439
|
|
|
375
|
-
test('the
|
|
440
|
+
test('the targeted doorbell path hears an uncommitted nested edit without a polling pass', async () => {
|
|
376
441
|
const repo = await makeRepo('projects/live-dirty');
|
|
377
442
|
const projects = path.join(tempRoot, 'projects');
|
|
378
443
|
upsertWorkspace({ path: projects, name: 'Projects', source: 'computer' });
|
|
@@ -383,6 +448,8 @@ test('the live service hears an uncommitted nested edit without a polling pass',
|
|
|
383
448
|
const nested = path.join(repo, 'src', 'nested');
|
|
384
449
|
fs.mkdirSync(nested, { recursive: true });
|
|
385
450
|
fs.writeFileSync(path.join(nested, 'live.txt'), 'live\n');
|
|
451
|
+
const beforeCard = findCard(listRepoCards(), repo);
|
|
452
|
+
_private.ringDoorbell(beforeCard.replica);
|
|
386
453
|
|
|
387
454
|
const deadline = Date.now() + 5_000;
|
|
388
455
|
let card = findCard(listRepoCards(), repo);
|
|
@@ -419,6 +486,7 @@ test('editing an already-dirty file rings the checkpoint directly', async () =>
|
|
|
419
486
|
const card = findCard(listRepoCards(), repo);
|
|
420
487
|
const file = path.join(repo, 'dirty.txt');
|
|
421
488
|
fs.writeFileSync(file, 'first\n');
|
|
489
|
+
_private.ringDoorbell(card.replica);
|
|
422
490
|
|
|
423
491
|
let deadline = Date.now() + 5_000;
|
|
424
492
|
while (published.length === 0 && Date.now() < deadline) {
|
|
@@ -427,6 +495,7 @@ test('editing an already-dirty file rings the checkpoint directly', async () =>
|
|
|
427
495
|
published.length = 0;
|
|
428
496
|
|
|
429
497
|
fs.writeFileSync(file, 'second\n');
|
|
498
|
+
_private.ringDoorbell(card.replica);
|
|
430
499
|
deadline = Date.now() + 5_000;
|
|
431
500
|
while (published.length === 0 && Date.now() < deadline) {
|
|
432
501
|
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
@@ -13,6 +13,14 @@ const execFileAsync = promisify(execFile);
|
|
|
13
13
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm-repo-cards-test-'));
|
|
14
14
|
process.env.AMALGM_DIR = path.join(tempRoot, '.amalgm');
|
|
15
15
|
process.env.AMALGM_COMPUTER_ID = 'computer-test';
|
|
16
|
+
fs.mkdirSync(process.env.AMALGM_DIR, { recursive: true });
|
|
17
|
+
fs.writeFileSync(
|
|
18
|
+
path.join(process.env.AMALGM_DIR, 'computer.json'),
|
|
19
|
+
JSON.stringify({
|
|
20
|
+
computer_id: 'computer-test',
|
|
21
|
+
user_id: '11111111-1111-4111-8111-111111111111',
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
16
24
|
|
|
17
25
|
const { closeLocalDb, openLocalDb } = require('../state/db');
|
|
18
26
|
const {
|
|
@@ -100,7 +108,7 @@ test('a clean repo cards with exact head, clean counts, and full identity', asyn
|
|
|
100
108
|
await run(dir, 'remote', 'add', 'origin', 'git@github.com:amalgm/clean.git');
|
|
101
109
|
|
|
102
110
|
const card = await buildRepoCard(dir);
|
|
103
|
-
assert.equal(card.card, 'repo-card@
|
|
111
|
+
assert.equal(card.card, 'repo-card@3');
|
|
104
112
|
assert.equal(card.machine, 'computer-test');
|
|
105
113
|
assert.match(card.replica, /^rpl_[A-Za-z0-9_-]+$/);
|
|
106
114
|
assert.equal(card.path, await fs.promises.realpath(dir));
|
|
@@ -406,12 +414,31 @@ test('unchanged commits reuse their card object source and one new oid is resolv
|
|
|
406
414
|
await run(dir, 'branch', 'side');
|
|
407
415
|
const local = await buildRepoCard(dir);
|
|
408
416
|
await run(dir, 'remote', 'add', 'origin', 'https://github.com/amalgm/object-source-cache.git');
|
|
417
|
+
const { resourceId } = require('../workspace/wall').bindProject(
|
|
418
|
+
'018f0000-0000-7000-8000-00000000cace',
|
|
419
|
+
'11111111-1111-4111-8111-111111111111',
|
|
420
|
+
dir,
|
|
421
|
+
);
|
|
422
|
+
assert.equal(
|
|
423
|
+
require('../workspace/wall').projectForPath(dir)?.resourceId,
|
|
424
|
+
resourceId,
|
|
425
|
+
);
|
|
426
|
+
const ready = { kind: 'bundle', resourceId, checksum: 'ready' };
|
|
409
427
|
const previous = {
|
|
410
428
|
...local,
|
|
411
429
|
repo: { ...local.repo, remote: 'https://github.com/amalgm/object-source-cache' },
|
|
412
|
-
objects:
|
|
413
|
-
refs: local.refs.map((ref) => ({ ...ref, objects:
|
|
430
|
+
objects: ready,
|
|
431
|
+
refs: local.refs.map((ref) => ({ ...ref, objects: ready })),
|
|
414
432
|
};
|
|
433
|
+
const classified = await classifyDirectory(dir);
|
|
434
|
+
const replica = require('../workspace/identity').replicaForGitDir(
|
|
435
|
+
await fs.promises.realpath(classified.gitDir),
|
|
436
|
+
);
|
|
437
|
+
assert.equal(previous.replica, replica.replicaId);
|
|
438
|
+
assert.equal(
|
|
439
|
+
cardTest.reusableObjectSource(local.head.oid, local.head.oid, ready, resourceId),
|
|
440
|
+
ready,
|
|
441
|
+
);
|
|
415
442
|
const objects = require('../workspace/objects');
|
|
416
443
|
const original = objects.objectSourceFor;
|
|
417
444
|
let calls = 0;
|
|
@@ -422,8 +449,8 @@ test('unchanged commits reuse their card object source and one new oid is resolv
|
|
|
422
449
|
try {
|
|
423
450
|
const unchanged = await buildRepoCard(dir, previous);
|
|
424
451
|
assert.equal(calls, 0);
|
|
425
|
-
assert.equal(unchanged.objects.kind, '
|
|
426
|
-
assert.ok(unchanged.refs.every((ref) => ref.objects.kind === '
|
|
452
|
+
assert.equal(unchanged.objects.kind, 'bundle');
|
|
453
|
+
assert.ok(unchanged.refs.every((ref) => ref.objects.kind === 'bundle'));
|
|
427
454
|
|
|
428
455
|
write(dir, 'next.txt', 'next\n');
|
|
429
456
|
await run(dir, 'add', '.');
|
|
@@ -432,7 +459,7 @@ test('unchanged commits reuse their card object source and one new oid is resolv
|
|
|
432
459
|
assert.equal(calls, 1, 'HEAD and its branch share one content-addressed lookup');
|
|
433
460
|
assert.equal(changed.objects.kind, 'pending');
|
|
434
461
|
assert.equal(changed.refs.find((ref) => ref.name === 'main').objects.kind, 'pending');
|
|
435
|
-
assert.equal(changed.refs.find((ref) => ref.name === 'side').objects.kind, '
|
|
462
|
+
assert.equal(changed.refs.find((ref) => ref.name === 'side').objects.kind, 'bundle');
|
|
436
463
|
} finally {
|
|
437
464
|
objects.objectSourceFor = original;
|
|
438
465
|
}
|
|
@@ -10,6 +10,8 @@ const { promisify } = require('util');
|
|
|
10
10
|
|
|
11
11
|
const execFileAsync = promisify(execFile);
|
|
12
12
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm-checkpoint-reducer-'));
|
|
13
|
+
process.env.AMALGM_DIR = path.join(root, '.amalgm');
|
|
14
|
+
process.env.AMALGM_WORKSPACES_DIR = path.join(process.env.AMALGM_DIR, 'workspaces');
|
|
13
15
|
const merge = require('../workspace/merge');
|
|
14
16
|
|
|
15
17
|
test.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
@@ -118,3 +120,28 @@ test('an earlier inbound event advances underneath a pending local write', async
|
|
|
118
120
|
assert.equal(fs.readFileSync(path.join(repo, 'same.txt'), 'utf8'), 'pending local\n');
|
|
119
121
|
assert.equal(await treeText(repo, outcome.heldTree, 'same.txt'), 'inbound\n');
|
|
120
122
|
});
|
|
123
|
+
|
|
124
|
+
test('an inbound file write cannot overwrite a local write that races its observation', async () => {
|
|
125
|
+
const { repo, oid, tree: clean } = await makeRepo('pending-local-race');
|
|
126
|
+
const inboundPatch = await patchFor(repo, { 'same.txt': 'inbound\n' });
|
|
127
|
+
const inbound = await merge.treeOfPatch(repo, oid, inboundPatch);
|
|
128
|
+
const transition = require('../workspace/transition');
|
|
129
|
+
const applyFiles = transition.applyFiles;
|
|
130
|
+
transition.applyFiles = async (...args) => {
|
|
131
|
+
fs.writeFileSync(path.join(repo, 'same.txt'), 'local writer won the race\n');
|
|
132
|
+
return applyFiles(...args);
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
await assert.rejects(
|
|
136
|
+
merge.advance(repo, clean, inbound),
|
|
137
|
+
/local file changed before ordered apply/,
|
|
138
|
+
);
|
|
139
|
+
} finally {
|
|
140
|
+
transition.applyFiles = applyFiles;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
assert.equal(
|
|
144
|
+
fs.readFileSync(path.join(repo, 'same.txt'), 'utf8'),
|
|
145
|
+
'local writer won the race\n',
|
|
146
|
+
);
|
|
147
|
+
});
|