如何基于提交信息前缀自动升级package.json版本并纳入Commit?
解决Commit时基于前缀自动升级package.json版本的问题
让我帮你逐一拆解并解决遇到的核心问题:
1. 读取.git/COMMIT_EDITMSG的方式是否正确?
这个方式在prepare-commit-msg钩子中是完全正确的,但在pre-commit钩子中完全不可行,原因在于Git钩子的触发时机:
pre-commit触发时,用户还没输入提交信息(不管是用-m参数还是编辑器),此时.git/COMMIT_EDITMSG要么不存在,要么存的是上一次提交的内容——这就是你在pre-commit里只能拿到旧提交信息的根源。prepare-commit-msg触发时,Git已经生成了当前提交的信息(默认模板或-m传入的内容)并写入该文件,所以此时读取能拿到准确的本次提交信息。
2. pre-commit钩子中能否获取当前待提交的信息?
不能。pre-commit的触发节点是「用户输入提交信息之前、暂存区内容确定之后」,此时Git还没生成任何和当前提交相关的信息文件,没有渠道能拿到本次提交的前缀。因此pre-commit完全不适合实现「基于当前提交前缀升级版本」的需求。
3. prepare-commit-msg钩子中,如何将package.json的版本变更纳入当前Commit?
你的核心思路(执行git add ./package.json)是对的,只是需要补充几个细节确保生效:
关键调整点
- 检查
git add执行结果:shelljs执行命令后要判断返回码,避免命令失败却静默跳过:const addResult = shell.exec("git add ./package.json"); if (addResult.code !== 0) { console.error("Failed to stage package.json"); done(new Error("Git add failed")); return; } - 正确解析版本号:你代码里的
patch/minor/major变量未定义,需要从package.json中拆分:const [major, minor, patch] = pkg.version.split(".").map(Number); - 确保钩子脚本可执行:
.git/hooks/prepare-commit-msg文件需要有执行权限,内容示例:
执行#!/bin/sh npx gulp bump-versionchmod +x .git/hooks/prepare-commit-msg赋予权限即可。
为什么这样能生效?
prepare-commit-msg触发时,Git还处于提交流程的早期阶段:
- 此时执行
git add会把package.json的新版本变更加入暂存区,Git在后续提交步骤中会自动包含所有暂存文件——包括你刚刚添加的版本变更。 - 不管你是用
git commit -m "MJ: fix bug"直接提交,还是打开编辑器输入信息,这个流程都能正常工作。
调整后的完整Gulp任务示例
import * as fs from "fs"; import gulp from "gulp"; import * as shell from "shelljs"; import pkg from "./package.json"; // 定义常量 const MINOR_PREFIX = "^"; const PATCH_PREFIX = "MJ"; const BUMP_VERSION = { task: "bump-version" }; const EC = { NOT_VERSION: 0 }; const getCommitMsg = () => { try { return fs.readFileSync(".git/COMMIT_EDITMSG", "utf8").trim(); } catch (err) { console.error("Failed to read commit message:", err); return ""; } }; gulp.task( BUMP_VERSION.task, (done) => { const message = getCommitMsg(); if (!message) { done(new Error("Could not retrieve commit message")); return; } const isMinor = message.startsWith(MINOR_PREFIX); const isPatch = message.startsWith(PATCH_PREFIX); if (!isMinor && !isPatch) { console.log("No version bump prefix detected, skipping"); done(); return; } // 解析当前版本号 const [major, minor, patch] = pkg.version.split(".").map(Number); const newPatch = isPatch ? patch + 1 : patch; const newMinor = isMinor ? minor + 1 : minor; const newVersion = `${major}.${newMinor}.${newPatch}`; // 更新package.json const newPkg = { ...pkg, version: newVersion }; try { fs.writeFileSync("./package.json", JSON.stringify(newPkg, null, 4)); console.log(`Updated version to ${newVersion}`); } catch (err) { console.error("Failed to write package.json:", err); done(err); return; } // 暂存变更 const addResult = shell.exec("git add ./package.json"); if (addResult.code !== 0) { console.error("Failed to stage package.json"); done(new Error("Git add failed")); return; } done(); }, );
总结最佳实践
- 放弃
pre-commit钩子:它无法获取当前提交信息,完全不匹配你的需求。 - 采用
prepare-commit-msg钩子:既能拿到当前提交的前缀,又能通过git add将版本变更纳入本次提交。 - 给钩子脚本加执行权限,同时在Gulp任务中加入错误处理,避免静默失败。
内容的提问来源于stack exchange,提问作者MaxiJonson
相关产品推荐
相关产品推荐

