M0rtzz.com

May 26, 2024Last Updated: February 14, 2025

git commit规范及自动生成CHANGELOG.md

GitConfiguration11.5 min to read

git commit规范及自动生成CHANGELOG.md

本文以Ubuntu为例。

1.卸载旧版本Node.js并安装最新版本

sudo apt remove nodejs npm
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs

检查版本:

node --version

image-20240409105930459

npm --version

image-20240409104733079

2.安装相关Package

首先安装淘宝的cnpm代替npm:

sudo npm install -g cnpm -registry=https://registry.npmmirror.com

查看版本:

cnpm --version

image-20240409105105463

sudo cnpm install -g husky cz-customizable commit-and-tag-version conventional-changelog-cli 
echo '{ "path": "cz-customizable" }' > ~/.czrc

3.本地创建git仓库

解决Git显示中文文件名为乱码:

git config --global core.quotepath false

忽略文件权限变化:

git config --global core.filemode false

设置默认编辑器为vim

git config --global core.editor vim
mkdir test-git-commit && cd test-git-commit
git init

image-20240409105753718

4.远程创建git仓库

image-20240409105727305

初始化仓库:

image-20240409111419310

5.创建本地配置文件

code .

image-20240409111651453

新建.gitignore.cz-config.jstest.txtREADME.md,内容如下:


.gitignore

.cz-config.js

.cz-config.js

module.exports = {
    types: [
        {
            value: '✨ feat',
            name: '✨ feat:\tA new feature | 新功能'
        },
        {
            value: '🚧 wip',
            name: '🚧 wip:\tWork in progress | 正在开发中'
        },
        {
            value: '🐛 fix',
            name: '🐛 fix:\tA bug fix | Bug 修复'
        },
        {
            value: '🔥 remove',
            name: '🔥 remove:\tRemove code or files | 移除代码或文件'
        },
        {
            value: '⚰️ bury',
            name: '⚰️  bury:\tBury dead code | 埋葬无用代码'
        },
        {
            value: '💩 poop',
            name: '💩 poop:\tPoop | 写了一些屎一样待优化的代码'
        },
        {
            value: '💄 ui',
            name: '💄 ui:\tUpdated UI and style files | 更新UI'
        },
        {
            value: '🎨 style',
            name: '🎨 style:\tMarkup, white-space, formatting, missing semi-colons... | 风格'
        },
        {
            value: '🔨 script',
            name: '🔨 script:\tAdd or update the build system | 脚本'
        },
        {
            value: '💡 comment',
            name: '💡 comment:\tComment | 注释'
        },
        {
            value: '🍱 asset',
            name: '🍱 asset:\tAdd or update assets | 资源'
        },
        {
            value: '📸 image',
            name: '📸 image:\tAdd or update images | 图像'
        },
        {
            value: '🔧 config',
            name: '🔧 config:\tAdd or update configuration files | 配置文件'
        },
        {
            value: '🚚 path',
            name: '🚚 path:\tMove or rename resources (e.g.: files, paths, routes) | 移动'
        },
        {
            value: '🧱 chore',
            name: '🧱 chore:\tBuild process or auxiliary tool changes | 构建/工程依赖/工具'
        },
        {
            value: '🚨 lint',
            name: '🚨 lint:\tFix compiler or linter warnings | 编译/代码检查工具警告'
        },
        {
            value: '🚑️ hotfix',
            name: '🚑️ hotfix:\tCritical hotfix | 紧急修复'
        },
        {
            value: '✏️ typo',
            name: '✏️  typo:\tFix typos | 错别字'
        },
        {
            value: '🏷️ type',
            name: '🏷️  type:\tAdd or update types | 增加或更新类型'
        },
        {
            value: '🛂 passport',
            name: '🛂 passport:\tWork on code related to authorization, roles and permissions | 授权、身份和权限相关'
        },
        {
            value: '🦺 val',
            name: '🦺 val:\tAdd or update code related to validation | 验证相关'
        },
        {
            value: '⚓ hook',
            name: '⚓ hook:\tGit hook | 钩子'
        },
        {
            value: '🐳 docker',
            name: '🐳 docker:\tDocker container | Docker容器相关'
        },
        {
            value: '☸️ k8s',
            name: '☸️  k8s:\tKubernetes | Kubernetes相关'
        },
        {
            value: '💫 anim',
            name: '💫 anim:\tAdd or update animations and transitions | 动画和过渡'
        },
        {
            value: '👔 logic',
            name: '👔 logic:\tAdd or update business logic | 业务逻辑'
        },
        {
            value: '💬 text',
            name: '💬 text:\tAdd or update text and literals | 文本和文字'
        },
        {
            value: '⚡️ perf',
            name: '⚡️ perf:\tA code change that improves performance | 性能优化'
        },
        {
            value: '🗃️ db',
            name: '🗃️  db:\tPerform database related changes | 数据库相关'
        },
        {
            value: '🧵 thread',
            name: '🧵 thread:\tAdd or update code related to multithreading or concurrency | 线程'
        },
        {
            value: '👽️ api',
            name: '👽️ api:\tUpdate code due to external API changes | API相关'
        },
        {
            value: '🔐 secert',
            name: '🔐 secert:\tSecert | 秘钥'
        },
        {
            value: '💸 fund',
            name: '💸 fund:\tAdd sponsorships or money related infrastructure | 资金相关'
        },
        {
            value: '🚀 deploy',
            name: '🚀 deploy:\tDeploy stuff | 部署'
        },
        {
            value: '🚸 usability',
            name: '🚸 usability:\tImprove user experience or usability | 增强用户体验/可用性'
        },
        {
            value: '🌐 i18n',
            name: '🌐 i18n:\tInternationalization | 国际化'
        },
        {
            value: '🌐 l10n',
            name: '🌐 l10n:\tLocalization | 本地化'
        },
        {
            value: '🔀 merge',
            name: '🔀 merge:\tMerge branch | 合并'
        },
        {
            value: '⏪ revert',
            name: '⏪ revert:\tRevert | 回退'
        },
        {
            value: '🔖 release',
            name: '🔖 release:\tCreate a release commit | 发行版'
        },
        {
            value: '📦 build',
            name: '📦 build:\tBuild System | 打包构建'
        },
        {
            value: '🥅 catch',
            name: '🥅 catch:\tCatch errors | 捕捉错误'
        },
        {
            value: '🔊 add_log',
            name: '🔊 add_log:\tAdd or update logs | 添加日志'
        },
        {
            value: '🔇 rm_log',
            name: '🔇 rm_log:\tRemove logs | 移除日志'
        },
        {
            value: '➕ add_dep',
            name: '➕ add_dep:\tAdd dep | 添加依赖'
        },
        {
            value: '➖ rm_dep',
            name: '➖ rm_dep:\tRemove dep | 移除依赖'
        },
        {
            value: '⬆️ up_dep',
            name: '⬆️  up_dep:\tUpgrade dep | 升级依赖'
        },
        {
            value: '⬇️ down_dep',
            name: '⬇️  down_dep:\tDowngrade dep | 降级依赖'
        },
        {
            value: '📌 pin_dep',
            name: '📌 pin_dep:\tPin dependencies to specific versions | 固定依赖版本'
        },
        {
            value: '💚 fix_ci',
            name: '💚 fix_ci:\tFix CI Build | CI 修复'
        },
        {
            value: '👷 ci',
            name: '👷 ci:\tCI related changes | CI 配置'
        },
        {
            value: '📈 monitor',
            name: '📈 monitor:\tAdd or update analytics or track code | 分析或跟踪'
        },
        {
            value: '♻️ refactor',
            name: '♻️  refactor:\tA code change that neither fixes a bug or adds a feature | 代码重构'
        },
        {
            value: '✅ test',
            name: '✅ test:\tAdding tests | 测试'
        },
        {
            value: '⚗️ exp',
            name: '⚗️  exp:\tPerform experiments | 实验'
        },
        {
            value: '🎉 init',
            name: '🎉 init:\tBegin a project | 初始化'
        },
        {
            value: '🙈 ignore',
            name: '🙈 ignore:\tAdd or update a .gitignore file | 忽略'
        },
        {
            value: '📄 license',
            name: '📄 license:\tAdd or update license | 证书'
        },
        {
            value: '📝 doc',
            name: '📝 doc:\tDocumentation only changes | 文档'
        },
    ],
    messages: {
        type: '请选择提交类型(必填):',
        customScope: '请输入文件修改范围(可选):',
        subject: '请简要描述提交(必填):',
        body: '请输入详细描述(可选):',
        breaking: '列出任何\x1b[1;31mBREAKING CHANGES\x1b[0m(可选):',
        footer: '请输入要关闭的\x1b[1;38;5;129missue\x1b[0m(可选):',
        confirmCommit: '确定提交此说明吗?:'
    },
    allowCustomScopes: true,
    allowBreakingChanges: ['✨ feat', '🐛 fix', '🚧 wip', '🔥 remove', '🚚 path', '💩 poop', '⏪ revert', '➖ rm_dep', '➕ add_dep', '⬆️ up_dep', '⬇️ down_dep', '📌 pin_dep'],
    subjectLimit: 100
};

test.txt

1

README.md

# FIRST COMMIT

之后进行npm init

npm init

依次填写即可(注意git远程仓库url不能填错):

image-20240409112455334

生成.husky文件夹:

husky

image-20240409131309772

6.修改配置文件

修改.husky/_/pre_push

原先:

#!/usr/bin/env sh
. "${0%/*}/h"

修改后:

#!/usr/bin/env sh

git pull --rebase || {
    echo "\e[1;31m代码拉取失败\e[0m"
    exit 1
}

echo "\e[1;32m\e[1m代码拉取成功\e[0m"

commit-and-tag-version

# 获取仓库根目录
repo_root_dir=$(git rev-parse --show-toplevel)

# 获取最新的标签
latest_tag_global=$(git describe --tags "$(git rev-list --tags --max-count=1)")

git tag -d "${latest_tag_global}"

updateVersionTag() {
    latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)" 2>/dev/null)

    # 使用正则表达式从标签中提取主版本号、次版本号和修订号
    major_version=$(echo "${latest_tag}" | cut -d '.' -f 1 | sed 's/v//')
    minor_version=$(echo "${latest_tag}" | cut -d '.' -f 2)
    patch_version=$(echo "${latest_tag}" | cut -d '.' -f 3)

    # 如果是第一次提交,即没有标签
    if [ -z "${latest_tag}" ]; then
        new_tag="v1.0.0"
        echo "${new_tag}"
        return
    fi

    # 更新版本号
    if [ "${patch_version}" -eq 9 ]; then
        # 如果修订号为 9
        patch_version=0 # 将修订号重置为 0

        # 接下来检查次版本号
        if [ "${minor_version}" -eq 9 ]; then
            # 如果次版本号也为 9
            minor_version=0                      # 将次版本号重置为 0
            major_version=$((major_version + 1)) # 将主版本号加 1
        else
            # 如果次版本号不是 9
            minor_version=$((minor_version + 1)) # 将次版本号加 1
        fi
    else
        # 如果修订号不是 9
        patch_version=$((patch_version + 1)) # 将修订号加 1
    fi

    new_tag="v${major_version}.${minor_version}.${patch_version}"

    # 返回
    echo "${new_tag}"
}

updateVersionInPackageJson() {
    new_version="$1"
    sed -i "s/\"version\": \".*\"/\"version\": \"${new_version}\"/" "${repo_root_dir}/package.json"
}

new_tag=$(updateVersionTag)

updateVersionInPackageJson "${new_tag}"
git add "${repo_root_dir}/package.json"
git commit --amend -m "🔖 tag(package.json): ${new_tag}"

rm "${repo_root_dir}/CHANGELOG.md"
conventional-changelog -i "${repo_root_dir}/CHANGELOG.md" -s -r 0

# 获取当前仓库的远程URL
remote_url=$(git remote -v | grep origin | grep '(fetch)' | awk '{print $2}')

# 处理commit URL
case "${remote_url}" in
*github.com*)
    echo "\e[1;32mRemote URL is from GitHub.\e[0m"
    ;;
*gitee.com*)
    echo "\e[1;32mRemote URL is from Gitee.\e[0m"
    sed -i 's/commits\//commit\//g' "${repo_root_dir}/CHANGELOG.md"
    ;;
*gitcode.com*)
    echo "\e[1;32mRemote URL is from GitCode.\e[0m"
    sed -i 's/commits\//commits\/detail\//g' "${repo_root_dir}/CHANGELOG.md"
    ;;
*)
    echo "\e[1;31mRemote URL is from an unknown source.\e[0m"
    ;;
esac

git add "${repo_root_dir}/CHANGELOG.md"
git commit -m "📝 doc(CHANGELOG.md): automatic update"

git tag -a "${new_tag}" -m "🔖 tag: ${new_tag}"
echo "\e[1;32m更新后的标签为: ${new_tag}\e[0m"

7.测试

git add .

使用cz-cust代替git commit -m

cz-cust

选择init

image-20240409130252142

image-20240409130354052

或者起一个别名:

sudo vi /etc/profile # 为了使多用户和多Shell解释器同时生效

在最后加上:

alias cz='cz-cust'

保存退出:

source /etc/profile
git log
git push origin --set-upstream --follow-tags -u master

可以看到已经自动生成了CHANGELOG.md

image-20240409131748005

此时远程仓库已经更新:

image-20240409161015571

8.Tips

之后推送代码可使用以下命令:

git push --follow-tags && sed -i 's/^/# /' $(git rev-parse --show-toplevel)/.husky/_/pre-push && git push --follow-tags && sed -i 's/^# //' $(git rev-parse --show-toplevel)/.husky/_/pre-push