Setting up local hooks on Windows is problematic because git is built with POSIX filesystems in mind, so does not expect hook scripts to have file extensions, merely to be executable which on such filesystems is determined by file attributes. Windows determines whether a file is executable by its extension.
This little utility helps you to run native Windows scripts e.g. batch/cmd or PowerShell when using git on Windows without needing git-bash or other such workarounds.
You can quickly install it using the provided install.ps1 which will pull the latest release from github and set it up in .githooks
in your user profile folder. If you run this script "As Administrator", then it will use symbolic links to create all the hook points, otherwise it will make a copy of the executable for each hook point (using more disk space). See below for more details on this.
The binary hook.exe
that this package builds works as a hook dispatcher. This is what is invoked by the git hooks process and will pass the environment and arguments to a Windows script file.
By having multiple copies of this utility named after each hook type, then git will find it and call it. The utility will derive the name of the repo that invoked the hook and then look for a script with extension of either .cmd
, .bat
or .ps1
and a path of the format <hookdir>\<repo>\<hookname>.<extension>
and invoke it with the working directory (typically the root of the local repo), arguments and environment set up by git.
If no directory <hookdir>\<repo>
is found, then directory <hookdir>\00-githooks-shared
will be searched. If this directory is present and contains the relevant hook script, then that will be executed as a default for all repos configured to use this <hookdir>
If the environment variable GITHOOK_DEBUG
is present with any value, the dispatcher will print information about the hook being called. This can be useful to see exactly which hooks are called in which order for any git workflow involving hooks, e.g. a commit would output the following if you have not defined any hook scripts
(No script found for hook post-index-change)
(No script found for hook pre-commit)
(No script found for hook prepare-commit-msg)
(No script found for hook commit-msg)
(No script found for hook reference-transaction)
(No script found for hook reference-transaction) <- As many reference transactions as it needs.
(No script found for hook post-commit)
-
Create a directory to hold hooks, e.g.
C:\Users\myname\.githooks
-
Copy this utility into it (see setup further down)
-
Init a git repo and set up
hooksPath
cd C:\Users\myname\repos git init test-repo cd test-repo git config core.hooksPath C:/Users/myname/.githooks
You can set it globally for all current and future repos with
git config --global core.hooksPath C:/Users/myname/.githooks
-
Create a sub-directory in your hooks directory with the same name as the repo
New-Item -Type Directory -Path C:\Users\myname\.githooks -Name test-repo
-
Create a pre-commit hook for your repo in the new directory and put your logic in the script.
New-Item -Type File -Path C:\Users\myname\.githooks\test-repo -Name pre-commit.ps1
-
Try committing something to your new repo
For all hooks you want to support, you need to make a copy of hooks.exe
in the hooks directory you created.
If you have admin permission on your machine, you can use symlinks which take up no extra disk space and facilitate future upgrades:
- Run a PowerShell command prompt As Administrator
- CD to your hooks directory
- For each hook, make a symlink, e.g
New-Item -Type SymbolicLink -Name pre-commit.exe -Target hook.exe New-Item -Type SymbolicLink -Name pre-merge-commit.exe -Target hook.exe
If you do not have admin permission, then make copies:
-
Run a command prompt
-
CD to your hooks directory
-
For each hook, make a copy, e.g
copy hook.exe pre-commit.exe copy hook.exe pre-merge-commit.exe
Remember that if you update
hook.exe
from a newer release, then you must redo all the copies! -
You can delete
hook.exe
as it won't be invoked directly.
Some hooks need to receive one or more lines of data from git via standard input, e.g. reference-transaction and pre-push
. In PowerShell you can capture stdin
like this
# reference-transaction hook needs to read from stdin
param
(
[string]$State
)
Write-Host "refrence-transaction started - state $State"
$inputStream = [System.Console]::In
while ($line = $inputStream.ReadLine())
{
Write-Host "reference-transaction received stdin:" $line
}
The above if triggered by reference-transaction
will output something like this
refrence-transaction started - state prepared
reference-transaction received stdin: 37df81edea7f798982b66f4eadac531d3e730c88 baa414931de202d1abc809be2757660cb7542a5b HEAD
reference-transaction received stdin: 37df81edea7f798982b66f4eadac531d3e730c88 baa414931de202d1abc809be2757660cb7542a5b refs/heads/master
refrence-transaction started - state committed
reference-transaction received stdin: 37df81edea7f798982b66f4eadac531d3e730c88 baa414931de202d1abc809be2757660cb7542a5b HEAD
reference-transaction received stdin: 37df81edea7f798982b66f4eadac531d3e730c88 baa414931de202d1abc809be2757660cb7542a5b refs/heads/master