This turned out to be harder than I expected, but the best solution I found was the following 3 step process:
Step 1
Include the entire directory structure (but no files yet):--include=*/
Step 2
Include the specific files / contents of specific dirs you need:--include=/index.php
--include=/css/build/*
etc.
Step 3
Exclude everything else--exclude=*
Explanation
The reason we start by including the entire directory structure is to greatly simplify our include statements in part (2). Without it, we would need to have separate include statements for every parent directory of every file we wanted to include e.g.--include=/cssInstead of just:
--include=/css/build
--include=/css/build/*
--include=/css/build/*This is because the final exclude-all-else statement is very powerful, and if you just saying you want to include the files inside /css/build/ isn't enough - because the exclude-all will encounter /css, won't find a matching include statement and so will exclude it!
You may however, not want to include hidden directories e.g. .git/ or .svn/. To do this, we insert this statement right at the beginning:
--exclude=.*/
Final template
rsync -av
--exclude=.*/
--include=*/
<!-- START CUSTOM INCLUDES -->
--include=/index.php
--include=/.htaccess
--include=/favicon.ico
--include=/img/*
--include=/css/build/*
--include=/js/build/*
<!-- END CUSTOM INCLUDES -->
--exclude=*
src/
dest/
Addition
There may be situations where you need to just recirsively include all files and subdirs within a directory. To do that, you can use this:--include=css/**