mirror of
https://gitee.com/zoujingli/ThinkAdmin.git
synced 2026-06-07 20:48:09 +08:00
530 lines
13 KiB
Bash
530 lines
13 KiB
Bash
#!/bin/bash
|
||
|
||
# commit 类型 键
|
||
TYPE_MAP=(feat fix refactor style docs chore build ci pref test)
|
||
# commit 类型 值
|
||
TYPE_TITLE_MAP=(新增 修改 重构 样式 文档 其他 构建 持续集成 优化 测试)
|
||
# 当前日期+时间
|
||
NOW=$(date "+%F %H:%M")
|
||
# 起始日期
|
||
SINCE="last.Monday"
|
||
# 终止日期
|
||
UNTIL=$(date +%F)
|
||
# 是否覆盖文件,默认否
|
||
FOUCE=0
|
||
# 首行是否为生成日期,默认否
|
||
PRINT_TIME=0
|
||
# 输出目录
|
||
OUTPUT_DIR="log"
|
||
# 是否已设置作者, 默认否
|
||
HAS_SET_AUTHOR=0
|
||
|
||
# function,去除字符串两头空格
|
||
trim() {
|
||
trimmed=$1
|
||
# https://blog.csdn.net/dongwuming/article/details/50605911
|
||
# 从变量$string的结尾, 删除最长匹配$substring的子串
|
||
trimmed=${trimmed%% }
|
||
# 从变量$string的开头, 删除最长匹配$substring的子串
|
||
trimmed=${trimmed## }
|
||
# TODO 首部空格无法全部移除
|
||
trimmed=${trimmed## }
|
||
echo $trimmed
|
||
# 移除全部空格
|
||
# echo ${trimmed// /}
|
||
}
|
||
|
||
# function,是否应该强制生成文件
|
||
shouldFouceResolve() {
|
||
# 输出文件已存在 & 不强制生成 则提示并退出
|
||
if [ -e $OUTPUT -a $FOUCE -eq 0 ]; then
|
||
echo "${OUTPUT} already exists! \n
|
||
Please update package.json or remove log file or add '-f' follow the command!\n
|
||
"
|
||
exit 1
|
||
fi
|
||
}
|
||
# function,首行是否应该输出生成日期
|
||
shouldPrintTime() {
|
||
if [ $PRINT_TIME -eq 1 ]; then
|
||
echo "> Generated on ${NOW} By [Gen-Git-Log](https://www.npmjs.com/package/gen-git-log)\n"
|
||
|
||
case $MODE in
|
||
branch)
|
||
# 分支模式
|
||
TITLE="${TARGET}...${SOURCE}"
|
||
;;
|
||
tag)
|
||
# 标签模式
|
||
TITLE="${TARGET}...$(getVersion)"
|
||
;;
|
||
esac
|
||
|
||
if [ ! -z $TITLE ]; then
|
||
# 如果TITLE赋值则组装输出
|
||
TITLE="## [${TITLE}](${REMOTE}/compare/${TITLE})"
|
||
echo $TITLE
|
||
fi
|
||
fi
|
||
}
|
||
# function,获取版本
|
||
getVersion() {
|
||
# 判断是否自定义版本
|
||
if [ -z $VERSION ]; then
|
||
# 未传入版本
|
||
VERSION=v1.0.0
|
||
fi
|
||
trim $VERSION
|
||
}
|
||
# function,生成输出文件路径
|
||
generateOutPutPath() {
|
||
# 输出文件路径,默认“v版本.md”
|
||
echo "${OUTPUT_DIR}/$(getVersion).md"
|
||
}
|
||
# 生成指定SOURCE TARGET tag差异记录
|
||
genSingleTagLog() {
|
||
SOU=$1
|
||
TAR=$2
|
||
|
||
if [ ! "$3"x = "0"x ]; then
|
||
if [ $SOU = HEAD ]; then
|
||
# 如果是与最新HEAD对比则将HEAD设为version
|
||
TIT="## [$(getVersion)](${REMOTE}/compare/${TAR}...$(getVersion))"
|
||
else
|
||
TIT="## [${SOU}](${REMOTE}/compare/${TAR}...${SOU})"
|
||
fi
|
||
echo
|
||
echo
|
||
echo "${TIT} ($(git -C "${REPO}" log -1 --format=%ad --date=short $SOU))"
|
||
fi
|
||
|
||
# 直接使用分支比对查找所有匹配 log
|
||
GIT_PAGER=$(git -C "${REPO}" log --no-merges --reverse --format="${LOG_FORMAT}" "$SOU...$TAR")
|
||
|
||
if [ ! -z "$GIT_PAGER" ]; then
|
||
# 字符串分隔符
|
||
IFS="*"
|
||
# 分割字符串为数组
|
||
singleTagArr=($GIT_PAGER)
|
||
# 还原分割符,否则会导致if判断失效
|
||
IFS=""
|
||
# 循环处理数组
|
||
for s in ${singleTagArr[@]}; do
|
||
# 去除字符串两头空格
|
||
s=$(trim $s)
|
||
# 判断字符串非空
|
||
if [ ! -z $s ]; then
|
||
# 替换全角冒号
|
||
s=${s/:/:}
|
||
# 循环commit 类型
|
||
for type in ${TYPE_MAP[@]}; do
|
||
# 组织正则
|
||
reg="${type}:"
|
||
# 判断commit类型
|
||
if [[ ${s} == *"${reg}"* ]]; then
|
||
# 裁剪字符串
|
||
s=${s##*${reg}}
|
||
s=${s%@*}
|
||
# 移除空格
|
||
s=$(trim $s)
|
||
# 动态数组变量赋值
|
||
eval COMMIT_${type}='(${COMMIT_'${type}'[*]} $s)'
|
||
break
|
||
fi
|
||
done
|
||
fi
|
||
done
|
||
|
||
# 处理数据
|
||
typeIndex=0
|
||
for type in ${TYPE_MAP[@]}; do
|
||
# 拷贝数组
|
||
eval type='(${COMMIT_'${type}'[*]})'
|
||
|
||
# 判断数组是否含有元素
|
||
if [ ${#type[*]} != 0 ]; then
|
||
echo "#### ${TYPE_TITLE_MAP[$typeIndex]}"
|
||
|
||
for i in ${type[@]}; do
|
||
echo "* ${i}"
|
||
done
|
||
echo
|
||
fi
|
||
let typeIndex++
|
||
done
|
||
fi
|
||
}
|
||
|
||
# 生成tag
|
||
genTagLog() {
|
||
LOG_FORMAT="$FORMAT_DEFAULT @%ae"
|
||
|
||
OUTPUT=$(generateOutPutPath)
|
||
|
||
shouldFouceResolve
|
||
|
||
# 获取最新标签
|
||
LASTEST_TAG=$(git -C "${REPO}" describe --tags $(git -C "${REPO}" rev-list --tags --max-count=1))
|
||
|
||
if [ -z $SOURCE ]; then
|
||
SOURCE="HEAD"
|
||
fi
|
||
if [ -z $TARGET ]; then
|
||
TARGET=$LASTEST_TAG
|
||
fi
|
||
|
||
case $1 in
|
||
copy)
|
||
# 如果传参为 copy 则将结果输出至剪切板
|
||
genSingleTagLog $SOURCE $TARGET 0 | pbcopy
|
||
;;
|
||
release-note)
|
||
# 如果传参为 release-note 则将结果直接输出
|
||
genSingleTagLog $SOURCE master 0
|
||
;;
|
||
*)
|
||
(
|
||
shouldPrintTime
|
||
genSingleTagLog $SOURCE $TARGET 0
|
||
) >$OUTPUT
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# 传参覆盖默认值
|
||
while getopts "m:a:s:u:S:T:r:v:ftd:h" arg; do
|
||
case $arg in
|
||
m)
|
||
# 模式
|
||
MODE=$OPTARG
|
||
;;
|
||
a)
|
||
# 作者
|
||
AUTHOR=$OPTARG
|
||
# 已设置作者
|
||
HAS_SET_AUTHOR=1
|
||
;;
|
||
s)
|
||
# 起始日期
|
||
SINCE=$OPTARG
|
||
;;
|
||
u)
|
||
# 终止日期
|
||
UNTIL=$OPTARG
|
||
;;
|
||
S)
|
||
# 源分支/标签
|
||
SOURCE=$OPTARG
|
||
;;
|
||
T)
|
||
# 目标分支/标签
|
||
TARGET=$OPTARG
|
||
;;
|
||
r)
|
||
# Git 仓库本地路径
|
||
REPO=$OPTARG
|
||
;;
|
||
v)
|
||
# 自定义版本
|
||
VERSION=$OPTARG
|
||
;;
|
||
f)
|
||
# 强制覆盖文件
|
||
FOUCE=1
|
||
;;
|
||
t)
|
||
# 首行输出生成日期
|
||
PRINT_TIME=1
|
||
;;
|
||
d)
|
||
# 输出目录路径
|
||
OUTPUT_DIR=$OPTARG
|
||
;;
|
||
h)
|
||
echo "
|
||
Usage:\n
|
||
git-log [options]\n
|
||
|
||
Options:\n
|
||
-m 生成模式 默认:无(周报),可选:branch(分支比对)、tag(标签比对)、changelog(汇总日志)
|
||
-a 想要过滤的作者 默认:$(git config user.name)
|
||
-s 起始日期 默认:上周一,格式:2018-01-01
|
||
-u 终止日期 默认:当天,格式:2018-01-01
|
||
-S 源分支/标签 默认:无,比对模式:当前分支/最近标签
|
||
-T 目标分支/标签 默认:无,比对模式:当前分支/当前HEAD
|
||
-r Git 仓库本地路径 默认:当前目录
|
||
-v 版本号 默认:无,比对模式:仓库路径下 package.json 中 VERSION 字段值
|
||
-f 覆盖文件 默认:否,不需要传值
|
||
-t log 首行为生成日期 默认:否,不需要传值
|
||
-d log 输出目录 默认:仓库路径下 log 文件夹
|
||
"
|
||
exit 2
|
||
;;
|
||
?)
|
||
echo "unknown argument"
|
||
exit 3
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 判断是否需要设置默认作者
|
||
if [ $HAS_SET_AUTHOR -eq 0 ]; then
|
||
# 没有设置过则使用默认作者
|
||
AUTHOR=$(git -C "${REPO}" config user.name)
|
||
fi
|
||
|
||
# 获取远程仓库地址
|
||
REMOTE=$(git -C "${REPO}" remote -v)
|
||
REMOTE=${REMOTE#*git@}
|
||
if [ ${REMOTE:0:6}x = "origin"x ]; then
|
||
# 如果是origin开头认为是http(s)协议checkout下来的,否则是ssh
|
||
REMOTE=${REMOTE#*origin}
|
||
REMOTE=${REMOTE%%.git *}
|
||
else
|
||
REMOTE=${REMOTE%%.git *}
|
||
REMOTE="http://${REMOTE/://}"
|
||
fi
|
||
|
||
REMOTE=$(trim $REMOTE)
|
||
# 格式化 https://ruby-china.org/topics/939
|
||
# %H: commit hash
|
||
# %h: 短commit hash
|
||
# %an: 提交人名字
|
||
# %ae: 提交人邮箱
|
||
# %cr: 提交日期, 相对格式(1 day ago)
|
||
# %d: ref名称
|
||
# %s: commit信息标题
|
||
# %cd: 提交日期 (--date= 制定的格式)
|
||
FORMAT_DEFAULT=" * %s ([%h](${REMOTE}/commit/%H)) "
|
||
|
||
# 判断是否指定仓库路径重写输出目录路径
|
||
if [ -z $REPO ]; then
|
||
OUTPUT_DIR="./${OUTPUT_DIR}"
|
||
else
|
||
OUTPUT_DIR="${REPO}/${OUTPUT_DIR}"
|
||
fi
|
||
|
||
# 指定目录路径不存在则创建
|
||
if [ ! -e $OUTPUT_DIR ]; then
|
||
mkdir $OUTPUT_DIR
|
||
fi
|
||
|
||
# 判断提交人,设定输出路径
|
||
if [ -z $AUTHOR ]; then
|
||
# 提交人为空
|
||
LOG_FORMAT="$FORMAT_DEFAULT <%an>"
|
||
|
||
# 输出文件路径,默认“当前日期.md”
|
||
OUTPUT="${OUTPUT_DIR}/$(date +%F).md"
|
||
else
|
||
# 有提交人
|
||
LOG_FORMAT="$FORMAT_DEFAULT - %cr"
|
||
|
||
# 输出文件路径,默认“提交人名.md”
|
||
OUTPUT="${OUTPUT_DIR}/${AUTHOR}.md"
|
||
fi
|
||
|
||
# 默认分支为当前分支
|
||
CURRENT_BRANCH=$(git -C "${REPO}" rev-parse --abbrev-ref HEAD)
|
||
|
||
case $MODE in
|
||
branch)
|
||
LOG_FORMAT="$FORMAT_DEFAULT @%ae"
|
||
|
||
OUTPUT=$(generateOutPutPath)
|
||
|
||
shouldFouceResolve
|
||
|
||
if [ -z $SOURCE ]; then
|
||
SOURCE=$CURRENT_BRANCH
|
||
fi
|
||
if [ -z $TARGET ]; then
|
||
TARGET=$CURRENT_BRANCH
|
||
fi
|
||
|
||
# 直接使用分支比对查找所有匹配 log
|
||
GIT_PAGER=$(git -C "${REPO}" log "$SOURCE...$TARGET" --no-merges --reverse --format="${LOG_FORMAT}")
|
||
|
||
(
|
||
shouldPrintTime
|
||
|
||
if [ ! -z "$GIT_PAGER" ]; then
|
||
# 字符串分隔符
|
||
IFS="*"
|
||
# 分割字符串为数组
|
||
arr=($GIT_PAGER)
|
||
# 还原分割符,否则会导致if判断失效
|
||
IFS=""
|
||
# 循环处理数组
|
||
for s in ${arr[@]}; do
|
||
# 去除字符串两头空格
|
||
s=$(trim $s)
|
||
# 判断字符串非空
|
||
if [ ! -z $s ]; then
|
||
# 替换全角冒号
|
||
s=${s/:/:}
|
||
# 循环commit 类型
|
||
for type in ${TYPE_MAP[@]}; do
|
||
# 组织正则
|
||
reg="${type}:"
|
||
# 判断commit类型
|
||
if [[ ${s} == *"${reg}"* ]]; then
|
||
# 裁剪字符串
|
||
s=${s##*${reg}}
|
||
s=${s%@*}
|
||
# 移除空格
|
||
s=$(trim $s)
|
||
# 动态数组变量赋值
|
||
eval COMMIT_${type}='(${COMMIT_'${type}'[*]} $s)'
|
||
break
|
||
fi
|
||
done
|
||
fi
|
||
done
|
||
|
||
# 处理数据
|
||
typeIndex=0
|
||
for type in ${TYPE_MAP[@]}; do
|
||
# 拷贝数组
|
||
eval type='(${COMMIT_'${type}'[*]})'
|
||
|
||
# 判断数组是否含有元素
|
||
if [ ${#type[*]} != 0 ]; then
|
||
echo "#### ${TYPE_TITLE_MAP[$typeIndex]}"
|
||
|
||
for i in ${type[@]}; do
|
||
echo "* ${i}"
|
||
done
|
||
echo
|
||
fi
|
||
let typeIndex++
|
||
done
|
||
else
|
||
echo "${SOURCE}...${TARGET} 分支无差异"
|
||
fi
|
||
) >$OUTPUT
|
||
;;
|
||
tag)
|
||
genTagLog
|
||
;;
|
||
changelog)
|
||
if [ -z $REPO ]; then
|
||
OUTPUT="./CHANGELOG.md"
|
||
else
|
||
OUTPUT="${REPO}/CHANGELOG.md"
|
||
fi
|
||
|
||
LOG_FORMAT="$FORMAT_DEFAULT @%ae"
|
||
|
||
shouldFouceResolve
|
||
|
||
# 标签列表
|
||
TAG_LIST=$(git -C "${REPO}" tag)
|
||
|
||
# 字符串分隔符
|
||
IFS="v"
|
||
# 分割字符串为数组
|
||
arr=($TAG_LIST)
|
||
# 还原分割符,否则会导致if判断失效
|
||
IFS=""
|
||
# 第一次提交commit, 取duan hash
|
||
LAST_TAG=$(git -C "${REPO}" rev-list --reverse HEAD | head -1)
|
||
LAST_TAG=${LAST_TAG:0:6}
|
||
FIRST_TAG="HEAD"
|
||
|
||
len=$((${#arr[@]} - 1))
|
||
|
||
(
|
||
# 循环处理数组
|
||
for ((i = $len; i > 0; i--)); do
|
||
s=${arr[$i]}
|
||
# 去除字符串两头空格
|
||
s=v$(trim $s)
|
||
# 判断字符串非空
|
||
if [ ! -z $s ]; then
|
||
# 不知道为什么一定要缓存一下,否则$s值错乱
|
||
TEMP=$s
|
||
echo $(genSingleTagLog $FIRST_TAG $s)
|
||
FIRST_TAG=$TEMP
|
||
fi
|
||
done
|
||
|
||
genSingleTagLog $FIRST_TAG $LAST_TAG
|
||
) >$OUTPUT
|
||
;;
|
||
publish)
|
||
# 发布模式
|
||
# 站在要发布分支上
|
||
# 同步代码
|
||
# 修改version
|
||
# 执行该命令
|
||
# 根据版本号生成log文件
|
||
genTagLog
|
||
# 添加所有文件到暂存区
|
||
git -C "${REPO}" add --all
|
||
# 生成commit
|
||
git -C "${REPO}" commit -m "chore: publish version $(getVersion)"
|
||
|
||
# ***********目前大部分项目不允许使用命令行操作master代码,以下代码暂时废弃************** #
|
||
# 用户确认目标分支 https://ask.helplib.com/bash/post_113951
|
||
# BRANCH="master"
|
||
# read -p"Target branch: master. (y: confirm; n: enter the target branch; c: cancel) " -n 1 -r
|
||
# echo "\n"
|
||
|
||
# case $REPLY in
|
||
# y)
|
||
# ;;
|
||
# n)
|
||
# read -p"Please enter the target branch (For example, master): " -r
|
||
# BRANCH=$REPLY
|
||
# ;;
|
||
# *)
|
||
# echo "canceled"
|
||
# exit 5
|
||
# ;;
|
||
# esac
|
||
# 切到branch
|
||
# git -C "${REPO}" checkout $BRANCH
|
||
# 合并分支到branch
|
||
# git -C "${REPO}" merge $CURRENT_BRANCH
|
||
# 生成tag
|
||
# git -C "${REPO}" tag -a "$(getVersion)" -m ""
|
||
# 推送tag
|
||
# git -C "${REPO}" push origin --tags
|
||
# 推送代码
|
||
git -C "${REPO}" push origin
|
||
# 复制release note到剪切板
|
||
pbcopy <$OUTPUT
|
||
# 完成
|
||
echo "Push success, Release note has been copied!"
|
||
;;
|
||
copy)
|
||
genTagLog "copy"
|
||
echo "Log has been copied to the clipboard"
|
||
exit 6
|
||
;;
|
||
release-note)
|
||
genTagLog "release-note"
|
||
exit 0
|
||
;;
|
||
*)
|
||
shouldFouceResolve
|
||
|
||
(
|
||
shouldPrintTime
|
||
# 先根据起始及终止时间查找符合条件的log并且把日期格式化后输出
|
||
# 之后遍历所有输出的日期,在根据日期查询当天内的log进行打印
|
||
git -C "${REPO}" log --since="${SINCE}" --until="${UNTIL}" --format="%cd" --date=short | sort -u | while read DATE; do
|
||
GIT_PAGER=$(git -C "${REPO}" log --no-merges --reverse --format="${LOG_FORMAT}" --since="${DATE} 00:00:00" --until="${DATE} 23:59:59" --author="${AUTHOR}")
|
||
if [ ! -z "$GIT_PAGER" ]; then
|
||
echo "[${DATE}]"
|
||
echo "${GIT_PAGER}"
|
||
echo
|
||
fi
|
||
done
|
||
) >$OUTPUT
|
||
;;
|
||
esac
|
||
|
||
echo "Log has been written to '${OUTPUT}'"
|