skip to content

Copying sourcefiles without .git folders with cpio

Sometimes you need to copy your local version of a project under subversion to a new location, without all the .git stuff. Here's a couple of ways of doing it.

TL;DR

cd DIR_FROM
❯ find .  \! \
  \( -name . -or -name ".git" -or -path "*/.git/*" \) \
  -print | cpio -padv PTH_TO

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 command below finds all the files we want to copy and ignores the rest.

❯ find DIR_FROM \! \
  \( -path "*/.git/*" -or -name ".git" \) \
  -print

It 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 "*/.git/*"
this is the condition "the path includes '/.git' anywhere in it"
-or
adds conditions together, matching any of them
-name ".git"
this is the condition "the name of the files is exactly '.git'
\)
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.

Delete all .git directories from a project

In an ideal world, I’d run the find command above with “-print”, check it’s ok, then I’d run it again with “-copy”. Except that there is no “-copy” action, so it’s back to man find to work out an action that will help.

It turns out the simplest way is to copy all files first, then find the .git ones and use the ‘-delete’ action to get rid of them.

❯ cp -R DIR_FROM DIR_TO
❯ find DIR_FROM \
  \( -path "*/.git/*" -or -name ".git" \) \
  -delete
cp -R DIR_FROM DIR_TO
this is how you copy a directory in Terminal. cp is the command to copy files on the shell, but it won't do directories. cp -R is used to copy directories. R stands for "recursive", it is common to many other shell commands
find DIR_TO \( -path "*/.git/*" -or -name ".git" \)
almost as before - but note there is no \! this time. That's because we want to find the files we don't need, rather than ignore them.
-delete
deletes the files it finds. I suggest to try it with -print first, just to double check

The above works fine, but read on for another way of doing it.

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 "*/.git/*" -or -name ".git" \\) \n  -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 "*/.git/*" -or -name ".git" \) \
  -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 . \
  \! \( -name . -or -name ".git" -or -path "*/.git/*" \) \
  -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 . \! \( -name . -or -path "*/.git/*" -or -name ".git" \)
similar to the previous find command, but note how DIR_FROM is replaced with '.', which means 'here'. Also, there is an extra condition, `-name .`, without which empty .git directories would be created.
-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 access time on copied files) -v (verbose, i.e., show me what you are doing)

And that’s that - exciting Unix fun.