🔀 打造更智能的 Git 提交合并命令:gix merge 实战

🔀 打造更智能的 Git 提交合并命令:gix merge 实战

在前两篇文章中,我们讲了为什么要开发 gix,也完成了 CLI 工具的基础搭建和 squash 命令的实现。

这一篇,我们要挑战一个更「智能」的命令 ------ gix merge。

它的目标是:通过交互选择任意两个 commit,自动合并成一个新的提交,不再受限于 HEAD~n 的范围,体验大大提升!


💡 为什么不用 rebase?

你可能会问,Git 原生的 rebase -i 也能做这事啊,干嘛重复造轮子?

原因有三:

  1. 记不住命令,交互复杂

  2. 不能自动化,不适合重复使用

  3. 无法直接用于团队规范(比如自动生成 message)

我们希望的是:

  • 输入 gix merge
  • 选择起始和结束的 commit
  • 填写 commit message
  • 自动 reset + commit + push(可选)

🎯 目标功能

功能项 说明
✅ 支持选择任意 commit 区间 不限于最近几次
✅ 支持交互式填写 message 或通过参数传入
✅ 支持自动 push 支持选择是否强推
✅ 支持命令参数直接执行 非交互场景也能跑

🧱 命令结构(入口)

修改 src/index.ts,加上参数定义:

bash 复制代码
program
  .command('merge')
  .description('交互式合并两个 commit')
  .option('-f, --from <hash>', '起始 commit')
  .option('-t, --to <hash>', '结束 commit')
  .option('-m, --message <msg>', '新的提交信息')
  .option('--push', '是否自动推送')
  .option('--force', '是否强推')
  .action(runMerge)

🧪 命令实现(核心逻辑)

新建 src/commands/merge.ts:

typescript 复制代码
import prompts from 'prompts'
import { execGit } from '../utils/git'
import chalk from 'chalk'

export async function runMerge(opts: {
  from?: string
  to?: string
  message?: string
  push?: boolean
  force?: boolean
}) {
  // 自动补全 commit 信息(交互)
  if (!opts.from || !opts.to) {
    const { from, to } = await prompts([
      {
        type: 'text',
        name: 'from',
        message: '请输入起始 commit(hash 或 HEAD~n)',
      },
      {
        type: 'text',
        name: 'to',
        message: '请输入结束 commit(hash 或 HEAD)',
      }
    ])
    opts.from = from
    opts.to = to
  }

  if (!opts.message) {
    const { msg } = await prompts({
      type: 'text',
      name: 'msg',
      message: '请输入新的 commit message:'
    })
    opts.message = msg
  }

  if (!opts.from || !opts.to || !opts.message) {
    console.log(chalk.red('❌ 输入信息不完整,终止操作'))
    return
  }

  // 开始合并
  const range = `${opts.from}^..${opts.to}`
  await execGit(`reset --soft ${opts.from}`)
  await execGit(`commit -m "${opts.message}" --no-verify`)

  console.log(chalk.green(`🎉 合并完成!从 ${opts.from} 到 ${opts.to}`))

  // 自动推送
  if (opts.push) {
    const currentBranch = await execGit('rev-parse --abbrev-ref HEAD', { silent: true })
    const pushCmd = `push origin ${currentBranch}${opts.force ? ' --force' : ''}`
    await execGit(pushCmd)
    console.log(chalk.green('🚀 已自动推送'))
  }
}

✨ 效果演示

sql 复制代码
gix merge

终端交互:

sql 复制代码
? 请输入起始 commit(hash 或 HEAD~n): HEAD~3
? 请输入结束 commit(hash 或 HEAD): HEAD
? 请输入新的 commit message: feat: 合并配置优化相关提交
🎉 合并完成!从 HEAD~3 到 HEAD

再来个直接执行的用法:

sql 复制代码
gix merge -f HEAD~2 -t HEAD -m "feat: 合并优化项" --push --force

📦 提示:如何查看历史 commit?

可以配合下面的 Git 命令快速浏览历史:

css 复制代码
git log --oneline --decorate

或直接封装成 gix log 也是后续可扩展的方向。


📌 代码小贴士

如果你希望自动获取当前分支:

rust 复制代码
const currentBranch = await execGit('rev-parse --abbrev-ref HEAD', { silent: true })

支持 silent 模式,在你的 execGit 函数里可以加个 stdio 控制逻辑。


🔮 下一步计划

下一篇我们来讲讲 CLI 工具怎么"做得像个产品"一样:

  • 如何把 gix 发布到 npm
  • 如何维护多个版本
  • 如何结合 CI/CD 自动发布

🧩 项目地址

欢迎来逛逛 ➜ github.com/nianyi778/g...

欢迎 Star ⭐ + PR,或者提建议~

相关推荐
小声读源码3 分钟前
【技巧】dify前端源代码修改第一弹-增加tab页
前端·pnpm·next.js·dify
假客套12 分钟前
2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
前端·uni-app·旅游
寒山李白20 分钟前
Spring Boot面试题精选汇总
java·spring boot·后端·面试
Captaincc26 分钟前
Ilya 现身多大毕业演讲:AI 会完成我们能做的一切
前端·ai编程
teeeeeeemo39 分钟前
Vue数据响应式原理解析
前端·javascript·vue.js·笔记·前端框架·vue
Sahas101943 分钟前
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.
前端·javascript·vue.js
磊叔的技术博客1 小时前
随笔小记:SpringBoot 3 集成 SpringDoc OpenAPI
spring boot·后端
Jinxiansen02111 小时前
Vue 3 实战:【加强版】公司通知推送(WebSocket + token 校验 + 心跳机制)
前端·javascript·vue.js·websocket·typescript
MrSkye1 小时前
React入门:组件化思想?数据驱动?
前端·react.js·面试
BillKu1 小时前
Java解析前端传来的Unix时间戳
java·前端·unix