To copy a directory tree ignoring all the .svn folders, use this
cd DIR_FROM
find . \! \( -name . -or -name ".svn" -or -path "*/.svn/*" \) -print | cpio -padv PTH_TO
I use Subclipse for development, it does a great job of keeping track of what files are moved or deleted files. However, it does get confused when duplicating complex directory structures. In those cases, I need to do the duplicating by hand, using Terminal.
Restating the problem in more generic terms, what I need to find out is "how to copy files to a new directory, matching certain conditions, and preserving the directory structure". The specific conditions we are looking for are: ignore anything inside a folder called .svn, including the folder itself.
Finding files matching a set of conditions
The shell command to find files matching certain conditions is called, unsurprisingly, find, a powerful command. It includes 'actions' which can be performed on the found files. The conditions mentioned above can be expressed in find as
find DIR_FROM \! \( -path "*/.svn/*" -or -name ".svn" \) -print
which breaks down as
find DIR_FROM- run the find command, searching in DIR_FROM (in OS X, you can just drag DIR_FROM from the Finder to the Terminal window instead of typing it all out)
\!- since we are ignoring files, use ! (negative) to find files which do NOT match the pattern. The slash is needed, because esclamation marks mean something to Terminal, and we want Terminal to ignore that
\(- the parenthesis groups conditions together, since we have more than one. Again, the slash is needed because we want Terminal to ignore its special meaning
-path "*/.svn/*"- this is the condition "the path includes '/.svn' anywhere in it"
-or- adds conditions together, matching any of them
-name ".svn"- this is the condition "the name of the files is exactly '.svn'
\)- ends the group of condition. The negative ! applies to all of them.
-print- At this stage, just prints the names out on the terminal. Always safe to run this first, to double check the results are as expected.
Copying a directory structure on the command line
It would be great if find had a 'copy found files' action - sadly, it doesn't. It does, however, have a 'delete' action. With that, the simplest approach would be to copy everything first, then remove the files who don't match the pattern - or, in my case, those who match the ignore pattern. You can run this from any directory.
cp -R DIR_FROM DIR_TO
find DIR_FROM \( -path "*/.svn/*" -or -name ".svn" \) -delete
cp -R DIR_FROM DIR_TO- cp is the command to copy files on the shell. cp -R is used to copy directories. R stands for "recursive", it is common to many other shell commands
find DIR_FROM \( -path "*/.svn/*" -or -name ".svn" \)- almost as before - but note there is no \! this time. That's because we are going to remove files, rather than copy them.
-delete- deletes the files it finds. I suggest to try it with -print first, just to double check
The above works fine, but if you want to learn other ways of doing it just for the sake of it, read on.
Finding files and passimg them to the cp command
My initial approach when trying to move files across was to use find with the '-exec' action and the cp command, like this:
find DIR_FROM \! \( -path "*/.svn/*" -or -name ".svn" \) -exec cp {} DIR_TO \;
This looks good at first, but there's a problem - it finds and copies the files, but it doesn't preserve the directory structure, and the files end up all in the same directory. Apparently on Linux you can tell cp to preserve the directory structure with the -b --parent option, but this doesn't work on OS X.
find DIR_FROM \! \( -path "*/.svn/*" -or -name ".svn" \) -exec cp -b --parent {} DIR_TO \;
Using cpio to copy files
cpio is an archiving utility similar to tar, but it has an important difference - it can copy files into and archive and then out again to a different location, without actually creating an archive in between. In other words, it copies directory structures. The only snag is that you have to cd to the directory where you are copying from.
cd DIR_FROM
find . \! \( -path "*/.svn/*" -or -name ".svn" \) -print | cpio -padv PTH_TO
cd DIR_FROM- move to the directory you are copying - this is where the directory structure will be calculated from
find . \! \( -path "*/.svn/*" -or -name ".svn" \)- the usual one; DIR_FROM is replaced with '.', which means 'here'
-print- prints out the filename
|- pass the results of the command on the left (find) to the one on the right (cpio)
cpio -padv PTH_TO- run the cpio command in 'move files across' mode (p) with options d (create directory structure) -a (reset accss time on copied files) -v (verbose, i.e., show me what you are doing)
Now this is almost there - except that it creates empty .svn directories, even though the find command is not listing them. It turns out that I needed to exclude the current directory from the results, '.', otherwise the complete directory tree would be created. Finally, the complete command is
cd DIR_FROM
find . \! \( -name . -or -name ".svn" -or -path "*/.svn/*" \) -print | cpio -padv PTH_TO