1. 先说场景:我为什么要折腾"批量删除远程tag"
1.1 一次把tag打成灾难的经历
事情是这样的:我们的项目是个发布节奏比较快的服务端工程,每次发版都要打tag。上上迭代为了赶版本,我在测试环境一口气打了一排 v1.3.x 的tag,本来图省事,结果第三天产品说需求调整,这批版本全部作废,需要把对应tag从远程仓库清理干净。
我当时第一反应是手动删。执行 git push origin --delete tag名,删一个、确认一次、再删下一个。刚开始还行,删到第十几个就麻了——同样格式的命令重复敲了几十遍,不仅手指累,脑子也累。更要命的是仓库里还混着之前遗留的一堆 test_ 开头的tag,这次正好想一起清掉,结果数了数,接近五十个。
那个下午,我一边删一边想:这种纯重复操作,为什么不用脚本解决?于是放下鼠标,打开Git Bash,花二十分钟写了个批量删除远程tag的小脚本,跑完不到两分钟,几十个tag全部清干净。这篇文章就是记录我当时完整的排查路径、脚本思路和踩过的坑,希望对同样被一堆远程tag困扰的朋友有帮助。
1.2 为什么Git命令不能"一行搞定"
很多人第一反应可能是:Git不是有 git tag -d 吗?加个通配符不就行了?
这里必须拆开说清楚:git tag -d 删的是本地tag,不是远程仓库的tag。你本地删得再干净,git push 后远程的tags还在。更关键的是,Git原生并没有提供 git push --delete --pattern 这样的批量删除命令,你不能对着远程说"把 v1.3 开头的tag都给我删了"。
如果你项目用的是GitLab或者GitHub,网页上确实可以进仓库的Tags页面手动删除,但tag多了以后这个页面加载都卡,一个个点删除按钮同样费劲,而且容易误删。
所以结论很直接:批量删远程tag这件事,本质上是"批量构造并执行删除指令"的问题,那就该交给脚本。
1.3 这篇记录适合谁看
如果你满足下面几个条件,这篇内容对你应该是刚需:
- 日常开发在Windows上,终端用的是Git Bash,不是纯Linux环境
- 对Git命令有一定基础,但没怎么写过shell脚本
- 仓库里的tag数量已经多到不想在页面上手动翻
- 想找一个安全、可复用的方式,批量清理远程tag
不需要你会什么高深的bash语法,脚本总共三十行不到。真正难的不是脚本本身,而是搞清楚"远程tag在Git里到底是怎么存的"和"Windows环境下的几个隐藏坑"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 删除远程tag这件事的本质:你其实是在"推",不是"删"
2.1 远程tag看起来是"删",实际是一次特殊的push
先说一个很多初学者容易想不通的点:远程tag不是一个独立在服务器上的实体文件,它在Git的refs体系里就是一条引用记录。
远程仓库的tag存储在服务器端的 refs/tags/ 目录下,比如你打了一个 v1.3.1 的tag,远端对应的就是 refs/tags/v1.3.1 这条引用。
删除远程tag的标准命令:
bash复制git push origin --delete v1.3.1
这条命令的本质是什么?是推送一个空的引用更新到远程,让远程仓库的 refs/tags/v1.3.1 这条引用消失。
理解了这一点,批量删除的思路就打开了:既然删除的本质是"构造一个更新远程refs的指令",那我只要把远程所有 refs/tags/ 下的引用拉取出来,过滤出我要删的那些,然后逐个执行 git push origin --delete 就行了。
2.2 先搞清楚tag到底存在哪
在写脚本之前,我先做了一步探查。本地仓库里看tag,用的是 git tag -l,但这个命令默认只能看到本地存在的tags。如果我在别人的电脑上clone了一份仓库,或者远程tags是其他人推上去的,本地可能根本没有对应记录。
真正能反映远程tags全貌的命令是:
bash复制git ls-remote --tags origin
输出大概是这样的:
text复制1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0 refs/tags/v1.3.1
2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1 refs/tags/v1.3.1^{}
3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2 refs/tags/v1.3.2
4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3
