Can Git hook scripts be managed along with the repository?

GitGithooks

Git Problem Overview


We'd like to make a few basic hook scripts that we can all share -- for things like pre-formatting commit messages. Git has hook scripts for that that are normally stored under <project>/.git/hooks/. However, those scripts are not propagated when people do a clone and they are not version controlled.

Is there a good way to help everyone get the right hook scripts? Can I just make those hook scripts point to version controlled scripts in my repo?

Git Solutions


Solution 1 - Git

In Git 2.9, the configuration option core.hooksPath specifies a custom hooks directory.

Move your hooks to a hooks tracked directory in your repository. Then, configure each instance of the repository to use the tracked hooks instead of $GIT_DIR/hooks:

git config core.hooksPath hooks

In general, the path may be absolute, or relative to the directory where the hooks are run (usually the working tree root; see DESCRIPTION section of man githooks).

Solution 2 - Git

Theoretically, you could create a hooks directory (or whatever name you prefer) in your project directory with all the scripts, and then symlink them in .git/hooks. Of course, each person who cloned the repo would have to set up these symlinks (although you could get really fancy and have a deploy script that the cloner could run to set them up semi-automatically).

To do the symlink on *nix, all you need to do is:

root="$(pwd)"
ln -s "$root/hooks" "$root/.git/hooks"

use ln -sf if you're ready to overwrite what's in .git/hooks

Solution 3 - Git

For Node.js users a simple solution is to update package.json with

{
  "name": "name",
  "version": "0.0.1",
  ......
  "scripts": {
    "preinstall": "git config core.hooksPath hooks",

The preinstall will run before

> npm install

and redirects Git to look for hooks inside the .\hooks (or whatever name you choose) directory. This directory should mimic .\.git\hooks in terms of file name (minus the .sample) and structure.

Imagine Maven and other build tools will have an equivalent to preinstall.

It should also work across all platforms.

If you need any more information, see Two ways to share Git hooks with your team.

Solution 4 - Git

If your project is a JavaScript project and you use npm as the package manager, you can use shared-git-hooks to enforce Git hooks on npm install.

Full disclosure: I wrote this package

Solution 5 - Git

Most of the modern programming languages, or rather their build tools, support plugins to manage Git hooks. That means all you need to do is configure your package.json, pom.xml, etc. files, and anyone in your team will have no option but to comply unless they change the build file.

The plugin will add content to .git directory for you.

Examples:

Git Build Hook Maven Plugin

githook-maven-plugin

git-hooks-js

Solution 6 - Git

Use git-hooks. It routes .git/hooks invoke into scripts under the project directory, githooks.

There are also a lot of features to enable you to minimize copy and symlink hook all over the place.

Solution 7 - Git

We are using Visual Studio solutions (and thus projects) which have pre and post build events. I'm adding an additional project named 'GitHookDeployer'. The project self modifies a file in the post build event. That file is set to copy to the build directory. Thus the project is build every time and is never skipped. In the build event, it also makes sure that all git hooks are in place.

Note that this is not a general solution, as some projects, of course, have nothing to build.

Solution 8 - Git

I wanted to merge several answers into one. Assuming you are in your project/ directory:

Setup your custom hooks

  1. Create .githooks directory and place your hooks in it. (See .git/hooks for examples)

  2. Create a .gitconfig file that points the directory ¹:

    git config -f .gitconfig core.hooksPath .githooks
    
  3. Create the following rule in your Makefile: ²

    enable-git-hooks:
    	git config --local include.path ../.gitconfig
    	$(warning REMEMBER, YOU MUST HAVE REVIEWED THE CUSTOM HOOKS!)
    

Enable your custom hooks

Every developer should explicitly enable these custom hooks after reviewing them. Add a directive to your README, something like that:

> Enable custom hooks AFTER REVIEWING them by make enable-git-hooks.

Solution 9 - Git

You could use a managed solution for pre-commit hook management like pre-commit. Or a centralized solution for server-side git-hooks like Datree.io. It has built-in policies like:

  1. Detect and prevent merging of secrets.
  2. Enforce proper Git user configuration.
  3. Enforce Jira ticket integration - mention ticket number in pull request name / commit message.

It won't replace all of your hooks, but it might help your developers with the most obvious ones without the configuration hell of installing the hooks on every developers computer/repo.

Disclaimer: I am one of Datrees founders

Solution 10 - Git

You can make your hooks folder another Git repository and link it as a submodule...

I guess it is worth it only if you have a lot of members and hooks changed regularly.

Solution 11 - Git

Ideally, hooks are written in Bash, if you follow the sample files. But you can write it in any language available, and just make sure it has the executable flag.

So, you can write a Python or Go code to achieve your goals, and place it under the hooks folder. It will work, but it will not be managed along with the repository.

Two Options

a) Multi Scripts

You can code your hooks inside your help, and add a small fragment of code to hooks, to call your perfect script, like this:

$ cat .git/hooks/pre-commit
#!/bin/bash
../../hooks/myprecommit.js

b) Single Script

A cooler option is to add just one script to rule them all, instead of several ones. So, you create a hooks/mysuperhook.go file and point every hook you want to have to it.

$ cat .git/hooks/pre-commit
#!/bin/bash
../../hooks/mysuperhook.go $(basename $0)

The parameter will provide your script which hook was triggered, and you can differentiate it inside your code. Why? Sometimes you might want to run the same check for commit and push, for instance.

And then?

Then, you might want to have further functionalities, like:

  • Trigger the hook manually to check if everything is OK even prior to a commit or push. If you just call your script (option a or b) would do the trick.
  • Trigger the hooks on CI, so you don't need to rewrite the same checks for CI. It would be just calling the commit and push triggers, for instance. The same as the above should solve it.
  • Call external tools, like a markdown validator, or a YAML validator. You can make syscalls and need to handle standard output and standard error.
  • Make sure all developers have a simple way to install the hooks, so a nice script needs to be added to the repository to replace default hooks with the correct ones
  • Have some global helpers, like a check to block commits to develop and master branches, not having to add it to every repository. You can solve it by having another repository with global scripts.

Can this be simpler?

Yes, there are several tools to help you manage Git hooks. Each of them is tailored to tackle the problem from a different perspective, and you might need to understand all of them to get the one that is best for you or your team. GitHooks.com offers a lot of reading about hooking, and several tools available today.

As of today, there are 21 projects listed there with different strategies to manage Git hooks. Some only do it for a single hook, some for a specific language, and so on.

One of those tools, written by me and offered for free as an open-source project, is called hooks4git. It is written in Python (because I like it), but the idea is to handle all items listed above in a single configuration file called .hooks4git.ini, which lives inside your repository and can call any script you want to call, in any language.

Using Git hooks is absolutely fantastic, but the way they are offered usually only gets people away from it.

Solution 12 - Git

For Gradle users

I found these scripts very useful for Gradle projects.

build.gradle

apply from: rootProject.file('gradle/install-git-hooks.gradle')

gradle/install-git-hooks.gradle
tasks.create(name: 'gitExecutableHooks') {
    doLast {
        Runtime.getRuntime().exec("chmod -R +x .git/hooks/");
    }
}
task installGitHooks(type: Copy) {
    from new File(rootProject.rootDir, 'pre-commit')
    into { new File(rootProject.rootDir, '.git/hooks') }
}
gitExecutableHooks.dependsOn installGitHooks
clean.dependsOn gitExecutableHooks
pre-commit
.... your pre commit scripts goes here

Solution 13 - Git

I'm currently working on this in our codebase and I came across a library called husky which simplifies how to use and share GitHub Hooks across your team. I highly recommend looking into that.

Solution 14 - Git

Inspired by a3765910's answer for Gradle but modified to run every build.

Add the following to your app's build.gradle (assuming you already created the githooks/pre-commit file you want to keep in source control):

copy {
    from new File(rootProject.rootDir, 'githooks/pre-commit')
    into { new File(rootProject.rootDir, '.git/hooks') }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPat NotzView Question on Stackoverflow
Solution 1 - GitMax ShenfieldView Answer on Stackoverflow
Solution 2 - GitmipadiView Answer on Stackoverflow
Solution 3 - GitShane GannonView Answer on Stackoverflow
Solution 4 - GitkiliancView Answer on Stackoverflow
Solution 5 - GityuranosView Answer on Stackoverflow
Solution 6 - GitcattailView Answer on Stackoverflow
Solution 7 - GitMike de KlerkView Answer on Stackoverflow
Solution 8 - GitceremcemView Answer on Stackoverflow
Solution 9 - GitShimon ToltsView Answer on Stackoverflow
Solution 10 - GitDo-do-newView Answer on Stackoverflow
Solution 11 - GitLovatoView Answer on Stackoverflow
Solution 12 - Gita3765910View Answer on Stackoverflow
Solution 13 - GitCrypto BatmanView Answer on Stackoverflow
Solution 14 - GitAdam JohnsView Answer on Stackoverflow