我想警告用户他们的提交消息是否遵循某组指南,然后给他们选项来编辑他们的提交消息,忽略警告或取消提交。问题是我似乎无法访问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
#?
消息是正确的,但它实际上并没有停止输入。我也尝试使用更简单的“读取”命令,它也有同样的问题。似乎问题在于,此时git控制了stdin并提供了自己的输入。我该如何解决这个问题?
更新:似乎这可能是this question的副本,遗憾的是这似乎表明我运气不好。
答案 0 :(得分:135)
调用exec < /dev/tty
为键盘分配标准输入。在post-commit git hook中为我工作:
#!/bin/sh
echo "[post-commit hook] Commit done!"
# Allows us to read user input below, assigns stdin to keyboard
exec < /dev/tty
while true; do
read -p "[post-commit hook] Check for outdated gems? (Y/n) " yn
if [ "$yn" = "" ]; then
yn='Y'
fi
case $yn in
[Yy] ) bundle outdated --pre; break;;
[Nn] ) exit;;
* ) echo "Please answer y or n for yes or no.";;
esac
done
答案 1 :(得分:5)
commit-msg
挂钩不在交互式环境中运行(正如您所注意到的那样)。
可靠地通知用户的唯一方法是向stdout写入错误,将提交消息的副本放在BAD_MSG
文件中并指示用户编辑文件并git commit --file=BAD_MSG
< / p>
如果您对环境有一定的控制权,您可以使用备用编辑器,它是一个检查建议消息的包装脚本,并且可以使用额外的注释消息重新启动编辑器。
基本上,您运行编辑器,检查根据您的规则保存的文件。如果失败,请将警告消息(带有前导#
)添加到文件中,然后重新启动编辑器。
您甚至可以允许他们在邮件中放入#FORCE=true
行,这会阻止检查并继续。
答案 2 :(得分:1)
要使select
停止输入,您还可以尝试从stdin
重定向select
/dev/fd/3
(请参阅:Read input in bash inside a while loop)。
# sample code using a while loop to simulate git consuming stdin
{
echo 'fd 0' | while read -r stdin; do
echo "stdin: $stdin"
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*) echo ${local_editor} $1
echo verify_info "$1" $2
;;
*) echo "CHOICE = ${CHOICE}"
exit 1
;;
esac
done 0<&3 3<&-
done
} 3<&- 3<&0
答案 3 :(得分:1)
从命令行运行git commit时这很好用。在Windows上(没有尝试使用linux),如果你使用gitk或者git-gui,你就无法提示,因为你在&#34; exec&lt;的/ dev / TTY&#34;线。
sollution是在你的钩子中调用git-bash.exe:
.git / hooks / post-commit包含:
#!/bin/sh
exec /c/Program\ Files/Git/git-bash.exe /path/to/my_repo/.git/hooks/post-checkout.sh
.git / hooks / post-commit.sh文件包含:
# --------------------------------------------------------
# usage: f_askContinue "my question ?"
function f_askContinue {
local myQuestion=$1
while true; do
read -p "${myQuestion} " -n 1 -r answer
case $answer in
[Yy]* ) printf "\nOK\n"; break;;
[Nn]* ) printf "\nAbandon\n";
exit;;
* ) printf "\nAnswer with Yes or No.\n";;
esac
done
}
f_askContinue "Do you want to continue ?"
echo "This command is executed after the prompt !"
答案 4 :(得分:0)
read -p "Question? [y|n] " -n 1 -r < /dev/tty
echo
if echo $REPLY | grep -E '^[Yy]$' > /dev/null; then
#do if Yes
else
#do if No
fi
答案 5 :(得分:0)
如何在Node.js或TypeScript中实现
编辑:我做了一个npm package
我看到人们对Eliot Sykes answer中的其他语言进行评论,但是JavaScript解决方案有点长,所以我将给出一个单独的答案。
我不确定是否需要O_NOCTTY
,但似乎没有任何影响。我真的不明白什么是控制终端。 GNU docs description。我认为这意味着打开O_NOCTTY
后,您将无法向该进程发送CTRL+C
(如果它还没有控制终端)。在这种情况下,我将其保持打开状态,以便您不控制生成的进程。我认为主节点进程应该已经具有控制终端。
我改编了这个GitHub issue
的答案我没有看到有关如何使用tty.ReadStream
构造函数的任何文档,所以我做了一些反复试验/深入研究Node.js source code。
您必须使用Object.defineProperty
,因为Node.js internals也使用了它,并且没有定义设置器。一种替代方法是执行process.stdin.fd = fd
,但这样可以得到重复的输出。
无论如何,我想将此与Husky.js一起使用,到目前为止,它似乎仍然有效。我有空的时候应该把它变成一个npm包。
Node.js
#!/usr/bin/env node
const fs = require('fs');
const tty = require('tty');
if (!process.stdin.isTTY) {
const { O_RDONLY, O_NOCTTY } = fs.constants;
let fd;
try {
fd = fs.openSync('/dev/tty', O_RDONLY + O_NOCTTY);
} catch (error) {
console.error('Please push your code in a terminal.');
process.exit(1);
}
const stdin = new tty.ReadStream(fd);
Object.defineProperty(process, 'stdin', {
configurable: true,
enumerable: true,
get: () => stdin,
});
}
...Do your stuff...
process.stdin.destroy();
process.exit(0);
TypeScript:
#!/usr/bin/env ts-node
import fs from 'fs';
import tty from 'tty';
if (!process.stdin.isTTY) {
const { O_RDONLY, O_NOCTTY } = fs.constants;
let fd;
try {
fd = fs.openSync('/dev/tty', O_RDONLY + O_NOCTTY);
} catch (error) {
console.error('Please push your code in a terminal.');
process.exit(1);
}
// @ts-ignore: `ReadStream` in @types/node incorrectly expects an object.
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/37174
const stdin = new tty.ReadStream(fd);
Object.defineProperty(process, 'stdin', {
configurable: true,
enumerable: true,
get: () => stdin,
});
}
...Do your stuff...
process.stdin.destroy();
process.exit(0);