Skip to content

Instantly share code, notes, and snippets.

@millken
Last active September 27, 2025 05:04
Show Gist options
  • Select an option

  • Save millken/bf05c02db74b1f5214fa57e41232589a to your computer and use it in GitHub Desktop.

Select an option

Save millken/bf05c02db74b1f5214fa57e41232589a to your computer and use it in GitHub Desktop.
#!/bin/bash
# 获取项目根目录
root_dir=$(pwd)
# 检查路径是否被 .gitignore 忽略
is_ignored() {
local path="$1"
# 使用 git check-ignore 判断,静默处理错误(非 git 仓库时视为未忽略)
if git check-ignore -q "$path" 2>/dev/null; then
return 0
fi
return 1
}
# 函数:为指定模块创建标签
create_tag_for_module() {
local module_dir=$1
local module_name=$1
# 如果模块被 .gitignore 忽略,则跳过
if is_ignored "$module_dir"; then
echo -e "\033[33mSkipping ignored module $module_dir (listed in .gitignore)\033[0m"
return 0
fi
# 切换到模块目录
cd "$module_dir" || { echo -e "\033[31mFailed to cd to $module_dir\033[0m"; return 1; }
# 输入前先高亮显示该模块已有标签
show_module_tags "$module_name"
# 提示用户输入版本号,直到格式合法;按 ESC 或空行取消
while true; do
read -r -p "Enter version for $module_name (e.g., 1.0.0) (press ESC to cancel): " version
# 取消条件:包含 ESC 字符或为空字符串
if [[ "$version" == *$'\e'* ]] || [ -z "$version" ]; then
echo -e "\033[33mCancelled version input. Returning to menu.\033[0m"
cd "$root_dir"
return 0
fi
# 版本格式校验
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "\033[31mInvalid version format. Use MAJOR.MINOR.PATCH (e.g., 1.2.3).\033[0m"
continue
fi
# 组合标签名并检查是否已存在
tag_name="${module_name}/v${version}"
if git show-ref --tags --verify --quiet "refs/tags/${tag_name}"; then
echo -e "\033[31mTag already exists: \033[1;33m${tag_name}\033[0m"
# 再次展示已有标签,帮助用户选择新版本
show_module_tags "$module_name"
continue
fi
break
done
# 创建标签(使用轻量级标签)
git tag "$tag_name"
echo -e "\033[32mCreated tag $tag_name for $module_name\033[0m"
# 返回到根目录
cd "$root_dir"
}
# 函数:显示特定模块的标签历史
show_module_tags() {
local module_name=$1
# 标题:加粗并使用黄色背景高亮
echo -e "\033[1;30;43m Tags for module ${module_name} \033[0m"
# 列出标签并逐行高亮显示,每行加粗并使用青色前缀
git tag -l "${module_name}/*" --sort=-v:refname | while read -r tag; do
if [ -n "$tag" ]; then
echo -e "\033[1;36m> \033[1;33m${tag}\033[0m"
fi
done
}
# 主菜单
while true; do
echo "
1. Tag all modules
2. Tag a specific module
3. Show tags for a module
4. Push all tags to remote
5. Force sync remote tags (delete local and re-fetch)
6. Exit
"
read -p "Choose an option: " choice
case $choice in
1)
# 为所有模块创建标签
for module_dir in $(find . -name go.mod -exec dirname {} \;); do
# 去掉前缀 "./"
dir="${module_dir#./}"
# 跳过当前目录
if [ -z "$dir" ] || [ "$dir" = "." ]; then
continue
fi
# 跳过被 .gitignore 忽略的目录
if is_ignored "$dir"; then
echo -e "\033[33mIgnored by .gitignore: $dir\033[0m"
continue
fi
# 处理模块目录
# create_tag_for_module "$dir"
echo "$dir"
done
;;
2)
# 为特定模块创建标签,允许重试,按 ESC 取消回到上级菜单
while true; do
read -r -p "Enter module name (press ESC to cancel): " module_name
# 如果输入包含 ESC 字符,则取消
if [[ "$module_name" == *$'\e'* ]] || [ -z "$module_name" ]; then
echo -e "\033[33mCancelled. Returning to menu.\033[0m"
break
fi
if [ -d "$module_name" ] && [ -f "$module_name/go.mod" ]; then
if is_ignored "$module_name"; then
echo -e "\033[33mModule $module_name is ignored by .gitignore. Skipping.\033[0m"
break
else
create_tag_for_module "$module_name"
break
fi
else
echo -e "\033[31mModule not found or invalid. Try again or press ESC to cancel.\033[0m"
fi
done
;;
3)
# 显示特定模块的标签
while true; do
read -r -p "Enter module name to show tags (press ESC to cancel): " module_name
if [[ "$module_name" == *$'\e'* ]] || [ -z "$module_name" ]; then
echo -e "\033[33mCancelled. Returning to menu.\033[0m"
break
fi
if [ -d "$module_name" ] && [ -f "$module_name/go.mod" ]; then
show_module_tags "$module_name"
break
else
echo -e "\033[31mModule not found or invalid. Try again or press ESC to cancel.\033[0m"
fi
done
;;
4)
# 推送所有标签到远程仓库
git push --tags
echo -e "\033[32mAll tags have been pushed to the remote repository.\033[0m"
;;
5)
# 强制同步远程 tags:确认并执行删除本地 tags 然后从远程拉取
while true; do
read -r -p "This will delete all local tags and re-fetch from remote. Continue? (y/N): " confirm
if [[ "${confirm}" == *$'\e'* ]] || [ -z "${confirm}" ]; then
echo -e "\033[33mCancelled. Returning to menu.\033[0m"
break
fi
if [[ "${confirm}" =~ ^[Yy]$ ]]; then
echo -e "\033[33mFetching all remotes and pruning...\033[0m"
git fetch --all --prune
echo -e "\033[33mDeleting all local tags...\033[0m"
# delete all local tags (xargs -r avoids errors if none)
git tag -l | xargs -r git tag -d
echo -e "\033[33mFetching tags from origin...\033[0m"
git fetch --tags origin
echo -e "\033[32mTags synchronized from remote.\033[0m"
break
else
echo -e "\033[33mCancelled. Returning to menu.\033[0m"
break
fi
done
;;
6)
# 退出
echo -e "\033[32mExiting.\033[0m"
exit 0
;;
*)
echo -e "\033[31mInvalid option. Please try again.\033[0m"
;;
esac
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment