如何从 commit-msg 挂钩中提示用户?
我想警告用户,如果他们的提交消息不遵循一组特定的准则,然后为他们提供编辑提交消息、忽略警告或取消提交的选项。问题是我似乎无法访问 stdin
。
下面是我的 commit-msg 文件:
function verify_info {
if [ -z "$(grep '$2:.*[a-zA-Z]' $1)" ]
then
echo >&2 $2 information should not be omitted
local_editor=`git config --get core.editor`
if [ -z "${local_editor}" ]
then
local_editor=${EDITOR}
fi
echo "Do you want to"
select CHOICE in "edit the commit message" "ignore this warning" "cancel the commit"; do
case ${CHOICE} in
i*) echo "Warning ignored"
;;
e*) ${local_editor} $1
verify_info "$1" $2
;;
*) echo "CHOICE = ${CHOICE}"
exit 1
;;
esac
done
fi
}
verify_info "$1" "Scope"
if [ $# -ne 0 ];
then
exit $#
fi
verify_info "$1" "Affects"
if [ $# -ne 0 ];
then
exit $#
fi
exit 0
这是我将范围信息留空时的输出:
Scope information should not be omitted
Do you want to:
1) edit the commit message 3) cancel the commit
2) ignore this warning
#?
消息是正确的,但实际上并没有停止输入。我也尝试过使用更简单的 read
命令,它也有同样的问题。问题似乎在于此时 git 已经控制了 stdin 并提供了自己的输入。我该如何解决这个问题?
更新:这似乎可能与 重复不幸的是,这个问题似乎表明我运气不好。
I want to warn the user if their commit message doesn't follow a certain set of guidelines, and then give them the option to edit their commit message, ignore the warning, or cancel the commit. The problem is that I don't seem to have access to stdin
.
Below is my commit-msg file:
function verify_info {
if [ -z "$(grep '$2:.*[a-zA-Z]' $1)" ]
then
echo >&2 $2 information should not be omitted
local_editor=`git config --get core.editor`
if [ -z "${local_editor}" ]
then
local_editor=${EDITOR}
fi
echo "Do you want to"
select CHOICE in "edit the commit message" "ignore this warning" "cancel the commit"; do
case ${CHOICE} in
i*) echo "Warning ignored"
;;
e*) ${local_editor} $1
verify_info "$1" $2
;;
*) echo "CHOICE = ${CHOICE}"
exit 1
;;
esac
done
fi
}
verify_info "$1" "Scope"
if [ $# -ne 0 ];
then
exit $#
fi
verify_info "$1" "Affects"
if [ $# -ne 0 ];
then
exit $#
fi
exit 0
Here is the output when I leave the Scope information blank:
Scope information should not be omitted
Do you want to:
1) edit the commit message 3) cancel the commit
2) ignore this warning
#?
The message is correct, but it doesn't actually stop for input. I've also tried using the simpler read
command, and it has the same problem. It seems that the problem is that at this point git has control of stdin
and is providing its own input. How do I fix this?
Update: It seems this might be a duplicate of this question which unfortunately seems to suggest I'm out of luck.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
调用 exec < /dev/tty 将标准输入分配给键盘。在提交后 git hook 中为我工作:
Calling
exec < /dev/tty
assigns standard input to the keyboard. Works for me in a post-commit git hook:commit-msg 挂钩不在交互式环境中运行(正如您所注意到的)。
可靠地通知用户的唯一方法是将错误写入标准输出,将提交消息的副本放入 BAD_MSG 文件中,并指示用户编辑该文件并 git 提交 - -file=BAD_MSG
如果您对环境有一定的控制权,您可以有一个备用编辑器,它是一个包装脚本,用于检查建议的消息,并且可以使用额外的注释消息重新启动编辑器。
基本上,您运行编辑器,根据您的规则检查保存的文件。如果失败,请将警告消息(以
#
开头)添加到文件中并重新启动编辑器。您甚至可以允许他们在消息中放入
#FORCE=true
行,这将抑制检查并继续。The
commit-msg
hook is not run in an interactive environment (as you have noticed).The only way to reliable notify the user would be to write an error to stdout, place a copy of the commit message in a
BAD_MSG
file and instruct the user to edit the file andgit commit --file=BAD_MSG
If you have some control over the environment you could have an alternate editor which is a wrapper script that checks the proposed message, and can restart the editor with an extra commented message.
Basically, you run the editor, check the file saved against your rules. and if it fails, prepend your warning message (with leading
#
) to the file and restart the editor.You could even allow them to put in a
#FORCE=true
line in the message which would suppress the check and continue.从命令行运行 git commit 时它工作正常,但在 Windows 上,如果您使用 gitk 或 git-gui,您将无法提示,因为您在 exec
exec < 上收到错误。 /dev/tty
行。解决方案是在钩子中调用 git-bash.exe :
.git/hooks/post-commit :
.git/hooks/post-commit.sh :
It works fine when running git commit from command line, but on Windows, if you use gitk or git-gui you won't be able to prompt because you get an error on the
exec < /dev/tty
line.The solution is to call
git-bash.exe
in your hook:.git/hooks/post-commit
:.git/hooks/post-commit.sh
:如何在 Node.js 或 TypeScript 中执行此操作
编辑: 我制作了一个 npm 包
我看到人们在 Eliot Sykes 中评论如何为其他语言执行此操作回答,但是JavaScript解决方案有点长,所以我会单独回答。
我不确定是否需要
O_NOCTTY
,但是它似乎没有影响任何事情。我不太明白什么是控制终端。 GNU 文档说明。我认为这意味着,在启用O_NOCTTY
的情况下,您将无法向进程发送CTRL+C
(如果它还没有控制)终端)。在这种情况下,我将保留它,这样您就无法控制生成的进程。我认为主节点进程应该已经有一个控制终端。我改编了这个GitHub问题的答案,
我没有看到任何有关如何使用 tty.ReadStream 构造函数的文档,因此我做了一些尝试和错误/深入研究了 Node.js 源代码。
您必须使用
Object.defineProperty
因为 Node.js 内部也使用它,并且没有定义 setter。另一种方法是执行 process.stdin.fd = fd ,但这样我会得到重复的输出。无论如何,我想将其与 Husky.js 一起使用,到目前为止它似乎有效。当我有时间的时候,我可能应该把它变成一个 npm 包。
Node.js
打字稿:
How to do it in Node.js or TypeScript
EDIT: I made an npm package
I see people commenting on how to do it for other languages in Eliot Sykes answer, but the JavaScript solution is a bit long so I'll make a separate answer.
I'm not sure if
O_NOCTTY
is required, but it doesn't seem to affect anything. I don't really understand what a controlling terminal is. GNU docs description. I think what it means is that withO_NOCTTY
on, you wouldn't be able to send aCTRL+C
to the process (if it doesn't already have a controlling terminal). In that case, I'll leave it on so you don't control spawned processes. The main node process should already have a controlling terminal, I think.I adapted the answer from this GitHub issue
I don't see any docs on how to use the
tty.ReadStream
constructor so I did a bit of trial and error / digging through Node.js source code.You have to use
Object.defineProperty
because Node.js internals uses it too, and doesn't define a setter. An alternative is to doprocess.stdin.fd = fd
, but I get duplicate output that way.Anyway, I wanted to use this with Husky.js and it seems to work so far. I should probably turn this into an npm package when I get the time.
Node.js
TypeScript:
要使
select
停止输入,您还可以尝试从/dev/fd/3select
的stdin
code> (参见:在 while 循环内读取 bash 中的输入)。To make
select
stop for input, you may also try to redirect thestdin
ofselect
from/dev/fd/3
(See: Read input in bash inside a while loop).