Git Hooks实战:提交前检查修改文件中是否包含调试代码

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Git Hooks实战:提交前检查修改文件中是否包含调试代码

说在前面

不知道大家有没有遇到这样一种情况,平时在写代码调试时有时候会使用到debugger,可能大部分时间在提交代码前会记得把debugger先删除,但可能也会存在将debugger提交上去的情况,那我们该怎么防止出现这种情况呢?

webpack 配置修改

开发过程中,经常需要使用console.logconsole.infoalert等操作来输出内容,测试代码,而在生产环境之中,这些打印的东西最好是不要显示、特别是用户名、密码相关。

一个个去删除、注释显然是很麻烦的一件事,所以我们可以通过修改配置变量,实现在开发环境打印,而生产环境不打印。

修改方法如下:

在项目的build/webpack.prod.conf.js文件之中,找到UglifyJsPlugin配置,在compress中添加如下代码即可。

new UglifyJsPlugin({
  uglifyOptions: { 
      compress: { 
          warnings: false, 
          // 打包的时候移除console、debugger 
          drop_debugger: true, // 移除debugger 
          drop_console: true, // 移除console 
          pure_funcs: ['console.log','console.info']
      }
  },
  sourceMap: config.build.productionSourceMap,
  parallel: true
}),

git hook监测删除

除了直接在webpack中配置之外,我们还可以在git提交前进行监测,将修改文件中的相关语句清除,这里以清除修改文件中的debugger语句为例子:

git hook简单了解

Git钩子是一组脚本,这些脚本对应着Git仓库中的特定事件,每一次事件发生时,钩子会被触发。这允许你可以定制化Git的内部行为,在开发周期中的关键点上触发执行定制化的脚本。

钩子脚本文件通常放置于项目目录的.git/hooks文件夹下。Git会在初始化项目时自动在这个文件夹下放置一些样例脚本。如果你查看.git/hooks文件夹下,会找到如下的文件:

我们可以简单了解一下这些钩子的触发时机:

  • applypatch-msg

这个钩子由git-am调用。它接受一个参数,即保存提议的提交日志消息的文件的名称。以非零状态退出导致git am在应用补丁之前中止。

允许钩子就地编辑消息文件,并可用于将消息规范化为某种项目标准格式。它还可以用于在检查消息文件后拒绝提交。

默认的applypatch-msg钩子在启用时,如果启用了commit-msg钩子,则运行commit-msg钩子。

  • commit-msg

这个钩子由git-commitgit-merge调用,可以通过——no-verify选项绕过。它接受一个参数,即保存提议的提交日志消息的文件的名称。以非零状态退出将导致命令中止。

允许钩子就地编辑消息文件,并可用于将消息规范化为某种项目标准格式。它还可以用于在检查消息文件后拒绝提交。

默认的commit-msg钩子在启用时,会检测到重复的挂件签到,如果发现了,就会中止提交。

  • fsmonitor-watchman

这个钩子在配置选项核心时被调用。根据要使用的钩子的版本,Fsmonitor被设置为.git/hooks/ Fsmonitor -watchman或.git/hooks/ Fsmonitor -watchmanv2。

Version 1有两个参数,一个是Version(1),另一个是自1970年1月1日午夜以来经过的纳秒时间。

Version 2有两个参数,一个是Version(2),另一个是token(用于标识token之后的更改)。对于守望者来说,这是一个时钟id。这个版本必须在文件列表之前向stdout输出新的令牌,后跟一个NUL。

钩子应该向stdout输出工作目录中自请求时间以来可能发生更改的所有文件的列表。逻辑应该是包容性的,这样它就不会错过任何潜在的更改。这些路径应该是相对于工作目录的根目录的,并由单个NUL分隔。

包含实际上没有更改的文件是可以的。应该包括所有更改,包括新创建和删除的文件。当重命名文件时,应该同时包含旧的和新的名称。

Git将根据给定的路径名限制检查更改的文件,以及检查未跟踪文件的目录。

告诉git“所有文件都已更改”的一种优化方法是返回文件名/。

退出状态决定git是否会使用来自钩子的数据来限制它的搜索。如果出现错误,它将退回到验证所有文件和文件夹。

  • post-update

git-receive-pack响应git push并更新其存储库中的引用时,会调用该钩子。当所有引用都被更新后,它会在远程存储库上执行一次。

它接受可变数量的参数,每个参数都是实际更新的ref的名称。

这个钩子主要用于通知,不会影响git receive-pack的结果。

post-update钩子可以告诉我们被推送的头是什么,但它不知道它们的原始值和更新值是什么,所以这是一个糟糕的地方。post-receive钩子获取ref的原始值和更新后的值。如果你需要的话,你可以考虑一下。

当启用时,默认的post-update钩子会运行git update-server-info来保持哑传输(例如HTTP)使用的信息是最新的。如果你要发布一个可以通过HTTP访问的Git存储库,你应该启用这个钩子。

标准输出和标准错误输出都被转发到另一端的git send-pack,因此您可以简单地为用户回显消息。

  • pre-commit

这个钩子由git-commit调用,可以通过——no-verify选项绕过。它不接受任何参数,在获得建议的提交日志消息并进行提交之前调用。以非零状态退出该脚本会导致git commit命令在创建提交之前终止。

默认的预提交钩子在启用时,会捕获带有尾随空格的行,并在找到这样的行时中止提交。

所有的git提交钩子都是用环境变量GIT_EDITOR=:来调用的,如果这个命令不会调出一个编辑器来修改提交消息。

默认的预提交钩子(当启用时)和钩子。allownonascii配置选项取消设置或设置为false -防止使用非ascii文件名。

  • pre-merge-commit

这个钩子由git-merge调用,可以用——no-verify选项绕过。它不接受任何参数,并且在成功执行合并之后,在获得建议的提交日志消息以进行提交之前调用。以非零状态退出该脚本会导致git merge命令在创建提交之前终止。

默认的预合并提交钩子在启用时,如果启用了预提交钩子,则运行预提交钩子。

如果该命令不会调出编辑器来修改提交消息,则使用环境变量GIT_EDITOR=:调用该钩子。

如果合并不能自动执行,则需要解决冲突并单独提交结果(参见git-merge[1])。此时,此钩子将不会被执行,但如果启用了预提交钩子,则会执行。

  • prepare-commit-msg

这个钩子被git-commit调用,在准备默认日志消息之后,在编辑器启动之前。

它需要一到三个参数。第一个是包含提交日志消息的文件的名称。第二个是提交消息的来源,可以是:message(如果给出了-m或-F选项);模板(如果给出了-t选项或配置选项commit)。模板设置);merge(如果提交是merge或者.git/MERGE_MSG文件存在);(如果.git/SQUASH_MSG文件存在);或commit,后跟提交对象名称(如果给出了-c、-c或——amend选项)。

如果退出状态非零,git commit将中止。

钩子的目的是就地编辑消息文件,它不会被——no-verify选项抑制。非零退出意味着钩子失败并终止提交。它不应该被用作预提交钩子的替代品。

Git附带的样例prepare-commit-msg钩子删除了提交模板注释部分中的帮助消息。

  • pre-push

这个钩子由git-push调用,可以用来阻止push的发生。钩子是用两个参数调用的,这两个参数提供了目标远程的名称和位置,如果没有使用命名的远程,两个值将是相同的。

关于要推送的内容的信息在钩子的标准输入中提供,格式如下:

<local ref> SP <local object name> SP <remote ref> SP <remote object name> LF

例如,如果命令git push origin master:foreign被运行,钩子会收到像下面这样的一行:

参考/头/主67890参考/头/外12345

尽管将提供完整的对象名称。如果外部ref还不存在,<远程对象名>将是全零对象名。如果要删除一个ref, <local ref>将作为(delete)提供,<local object name>将是全零对象名称。如果本地提交不是由可以扩展的名称(例如HEAD~,或者对象名称)指定的,那么它将按照最初给出的方式提供。

如果这个钩子以非零状态退出,git push将不推送任何东西而中止。关于拒绝推送的原因的信息可以通过写入标准错误发送给用户。

  • pre-rebase

这个钩子由git-rebase调用,可以用来防止分支被rebase。钩子可以带一个或两个参数调用。第一个参数是序列分叉的上游。第二个参数是正在重基的分支,在重基当前分支时不设置。

  • pre-receive

当git-receive-pack响应git push并更新其存储库中的引用时,会调用该钩子。就在开始更新远程存储库上的引用之前,调用预接收钩子。其退出状态决定了更新的成功或失败。

这个钩子执行一次接收操作。它不接受任何参数,但对于每个要更新的ref,它会在标准输入中接收一行格式如下:

SP <new-value> SP <ref-name> LF

其中<old-value>是存储在ref中的旧对象名称,<new-value>是存储在ref中的新对象名称,<ref-name>是ref的全名。创建新ref时,<old-value>是全零对象名称。

如果钩子以非零状态退出,则不会更新任何引用。如果钩子以0退出,更新钩子仍然可以阻止更新单个引用。

标准输出和标准错误输出都被转发到另一端的git send-pack,因此您可以简单地为用户回显消息。

git push命令行给出的push选项数——push-option=…可以从环境变量GIT_PUSH_OPTION_COUNT中读取,选项本身在GIT_PUSH_OPTION_0, GIT_PUSH_OPTION_1,…中找到,如果协商不使用推送选项阶段,则不会设置环境变量。如果客户端选择使用推送选项,但不传输任何选项,则计数变量将被设置为零,GIT_PUSH_OPTION_COUNT=0。

有关一些注意事项,请参阅gift -receive-pack中的“隔离环境”一节。

  • push-to-checkout

git-receive-pack响应git push并更新其存储库中的引用时,以及当push尝试更新当前签出的分支并且receive.denyCurrentBranch配置变量设置为updateInstead时,该钩子被git-receive- receive-pack调用。默认情况下,如果工作树和远程存储库的索引与当前签出的提交有任何差异,则拒绝这样的推送;当工作树和索引都匹配当前提交时,它们将被更新以匹配新推送的分支尖端。此钩子将用于覆盖默认行为。

钩子接收当前分支的尖端将被更新的提交。它可以以非零状态退出以拒绝推送(当它这样做时,它不能修改索引或工作树)。或者,它可以对工作树和索引进行任何必要的更改,使它们在当前分支的尖端更新到新提交时达到所需的状态,并以零状态退出。

例如,钩子可以简单地运行git read-tree -u -m HEAD "$1",以模拟git fetchgit push相反的方向运行,因为git read-tree -u -m的两树形式本质上与git switchgit checkout相同,它们在切换分支的同时保持工作树中的本地更改,而不会干扰分支之间的差异。

  • update

git-receive-pack响应git push并更新其存储库中的引用时,会调用该钩子。就在更新远程存储库上的ref之前,调用更新钩子。它的退出状态决定了refupdate的成功或失败。

对于每个要更新的ref,钩子执行一次,并接受三个参数:

被更新的ref的名称,

旧的对象名称存储在ref,

以及要存储在ref中的新对象名称。

update钩子的零退出允许更新ref。以非零状态退出会阻止git receive-pack更新该ref。

这个钩子可以用来防止某些refs上的强制更新,通过确保对象名称是一个提交对象,是由旧对象名称命名的提交对象的后代。也就是说,执行“仅快进”策略。

它也可以用来记录新旧状态。然而,它并不知道分支的全部集合,因此在天真地使用时,它最终会为每个ref发送一封电子邮件。post-receive钩子更适合于此。

在限制用户只能通过网络访问git命令的环境中,这个钩子可以用来实现访问控制,而不依赖于文件系统所有权和组成员关系。请参阅git-shell[1]了解如何使用登录shell来限制用户只能访问git命令。

标准输出和标准错误输出都被转发到另一端的git send-pack,因此您可以简单地为用户回显消息。

默认的更新钩子,当启用时——并且带有钩子。允许unannotated配置选项unset或设置为false -防止未注释的标签被推送。

完整钩子说明,请参考官网链接

脚本逻辑

1、声明脚本运行环境

这里我们使用node进行编写,所以脚本运行环境应该声明为node

#!/usr/bin/env node
2、获取当前暂存更改的文件信息

我们可以使用git status来获取当前暂存更改的文件目录,但部分同学可能会出现获取到的文件名中文只显示字符串的情况:

  • 原因

在默认设置下,中文文件名在工作区状态输出,中文名不能正确显示,而是显示为八进制的字符编码。

  • 解决办法

将git 配置文件 core.quotepath项设置为false。

quotepath表示引用路径

加上–global表示全局配置

git bash 终端输入命令:

git config --global core.quotepath false

我们可以看到修改过的文件路径。

3、提取 git status 信息中的变动文件路径

上一步我们知道了可以使用git status命令来获取暂存更改的文件信息,但获取到的信息并不是直接得到变动的文件路径,我们需要从获取到的信息中的变动文件路径。

let commitFile = child_process.execSync(command).toString();
commitFile = commitFile.split("\n");
console.log("commitFile: ", commitFile);

这里前面的五行其实是固定的,我们只需要从第六行开始获取即可,文件路径与变更类型描述是通过:隔开,所以我们分割一下:,再将一些特殊字符去掉即可。

const child_process = require("child_process");
const trimReg = /(\ +)|([ ])|([\r\n]|(["]))/g;
const fileList = [];
for (let i = 5; i < commitFile.length; i++) {
  const name = commitFile[i].split(":")[1];
  if (!name) break;
  fileList.push(decodeURI(name.replace(trimReg, "")));
}
console.log("fileList: ", fileList);

4、判断文件中是否包含 debugger

通过readFileSync获取文件内容,判断文件内容是否包含debugger,包括的话将文件路径记录并将文件中的debugger全部删除,然后再将修改后的文件重新git add -文件路径提交即可。

const child_process = require("child_process");
const fs = require("fs");
const editFiles = [];
fileList.forEach((file) => {
  const txt = fs.readFileSync(file, "utf8");
  if (txt.includes("debugger")) {
    editFiles.push(file);
    fs.writeFileSync(file, txt.replace(/(debugger);?(\s*\n)*/g, ""));
    const commandAdd = "git add " + file;
    child_process.execSync(commandAdd).toString();
  }
});
console.log("editFiles: ", editFiles);
console.log("已清除提交文件中的 debugger");

完整代码

#!/usr/bin/env node
const child_process = require("child_process");
const fs = require("fs");
const command = "git status";
const trimReg = /(\ +)|([ ])|([\r\n]|(["]))/g;
let commitFile = child_process.execSync(command).toString();
commitFile = commitFile.split("\n");
const fileList = [];
for (let i = 5; i < commitFile.length; i++) {
  const name = commitFile[i].split(":")[1];
  if (!name) break;
  fileList.push(decodeURI(name.replace(trimReg, "")));
}
const editFiles = [];
fileList.forEach((file) => {
  const txt = fs.readFileSync(file, "utf8");
  if (txt.includes("debugger")) {
    editFiles.push(file);
    fs.writeFileSync(file, txt.replace(/(debugger);?(\s*\n)*/g, ""));
    const commandAdd = "git add " + file;
    child_process.execSync(commandAdd).toString();
  }
});
console.log("editFiles: ", editFiles);
console.log("已清除提交文件中的 debugger");
process.exit(0);

参考材料

https://zhuanlan.zhihu.com/p/521707440

https://git-scm.com/docs/githooks

源码地址

gitee

https://gitee.com/zheng_yongtao/jyeontu-templates.git

公众号

关注公众号『前端也能这么有趣』,获取更多新鲜内容。

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
Shell 开发工具 git
git学习三:git使用:删除仓库,删除仓库内文件
通过GitHub的设置页面删除仓库,以及如何使用Git命令行删除仓库中的文件或文件夹。
135 1
git学习三:git使用:删除仓库,删除仓库内文件
|
1月前
|
开发工具 git
git如何修改提交代码时的名字和邮箱?
git如何修改提交代码时的名字和邮箱?
50 4
|
1月前
|
开发工具 git 索引
git上面中新建gitignore文件,并且去除已经在仓库版本管理中的文件夹
git上面中新建gitignore文件,并且去除已经在仓库版本管理中的文件夹
58 4
|
1月前
|
Java Linux 开发工具
IDEA中git提交前如何关闭code analysis以及开启格式化代码
【10月更文挑战第12天】本文介绍了在 IntelliJ IDEA 中关闭代码分析和开启代码格式化的步骤。关闭代码分析可通过取消默认启用检查或针对特定规则进行调整实现,同时可通过设置 VCS 静默模式在提交时跳过检查。开启代码格式化则需在 `Settings` 中配置 `Code Style` 规则,并通过创建 Git 钩子实现提交前自动格式化。
155 3
|
1月前
|
开发工具 git
git显示开发日志+WinSW——将.exe文件注册为服务的一个工具+图床PicGo+kubeconfig 多个集群配置 如何切换
git显示开发日志+WinSW——将.exe文件注册为服务的一个工具+图床PicGo+kubeconfig 多个集群配置 如何切换
37 1
|
2月前
|
Shell 网络安全 开发工具
git与gitee结合使用,提交代码,文件到远程仓库
本文介绍了如何将Git与Gitee结合使用来提交代码文件到远程仓库。内容涵盖了Git的安装和环境变量配置、SSH公钥的生成和配置、在Gitee上创建仓库、设置Git的全局用户信息、初始化本地仓库、添加远程仓库地址、提交文件和推送到远程仓库的步骤。此外,还提供了如何克隆远程仓库到本地的命令。
git与gitee结合使用,提交代码,文件到远程仓库
|
1月前
|
JavaScript 前端开发 开发工具
一身反骨的我,用--no-verify绕开了git代码提交限制!
【10月更文挑战第7天】一身反骨的我,用--no-verify绕开了git代码提交限制!
92 0
|
1月前
|
Linux 开发工具 git
企业级Git管理工作流分析--GIT实战详解
企业级Git管理工作流分析--GIT实战详解
36 0
|
IDE Shell 网络安全
【Git版本控制】使用TortoiseGit一键托管工程代码及版本控制
【Git版本控制】使用TortoiseGit一键托管工程代码及版本控制
268 0
|
6月前
|
数据可视化 开发工具 git
Git【实践 01】使用Git工具托管本地代码到GitHub简单易懂的图文教程(含Git+第三方工具TortoiseGit+中文语言包百度云盘资源)
Git【实践 01】使用Git工具托管本地代码到GitHub简单易懂的图文教程(含Git+第三方工具TortoiseGit+中文语言包百度云盘资源)
103 0