Migrations (especially scoped ones with `--only :wp-content:`) don't
have a great option for symlinks that point outside the pulled scope.
Bare `--follow-symlinks` pulls the content but mirrors it at its raw
source paths, scattered across the docroot. `--no-follow-symlinks` just
drops it, leaving dangling links.
This PR adds a destination form:
`--follow-symlinks=:fs-root:/.symlinks-bundle`. Escaping targets get
followed and consolidated into one private in-tree directory, nested by
source path, and their symlinks are repointed there. A scoped wp-content
pull ends up self-contained: no dangling links, no junk in the docroot.
**Placement**: remap rules first, then the bundle location if set, then
the default fs-root mirror. Anything inside a remapped wp-content lands
in the served tree, so the bundle only receives genuinely-external
content. Multiple links to one target share a single copy. Relative and
absolute targets are handled the same. Bare `--follow-symlinks` is
equivalent to `--follow-symlinks=:fs-root:` (the unchanged default), and
the value uses the `--remap` target grammar — it must stay within
`--fs-root`.
**Resume guard**: the bundle directory is fingerprinted on the first run
and checked on every run after. Changing (or dropping) it errors instead
of silently splitting placement; `--abort` resets it. Setting a bundle
directory also persists `follow_symlinks=true`, so a flag-less delta
can't index with follow off and delete previously-followed content.
**Behavior changes** (both previously produced missing content and
dangling links):
1. Under a narrow `--only`, followed targets outside the `--only` scope
are now pulled. They used to be skipped as "already covered" because
they sat under a preflight root, even though the scoped pull never
actually downloaded them.
2. Symlinks in the middle of a followed path (e.g. `/srv/wordpress →
/wordpress`) are now repointed at the locally-mirrored content, like
every other symlink. They used to be skipped whenever their absolute
target pointed outside `--fs-root`, even when the content itself had
been downloaded.
## Testing
The new `SymlinkBundleTest` (unit) + `import-52-symlink-bundle` (e2e,
broad + narrow `--only`) cover lots of cases here.
For manual testing though, the setup would be rather intense. You'll
need something like a symlinked plugin that points outside wp-content.
Then run `--only :wp-content:
--follow-symlinks=:fs-root:/.symlinks-bundle` and ensure the plugin's
source ended up in the new bundle location.