Skip to content

fix: Chain moveFrom through incompatible moves - #2444

Merged
taratatach merged 2 commits into
masterfrom
fix/chained-moves-via-incompatible
Jul 4, 2026
Merged

fix: Chain moveFrom through incompatible moves#2444
taratatach merged 2 commits into
masterfrom
fix/chained-moves-via-incompatible

Conversation

@taratatach

@taratatach taratatach commented Jul 3, 2026

Copy link
Copy Markdown
Member

When a remote move renamed i to an incompatible path i*, then
again to a compatible path z, move set dst.moveFrom = src (i*),
pointing at a path never materialized on the filesystem. The local
watcher then tried to move a non-existent i* to z and failed.

move now chains through src.moveFrom when the local side is not
up-to-date, so dst.moveFrom records the actual local path to move
from (the original i) instead of the intermediate incompatible
path. When the local side is already up-to-date or src has no
moveFrom, it uses src directly as before.

The platform incompatibilities integration tests are adapted to the
per-batch error collection (#2442): blockSyncFor is no longer the
right stub since blocking causes now flow through scheduleRetry,
called once per batch with the accumulated causes array. The new
shouldHaveBlockedFor uses sinon.match.some to assert the array
contains a matching cause, and reports actual blocked paths on
failure. New integration cases cover the
compatible → incompatible → compatible rename sequence.

Fixes #2446

Please make sure the following boxes are checked:

  • PR is not too big
  • it improves UX & DX in some way
  • it includes unit tests matching the implementation changes
  • it includes scenarios matching a new behaviour or has been manually tested
  • it includes relevant documentation

@taratatach taratatach self-assigned this Jul 3, 2026
  The platform incompatibilities integration tests stubbed
  `blockSyncFor` to assert which changes got blocked, but the
  per-batch error collection (#2442) means blocking causes now flow
  through `scheduleRetry`, called once at the end of the batch with
  the array of accumulated causes. Stubbing `blockSyncFor` no
  longer reflects what the sync actually does.

  We now stub `scheduleRetry` and have `shouldHaveBlockedFor`
  assert it was called with a causes array containing a cause
  matching the expected path and `IncompatibleDoc` code. sinon's
  `calledWithMatch` uses `deepEqual` (full equality) for array
  expectations rather than partial matching, so we use
  `sinon.match.some(sinon.match({...}))` to assert the array
  contains at least one element partially matching the expected
  cause. Failures are asserted via `should(bool).be.true(msg)` with
  a self-contained message listing the actual blocked paths, since
  sinon's default formatter dumps the matcher internals
  (`{ test, message }`) and is unreadable.
@taratatach
taratatach force-pushed the fix/chained-moves-via-incompatible branch from b340f49 to 46e15e8 Compare July 3, 2026 16:40
@taratatach taratatach changed the title WIP: fix chained moves via incompatible fix: Chain moveFrom through incompatible moves Jul 3, 2026
@taratatach
taratatach force-pushed the fix/chained-moves-via-incompatible branch from 46e15e8 to 28fe349 Compare July 3, 2026 18:23
  When a remote move renamed `i` to an incompatible path `i*`, then
  again to a compatible path `z`, `move` set `dst.moveFrom = src` (`i*`),
  pointing at a path never materialized on the filesystem. The local
  watcher then tried to move a non-existent `i*` to `z` and failed.

  `move` now chains through `src.moveFrom` when the local side is not
  up-to-date, so `dst.moveFrom` records the actual local path to move
  from (the original `i`) instead of the intermediate incompatible
  path. When the local side is already up-to-date or `src` has no
  `moveFrom`, it uses `src` directly as before. Child moves are
  excluded from chaining: their `moveFrom` points at the path before
  the parent move, which the parent's sync will relocate, so chaining
  would point at a path that will have already moved away.

  The `doChildMove` guard is simplified to rely on the presence of
  `parent.moveFrom` rather than a path match: with chaining, the
  parent's `moveFrom.path` can point to the original source instead
  of the intermediate path, making the comparison unreliable.
@taratatach
taratatach force-pushed the fix/chained-moves-via-incompatible branch from 28fe349 to 54bfca3 Compare July 3, 2026 19:56
@taratatach
taratatach marked this pull request as ready for review July 3, 2026 20:35
@taratatach
taratatach merged commit 1389fee into master Jul 4, 2026
12 of 17 checks passed
@taratatach
taratatach deleted the fix/chained-moves-via-incompatible branch July 4, 2026 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

doc.moveFrom of a chained rename points to the intermediate doc instead of the original

1 participant