81

With a one official repository as the remote, and multiple local repositories cloned from it, can a pre-commit hook be scripted on that main repository and be enforced on all clones of it?

Samer Buna
  • 8,821
  • 9
  • 38
  • 55
  • 5
    If you want *enforcement*, use an update hook in the central repo. If the hook is doing per-commit verification, you can still provide a pre-commit hook; developers will likely adopt it voluntarily, so that they can find out right away when they've done something wrong, rather than waiting until they try to push. – Cascabel Sep 13 '10 at 18:34
  • 1
    Possible duplicates: (http://stackoverflow.com/questions/3462955/) and (http://stackoverflow.com/questions/427207/) – blong Dec 10 '12 at 04:18

8 Answers8

54

I don't think so, as hooks are not cloned.
May be if that hook script is itself versioned, and then link to (symbolic link) in the clone servers (provided their OS support that link feature).

Or maybe if the hooks are part of a git template directory used for creating the clones (that would only ensure their presences in the clone repo, that would not guarantee they are actually used and executed).

But I don't think there is any "central" way to enforce a commit.


As Jefromi explains even more clearly in the comments (emphasis mine):

I think it really goes against the idea of a git repository to have enforced hooks distributed with the repo.
My clone is my repository. I should be able to use git on it however I like, including choosing whether or not to run hooks.
(And from a security standpoint, that'd be really kind of scary - no one should have the ability to force me to execute certain scripts whenever I run certain git commands.)

I agree with that comment, and have only seen ways to enforce rules applied locally, in a given specialized repo.
For instance, you wouldn't push to the central repo directly, but would first push to a QA repo which would accept your commit only if it follows certain rules. If it does, then the QA repo will push your commit to the central repo.

Another illustration directly derived from what I just mentioned would be "Serverless Continuous Integration with Git", a way to enforce locally private build that works before pushing them anywhere.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 8
    I think it really goes against the idea of a git repository to have enforced hooks distributed with the repo. My clone is *my* repository. I should be able to use git on it however I like, including choosing whether or not to run hooks. (And from a security standpoint, that'd be really kind of scary - no one should have the ability to force me to execute certain scripts whenever I run certain git commands.) – Cascabel Sep 13 '10 at 18:36
  • 1
    @Jefromi: you know what's scarry? When I typed the comment, before submitting my edited answer, I began typing 'add...', and FireFox on my computer did propose me: "add Jefromi's comment". Not the first time I have been there, obviously ;) – VonC Sep 13 '10 at 19:20
  • Note to self: see also http://stackoverflow.com/questions/3209208/what-is-the-cleverest-use-of-source-repository-that-you-have-ever-seen/3209767#3209767 – VonC Sep 13 '10 at 19:25
  • +1 for the symlinks proposal. Just make sure at least one hook updates the symlink, and all users have to do is run it ONCE. It's still optional - but hooks are versioned :-) Only safe for certain types of enviroments really. – WhyNotHugo Dec 01 '10 at 22:47
10

You can not have the pre-commit hook forced on peoples local repositories, but in your central repo you can still run a pre-receive hook.

F. ex I needed to be sure the commit messages obeyed certain rules (for trac integration etc) so I used following pre-receive hook, which checks every commit messages being pushed to the central repository, and will deny the push if it is not welformed.

#!/bin/sh
while read rev_old rev_new ref
do
    MALFORMED="$(git rev-list --oneline $rev_old..$rev_new | egrep -v '#[0-9]+' |  awk '{print $1}' )"
    if [ x"$MALFORMED" != x ]
    then
        echo Invallid commit message on $MALFORMED
        exit 1
    fi
done

for more info see f.ex https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks

Jens Timmerman
  • 9,316
  • 1
  • 42
  • 48
9

Two options:

1. Use Husky

If you are writing JavaScript, the best way to do this is with Husky. Husky has a postInstall script that will set up and manage your githooks. You can then configure precommit and prepush scripts in your package.json or a husky dotfile.

You can use this to run arbitrary scripts. I typically yarn lint and yarn test prepush.

2. Set core/hooksPath in git.config, then include /hooks in your repo

If you are not using JavaScript, or you cannot use Husky, you can clone commit hooks onto developer machines and check them into a repo, but you cannot force developers to run them.

To check in your hooks, create a hooks directory somewhere in your repo. Then put your hooks there instead of the usual .git/hooks directory. This is the part you can enforce.

The other part depends on developer goodwill. To set your hooks folder as the hooksPath, each developer must run:

git config core.hooksPath hooks

Now all the hooks in the hooks folder will run as you would expect.

superluminary
  • 47,086
  • 25
  • 151
  • 148
  • 1
    No need to shout the Yes and no. Please don't overdo formatting just to get attention. That said, a question: can the git config `core.hooksPath` be set in a `post-checkout` hook? That way, it would run after cloning. – MS Berends Sep 02 '22 at 07:00
6

can a pre-commit hook be scripted on that main repository and be enforced on all clones of it?

From githooks(5):

    pre-commit
      This hook is invoked by git commit, and can be bypassed with
      --no-verify option.

Since the hook can easily be bypassed, it seems the answer to your question is "no".

Also, since the .git/hooks directory is not cloned, there does not seem to be a mechanism to push it to the client.

blackbrandt
  • 2,010
  • 1
  • 15
  • 32
bstpierre
  • 30,042
  • 15
  • 70
  • 103
3

Very very old answers here. This is definitely possible these days.

Since Git 2.9 or so, the config core.hooksPath is available. Per the git documentation:

core.hooksPath

By default Git will look for your hooks in the $GIT_DIR/hooks directory. Set this to different path, e.g. /etc/git/hooks, and Git will try to find your hooks in that directory, e.g. /etc/git/hooks/pre-receive instead of in $GIT_DIR/hooks/pre-receive.

The path can be either absolute or relative. A relative path is taken as relative to the directory where the hooks are run (see the "DESCRIPTION" section of githooks[5]).

This configuration variable is useful in cases where you’d like to centrally configure your Git hooks instead of configuring them on a per-repository basis, or as a more flexible and centralized alternative to having an init.templateDir where you’ve changed default hooks.

This means that you can share Git hooks between client machines (perhaps using a non-.git folder inside the project, or using a dedicated Git repository locally cloned, or using sync software such as OneDrive or Dropbox) by just setting the core.hooksPath config to that (shared) folder.

MS Berends
  • 4,489
  • 1
  • 40
  • 53
  • But this will require every developer to set the correct `hooksPath`, it's not much different than before – Christian Vincenzo Traina Feb 14 '23 at 10:19
  • True, but it is. Now the relevant file is maintained and stored in one place, just copied for every developer. – MS Berends Feb 15 '23 at 12:51
  • Agree with @ChristianVincenzoTraina. There is not a big difference between asking the user to run `ln -s ../../hooks/pre-commit.sh .git/hooks/pre-commit` (which was always possible AFAIK) and `git config core.hooksPath hooks/`. The users still have to know that they need to set it up. – Alexander Klimetschek Mar 20 '23 at 23:28
  • For one reason, symbolic links are not supported on Windows, `git config` is. – MS Berends Mar 22 '23 at 05:48
  • Symbolic links are supported in Windows @MSBerends – Dennis Aug 22 '23 at 12:31
2

Assuming you have source code in your git repo that has a build system associated with it, you could configure the build system to set up the pre-commit hook, i.e. by moving or linking a pre-commit hook that ~is versioned.

I have not tried this yet. I came here while googling for a better solution.

Andrew Wagner
  • 22,677
  • 21
  • 86
  • 100
0

It's not fully automatic, but you can add hook scripts for a repo inside a hooks/ directory that you'd add to git, and then ask developers to symlink the hook script(s) once after they clone the repo:

ln -s ../../hooks/pre-commit.sh .git/hooks/pre-commit

Documenting in a project README might work well enough.

Alexander Klimetschek
  • 3,585
  • 31
  • 36
-2

I create a new file: pre-commit-hook.sh

#!/usr/bin/env bash
CHANGES=$(git whatchanged ..origin)

if [ ! -z "${CHANGES}" ]; then
    echo "There are changes in remote repository. Please pull from remote branch first."
    exit 1;
fi

exit 0;

And this is how I commit to Git:

bash pre-commit-hook.sh && git commit -m "<Commit message>"
Vishrant
  • 15,456
  • 11
  • 71
  • 120