fix(sessions): recheck idle tree during worktree move
This commit is contained in:
@@ -454,6 +454,50 @@ describe('moveSessionTreeToExistingWorktree', () => {
|
|||||||
expect(refreshCalls).toEqual([]);
|
expect(refreshCalls).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('rolls back the root and never moves a child that becomes busy after the root move starts', async () => {
|
||||||
|
const root = makeSession('root');
|
||||||
|
const child = makeSession('child');
|
||||||
|
const rootMove = deferred();
|
||||||
|
const previousRootMetadata = makeWorktreeMetadata({ path: '/old-root', label: 'Old root' });
|
||||||
|
const previousChildMetadata = makeWorktreeMetadata({ path: '/old-child', label: 'Old child' });
|
||||||
|
setStatuses('/source', { root: 'idle', child: 'idle' });
|
||||||
|
setStatuses('/destination', {});
|
||||||
|
storedMetadata.set(root.id, previousRootMetadata);
|
||||||
|
storedMetadata.set(child.id, previousChildMetadata);
|
||||||
|
moveSessionImplementation = async (session, sourceDirectory) => {
|
||||||
|
if (session.id === 'root' && sourceDirectory === '/source') {
|
||||||
|
return rootMove.promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const movePromise = moveSessionTreeToExistingWorktree({
|
||||||
|
root,
|
||||||
|
descendants: [child],
|
||||||
|
sourceDirectory: '/source',
|
||||||
|
destination: makeWorktreeMetadata(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => moveCalls.length === 1);
|
||||||
|
setStatuses('/source', { root: 'idle', child: 'busy' });
|
||||||
|
setStatuses('/destination', { root: 'idle' });
|
||||||
|
rootMove.resolve();
|
||||||
|
|
||||||
|
await expect(movePromise).rejects.toThrow('Session is not idle');
|
||||||
|
|
||||||
|
expect(moveCalls).toEqual([
|
||||||
|
{ sessionId: 'root', sourceDirectory: '/source', destinationDirectory: '/destination', moveChanges: true },
|
||||||
|
{ sessionId: 'root', sourceDirectory: '/destination', destinationDirectory: '/source', moveChanges: true },
|
||||||
|
]);
|
||||||
|
expect(metadataWrites).toEqual([
|
||||||
|
{ sessionId: 'root', metadata: latestMetadataResult },
|
||||||
|
{ sessionId: 'root', metadata: previousRootMetadata },
|
||||||
|
]);
|
||||||
|
expect(storedMetadata.get(root.id)).toBe(previousRootMetadata);
|
||||||
|
expect(storedMetadata.get(child.id)).toBe(previousChildMetadata);
|
||||||
|
expect(removeWorktreeCalls).toEqual([]);
|
||||||
|
expect(refreshCalls).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
test('reports an incomplete rollback explicitly and still does not remove the existing destination', async () => {
|
test('reports an incomplete rollback explicitly and still does not remove the existing destination', async () => {
|
||||||
const root = makeSession('root');
|
const root = makeSession('root');
|
||||||
const child = makeSession('child');
|
const child = makeSession('child');
|
||||||
|
|||||||
@@ -156,10 +156,11 @@ const moveSessionTreeTransaction = async (
|
|||||||
const moved: Session[] = [];
|
const moved: Session[] = [];
|
||||||
try {
|
try {
|
||||||
destination = await prepareDestination();
|
destination = await prepareDestination();
|
||||||
// Setup can take long enough for one of the sessions to start running, so
|
|
||||||
// verify the whole tree again immediately before the first move.
|
|
||||||
assertSessionsIdle(sessions, input.sourceDirectory);
|
|
||||||
for (const [index, session] of sessions.entries()) {
|
for (const [index, session] of sessions.entries()) {
|
||||||
|
// Setup and earlier moves can take long enough for a not-yet-moved
|
||||||
|
// descendant to start running, so re-check the remaining source tree
|
||||||
|
// immediately before each move.
|
||||||
|
assertSessionsIdle(sessions.slice(index), input.sourceDirectory);
|
||||||
// Transfer the checkout changes once with the root. Descendants only
|
// Transfer the checkout changes once with the root. Descendants only
|
||||||
// need their execution location updated.
|
// need their execution location updated.
|
||||||
await moveSessionToDirectory(session, input.sourceDirectory, destination.directory, index === 0);
|
await moveSessionToDirectory(session, input.sourceDirectory, destination.directory, index === 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user