Git分支按最后提交日期排序:三种实用命令详解
最近在维护一个持续迭代的项目时分支数量越堆越多很多功能分支合并完之后并没有及时清理本地残留了大量旧分支。想快速找出来“哪些分支已经几个月没人动”结果发现git branch默认只按字母序显示分支名完全看不出时间信息甚至很难判断这些分支是活跃还是已经废弃。于是我把 Git 分支按最后提交日期排序的方法系统整理了一遍这篇文章就是这次梳理的完整记录。本文会覆盖三种主流做法git branch --sort-committerdate最简单直接的排序命令。git for-each-ref --sort-committerdate --format...更灵活的完整解决方案。用git log自行拼接排序了解原理方便自定义。同时会讲清楚committerdate、authordate、creatordate的区别以及如何结合筛选条件定位长时间未更新的“僵尸分支”。新手可以跟着从准备演示仓库开始操作有经验的开发者可以直接跳到命令示例和最佳实践部分。1. 为什么需要按提交日期排序分支1.1 分支多了之后的“失控感”在多人协作的项目中每个需求、每个 Bug 修复都可能开一个分支。随着迭代次数增加分支列表会越来越长。默认的git branch输出长这样$ git branch bugfix/payment feature/login feature/order * master如果项目里只有 5 个分支这样看没有问题。但当分支数量到达 30 个、50 个甚至更多时仅凭分支名很难判断哪些分支还在活跃开发哪些分支已经合并完但忘了删除。尤其到了版本发布的节点需要确认“哪些分支还没合并”“哪些分支最近已经废弃”这时候最直观的指标就是“这个分支最后一次提交是什么时候”。按最后提交日期排序本质上是在回答一个问题这个分支到底还有没有人在维护。1.2 “最后提交日期”到底指什么Git 里的时间并不只有一种。在git log输出中每次提交至少有两个时间概念author date作者创建这次提交的时间。committer date提交者把这次提交写入当前分支/仓库的时间。平时git log默认显示的时间通常是作者时间。而在分支排序场景下我们更关心的是“这个分支最后被更新到哪一刻”对应的是committer date因为通过 rebase、cherry-pick、apply patch 等操作后committer date 会更新更能反映这个分支当前指向的提交实际进入仓库的时间。还需要注意一个容易混淆的概念分支本身并不是一个“容器”它只是一个指向某个提交的指针。所以“按分支最后提交日期排序”本质是查看“每个分支指针指向的那次提交的 committer date”然后按这个时间排序。这个理解对后面排查问题很有帮助。还有一个概念是creatordate它表示引用ref本身的创建或最后更新时间。针对分支这种引用creatordate和它指向的提交关系比较密切在多数使用场景下我们直接按提交时间排序即可。2. 环境准备与版本说明2.1 本教程的验证环境本文的命令在 Git 2.x 环境下验证通过具体场景如下操作系统Linux / macOS 均可Windows 用户建议使用 Git Bash 执行命令。Git 版本任意较新的 2.x 版本git branch --sort和git for-each-ref --sort都属于长期存在的功能。终端编码建议使用 UTF-8避免中文分支名显示异常。如果你还不确定自己电脑的 Git 版本可以执行git --version我本机测试时使用 Git 2.39 系列如果你的项目环境版本较老建议先升级再继续操作。如果你的电脑还没有安装 Git需要先完成安装和基础配置。这里简单提醒一下Windows 可以从 Git 官网下载安装包安装时保持默认选项即可macOS 可以使用 Homebrew 安装brew install git安装完成后至少配置用户名和邮箱否则 commit 操作会失败或提示缺失身份信息git config --global user.name your name git config --global user.email youexample.com2.2 准备一个便于演示的仓库为了能够看清楚排序效果这里先创建一个包含多个分支、且不同分支提交时间有差异的测试仓库。git init demo-branch-sort cd demo-branch-sort git config user.name dev git config user.email devexample.com echo # demo README.md git add . git commit -m init project接着创建几个分支并在每个分支上进行独立提交git checkout -b feature/login echo login feature login.txt git add . git commit -m add login feature git checkout master git checkout -b feature/order echo order feature order.txt git add . git commit -m add order feature git checkout master git checkout -b bugfix/payment echo fix payment payment.txt git add . git commit -m fix payment bug git checkout master echo update readme README.md git add . git commit -m update readme最终仓库里会有 4 个本地分支master最后一次提交是 “update readme”。feature/login最后一次提交是 “add login feature”。feature/order最后一次提交是 “add order feature”。bugfix/payment最后一次提交是 “fix payment bug”。由于每个分支都只提交了一次且时间点不同方便后续观察排序结果。3. 核心命令拆解三种排序方式下面我们一层一层把按最后提交日期排序的方案讲透。先看最省事的再看最灵活的最后讲一个“理解原理”的做法。3.1 最简单的git branch --sort这是日常使用中最直接的排序方式。写法git branch --sort-committerdate在这个输出中分支会按照“最后一次提交时间”倒序排列最新提交的分支排在最上面$ git branch --sort-committerdate * master bugfix/payment feature/order feature/login如果去掉前面的减号就变成正序排列git branch --sortcommitterdate在实际使用中我们绝大部分场景都希望“越新的分支排越前面”所以-committerdate是更常用的写法。配合-v参数可以同时看到每个分支的最新提交哈希和提交信息git branch -v --sort-committerdate输出示例* master a1b2c3d update readme bugfix/payment b2c3d4e fix payment bug feature/order c3d4e5f add order feature feature/login d4e5f6a add login feature这里的哈希和提交信息只是演示真实执行时以你仓库中的结果为准。git branch -v --sort特别适合日常快速浏览因为它一行就能看出分支最后改了什么。3.2 更灵活的git for-each-refgit branch --sort虽然简单但输出字段固定无法自由控制显示内容、时间格式和排序键。如果需要把分支和时间信息输出为表格或者方便脚本处理推荐使用git for-each-ref。基础用法git for-each-ref --sort-committerdate \ --format%(committerdate:iso8601) | %(refname:short) | %(subject) \ refs/heads其中refs/heads表示只遍历本地分支引用。%(committerdate:iso8601)是提交时间的 ISO8601 格式。%(refname:short)是去掉了refs/heads前缀的短分支名。%(subject)是该分支最新一次提交的第一行说明。输出示例2025-01-06 10:23:45 0800 | master | update readme 2025-01-06 10:12:30 0800 | bugfix/payment | fix payment bug 2025-01-06 10:02:11 0800 | feature/order | add order feature 2025-01-05 18:20:09 0800 | feature/login | add login feature注意上面示例里显示的时间是我为了演示补充的说明文字实际执行时看到的时间取决于你仓库里的真实提交时间。如果你想让人一眼看出分支多久没更新可以把时间格式改成相对时间git for-each-ref --sort-committerdate \ --format%(committerdate:relative) | %(refname:short) | %(subject) \ refs/heads输出示例2 hours ago | master | update readme 4 hours ago | bugfix/payment | fix payment bug 5 hours ago | feature/order | add order feature 2 days ago | feature/login | add login feature这种格式阅读起来非常直观特别适合分支清理前的“体检”环节。3.3 用git log自己拼接排序如果不想记忆for-each-ref的格式化字段也可以用git log配合 shell 循环手动拼接分支列表git branch | while read branch; do branch$(echo $branch | sed s/\* //) last_commit$(git log -1 --format%ci %s $branch 2/dev/null) echo $branch $last_commit done | sort -k2,2r这段脚本的大致逻辑是git branch列出所有本地分支带*的是当前分支。用sed去掉行首的*。git log -1 --format%ci %s取该分支最后一次提交的提交时间和提交说明。最后用sort -k2,2r按时间列倒序排序。这个方案比较绕而且每执行一个分支都要调用一次git log分支很多时速度会慢。推荐只是在理解“排序本质”时参考生产环境直接用git for-each-ref。3.4 排序字段的选择-committerdate并不是唯一可用的排序字段Git 还支持按多个时间维度排序committerdate提交者时间最常见适合判断分支最后的活跃时间。authordate作者时间适合追溯原作者是谁、原始提交时间是什么。creatordate引用创建时间适合查看分支创建的时间。在绝大多数分支清理场景里我们只需要关注committerdate。因为 rebase、cherry-pick、fast-forward 合并等操作都会更新提交者时间用它判断分支“最后被更新”最准确。如果你还想看一下当前 HEAD 指向哪个分支可以用git for-each-ref --sort-committerdate \ --format%(HEAD) %(refname:short) | %(committerdate:relative) \ refs/heads%(HEAD)如果以*开头说明那个分支就是当前所在分支。4. 完整实战按最后提交日期管理本地分支这一节把上面的命令串起来做一次完整的实战演示覆盖查看、筛选、清理准备整个流程。4.1 查看默认分支列表先回到前面创建的演示仓库cd demo-branch-sort不添加任何参数时分支按名称排序列出$ git branch bugfix/payment feature/login feature/order * master这个列表在分支数量少的时候还可以接受但信息量仅限于名字。4.2 按提交日期倒序查看执行git branch -v --sort-committerdate输出大致为* master a1b2c3d update readme bugfix/payment b2c3d4e fix payment bug feature/order c3d4e5f add order feature feature/login d4e5f6a add login feature排序结果从最新提交到最旧提交排列。在真实项目中这种视图能立刻发现哪些分支最近还在活跃哪些已经停了很久。4.3 用for-each-ref输出更完整的信息如果觉得-v的信息还不够可以用下面的命令把时间精确到秒并且显示提交人和提交说明git for-each-ref --sort-committerdate \ --format%(committerdate:iso8601) | %(refname:short) | %(authorname) | %(subject) \ refs/heads在团队协作中这种格式非常适合直接导出给同事确认“这个分支是你负责的吗最后提交时间是 X还要继续保留吗”4.4 只看远程仓库分支本地分支往往和远程分支对应。如果只想看远程分支把refs/heads换成refs/remotesgit for-each-ref --sort-committerdate \ --format%(committerdate:iso8601) | %(refname:short) | %(subject) \ refs/remotes也可以直接使用简短写法git branch -r --sort-committerdate这里有一点要注意远程分支refname默认显示为origin/feature/login这样的形式。如果想去掉origin/前缀可以用%(refname:lstrip2)代替%(refname:short)git for-each-ref --sort-committerdate \ --format%(committerdate:iso8601) | %(refname:lstrip2) | %(subject) \ refs/remoteslstrip2的意思是去掉引用路径的前两段也就是去掉refs/remotes和origin。4.5 结合相对时间定位不活跃分支分支清理前最常问的问题是“哪些分支超过 30 天没有更新了”先把所有分支按相对时间展示出来git for-each-ref --sort-committerdate \ --format%(committerdate:relative) | %(refname:short) | %(subject) \ refs/heads输出中你会看到类似2 days ago、6 weeks ago、3 months ago这样的相对时间。相对时间是动态计算的适合人工阅读但它不是固定字段拿去做精确的日期过滤时不方便。如果需要精确过滤超过 30 天的分支可以借助 unix 时间戳写一段小脚本git for-each-ref --sort-committerdate \ --format%(committerdate:unix) %(refname:short) refs/heads | while read ts branch; do now$(date %s) diff$(( (now - ts) / 86400 )) if [ $diff -gt 30 ]; then echo $branch : $diff days fi done这段脚本的思路%(committerdate:unix)输出 Unix 时间戳。date %s获取当前时间戳。两者相减得到秒数再除以 86400 换算成天数。diff -gt 30表示超过 30 天未更新的分支会被打印出来。注意这段脚本需要 bash 或 Git Bash 环境Windows 的 CMD/PowerShell 直接执行可能不兼容。5. 常用组合筛选、统计与脚本化5.1 找出已经合并到主分支的旧分支除了按时间排序还可以结合--merged参数只显示已经合并到指定分支的旧分支git branch --merged master --sort-committerdate这种命令非常适合清理场景已经合并到 master 的分支如果没有特殊原因通常就是可以删除的候选对象。再配合最后提交时间就能把“已合并 长时间未更新”的分支一次性列出来。如果要列出未合并分支把--merged改成--no-mergedgit branch --no-merged master --sort-committerdate未合并分支即使旧也可能包含尚未合入的功能或专属环境的修复删除前必须二次确认。5.2 统计每个分支的最后提交人和提交邮箱团队维护时如果想知道“某个分支最近是谁在提交”用committeremail和authorname字段git for-each-ref --sort-committerdate \ --format%(committerdate:iso8601) | %(refname:short) | %(committeremail) | %(subject) \ refs/heads输出后可以按提交人过滤例如查看某个开发者的最近分支提交情况git for-each-ref --sort-committerdate \ --format%(committerdate:iso8601) | %(refname:short) | %(committeremail) \ refs/heads | grep zhangsanexample.com这里grep只是简单的字符串过滤实际上这种方式比在浏览器里翻 GitLab/GitHub 分支列表要快得多。5.3 把命令固化成 Git 别名每次输入一大段for-each-ref命令比较麻烦可以把它配置成 Git 别名。在终端执行git config --global alias.recent for-each-ref --sort-committerdate --format%(committerdate:relative) | %(refname:short) | %(authorname) | %(subject) refs/heads配置好之后直接执行git recent就能看到所有本地分支的最后提交时间和提交信息。也可以配置查看远程分支的别名git config --global alias.remote-recent for-each-ref --sort-committerdate --format%(committerdate:relative) | %(refname:lstrip2) | %(authorname) | %(subject) refs/remotes日常使用git remote-recent即可。在 Windows 环境如果使用 Git Bash别名里的单引号不会出问题如果你在 PowerShell