78

如果用户的提交消息不遵循特定的准则,我想警告用户,然后让他们选择编辑提交消息、忽略警告或取消提交。问题是我似乎无法访问标准输入。

下面是我的 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 信息留空时的输出:

Scope information should not be omitted
Do you want to:
1) edit the commit message  3) cancel the commit
2) ignore this warning
#?

该消息是正确的,但它实际上并没有停止输入。我也尝试过使用更简单的“读取”命令,它也有同样的问题。似乎问题在于此时 git 控制了标准输入并提供了自己的输入。我该如何解决?

更新:看来这可能是这个问题的重复,不幸的是这似乎表明我不走运。

4

6 回答 6

167

调用exec < /dev/tty将标准输入分配给键盘。在提交后的 git 挂钩中为我工作:

#!/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
于 2012-04-04T16:49:05.757 回答
7

commit-msg钩子不在交互式环境中运行(如您所见)。

可靠地通知用户的唯一方法是将错误写入标准输出,将提交消息的副本放在BAD_MSG文件中并指示用户编辑文件并git commit --file=BAD_MSG


如果您对环境有一定的控制权,您可以有一个备用编辑器,它是一个检查建议消息的包装脚本,并且可以使用额外的注释消息重新启动编辑器。

基本上,您运行编辑器,根据您的规则检查保存的文件。如果失败,请将您的警告消息(带前导#)添加到文件中并重新启动编辑器。

您甚至可以允许他们#FORCE=true在消息中添加一行,这将禁止检查并继续。

于 2010-08-20T18:23:28.140 回答
3
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
于 2019-02-25T13:05:30.763 回答
2

如何在 Node.js 或 TypeScript 中实现

编辑:我做了一个npm 包


我看到人们在Eliot Sykes 回答中评论如何为其他语言做到这一点,但 JavaScript 解决方案有点长,所以我会单独回答。

我不确定是否O_NOCTTY需要,但它似乎不会影响任何事情。我真的不明白什么是控制终端。GNU 文档说明。我认为这意味着O_NOCTTY打开时,您将无法将 a 发送CTRL+C到进程(如果它还没有控制终端)。在这种情况下,我会保留它,这样您就不会控制生成的进程。我认为主节点进程应该已经有一个控制终端。

我改编了这个GitHub 问题的答案

我没有看到任何关于如何使用tty.ReadStream构造函数的文档,所以我做了一些试验和错误/挖掘Node.js 源代码

您必须使用它,Object.defineProperty因为Node.js 内部也使用它,并且没有定义 setter。另一种方法是做process.stdin.fd = fd,但我这样得到重复的输出。

无论如何,我想将它与Husky.js一起使用,它似乎到目前为止工作。当我有时间的时候,我可能应该把它变成一个 npm 包。

节点.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);

打字稿:

#!/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);
于 2019-07-27T02:18:18.870 回答
2

从命令行运行 git commit 时,这工作正常。在 windows 上(没有在 linux 上尝试过)如果你使用 gitk 或 git-gui 你将无法提示,因为你在 "exec < /dev/tty" 行上得到一个错误。

解决方案是在您的钩子中调用 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 !"
于 2017-04-26T11:58:20.583 回答
1

select停止输入,您还可以尝试重定向from stdin(请参阅:Read input in bash inside a while loop)。select/dev/fd/3

# 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
于 2013-05-03T11:27:29.713 回答