49

Sometimes I need to perform following command

cp -rv demo demo_bkp

However I want to ignore all the files in directory .git . How do I achieve that? It takes a long time to copy .git files and I do not need those files.

Nick Vanderbilt
  • 36,724
  • 29
  • 83
  • 106
  • Possible duplicate of [Copy folder recursively, excluding some folders](http://stackoverflow.com/questions/2193584/copy-folder-recursively-excluding-some-folders) – phuclv Jan 06 '16 at 09:01

5 Answers5

116

To ignore a git directory specifically, I'd try git export first.

But in general, to copy a directory tree excluding certain files or folders, I'd recommend using rsync instead of cp. The syntax is mostly the same, but rsync has way more options, including one to exclude selected files:

rsync -rv --exclude=.git demo demo_bkp

See e.g. the man page for more info.

Community
  • 1
  • 1
David Z
  • 128,184
  • 27
  • 255
  • 279
  • I'm not sure whether there are other options that would have prevented this, or it's something to do with Linux versions but if I follow your example I get `demo` as a subdirectory of `demo_bkp`. However, using `rsync -rv --exclude=.git demo/ demo_bkp` creates a new directory that contains all of the files from the first directory. – Mark Birbeck Jul 18 '17 at 13:34
  • 1
    So far, how to `cp` while excluding git is exactly what i am looking for! I do not want to use rsync in my scripts. Wouldn't be better to advise `tar` instead? https://stackoverflow.com/a/2193806/2494754 – NVRM Apr 04 '18 at 21:15
  • @Cryptopat For this question, no, it wouldn't be better to advise `tar` instead (IMO) because `rsync` is a superior option. It's specifically designed for this task and the usage is more straightforward. If you don't want to use `rsync`, I suggest one of two things: (1) Try one of the solutions recommended in the other answers to this question. That's part of why there are multiple answers. Or (2) post a separate followup question asking how to do this copy without using `rsync`, and including any other constraints you may have. – David Z Apr 04 '18 at 21:41
  • sync is not a copy. It is creating links from the new folder to the old one. Any changes in the old folder will be reflected in the new one. this is not a copy. – IgorAlves May 16 '19 at 18:05
  • 1
    @zwitterion's comment is not correct. `rsync` (not `sync`, which is a tool to flush write buffers) does not create links, but instead copies the files. Changes to the old folder will not be reflected in the new folder (unless `rsync` is run again, obviously) – Torque Jun 08 '21 at 10:55
11

OK. Brace yourself. This isn't pretty.

find demo -depth -name .git -prune -o -print0 | cpio -0pdv --quiet demo_bkp

What's going on here?

  1. find demo | cpio -p demo_bkp finds files matching whatever criteria you want and uses cpio to copy them (so-called "pass-through" mode).

  2. find -depth changes the order the files are printed in to match the order cpio wants.

  3. find -name .git -prune tells find not to recurse down .git directories.

  4. find -print0 | cpio -0 has find use NUL characters (\0) to separate file names. This is for maximum robustness in case there are any weirdly-named files with spaces, newlines, or other unusual characters.

  5. cpio -d creates directories as needed.

  6. cpio -v --quiet prints each file name while omitting the "X blocks copied" message cpio normally prints at the end.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
5

I think this will do the trick:

cd demo
find . -not -path \*/.\* -type d -exec mkdir -p -- ../demo_bkp/{} \;
find . -not -path \*/.\* -type f -exec cp -- {} ../demo_bkp/{} \;

First finds and creates each directory. Then finds and copies each file.

Note it will not work with special files (symbolic links, etc).

LatinSuD
  • 1,779
  • 12
  • 19
3

Continuing on the rsync idea, if you have so many file patterns to ignore, you can use the --exclude-from=FILE option.

The --exclude-from=FILE option takes a file [FILE] that contains exclude patterns (one pattern per line) and excludes all the files matching the patterns.

So, say you want to copy a directory demo to demo_bkp, but you want to ignore files with the patterns - .env, .git, coverage, node_modules. You can create a file called .copyignore and add the patterns line by line to .copyignore:

.env
.git
coverage
node_modules

Then run:

rsync -rv --exclude-from=./.copyignore demo demo_bkp

That's it. demo would be copied into demo_bkp with all the files matching the patterns specified in .copyignore ignored.

0

Simple solution, possibly best runtime, conforming to OP (who wants to use cp):

touch /path/to/target/.git
cp -n -ax * /path/to/target/
rm /path/to/target/.git

This exploits the -n option of cp, which forces cp to not overwrite existing targets.

Drawback: Works with GNU cp. If you don't have GNU cp, then the cp operation might return an error code (1), which is annoying because then you can't tell if it was a real failure.

Johannes
  • 2,901
  • 5
  • 30
  • 50