目录:网上冲浪指南

我的第一个 pre-commit Hook

2019/05/14
内容导航

最近遇到了 EF Core + SQL Server 2008 的一个 Bug,让我不得不用 Dapper 重新的实现一些查询的逻辑,为此,我只能将 SQL 小作文写在 .cs 文件中。不过这样就失去了 SQL 代码的语法高亮以及代码检查,所以我只好又把这部分的 SQL 代码写在的单独的一个文件中。不过这样还是不够的,这两处代码并不能实时同步,很可能之后改了其中一个地方就忘了去修改另一处代码。为了避免这种情况的发生,我决定用 pre-commit hook 来提醒自己。

bash OR fake.build ?

.git/hooks 文件夹中有各种 hook 的样例,这些样例都是使用 bash 编写的,然而我大部分都看不太懂。对我来说,Bash 就是编程语言中的温州话 不过还好这个项目本身用到了 Fake.Build,所以用 Fake 来写这个钩子应该会更适合我一些。

首先我需要列出所有处于暂存区的文件,Fake 提供了 Fake.Tools.Git 模块,里面封装了常用的 Git 操作。虽然文档中说可以使用 FileStatus.getAllFiles 来获取当前所有已改动的文件,但是我实际测试后发现并不能达到这种效果。所以最终我还是在 Fake 里面执行了 git 命令来达到我的目的:

git diff --name-only --cached

接着,如果这次改动涉及到了我写在 .cs 文件中的魔法字符串,那么需要向正在 commit 的开发人员提示一下。直接输出一个 Warning 很可能并没有什么卵用,因为大多数开发者并不会去理睬警告。所以我需要让提交人员进行一些操作,确认了这里面的提示才能继续提交,这里我用了 Fake.Core.UserInput 模块。但是当我们通过 git hook 的方式运行的时候会发现 Fake 脚本获取不到 tty,这会导致用户的输入无法被脚本捕获,我猜测大概率是因为 git 重定向了 hook 的 stdin 。

解决这个问题最简单的方式就是在 pre-commit 脚本中重新获取 tty,这里就用到了爆栈网网友提供的一条命令:

exec < /dev/tty

在我看来 exec 又是一条魔法命令,当调用 exec 且不传递任何参数的时候,所有对 exec 的重定向操作都会作用到当前 shell 上,也就是说上面的这条命令帮助我们重新夺回了 tty 作为当前 shell 脚本的 stdin 。

虽然用 F# 也可以实现替换当前 stdin 的功能,但还是直接使用 shell 脚本要方便一些。