使用GPT和copilot写一个git查看源码的工具

一、需求背景

在看一些包源码的时候,想了解一下作者实现这个工具的思路,工程化是如何实现的,演变到如今这个庞大的库都经历了什么。会去思考一个问题,为啥别人能写得这么好,能够有这么牛的技术深度和广度,但面对经过无数次迭代的库来说,想要阅读下源码实在太困难了,所以想着回到最初的那个版本,一步一步的看下来,这样才能够更好的理解作者的思路,也能够更好的学习到别人的技术。

当然,这个需求十分简单,可能会说用source-tree或者gitlen的图形化界面查看一下不就好了吗?确实,但命令行的形式能让我快速的切换,配合iterm2的历史记录,选择下方向键和回车确认就好了,不用冗长的时间线上去找commit,切换好分支之后,倒是可以配合对应的gitlen图形化界面更直观的查看变更文件。主要是想要利用copilot快速的编写一个node工具,体会AI辅助写代码的便捷性,找到对应的hash值用git switch切换也行。

本次需要支持的命令使用形式如下:

  1. -b代表指定分支基准,代表了branch
  2. -d代表了分支方向,指定向前还是向后查看或者是回到第一个commit
  3. 由于要支持多命令格式,以便未来扩展,view子命令代表查看,back子命令代表回到被查看分支。
shell 复制代码
$ rh view -b main -d first
$ rh back

采用的库选择了zxsade

1.1 zx介绍(链接

  1. 简洁易用:它提供了一种清晰而简洁的语法,使得编写和执行命令行操作变得容易。使用zx,您可以直接在脚本中编写和执行命令,而无需在Shell中切换。
  2. 跨平台:可以在主流操作系统上运行(如Windows、macOS、Linux等),保证了脚本在不同平台上的兼容性。
  3. 内置功能:提供了许多常用的功能和工具,如重试、CLI进度条、文件操作、路径操作等,使得在脚本中完成常见的任务更加方便。
  4. 无需导入包:内置了常用的Node.js内置模块和第三方包,您可以在脚本中直接使用它们,无需导入或安装额外的包,比如import 'zx/globals'将全局导入,或者可以直接引入具体的包,避免eslint错误import { echo } from 'zx'

1.2 sade介绍(链接

sade是一个用于构建命令行工具的轻量级npm包,相对于市面上的其他命令行工具,具有以下几个优势:

  1. 轻量级:sade的设计目标是保持代码简洁,最小化依赖和内存占用。底层依赖mri进行命令行解析,使得构建轻量级的命令行工具变得更加容易。
  2. 功能强大:可以轻松地定义命令行选项、处理命令行参数、注释文档生成等等。此外,它还提供了许多有用的API和钩子,使得自定义命令行工具变得更加灵活。

1.3 平台介绍

灵犀百通 是一个免费的GPT对话文本生成网站,利用的是GPT3.5,如果你有钱的话,可以直接买ChatGPT4.0来体验更好的效果。 Vscode Copilot是一个辅助代码生成的vscode插件,联系上下文给出最佳的代码。

二、实施步骤

2.1 编写命令行解析骨架

在AI对话窗口给出想要的步骤,如果GPT不了解你使用的npm库,可以把整个npm包的README.md说明喂给它(暂时还不知道怎么喂链接给它,效果很不好,有人知道的话底下评论

然后向它提供一些功能编写思路,就能产生大概得命令行框架模板了:

定义好命令行工具的入口:

typescript 复制代码
#!/usr/bin/env zx

import sade from 'sade';
import { $, cd } from 'zx';

const prog = sade('rh');

// 定义view子命令
prog
  .command('view')
  .option('-b, --branch', 'Specify the branch')
  .option('-d, --direction', 'Specify the direction')
  .action(({ branch, direction }) => {
    console.log(`Viewing branch: ${branch}`);
    console.log(`Direction: ${direction}`);
    // 在这里编写具体的逻辑
  });

// 定义back子命令
prog
  .command('back')
  .action(() => {
    console.log('Going back to the previous branch');
    // 在这里编写具体的逻辑
  });

// 解析命令行参数并执行对应的命令
prog.parse(process.argv);

在上述示例中,我们首先导入了需要使用的包:sade和zx。然后创建了一个命令行入口对象prog,使用sade初始化,传入命令行的名称rh。 接下来,我们使用prog.command方法分别定义了viewback两个子命令。

  • view子命令使用.option方法定义了两个选项-b, --branch-d, --direction,分别用于指定分支和方向。
  • back子命令没有定义选项。 在.action函数中,我们可以获取到子命令定义的选项的值,并根据需要编写具体的逻辑。 最后,使用prog.parse解析命令行参数,并执行对应的命令。 然后在对应的.action函数中编写逻辑来处理具体的查看和回到分支的操作。

2.2 编写本地分支校验逻辑

利用copilot编写校验本地分支,直接在函数的行注释中给出提示语,等待函数的生成按下Tab

这个函数的作用是检查是否存在指定的分支,并输出当前要查看源代码的分支。函数首先通过执行命令git branch获取所有的分支信息,并将结果进行处理。然后,它会检查。当查找到的分支列表中不包含目标分支时,函数会输出一条错误信息,并调用process.exit(1)来终止程序的执行。这表示程序将以退出码1退出。如果目标分支存在于查找到的分支列表中,函数将输出一条信息,指示当前要查看源代码的分支是哪个。

这时候看到copilot给出了完美的内容,不行就疯狂喂它,多写点注释。

2.3 编写切换分支函数

同样,需要你给出实现思路的注释语句,没有实现思路就去问问GPT,这里我想了想,给出的是找到所有的commit,如果direction指定为first,则回到第一个commit,next就切换到目标分支的下一个commit,pre就切换到目标分支的上一个commit,等待代码生成按住Tab

这里它会逐行生成,最终生成的效果如下:

当然,你还可以对这个函数进行完善,比如说,校验本地是否有git仓库、执行next和pre的时候,遇到第一条和最后一条命令的处理方式等。

2.5 实现back子命令,回到被查看的分支

这个函数实现的功能是处理一个回退命令。具体步骤如下,当然也是copilot写的,我们只是给与实现思路:

  1. 使用 $ 函数执行命令 git reflog,获取 Git 的引用日志。
  2. 通过 stdout 属性获取命令输出结果,并使用 trim() 方法去除首尾空格。
  3. 使用 split('\n') 方法将输出结果按行分割,得到一个数组。
  4. 使用 find() 方法遍历数组,找到包含 'checkout' 的行,即最近一次执行的 checkout 操作。
  5. 如果没有找到最近一次的 checkout 操作,使用 echo 模板字符串输出提示信息,并使用 process.exit(1) 终止程序执行。
  6. 如果找到了最近一次的 checkout 操作,使用 split(' ') 方法将行按空格分割,得到一个数组。
  7. 通过索引为5的元素获取最近一次 checkout 操作的提交哈希值。
  8. 使用 $ 函数执行 git checkout 命令,将代码回退到最近一次 checkout 的提交。
  9. 使用 chalk 库将提示信息的部分文字着色,并使用 echo 模板字符串输出成功回退的提示信息
typescript 复制代码
/**
 * Handles the back command by finding the most recent checkout operation in the git reflog and checking out to the commit of the branch being viewed.
 * @returns Promise<void>
 */
async function handleBackCommand() {
  const lastCheckout = (await $`git reflog`).stdout.trim().split('\n').find(line => line.includes('checkout'))
  if (!lastCheckout) {
    echo`没有找到最近一次checkout的操作`
    process.exit(1)
  }
  const lastCheckoutCommit = lastCheckout.split(' ')[5]
  await $`git checkout ${lastCheckoutCommit}`
  echo`${chalk.green(`回到 ${chalk.blue(lastCheckoutCommit)} 成功`)}`
}

2.4 为函数添加jsDoc注释

对于copilot来说,选中要添加注释的函数,按住command + i可以快速唤起对话,然后输入/按空格选中/doc,等待注释的生成,点击Accept就可以了。这都是一些快捷snippet,背后都为你写好对应的GPT提示词了。

三、发布npm包

在发布npm包时,需要注意定义以下字段来执行bin命令:

"bin"字段:该字段用于定义要在命令行中执行的主要脚本文件的路径。在package.json中,"bin"字段的值为"rh": "./bin/rh.mjs",它指定了要执行的命令rh所对应的脚本文件路径为./bin/rh.mjs。

"files"字段:该字段用于指定发布包时要包含在包中的文件和目录列表。它指定了需要包含在发布的npm包中的文件和目录。"files"字段的值为["bin", "dist"],表示发布的包将包含bin目录和dist目录下的文件。

"main"字段:该字段用于指定在引入该包时,会被默认加载的入口文件的路径。在上述示例的package.json中,"main"字段的值为"./dist/rh.js",表示引入该包时,会默认加载./dist/rh.js文件。

"types"字段:该字段用于指定该包的类型声明文件的路径。在上述示例的package.json中,"types"字段的值为"./dist/rh.d.ts",表示指定了类型声明文件的路径为./dist/rh.d.ts。

"exports"字段:该字段用于指定包的模块导出方式。它允许您在不同的环境中使用不同的导入方式。在上述示例的package.json中,"exports"字段的值为:

json 复制代码
"exports": {
  ".": {
    "import": "./dist/rh.js",
    "require": "./dist/rh.js",
    "default": "./dist/rh.js"
  }
}
typescript 复制代码
import { main } from './factory'
export * from './type'
export default main

然后定义了bin脚本进行打包:

typescript 复制代码
#!/usr/bin/env node
import main from '../src/index'
main()
json 复制代码
{
  "build": "tsup bin/rh.ts --format esm --clean --dts",
  "stub": "tsup bin/rh.ts --format esm",
  "dev": "tsup bin/rh.ts --format esm --watch"
}

切换到npm源,使用npm publish进行npm包的发布。 实现的效果:

最后,可以试着使用pnpm i read-helper -g来体验一下哦!欢迎一键三连!👏🏻

相关推荐
Jerry Lau6 分钟前
go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
前端·golang·gin
我命由我1234535 分钟前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js
0wioiw036 分钟前
Flutter基础(前端教程③-跳转)
前端·flutter
落笔画忧愁e38 分钟前
扣子Coze纯前端部署多Agents
前端
海天胜景41 分钟前
vue3 当前页面方法暴露
前端·javascript·vue.js
GISer_Jing1 小时前
前端面试常考题目详解
前端·javascript
Boilermaker19922 小时前
【Java EE】SpringIoC
前端·数据库·spring
中微子2 小时前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上10242 小时前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
卷积殉铁子2 小时前
低代码 + AIGC = 开发者的“双倍快乐”!效率起飞,告别996!
低代码·aigc