在 Swift 中编写Git Hooks脚本的方法

目录
  • 前言
  • 用git hooks自动生成提交信息
  • 为什么我使用Swift?
  • 让我们开始吧
  • 编写git钩子
  • 检索提交消息
  • 注意:
  • 检索问题编号
  • 修改提交信息
  • 设置git钩子
  • 测试结果
  • 参考资料

前言

这周,我决定完成因为工作而推迟了一周的TODO事项来改进我的Git工作流程。

为了在提交的时候尽可能多的携带上下文信息,我们让提交信息包含了正在处理的JIRA编号。这样,将来如果有人回到我们现在正在提交的源代码,输入​ ​git blame​ ​,就能很容易的找出JIRA的编号。

每次提交都包含这些信息可能会有点乏味(如果你使用了类似TDD[1]之类的方法,您会提交的更加频繁),而且,尽管像Tower[2]这样的git客户端会让此变得容易一些,但是您仍然需要手动将问题编号复制粘贴到提交消息中,并且记住这样做,这是我最难以解决的问题:sweat_smile:。

出于这个原因,我开始寻求了解git hooks,试图自动化这项任务。我的想法是能够从git分支获取JIRA编号(我们有一个分支命名约定,形如:story/ISSUE-1234_branch-name),然后将提交消息更改为以JIRA编号为前缀,从而生成最终结果消息:ISSUE-1234-其他原本的提交信息。

用git hooks自动生成提交信息

​Git Hooks[3] 提供了一种在运行某些重要的git命令时触发自定义操作的方法,例如在一次commit或者push之前执行一些操作。

在本例中,我使用了 commit-msg 钩子,它能够在当前提交信息生效前修改此信息。钩子由一个参数调用,该参数是指向包含用户输入的提交消息的文件的路径。这意味着,为了改变提交消息,我们只需要从文件中读取、修改其内容,然后写回调用挂钩的文件。

要创建git钩子,我们需要在 .git/hooks​ 路经下提供一个可执行脚本。我的钩子放在了 .git/hooks/commit-msg 路经之下。

为什么我使用Swift?

Git hooks可以使用任何你熟悉的,并且在主机上安装了解释器(通过shebang来指定)的脚本语言来编写。

虽然有很多更受欢迎的选项,比如bash​、ruby等等,但我还是决定使用Swift。因为我对Swift更熟悉,因为我每天都在使用它,而且我真的非常喜欢它强大的类型语法以及低内存占用。

让我们开始吧

你可以使用任何你喜欢的IDE编写Swift脚本。但是如果你想要有适当的代码补全以及调试能力,你可以为其创建一个Xcode项目。为此,在 macOS​ 下选择 Command Line Tool 创建一个新的项目。

在创建的文件顶部加上Swift shebang,引入Foundation库。

#!/usr/bin/swift
import Foundation

这样当git执行文件时,shebang将确保使用文件作为输入数据调用/usr/bin/swift二进制文件。

编写git钩子

项目已经全部设置好,所以现在可以编写git挂钩了。让我们走完所有的步骤。

检索提交消息

要做的第一件事就是从脚本传进来的参数检索临时提交文件的路径然后读取文件内容。

let commitMessageFile = CommandLine.arguments[1]
guard let data = FileManager.default.contents(atPath: commitMessageFile),
      let commitMessage = String(data: data, encoding: .utf8) else {
    exit(1)
}

在上面的代码片段中,我们首先拿到了提交文件的路径(git​传递给脚本),然后通过FileManagerAPI​读取了文件内容。如果因为一些原因检索失败了,我们退出(exit​)脚本同时返回状态码1,这将告诉git终止此次提交。

注意:

根据git hooks文档,如果任何钩子脚本返回的状态码大于​ ​0​ ​,它都将终止即将要要发生的操作。这将在本文后面的部分中使用,以便在不需要做任何修改而优雅地退出。

检索问题编号

既然提交信息的字符串已经可用,接下来就需要找到当前分支并从中检索到问题编号。正如本文前面提到的,这只可能是因为团队对分支命名的严格格式,在其名称中始终包含JIRA编号(例如,story/ISSUE-1234_some-awesome-feature-work)。

为了实现这一点,我们必须检索当前的工作分支,然后用正则表达式从中检索问题编号。

让我们从添加脚本调用zsh shell​命令的能力开始。通过使用Process​api,脚本可以与git命令行界面交互。

func shell(_ command: String) -> String {
    let task = Process()
    let outputPipe = Pipe()
    let errorPipe = Pipe()
    task.standardOutput = outputPipe
    task.standardError = errorPipe
    task.arguments = ["-c", command]
    task.executableURL = URL(fileURLWithPath: "/bin/zsh")
    do {
        try task.run()
        task.waitUntilExit()
    } catch {
        print("There was an error running the command: \(command)")
        print(error.localizedDescription)
        exit(1)
    }
    guard let outputData = try? outputPipe.fileHandleForReading.readToEnd(),
          let outputString = String(data: outputData, encoding: .utf8) else {
        // Print error if needed
        if let errorData = try? errorPipe.fileHandleForReading.readToEnd(),
           let errorString = String(data: errorData, encoding: .utf8) {
            print("Encountered the following error running the command:")
            print(errorString)
        }
        exit(1)
    }
    return outputString
}

现在实现了shell​命令,那么就可以使用它询问git当前分支是什么,然后尽可能的从中提取出问题编号。

let gitBranchName = shell("git rev-parse --abbrev-ref HEAD")
    .trimmingCharacters(in: .newlines)
let stringRange = NSRange(location: 0, length: gitBranchName.utf16.count)
guard let regex = try? NSRegularExpression(pattern: #"(\w*-\d*)"#, options: .anchorsMatchLines),
    let match = regex.firstMatch(in: gitBranchName, range: stringRange) else {
    exit(0)
}
let range = match.range(at: 1)
let ticketNumber = (gitBranchName as NSString)
    .substring(with: range)
    .trimmingCharacters(in: .newlines)

请注意,如果没有匹配项(即分支名称中不包含JIRA问题编号),脚本将以0的状态退出,允许提交继续进行,而不进行任何更改。这是为了不破坏诸如main或其他测试/调查分支中的工作流。

修改提交信息

为了更改提交消息,必须将脚本开头读取的文件内容(包含提交消息)写回同一路径。

在这种情况下,只需要做一个更改,即在提交信息的前面加上JIRA编号和(-),以将其与提交信息的其余部分很好地分开。还必须确保检查了提交信息字符串,仅在编号不存在时才添加编号:

if !commitMessage.contains(ticketNumber) {
    do {
        try "\(ticketNumber) - \(commitMessage.trimmingCharacters(in: .newlines))"
            .write(toFile: commitMessageFile, atomically: true, encoding: .utf8)
    } catch {
        print("Could not write to file \(commitMessageFile)")
        exit(1)
    }
}

设置git钩子

现在脚本已经准备好了,是时候把它放在git可以找到它的位置了。Git钩子可以全局设置,也可以基于单个repo设置。

我个人对这类脚本的偏好是基于单个repo设置,因为这样可以在出现问题时为您提供更多的控制和可见性,并且如果钩子开始失败,它会在它设置的repo中失败,而不是全局都失败。

要设置它们,我们只需要使文件可执行,重命名并将其复制到所要设置repo的.git/hooks/路径之下:

chmod +x main.swift
mv main.swift <path_to_your_repo>/.git/hooks/commit-msg

测试结果

现在repo已经全部设置好了,剩下的就是对部署的脚本进行测试。在下面的截屏中,创建了两个分支,一个带有问题编号,一个没有,它们有着相同的提交信息。可以看出脚本运行正常,并且只在需要时才更改提交消息!

参考资料

[1]  TDD:  https://en.wikipedia.org/wiki/Test-driven_development

[2]  Tower:  https://www.git-tower.com/mac

[3]  Git Hooks:  https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks

到此这篇关于在 Swift 中编写脚本:Git Hooks的文章就介绍到这了,更多相关Git Hooks脚本编写内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用eslint和githooks统一前端风格的技巧

    前端团队开发时,是必须要有一个统一的前端规范的,用一套统一的规范来规范开发者,可以有效的避免在提交和拉取代码时造成的代码错乱问题,这边文章主要讲下我们团队的代码规范使用,eslint结合vscode保存时自动修复不规范代码,githooks提交代码时的eslint校验和信息规范. 添加eslint vue-cli3构建一个新项目(包含eslint模块),完成后添加 .eslintrc.js 配置如下: module.exports = { root: true, parserOptions: {

  • vue-cli创建的项目中的gitHooks原理解析

    前言 在使用 vue create my-app 创建项目的时候,Vue 会自动帮我们做好一些预配置,你可以不使用它,但是一旦需要的时候,突然发现,咦~原来它已经帮我做好准备工作了,只需要按自己的需求配置一下就可以了,就会觉得 vue-cli 很贴心啊,帮我们节省了很多时间. 在 package.json 文件中会发现 gitHooks . lint-staged 等字段,不难看出它是在我们执行 git 命令的时候会自动执行的一些额外的操作,比如语法提示.错误提示等. 这个完整的过程是怎样的呢?

  • 在 Swift 中编写Git Hooks脚本的方法

    目录 前言 用git hooks自动生成提交信息 为什么我使用Swift? 让我们开始吧 编写git钩子 检索提交消息 注意: 检索问题编号 修改提交信息 设置git钩子 测试结果 参考资料 前言 这周,我决定完成因为工作而推迟了一周的TODO事项来改进我的Git工作流程. 为了在提交的时候尽可能多的携带上下文信息,我们让提交信息包含了正在处理的JIRA编号.这样,将来如果有人回到我们现在正在提交的源代码,输入​ ​git blame​ ​,就能很容易的找出JIRA的编号. 每次提交都包含这些信

  • Java程序中实现调用Python脚本的方法详解

    本文实例讲述了Java程序中实现调用Python脚本的方法.分享给大家供大家参考,具体如下: 在程序开发中,有时候需要Java程序中调用相关Python脚本,以下内容记录了先关步骤和可能出现问题的解决办法. 1.在Eclipse中新建Maven工程: 2.pom.xml文件中添加如下依赖包之后update maven工程: <dependency> <groupId>org.python</groupId> <artifactId>jython</ar

  • IDEA中解决 git pull 冲突的方法

    0.事先准备. 1)把远程仓库的README.md内容改写为bbb(原先为aaa). 2)本地仓库的README.md内容改写为ccc(原先也为aaa). 以此来模仿代码冲突. 1.先commit 再pull pull的时候会提示有冲突,需要你进行手动merge Accept Yours 就是直接选取本地的代码,覆盖掉远程仓库的 Accept Theirs 是直接选取远程仓库的,覆盖掉自己本地的 我们选择Merge,自己手动行进选择.修改. 这里左边部分是你本地仓库的代码,右边部分是远程仓库的代

  • Swift中字典与JSON转换的方法

    Swift中经常会遇到字典和字符串的相互转换,因此可以转换可以封装起来,转换代码如下: func convertStringToDictionary(text: String) -> [String:AnyObject]? { if let data = text.data(using: String.Encoding.utf8) { do { return try JSONSerialization.jsonObject(with: data, options: [JSONSerializat

  • swift中可选值?和!使用的方法示例

    Optional 可选值 Optional是 Swift 的一大特色,也是 Swift 初学者最容易困惑的问题. 定义变量时,如果指定该变量是可选的,表示该变量可以有一个指定类型的值,也可以是 nil. 此外,Swift的nil也和Objective-C有些不一样,在Objective-C中,只有对象才能为nil,而在Swift里,当基础类型(整形.浮点.布尔等)没有值时,也是nil,而不是一个初始值,没有初始值的值,是不能使用的,这就产生了Optional类型.定义一个Optional的值很容

  • Swift中如何避免循环引用的方法

    内存管理中经常会遇到的一个问题便是循环引用.首先,我们来了解一下iOS是如何进行内存管理的. 和OC一样,swift也是使用自动引用计数ARC(Auto Reference Counteting)来自动管理内存的,所以我们不需要过多考虑内存管理.当某个类实例不需要用到的时候,ARC会自动释放其占用的内存. ARC ARC(Automatic Reference Counting) 是苹果的自动内存管理机制.正如其名:自动引用计数,根据引用计数来决定内存块是否应该被释放. 当一个对象被创建的时候,

  • 在 Vue 中编写 SVG 图标组件的方法

    在考虑了将矢量图标从图标字体迁移到内联 SVG 的 原因 之后,我在 Vue.js 中找到了一个用 SVG 替换图标字体的解决方案,同时仍能保持使用图标字体的灵活性和易用性--能够使用 CSS 轻松改变图标的大小.颜色以及其它属性. 一种流行的方法是使用 v-html 指令和 npm 模块 html-loader 来将 SVG 导入到我们的 Vue 模板中,并在 Vue 的生命周期函数 mounted() 中修改渲染的 <svg> 元素.CSS 样式可以直接应用到 <svg> 元素

  • Spring Boot中自动执行sql脚本的方法实例

    目录 背景 实现核心 实现方法 注意 总结 说明:所有的代码基于SpringBoot 2.0.3版本 背景 在应用程序启动后,可以自动执行建库.建表等SQL脚本.下文中以要自动化执行people.sql脚本为例说明,脚本在SpringBoot工程中的路径为:classpath:people.sql,脚本的具体内容如下: CREATE TABLE IF NOT EXISTS people( persion_id BIGINT NOT NULL AUTO_INCREMENT, first_name

  • 使用Python编写提取日志中的中文的脚本的方法

    由于工作需要在一大堆日志里面提取相应的一些固定字符,如果单纯靠手工取提取,数据量大,劳心劳力,于是自然而然想到了用Python做一个对应的提取工具,代替手工提取的繁杂,涉及中文字符,正则表达式不好匹配,但不是不可以实现,这个以后优化时再说. 需求描述: 一个父目录中存在多个子文件夹,子文件夹下有多个txt形式化的Log日志,要求从所有地方Log日志中找出CardType=9, CardNo=0时的CardID的值,并将其统计存储到一个文本文件中,要求CardID不能够重复. 需求解析: 首先获取

  • 在 Shell 提示符中显示 Git 分支名称的方法

    Git 的好处之一就是把代码的分支管理变成了一件极其便捷的事情,分支只保留差异,不用复制任何文件,不用连接网络,快速创建,用完即删.Git 分支与项目的复杂程度无关,不管你的项目多么复杂,创建 Git 分支永远都是瞬间的事情.同时,因为保留了父类分支的信息,所以分支的合并也变得异常简单. 当在一个项目中频繁使用多个分支时,可以使用 git status 命令查询自己现在正工作在哪个分支下面,不过难免有脑子发昏的时候,忘记自己在哪个分支下面,因而发生误操作之类的杯具. 那么把分支显示在 Shell

随机推荐