24

I have a pre-commit hook and I want to add it to the repository so that by checking it out my colleagues have it instantly in place.
However if I try to add it ( being in the root directory of my project) I get the following result:

$ git add  .git/hooks/pre-commit
error: Invalid path '.git/hooks/pre-commit'
error: unable to add .git/hooks/pre-commit to index

Any idea if this work and how to achieve my goal?

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
Calamity Jane
  • 2,189
  • 5
  • 36
  • 68
  • Does this answer your question? [Can Git hook scripts be managed along with the repository?](https://stackoverflow.com/questions/427207/can-git-hook-scripts-be-managed-along-with-the-repository) – andruso Aug 17 '20 at 14:08
  • Thanks for answering after 4 years ;) . By now I prefer the solution I added in Dec 2019 using "brainmaestro/composer-git-hooks" – Calamity Jane Aug 20 '20 at 13:01

3 Answers3

11

checking it out my colleagues have it instantly in place

Sept. 2015: That is not possible: a hook can be put in source control (you simply copy the script in your git repo), but it cannot be "automatically in place" (active) on clone/checkout: that would be way too dangerous, depending on what the hook is actually doing.
See also "Git remote/shared pre-commit hook"

You would still need to activate it, by adding a symlink (even on Windows) to the pre-commit hook present in the git repo.

Update Dec. 2016: the OP Calamity Jane mentions in the comments:

I solved it by now in symfony2 projects (and with others, it also should work) to have it as part of the composer.json.
So if a colleague is doing a composer install or composer update, it is automatically placed in the correct place.

"scripts": { "dev-install": [ "bash setup_phpcs.sh" ] }, 

So on dev, setup_phpcs.sh is automatically called and that copies the hook from a folder in the repository to the right place.
And since the hook is part of the repository it can be easily updated and distributed.

As noted by Hi-Angel in the comments:

I figured that out: Emacs has build-aux dir with hooks, and, upon running autogen.sh, all hooks are copied from there.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • With another repository I am using a symlink to a central repository with the effect that half of my colleagues are not using the hook. But well if it doesn't work that way, then I will have it like with my other repository and I can also understand why this is not desired. – Calamity Jane Sep 18 '15 at 11:01
  • Thanks for answering *why*, that's what I wanted to know about @VonC. – yurisich Nov 16 '15 at 21:47
  • By the way I solved it by now in symfony2 projects ( and with other it also should work) to have it as part of the composer.json. So if a colleague is doing a composer install or composer update it is automatically placed in the correct place. "scripts": { "dev-install": [ "bash setup_phpcs.sh" ] }, So on dev setup_phpcs.sh is automatically called and that copies the hook from a folder in the repository to the right place. And since the hook is part of the repository it can be easily updated and distributed. – Calamity Jane Dec 19 '16 at 11:58
  • @CalamityJane Nice! I have included your comment in the answer for more visibility. – VonC Dec 19 '16 at 12:01
  • But when I clone e.g. Emacs projects, it has hooks in it, which means somehow they managed to add them. – Hi-Angel Apr 21 '19 at 19:17
  • @Hi-Angel I suppose magit is adding those hooks: https://github.com/magit/with-editor/commit/87c96b3a7cdf9ab9477e27f1ae2010883a83a426 – VonC Apr 21 '19 at 19:26
  • Thanks, I figured that out: Emacs has `build-aux` dir with hooks, and upon running `autogen.sh` all hooks are copied from there. – Hi-Angel Apr 21 '19 at 19:56
  • 1
    @Hi-Angel Well spotted. I have included your comment in the answer for more visibility. – VonC Apr 21 '19 at 20:03
2

If your repository is an npm application, you can add this nice library as a dependency: Husky

File package.json

...
"devDependencies": {
    ...
    "husky": ">=4"
},
"husky": {
    "hooks": {
        "pre-commit": "npm test"
    }
}

pre-commit can be basically any command: if it ends with exit code 0, the commit passes, otherwise the commit is interrupted.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Klesun
  • 12,280
  • 5
  • 59
  • 52
0

For PHP and PHP Composer:

FYI, in the year 2019, there is also this option:

Require this package: "brainmaestro/composer-git-hooks"

And add the following lines to your composer.yaml file:

"extra": {
    "hooks": {
        "commit-msg": [
            "regex=\"^([A-Z]{2,4}-[0-9]{1,4}|(no-ticket|NO-TICKET)):[\\s]*.{10,}\"",
            "file=`cat $1`",
            "if ! [[ $file =~ $regex ]]; then",
            "  echo \"ERROR - Commit message is wrong or too short. E.g. XXX-33: Description or no-ticket : Description\"",
            "  exit 1",
            "fi"
        ],
        "pre-commit": [
            "git status --porcelain | grep -e '^ [AM]\\(.*\\).php$' | cut -c 3- | while read line; do",
            "ROOT=`php -r \"echo __DIR__;\"`",
            "bin/php-cs-fixer fix -nq --config=$ROOT/.php_cs \"$line\";",
            "bin/phpcbf --standard=PSR2 --encoding=utf-8 -n -p \"$line\";",
            "git add \"$line\";",
            "done",
            "echo committing on branch $(git rev-parse --abbrev-ref HEAD)"
        ]
    }
}

This is the example, which works for me. What it basically does is this:

Every time you run "composer install" or "composer update", the hooks in folder .git/hooks are checked. If the hooks are already in place nothing happens. If they are missing, then the lines from above are parsed into the the hooks with a shebang at the beginning. They are then executed each time somebody is triggering a hook.

If you don't have big scripts that is IMO the better solution than copying scripts around.

Note: if you change the lines in the composer.json for the hooks, you have to delete the respective hook first before you run "composer install" or nothing will change.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Calamity Jane
  • 2,189
  • 5
  • 36
  • 68