60

Is it possible to run PowerShell scripts as git hooks?

I am running git in a PowerShell prompt, which shouldn't make any difference, but I can't seem to get them to work, as the hooks are named without extensions, and PowerShell needs (AFAIK) the .ps1 extension. I am not sure if that is the issue, or something else.

6
  • Isn't it possible to make the script invoke the powershell script (or any other script for that matter, regardless of their extension)? Commented Apr 12, 2011 at 1:32
  • 1
    Can you give a bit more information about git hooks. Commented Apr 12, 2011 at 4:45
  • @JPBlanc: The githooks manpage. I have no idea if there is different documentation provided for the Windows version(s). Commented Apr 12, 2011 at 6:09
  • holygeek - do you have an example of firing off a PowerShell script from a bash script? I can't find any examples, and I'm not sure how to go about it. Commented Apr 13, 2011 at 4:41
  • Erick: You should be able to call it via powershell -file someScript.ps1 args Commented Oct 4, 2011 at 10:39

11 Answers 11

52

Since powershell 7.2 the script must have .ps1 extension on Windows. So this answer will not work.


You can embed PowerShell script directly inside the hook file. Here is an example of a pre-commit hook I've used:

#!/usr/bin/env pwsh

# Verify user's Git config has appropriate email address
if ($env:GIT_AUTHOR_EMAIL -notmatch '@(non\.)?acme\.com$') {
    Write-Warning "Your Git email address '$env:GIT_AUTHOR_EMAIL' is not configured correctly."
    Write-Warning "It should end with '@acme.com' or '@non.acme.com'."
    Write-Warning "Use the command: 'git config --global user.email <[email protected]>' to set it correctly."
    exit 1
}

exit 0

This example requires PowerShell Core but as a result it will run cross-platform (assuming this file has been chmod +x on Linux/macOS).

Sign up to request clarification or add additional context in comments.

8 Comments

Starting in October 2018, this should be the Accepted Answer. Thanks Keith.
Great answer! This #!/usr/bin/env pwsh "trick" works also for git rebase --exec and git bisect run. Unfortunately I noticed that pwsh doesn't exit with error code when script fails so be sure to change your script to use exit keyword when somethings fails.
This solution doesn't seem to work for me - /usr/bin/env: 'pwsh': No such file or directory, and I have powershell core installed
Unfortunaly, since powershell 7.2, the script must have .ps1 extension on Windows (github.com/PowerShell/PowerShell/releases/tag/v7.2.0-preview.9). A wrapper script like some answsers below will be needed.
Yeah, that was disappointing. :-(
|
38

Rename pre-commit.sample to pre-commit in hooks folder. Then make pre-commit.ps1 powershell script file in same folder.

#!/bin/sh
c:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy RemoteSigned -File '.git\hooks\pre-commit.ps1'

3 Comments

I think the syntax in this command line is wrong after -Command it's expecting an inline powershell command but you are specifying a file as well. It will throw an error about -File not being recognised as the name of a cmdlet, function or script file.
See my answer below if you get errors like "#! is not recognized..." or "The term -File is not recognized...".
to make it work across platforms, you could enclose above in an if statement if [[ -f path_to_powershell.exe ]]; then etc etc ; fi... And yes above works, for anyone in doubt. Thanks, this was useful Kim.
11

For the sake of completeness:

If you only have Windows PowerShell and not PowerShell Core installed then Keith Hill's neat answer doesn't work. The various answers that use a bash script to run PowerShell, passing in the path to the PowerShell script to run, are straight-forward and the way I chose to go in the end. However, I discovered there is another way:

Create two files for the git hook, say pre-commit and pre-commit.ps1. The pre-commit.ps1 file is the file that PowerShell will run. The other pre-commit file (without a file extension) is empty apart from a PowerShell interpreter directive on the first line:

#!/usr/bin/env powershell

Git will run the pre-commit file, parse the PowerShell interpreter directive and run up PowerShell, passing in the path to the pre-commit file. PowerShell will assume the file passed in should have a ".ps1" extension. It will search for pre-commit.ps1 and, since you created a file with that name and extension, PowerShell will find it and run it.

This approach is nice and simple but, in the end, I decided against it because it seemed a little "magical" and might have maintainers scratching their heads about how it works.

5 Comments

Powershell Core is the way ahead. MSFT has said all new stuff will be there so I think you're safe move to it. The only downside I can see as of this writing is that Windows 10 didn't make it a choice in the Windows+x shortcut ... yet.
This is brilliant. It was the only solution that worked for me right away.
@NoRefundsNoReturns: The problem with using PowerShell Core is portability. If would be okay if I were just writing scripts to run on my own machine. However, I write scripts that other developers can use, or that can be used on servers. So I have to stick to PowerShell 5.1 until PowerShell Core is deployed by default.
I also found this phenomenon. It feels really wonderful!
This what worked for me, too. However, before it would work I had to open git bash AS AN ADMINISTRATOR and then run powershell Set-RemoteExecutionPolicy RemoteSigned
9

Kim Ki Won's answer above didn't work for me, but it has upvotes so I'll assume it works for some people.

What worked for me was dropping the bin/sh and instead of executing using -File, executing the command directly:

c:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy RemoteSigned -Command .\.git\hooks\pre-commit.ps1

1 Comment

I was able to invoke a powershell from my precommit hook like this. Thanks for the comment. I don't know if it's the correct area to ask this question but is there a way i can get the return value of the powershell here and check it
9

Here's a starting PWSH script that I've been using for my PowerShell Git Hooks since reading Keith Hill's answer. Very nice.

#!/usr/bin/env pwsh

Process {
    Write-Information -MessageData "I Ran" -InformationAction Continue
}
Begin {
    Write-Information -MessageData "Beginning" -InformationAction Continue
}
End {
    Write-Information -MessageData "Ending" -InformationAction Continue

    Exit 0
}

I should also mention I share a single copy of hooks across all my repos. My repos all live in R:\Git and I created R:\Git\Hooks and used https://git-scm.com/docs/githooks to git config core.hooksPath=R:\Git\Hooks globally. Life is good.

Comments

8

From what I gather the only option due to Git's design here would be a bash script calling PowerShell. Unfortunate, but then again, Git didn't place any thought on non-Linux compatibility.

5 Comments

This seems to be the answer. Is it a pity - we're not all bash lovers, and bash on windows will always be second place. Thanks.
If git supported scripting for arbitrary platforms, how different would the config files for those hooks really look from the bash script bootstraps?
As of October 2018 you can directly use Powershell Core in the pre-commit hook as detailed by Keith Hill. Life is good. Please consider changing the accepted answer.
If you're looking for a different, possible better answer that lets you use Powershell Core directly, see my response to this question. The world has changed since this question was asked / answered and now it's almost too easy to do this.
So how do you do this? What should the bash script be named? What should it contain? What has to be installed and configured for it to run and work? In what context would this all actually work while using git (would you have to use git from bash?)?
6

I have been looking for this myself, and i found the following:

Git Powershell pre-commit hook (Source)

## Editor's note: Link is dead as of 2014-5-2.  If you have a copy, please add it.

PHP Syntax check for git pre-commit in PowerShell (Soure)

##############################################################################
#
# PHP Syntax Check for Git pre-commit hook for Windows PowerShell
#
# Author: Vojtech Kusy <[email protected]>
#
###############################################################################

### INSTRUCTIONS ###

# Place the code to file "pre-commit" (no extension) and add it to the one of 
# the following locations:
# 1) Repository hooks folder - C:\Path\To\Repository\.git\hooks
# 2) User profile template   - C:\Users\<USER>\.git\templates\hooks 
# 3) Global shared templates - C:\Program Files (x86)\Git\share\git-core\templates\hooks
# 
# The hooks from user profile or from shared templates are copied from there
# each time you create or clone new repository.

### SETTINGS ###

# Path to the php.exe
$php_exe = "C:\Program Files (x86)\Zend\ZendServer\bin\php.exe";
# Extensions of the PHP files 
$php_ext = "php|engine|theme|install|inc|module|test"
# Flag, if set to 1 git will unstage all files with errors, se to 0 to disable
$unstage_on_error = 0;

### FUNCTIONS ###

function php_syntax_check {
    param([string]$php_bin, [string]$extensions, [int]$reset) 

    $err_counter = 0;

    write-host "Pre-commit PHP syntax check:" -foregroundcolor "white"

    git diff-index --name-only --cached HEAD -- | foreach {             
        if ($_ -match ".*\.($extensions)$") {
            $file = $matches[0];
            $errors = & $php_bin -l $file           
            if ($errors -match "No syntax errors detected in $file") {
                write-host $file ": OK" -foregroundcolor "green"
            }
            else {              
                write-host $file ":" $errors -foregroundcolor "red"
                if ($reset) {
                    git reset -q HEAD $file
                    write-host "Unstaging" $file "..." -foregroundcolor "magenta"
                }
                $err_counter++
            }
        }
    }

    if ($err_counter -gt 0) {
       exit 1
    }    
}

### MAIN ###

php_syntax_check $php_exe $php_ext $unstage_on_error

The code is for a pre-commit hook, but you could modify it to do pretty much anything. Should help what you need to do!

1 Comment

The first one does not work for me. The relative path to the powershell script does not resolve correctly.
3

This is my git hook on Windows located in .\git\hooks.

post-update

#!/bin/sh
c:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy Bypass -Command '.\post-update.ps1'

Powershell script located in the project root folder (where you initially run git init). Powershell goes to another repository and calls pull, updating that repository.

post-update.ps1

Set-Location "E:\Websites\my_site_test"
$env:GIT_DIR = 'E:\Websites\my_site_test\.git';
$env:GIT_EXEC_PATH= 'C:\Program Files (x86)\Git/libexec/git-core';
git pull

Comments

1

Better solutions for pwsh in the new era

Many of the answers above are many years old, and there are now simpler and better options.

In the days of Windows PowerShell, it was not possible to use #! /usr/bin/env powershell, because it did not accept files without extensions.

The workaround was to create script files in the directory with the same name but with the extension .ps1, which was mentioned in someone else's answer. But this takes advantage of a possible undocumented internal implementation, and unexpected problems may occur.

However, in the pwsh era, running script files without extensions has been supported for cross-platform compatibility. Even on windows platforms, it is only necessary to add #! /usr/bin/env pwsh, you can write scripts directly in this file without any other additional actions.

1 Comment

Thanks for the info Andy. This was already explained in existing answers (see the one by Keith Hill, for example), so it may be worth rather upvoting the existing answer(s) than adding another one.
0

I didn't get Simon's answer to work, potentially because of other things in my path not being parsed properly by the windows git environment and/or using bonobo git server.

My objective was writing a pre-receive hook for a repository hosted in bonobo.

I ended up with the following shebang:

#!/c/Windows/System32/WindowsPowerShell/v1.0/powershell

Otherwise works identically:

  • Create pre-receive file with only shebang
  • Create pre-receive.ps1 in hooks directory. Powershell loads this instead.

In my case, for some cleanliness, i also used

mklink <path-to-repo>\hooks\pre-receive.ps1 c:\scripts\hooks\someLibraryScript.ps1

This allows me to keep my scripts in a central repository, of course.

EDIT: It's worth noting i did not manage to get Powershell to accept the stdin stream for the pre-receive hook. As a result, i'm still using a shell script to bootstrap powershell and pipe, rather than redirect, stdin to powershell.

In this case, i used the following shell script for my pre-receive hook:

#!/bin/sh
IFS=
echo `cat -` | powershell.exe -NoProfile -ExecutionPolicy Bypass -File "c:\scripts\hooks\pre-receive.ps1"

Powershell seems satisfied with that.

1 Comment

If you don't want to mess with links, you can also set the core.hooksPath config (eg. git config core.hooksPath) see docs
0

assume you have:

  • pwsh installed
  • your branch is feature/jira-1234-impl-something
  • you make a commit git commit -m "add user service"
  • you expect to have jira branch as prefix [jira-1234] add user service

Follow steps below to setup:

  1. create a ps1 script, this script will modify your commit message:
# create a file prepare-commit-msg.ps1 in the git hook directory
# not that you can name it whatever you want
ni .git/hooks/prepare-commit-msg.ps1
  1. use below script for the file prepare-commit-msg.ps1 above:
# Requires -Version 5.1 # Or higher for cross platform compatibility

# Get the branch name
$BRANCH_NAME = git rev-parse --abbrev-ref HEAD 2>$null

# with git commit, args[0] is the commit message file
# $args # print the args

# Check if the branch name is valid and not in a detached HEAD state.
if (-not [string]::IsNullOrEmpty($BRANCH_NAME) -and $BRANCH_NAME -ne "HEAD" -and $env:SKIP_PREPARE_COMMIT_MSG -ne 1) {

    # Define the prefix pattern
    $PREFIX_PATTERN = '[A-Z]{2,5}-[0-9]{1,6}'

    # Match the prefix pattern against the branch name
    if ($BRANCH_NAME -match $PREFIX_PATTERN) {

        # Extract the prefix
        $PREFIX = $Matches[0]

        # Count the occurrences of the prefix in the commit message
        $PREFIX_IN_COMMIT = $args.Count -gt 0 ? (Get-Content $args[0] | Select-String "${PREFIX}:" | Measure-Object).Count : 0

        # Check if the prefix exists and is not already in the commit message
        if (-not [string]::IsNullOrEmpty($PREFIX) -and $PREFIX_IN_COMMIT -eq 0) {

            # Modify the commit message file
            $CommitMessage = Get-Content $args[0] # get the commit message
            $CommitMessage = "[${PREFIX}]: " + $CommitMessage # add the prefix to the commit message
            $CommitMessage | Set-Content $args[0] # write the commit message to the file
        }
    }
}

feel free to adjust the pwsh script to your needs.

  1. finally, modify the actual file that git will trigger
# create a file EXACT name .git/hooks/prepare-commit-msg
ni .git/hooks/prepare-commit-msg

use following script:

#!/bin/sh
pwsh -ExecutionPolicy RemoteSigned -File "$(dirname "$0")/prepare-commit-msg.ps1" "$1" "$2" "$3"

what actually happen:

In your .git/hook/ folder, there are 2 files:

prepare-commit-msg
prepare-commit-msg.ps1

whatever your terminal/application you use, git always execute the file prepare-commit-msg

but depends on the shell (for example on Windows):

  • if you use git bash, it can execute commands available for "bash shell", which is direct script in prepare-commit-msg
  • if you use pwsh, of course bash script cannot be executed. That's why you have to "redirect" it to execute pwsh script.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.