使用PowerShell脚本作为git钩子的运行方式

50

能否将PowerShell脚本用作git挂钩运行?

我在PowerShell提示符中运行git,这不应该有任何区别,但是我似乎无法使它们工作,因为挂钩的名称没有扩展名,而PowerShell需要(据我所知).ps1扩展名。 我不确定是否是这个问题,还是其他什么问题。


脚本无法调用PowerShell脚本(或任何其他脚本,无论其扩展名如何)吗? - holygeek
1
你能提供一些关于git hooks的更多信息吗? - JPBlanc
@JPBlanc: The githooks manpage. 我不知道Windows版本是否提供了不同的文档。 - intuited
holygeek - 你有没有一个从bash脚本中启动PowerShell脚本的例子?我找不到任何例子,也不确定该如何处理。 - Erick T
Erick: 你应该能够通过 powershell -file someScript.ps1 args 调用它。 - Joey
很好的问题,任何在2019年7月之后阅读此处的人都应该跳过已接受的答案,并查看更近期流行的回答,这使得问题变得轻而易举。SO应该有一种方法来删除已接受的答案,或者至少让社区答案出现在那个绿色勾号之前。时间可以改变一个伟大问题的答案,SO应该允许这样做。 - No Refunds No Returns
10个回答

46

自从powershell 7.2版本以后,Windows系统的脚本必须使用 .ps1 扩展名。因此,这个答案将不起作用。


您可以直接在挂钩文件中嵌入PowerShell脚本。这是我使用过的一个挂钩的示例:

#!/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 <name@acme.com>' to set it correctly."
    exit 1
}

exit 0

这个例子需要PowerShell Core,但结果将在跨平台上运行(假设该文件已经在Linux/macOS上进行了chmod +x)。


4
从2018年10月开始,这应该是被接受的答案。谢谢Keith。 - No Refunds No Returns
3
好的回答!这个“技巧”#!/usr/bin/env pwsh同样适用于git rebase --execgit bisect run。不幸的是,我注意到当脚本失败时,pwsh不会以错误代码退出,因此请确保在某些情况下更改您的脚本以使用exit关键字。 - Mariusz Pawelski
3
这个解决方案对我似乎不起作用 - /usr/bin/env: 'pwsh': 没有那个文件或目录,而且我已经安装了 PowerShell Core。 - Bort
2
非常感谢您!如果出于速度考虑想要在没有配置文件的情况下运行PowerShell(我肯定会这样做),则可以在顶部行中执行以下操作: #!/usr/bin/env -S pwsh -NoProfile - CubanX
2
是的,那真是令人失望。 :-( - Keith Hill
显示剩余3条评论

35

将hooks文件夹中的pre-commit.sample重命名为pre-commit。 然后在相同的文件夹中创建一个名为pre-commit.ps1的powershell脚本文件。

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

2
我认为这个命令行中的语法是错误的,在 -Command 之后它期望一个内联 PowerShell 命令,但你同时指定了一个文件。它会抛出一个关于 -File 不被识别为 cmdlet、函数或脚本文件名的错误。 - leinad13
如果您遇到“#!未被识别…”或“-File未被识别…”等错误,请参阅我的答案 - Taran
为了使其跨平台工作,您可以将上述内容放在if语句中,如 if [[ -f path_to_powershell.exe ]]; then 等等 ; fi... 是的,以上内容是有效的,对于任何怀疑的人都很有用。谢谢,Kim。 - Jepper

9
这是一个起始 PWSH 脚本,我一直在使用它来管理我的 PowerShell Git 钩子,自从阅读了 Keith Hill 的回答后,我觉得非常不错。
#!/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
}

我还应该提到,我在所有存储库中共享一个钩子副本。 我的存储库都位于R:\Git中,我创建了R:\Git \Hooks并全局使用 https://git-scm.com/docs/githooks git config core.hooksPath = R:\ Git \ Hooks 配置为全局。 生活不错。


9
< p > Kim Ki Won 上面的答案对我没有用,但因为它有赞,所以我会认为它对一些人有效。

对我有用的是删除bin/sh,而不是使用-File执行,直接执行命令:

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

我通过以下方式从我的Precommit挂钩中调用了PowerShell。感谢评论。我不知道这是否是正确的地方来问这个问题,但我能否在这里获取PowerShell的返回值并检查它? - Arjun Menon

8
据我所了解,由于Git的设计,唯一的选择是编写一个调用PowerShell的bash脚本。这很不幸,但话说回来,Git并没有考虑非Linux兼容性的问题。

1
这似乎是答案。很遗憾 - 我们并不都喜欢Bash,而且在Windows上运行的Bash始终是次要的。谢谢。 - Erick T
如果git支持任意平台的脚本编写,那么这些钩子的配置文件与bash脚本引导程序的区别会有多大呢? - brianary
3
截至2018年10月,您可以直接使用Keith Hill详细介绍的Powershell Core作为pre-commit hook。生活很美好。请考虑更改已接受的答案。 - No Refunds No Returns
3
如果您正在寻找一种不同的、可能更好的答案,让您直接使用Powershell Core,请参阅我对这个问题的回答。自从提出/回答这个问题以来,世界已经发生了变化,现在做到这一点几乎是“太”容易了。 - No Refunds No Returns
那么你要怎么做呢?Bash脚本应该命名为什么?它应该包含什么?为了让它运行和工作,必须安装和配置什么?在使用git的情况下,这一切实际上是如何工作的(你是否必须从bash中使用git)? - undefined

8
为了完整起见:
如果您只安装了Windows PowerShell而没有安装PowerShell Core,则Keith Hill的好答案不起作用。 使用bash脚本运行PowerShell并传递要运行的PowerShell脚本路径的各种答案很简单,这也是我最终选择的方式。 但是,我发现还有另一种方法:
为git钩子创建两个文件,比如pre-commit和pre-commit.ps1。 pre-commit.ps1文件是PowerShell将要运行的文件。 另一个没有文件扩展名的pre-commit文件除第一行的PowerShell解释器指令外为空。
#!/usr/bin/env powershell

Git会执行pre-commit文件,解析PowerShell解释器指令并启动PowerShell,将pre-commit文件的路径传递给它。 PowerShell会认为传递的文件应该具有“.ps1”扩展名。 它将搜索pre-commit.ps1文件,并且由于您创建了该名称和扩展名的文件,因此PowerShell将找到它并运行它。

这种方法简单明了,但最终我决定放弃它,因为它似乎有点“神奇”,可能会让维护者对其工作方式感到困惑。


PowerShell Core是未来的发展方向。微软已经表示所有新的东西都会在这里,所以我认为你可以放心转移到它上面。截至目前,我唯一能看到的缺点是Windows 10没有将其作为“Windows+x”快捷方式中的选择...但还没有。 - No Refunds No Returns
这太棒了。这是唯一立即为我解决问题的解决方案。 - Ryan P
1
@NoRefundsNoReturns:使用PowerShell Core的问题在于可移植性。如果我只是编写脚本在自己的机器上运行,那还好。但是,我编写的脚本可能会被其他开发人员使用,或者在服务器上使用。因此,在PowerShell Core默认部署之前,我必须坚持使用PowerShell 5.1。 - Simon Elms
我也发现了这个现象。感觉真的很棒! - Andy
1
这对我也起作用了。但是,在此之前,我必须以管理员身份打开 git bash,然后运行 powershell Set-RemoteExecutionPolicy RemoteSigned。 - Seth Spearman

5

我一直在寻找这个内容,最后找到了以下信息:

Git Powershell 预提交挂钩 (源代码)

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

PowerShell 中用于 git pre-commit 的 PHP 语法检查 (源代码)

##############################################################################
#
# PHP Syntax Check for Git pre-commit hook for Windows PowerShell
#
# Author: Vojtech Kusy <wojtha@gmail.com>
#
###############################################################################

### 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

这段代码是为一个预提交钩子而写的,但你可以修改它来实现几乎任何功能。应该能帮助你需要做的事情!

1
第一个对我不起作用。PowerShell脚本的相对路径无法正确解析。 - Klas Mellbourn

3
这是我在Windows上的git钩子,位于.\git\hooks中。 post-update
#!/bin/sh
c:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy Bypass -Command '.\post-update.ps1'

在项目根目录中(即您最初运行git init的位置)的PowerShell脚本。 PowerShell转到另一个存储库并调用pull,更新该存储库。

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

1

新时代下 pwsh 的更好解决方案

以上许多答案都是多年前的,现在有更简单、更好的选择。

在 Windows PowerShell 时代,无法使用 #! /usr/bin/env powershell,因为它不接受没有扩展名的文件。

解决方法是在同名目录下创建扩展名为 .ps1 的脚本文件,这也被提到在其他人的答案中。但这利用了可能未记录的内部实现,可能会出现意外问题。

然而,在 pwsh 时代,运行没有扩展名的脚本文件已经得到支持以实现跨平台兼容性。即使在 Windows 平台上,只需添加 #! /usr/bin/env pwsh,就可以直接在此文件中编写脚本,无需任何其他额外操作。


谢谢信息,Andy。这已经在现有的答案中解释过了(例如,参见Keith Hill的答案),因此值得投票支持现有的答案,而不是再添加一个新的答案。 - jamiebarrow

0

我没有成功实现Simon的答案,可能是因为我的路径中的其他内容没有被Windows Git环境正确解析,或者是因为使用了Bonobo Git服务器。

我的目标是为在Bonobo托管的存储库编写一个pre-receive hook。

最终我得到了以下的shebang:

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

否则的话,功能是相同的:

  • 创建只有 shebang 的 pre-receive 文件
  • 在 hooks 目录中创建 pre-receive.ps1。Powershell 会加载这个文件。

在我的情况下,为了保持整洁,我也使用了以下方法:

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

当然,这样我就可以把我的脚本保存在中央仓库中。

编辑:值得注意的是,我没有成功让 Powershell 接受 pre-receive 钩子的 stdin 流。因此,我仍然使用 shell 脚本来引导 powershell 和管道,而不是重定向stdin到powershell。

在这种情况下,我使用以下 shell 脚本作为我的 pre-receive 钩子:

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

Powershell 对此似乎很满意。


如果你不想在链接中进行修改,你也可以设置 core.hooksPath 配置(例如 git config core.hooksPath)。参见文档 - ehiller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接