Skip to content

Keep locally installed files when pulling onto an existing site#4283

Open
epeicher wants to merge 2 commits into
trunkfrom
stu-1951-make-pull-reprint-not-delete-local-files
Open

Keep locally installed files when pulling onto an existing site#4283
epeicher wants to merge 2 commits into
trunkfrom
stu-1951-make-pull-reprint-not-delete-local-files

Conversation

@epeicher

@epeicher epeicher commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Related issues

Merge order matters. reprint exits 1 on an unknown option, so this must not land until reprint#383 is released and the bundled phar is bumped — otherwise every pull fails with Unknown option: --preserve-local-content.

How AI was used in this PR

Claude Code was used to investigate the root cause, implement the change here and in reprint#383, and run the end-to-end verification below. The diff was reviewed by hand; an earlier revision of the reprint docblocks referenced Studio commands and was rewritten, since reprint is a library and shouldn't document its consumers.

Proposed Changes

Pulling onto a site that already has content deleted everything the user had installed. Create a site, install a plugin, pull → the plugin is gone. Themes, uploads and any custom wp-content folder went with it.

The first pull passes --force to flat-docroot so it can overwrite the blank install. But for the common layout, flat-docroot wants wp-content to be a single symlink into the raw scratch dir, and --force makes it recursively delete the existing real wp-content before creating that link. Incremental pulls were unaffected, because --force isn't passed and wp-content is already a symlink — which is why this only ever showed up on the first pull.

This passes reprint's new --preserve-local-content, so wp-content is merged instead of replaced: entries the remote also has are linked (the remote wins), and entries that exist only locally are left alone.

Two things worth flagging for review:

  • The flag goes on every pull, not just the first. Once merged, wp-content is a real directory, and a later pull without the flag fails trying to symlink over it.
  • flat-docroot is re-run after the deferred file tail. Those files land in the raw dir after the flatten, and the site now links entries individually rather than aliasing the whole wp-content, so without a second pass late-arriving media would stay unlinked until the next pull.

Behaviour change: wp-content is now a real directory rather than a single symlink, so plugins installed after a pull live in the site directory instead of reprint's raw scratch. That's the cleaner ownership split and is what makes them survive, but it is a change in where files land.

Note this is a different problem from preserveUnselectedLocalContent, which protects content the user chose not to pull; this protects content the remote never had.

Testing Instructions

Requires a build with reprint#383 in the bundled phar.

  1. studio create --name test --path ~/Studio/test --start
  2. Install a plugin that is not on the remote (drop a folder in wp-content/plugins/). Optionally add a theme, an upload and a custom wp-content folder.
  3. studio pull-reprint --path ~/Studio/test --url <a connected site>
  4. The plugin, theme, upload and custom folder are all still there, alongside the remote's content. ls -ld ~/Studio/test/wp-content shows a real directory.
  5. Install a second local-only plugin and pull again — it survives, and the pull exits cleanly.

Verified end-to-end on a real connected site: first pull and incremental pull both exit 0, all local-only content preserved, remote content linked, no dangling symlinks, site returns 200. npm -w wp-studio run build is clean.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

…1951)

The first pull passes --force to flat-docroot to overwrite the blank install,
which also deleted every plugin, theme and upload already in wp-content.

Pass reprint's --preserve-local-content so wp-content is merged rather than
replaced. It goes on every pull, not just the first: once merged, wp-content
is a real directory, and a later pull without the flag fails trying to symlink
over it.

Re-run flat-docroot after the deferred file tail as well. Those files land in
the raw directory after the flatten, and the flattened site now links entries
individually rather than aliasing the whole wp-content, so without a second
pass they would stay unlinked until the next pull.
@wpmobilebot

wpmobilebot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 978b5c9 vs trunk

app-size

Metric trunk 978b5c9 Diff Change
App Size (Mac) 1361.88 MB 1355.71 MB 6.17 MB 🟢 -0.5%

site-editor

Metric trunk 978b5c9 Diff Change
load 1092 ms 1043 ms 49 ms ⚪ 0.0%

site-startup

Metric trunk 978b5c9 Diff Change
siteCreation 7012 ms 7004 ms 8 ms ⚪ 0.0%
siteStartup 2355 ms 2355 ms 0 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@epeicher
epeicher requested a review from gcsecsey July 21, 2026 17:13
The exact-equality assertion predates the flag. Also assert the delta
pull still passes it: without --force it is what lets the flatten
succeed over the real wp-content directory the first merge left behind.
@epeicher epeicher changed the title Keep locally installed files when pulling onto an existing site (STU-1951) Keep locally installed files when pulling onto an existing site Jul 22, 2026
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.

2 participants