1

I wrote a git commit hook and tested it out in a repo called myrepo. I would now like to copy it to all the repos I have in my ~/repos folder. To do that I tried:

$ cd ~/repos/myrepo/.git/hooks
$ cp prepare-commit-msg ~/repos/*/.git/hooks

When I run this the cp errors with the following message on each directory in repos:

cp: -r not specified; omitting directory <directory-name>

I'm 100% sure prepare-commit-msg is not a directory. Why is cp complaining that I'm not using -r?

2 Answers2

3

find is probably the tool you're looking for. You can use it to find each directory that is named hooks and resides under your ~/repos/ folder. For each such directory that find locates, you tell it to copy your file there.

This will cause one error message for the myrepo repo when it tries to copy a file to itself, but that error can be ignored.

cd ~/repos/myrepo/.git/hooks
find ~/repos/ -type d -name hooks -exec cp -vp prepare-commit-msg {} \;

This command can be improved to handle cases of odd characters in directories or filenames, but that's the gist of it.

As for why your original cp command doesn't work, there's probably a question that explains it better, but it's because the shell expands your command to become:

cp prepare-commit-msg ~/repos/repo_A/.git/hooks ~/repos/repo_B/.git/hooks ~/repos/repo_C/.git/hooks ~/repos/repo_D/.git/hooks ~/repos/repo_E/.git/hooks

and given that syntax, cp thinks that you want it to copy the file prepare-commit-msg and four directories ~/repos/repo_A/.git/hooks through ~/repos/repo_D/.git/hooks and place all of the copied files/directories in ~/repos/repo_E/.git/hooks. That is NOT what you want to do, of course, but cp is telling you that it won't copy directories recursively unless you use the -r option.

If you want to structure your solution somewhat like your original syntax, you could use:

cd ~/repos/myrepo/.git/hooks
dirs=( ~/repos/*/.git/hooks )
for d in "${dirs[@]}"
do
    cp -vp prepare-commit-msg "$d/"
done

Methods which involve parsing the output of ls are not recommended.

Jim L.
  • 7,997
  • 1
  • 13
  • 27
0

You could wrap cp with your own cp2dirs:

function cp2dirs(){
  from="$1";shift
  [ -f "$from" ] && while (($#)); do cp "$from" "$1/"; shift; done
}

Then cp2dirs prepare-commit-msg ~/repos/*/.git/hooks does