在实际 Git 仓库里,分支数量一旦多起来,按字母顺序展示的git branch列表几乎不提供有效信息。开发者真正想知道的是:哪些分支最近还在提交,哪些分支已经几个月没有动静,哪些分支可以进入清理流程。按最后提交时间(last commit date)排序分支,就是 Git 官方语法里最直接、也最常被忽略的一组能力。这篇文章围绕git branch --sort和git for-each-ref两条主线,讲清楚排序原理、命令写法、输出格式、验证方法和清理实战。
这篇文章适合三类读者:一是刚从 SVN 切到 Git、正在建立分支管理习惯的同学;二是仓库里堆了几十个历史分支、想安全清理的老手;三是在 CI 或发布脚本里需要按时间输出分支列表的运维和平台开发。读完可以做到:用一条命令把分支按最后提交时间倒序排出来,在列表里看到日期、短哈希和提交说明,定位超过 30 天或 90 天未更新的分支,并在删除前完成安全性检查。
1. 为什么需要“按最后提交时间排序分支”
1.1 多分支仓库里的真实困难
一个仓库同时存在主干分支、功能分支、修复分支、发布分支和试验分支时,git branch默认按分支名做字典序排序。这种顺序只能告诉用户“有哪些分支”,无法告诉用户“这些分支现在处于什么状态”。
举个例子:feature/login、feature/old-refactor、release/2024-fix这三个分支分别对应三个月前、两年前、一个月前的提交,但从字母序列表里完全看不出差异。开发者在没有上下文的情况下很容易误操作,比如基于一个早已废弃的旧分支继续开发,或者把还没合并完的分支当作已归档分支删除。
排序之后,列表会直接暴露两个关键信息:分支最后一次提交发生在什么时候,以及所有分支在时间轴上的相对先后。这个信息对分支清理、代码审查排队、发布前检查都非常有用。
1.2 排序命令解决什么问题
Git 提供两类原生的“按时间排序分支”能力:
git branch --sort=<字段>:给git branch添加排序参数,输出仍然是常规分支列表,适合人眼快速浏览。git for-each-ref --sort=<字段> --format=<格式>:可以按任意引用字段排序,并自定义输出列,适合写脚本和做自动化处理。
两者都支持在字段前加-表示倒序。比如--sort=-committerdate表示按提交时间从新到旧。理解这两条命令之后,几乎所有“找出最近活跃分支”“找出陈旧分支”的需求都可以用一行命令解决,不需要借助第三方工具。
2. 先分清两个时间:提交时间与修改时间
2.1 authordate 与 committerdate 的区别
Git 里的每个提交对象包含两个时间字段:作者时间(author date)和提交时间(committer date)。
- author date 表示代码作者最初完成这份工作的时间。
- committer date 表示提交对象被写入当前仓库的时间。
正常情况下,同一个提交的两个时间相同。但只要提交对象经历被修改、重写或搬运的操作,二者就会分叉。常见触发场景包括:
git commit --amend:修改上一次提交,committer date 更新,author date 保持原样。git rebase:重放提交到新基线上,committer date 更新,author date 默认保留。git cherry-pick:把提交搬到另一个分支,committer date 更新,author date 保留。git reset后重新提交:新提交的 author date 和 committer date 都变成当前时间。
所以,如果你关心的是“这个分支最近有没有人动过”,应该看 committer date;如果你关心的是“这份工作最初是什么时候写的”,应该看 author date。
2.2 为什么排序要优先看 committerdate
“按最后提交日期排序分支”这个需求,通常指的是“分支最近的活动时间”。一个两年前写的功能,昨天被 rebase 到最新主干上,它的 author date 仍然是两年前,但 committer date 是昨天。此时如果按 author date 排序,这个分支会被误判为陈旧分支;按 committer date 排序,才能正确反映它刚刚经历过代码整理,仍然值得关注。
反之,如果一个分支今年刚创建,但提交是从三年前的工作分支里 cherry-pick 过来的,它的 author date 可能是三年前。只按 author date 排序,会把一个刚产生的发布分支看成老古董。
因此,本文所有排序示例默认使用committerdate。只有当项目有明确的“按代码写作时间归档”需求时,才改用authordate。
2.3 相关排序字段速查
| 字段 | 含义 | 什么时候变化 | 推荐场景 |
|---|---|---|---|
committerdate | 提交对象被写入仓库的时间 | amend、rebase、cherry-pick、重新提交 | 判断分支最近是否活跃 |
authordate | 作者最初编写代码的时间 | 默认提交时与 committerdate 相同 | 判断工作本身何时完成 |
creatordate | 引用创建者的日期 | 分支指向新提交后随之变化 | 查看分支 tip 提交时间 |
refname | 引用完整名称 | 不随时间变化 | 作为辅助排序或过滤条件 |
需要说明的是,对分支这类指向提交的引用,creatordate通常等于分支当前指向提交的committerdate,所以社区里许多脚本也直接使用%(creatordate:short)做输出。实际使用中,committerdate和creatordate在分支列表上的表现基本一致。
3. 准备一个带历史时间戳的测试仓库
3.1 确认 Git 版本和 Shell 环境
git branch --sort从 Git 2.7 开始提供,主流发行版默认安装的 Git 版本已经全部高于这个版本。动手前先确认环境:
git --version git config --global user.name "dev" git config --global user.email "dev@example.com"Windows 下如果还没安装 Git,可以使用官方安装包或包管理器安装;macOS 一般自带 Git,也可以通过 Homebrew 更新。验证git --version能正常输出即可进入下一步。
测试仓库会用到GIT_COMMITTER_DATE和GIT_AUTHOR_DATE环境变量伪造历史时间。以下示例按 Bash 编写:Linux 和 macOS 默认都是 Bash,Windows 上可以在 Git Bash 中执行。
3.2 创建带不同提交时间的测试分支
为了看清排序效果,在同一个空仓库里制造三个提交时间明显不同的分支:
git init demo-sort cd demo-sort # 1. 主干上的初始提交,时间设置为 2023 年 echo "base" > base.txt git add base.txt GIT_COMMITTER_DATE="2023-01-10 09:30:00 +0800" \ GIT_AUTHOR_DATE="2023-01-10 09:30:00 +0800" \ git commit -m "initial commit" # 2. 从主干创建旧功能分支,提交时间 2023 年 3 月 git checkout -b feature/old-branch echo "old" > old.txt git add old.txt GIT_COMMITTER_DATE="2023-03-01 10:00:00 +0800" \ GIT_AUTHOR_DATE="2023-03-01 10:00:00 +0800" \ git commit -m "old feature work" # 3. 回到主干,创建新功能分支,提交时间 2025 年 6 月 git checkout main git checkout -b feature/recent echo "recent" > recent.txt git add recent.txt GIT_COMMITTER_DATE="2025-06-15 14:00:00 +0800" \ GIT_AUTHOR_DATE="2025-06-15 14:00:00 +0800" \ git commit -m "recent feature work" # 4. 回到主干,更新主干分支,提交时间 2024 年 9 月 git checkout main echo "docs" > docs.txt git add docs.txt GIT_COMMITTER_DATE="2024-09-20 16:00:00 +0800" \ GIT_AUTHOR_DATE="2024-09-20 16:00:00 +0800" \ git commit -m "update docs"这段命令的关键点在于:每个分支的 tip 提交时间都不相同,这样排序结果才有区分度。设置时间时使用了带时区的格式2023-03-01 10:00:00 +0800,避免 Git 解析日期时产生歧义。
3.3 检查分支结构是否就绪
执行git branch -v确认仓库结构:
git branch -v预期输出类似:
feature/old-branch 1a2b3c4 old feature work feature/recent 04f3a2c recent feature work * main b7c9d81 update docs此时三个分支的 tip 提交分别是:
feature/recent:2025-06-15main:2024-09-20feature/old-branch:2023-03-01
这样一来,后面所有排序命令都能看到可预期的结果。
4. 用 git branch --sort 快速排序分支
4.1 基础排序:升序与降序
按提交时间从旧到新升序排列:
git branch --sort=committerdate预期输出:
feature/old-branch * main feature/recent按提交时间从新到旧降序排列:
git branch --sort=-committerdate预期输出:
feature/recent * main feature/old-branch--sort=后面的字段名带不带-前缀,决定排序方向。这个前缀必须紧贴字段名,写成--sort -committerdate是错误用法,因为空格会把-committerdate解析成独立的命令行参数。
4.2 在列表中显示提交信息
默认的git branch只显示分支名。想同时看到 tip 提交的短哈希和提交说明,可以叠加-v:
git branch -v --sort=-committerdate预期输出:
feature/recent 04f3a2c recent feature work * main b7c9d81 update docs feature/old-branch 1a2b3c4 old feature work如果还需要显示与上游分支的追踪关系,使用-vv。但要注意,git branch -v默认不显示日期字段。要想把日期直接列出来,应该使用第 5 节的git for-each-ref,它在自定义输出方面更灵活。
4.3 排序本地分支与远程分支
只看远程追踪分支:
git branch -r --sort=-committerdate本地和远程一起看:
git branch -a --sort=-committerdate远程分支对应的提交对象同样带有 commit 日期,所以排序规则和本地分支完全一致。需要提醒的是,远程追踪分支(origin/xxx)只有在git fetch或git fetch --prune之后才会更新,排序结果反映的是本地同步过的远程状态,并不一定是服务器上最新的实时状态。
5. 用 git for-each-ref 自定义分支列表输出
5.1 自定义格式化输出
git branch --sort适合快速浏览,但输出格式无法定制。想要“日期 + 分支名 + 短哈希 + 提交说明”这种明细列表,可以用git for-each-ref:
git for-each-ref --sort=-committerdate refs/heads \ --format='%(committerdate:short) | %(refname:short) | %(objectname:short) | %(subject)'预期输出:
2025-06-15 | feature/recent | 04f3a2c | recent feature work 2024-09-20 | main | b7c9d81 | update docs 2023-03-01 | feature/old-branch | 1a2b3c4 | old feature workrefs/heads是本地分支引用的命名空间,把它作为过滤参数,命令就只处理本地分支。想看远程追踪分支,把refs/heads换成refs/remotes即可。
5.2 常用格式字段解释
--format支持多种占位符,下面列出分支列表最常用的几个:
| 占位符 | 说明 | 输出示例 |
|---|---|---|
%(committerdate:short) | 提交时间,YYYY-MM-DD | 2025-06-15 |
%(committerdate:relative) | 相对当前时间的文案 | 3 months ago |
%(committerdate:iso) | ISO 8601 时间 | 2025-06-15T14:00:00+08:00 |
%(committerdate:unix) | Unix 时间戳 | 1749991200 |
%(authordate:short) | 作者时间,YYYY-MM-DD | 2025-06-15 |
%(refname:short) | 去掉 refs/heads/ 前缀的分支短名 | feature/recent |
%(objectname:short) | 提交短哈希 | 04f3a2c |
%(subject) | 提交主题 | recent feature work |
%(authorname) | 作者名 | dev |
想输出相对时间,把:short换成:relative:
git for-each-ref --sort=-committerdate refs/heads \ --format='%(committerdate:relative) | %(refname:short)'输出文案取决于执行命令当天的日期,例如“3 months ago”“1 year ago”。这种格式适合人看,不适合脚本判断,因为文案会随当前时间漂移。
5.3 用子字符串过滤分支
git for-each-ref的过滤参数支持通配符。只看某个功能前缀下的分支:
git for-each-ref --sort=-committerdate 'refs/heads/feature/*' \ --format='%(committerdate:short) %(refname:short)'也可以把输出交给grep做关键字过滤:
git for-each-ref --sort=-committerdate refs/heads \ --format='%(committerdate:short) %(refname:short)' \ | grep -E 'release|hotfix'这里的重点是理解refs/heads/feature/*的路径匹配规则。分支名中带斜杠不影响排序,%(refname:short)会原样输出完整的短名称。
6. 验证排序结果是否正确
6.1 用 git log 交叉核对单分支日期
排序命令输出之后,有必要对抽样分支做一次交叉验证,确认排序依据确实是目标时间字段。查看某个分支 tip 提交的完整时间