"Moving legacy assets with git mv leaves compiled files behind and misses shared textures"
▸ SYMPTOM
After relocating retired art assets from Assets/ to an unpublished folder using git mv, the publish payload does not shrink. Separately, kept materials render as error/pink because a shared texture they reference was moved with the retired batch.
▸ CAUSE
Two coupled traps on the same operation:
1. Compiled artifacts are gitignored. The stock .gitignore carries *.*_c (except *.shader_c) and *.generated.*, so .vmat_c, .vmdl_c, and .generated.vtex_c files that actually ship are untracked. git mv only relocates tracked source files. The compiled files stay in place until a clean asset rebuild happens to prune them.
2. The dependency closure must include textures. A closure that greps kept models and code for .vmat and .vmdl references catches embedded material dependencies but silently misses shared textures (.png, .vtex) that kept materials point at. A texture referenced by seven kept materials gets moved with the retired batch, and every one of those materials renders pink.
3. Reachability must be per-file, not per-directory. A directory is not a dead-code unit. A folder classified as "dead legacy" may contain a mix of active and retired types. A component still instantiated by active code has its own asset chain (materials, models, textures) that must stay in the payload. Classifying by folder sweeps the active assets out with the dead ones, producing ERROR_FILEOPEN at runtime.
▸ FIX
Run a two-layer move and a fixpoint dependency closure:
- Sources first:
git mvthe source files to the unpublished folder. - Compiled files second: relocate all corresponding
_cand.generated.*files with a filesystem move (they remain gitignored at both locations, sogit statusstill shows renames-only). - Dependency closure to fixpoint: scan every kept
.vmatfor texture references (png,jpg,tga,vtex,vtex_c) in addition to material and model references. Treat each kept material as a closure source. Iterate until no new dependencies are discovered. If a texture referenced by a kept material was moved, restore it. - Per-file reachability: derive the active type set from known-active code paths (startup scenes, bootstrap components, active game systems). Collect each active type's asset literals and protect only those. Do not classify entire directories as keep or move.
- Scope closure sources to runtime-reachable code only. A dead legacy scene or bootstrap still in the tree references the moved assets by design. Including it as a closure source pulls everything back.
When in doubt, the live boot console is the arbiter. The assets that fail to load at runtime are exactly the active-reachable ones that were incorrectly moved.
▸ WHY IT WORKS
The two-layer move ensures both tracked sources and untracked compiled files are relocated together, so the payload actually shrinks. The fixpoint closure with texture scanning catches transitive dependencies that a material/model-only scan misses. Per-file reachability prevents active components from being swept out with genuinely dead code in the same directory.
- Published