上周有个读者问我:公司半年复盘要交个人代码贡献数据,项目代码平时都在 IDEA 里打开,能不能直接在 IDEA 里统计提交代码行数排名?我说当然能,但这里有一个容易踩坑的前提——IDEA 自带的 Git 日志窗口只会给你一张提交记录列表,不会自动把每个开发者的提交行数汇总成榜单。很多人第一反应是去插件市场找工具,或者翻 Git 命令自己算,这两件事我都建议做。这篇就把我在实际项目里验证过的三种统计思路完整过一遍:插件方案、命令行方案、固化脚本方案,顺带把合并提交、换行符、身份配置这些很容易让数字对不上的坑也一并排掉。不管你是自己写着玩想看看产出量,还是团队要做月度复盘,都能找到适合你的招。
1. 为什么 IDEA 没有现成的提交行数排名:先把统计口径弄清
想搞清楚怎么在 IDEA 里做提交行数排名,第一步不是下载工具,而是先想明白一件事:IDE 为什么不直接内置这个功能?IDEA 的定位是编码和版本控制操作,Git 日志面板展示的是 commit 的元信息和变更文件列表,属于"过程记录",而"统计每位作者累积写了多少行"属于聚合分析,已经超出了 IDE 的核心边界。这就像记事本不会给你内置字数排行榜一样,不是做不到,而是开发者日常不需要。所以我们要么自己装插件,要么自己写脚本,本质上都是在给 IDE 补课。
1.1 三种"行数"口径,决定你看到的是完全不同的榜单
用 Git 生态的术语来说,每次提交的变更可以拆成三组数字:新增行(additions)、删除行(deletions)、净增行(net,即新增减去删除)。绝大多数统计工具展示的排名表里都会有这三列,但你在口头讨论"提交代码行数排名"的时候,必须先确定按哪一列排。否则一个爱删冗余代码的重构型开发者,可能新增行和删除行都很多、净增却很一般;一个疯狂复制粘贴的同学,净增倒是惊人。
| 统计口径 | 含义 | 适合判断什么 |
|---|---|---|
| 累计新增 | 所有提交里新增行的总和 | "写了多少新代码" |
| 累计删除 | 所有提交里删除行的总和 | "做了多少清理和重构" |
| 净增行 | 新增减删除 | "代码库实际增长了多少" |
| 当前存量归属 | 用 blame 把现有每一行归属到作者 | "现在仓库里的代码谁写的比例最高" |
第一种和第三种是最常见的诉求。要注意的是,git blame的"存量归属"和提交历史里的"累计行数"是两码事:一个老模块如果被三个人来回改过,累计新增可能每人三千行,但 blame 一下可能发现现存代码里某个人只占几百行。日常讲的"提交行数排名"默认看的是累计口径,这也是后面所有命令和插件的统计基准。
1.2 提交次数排名和行数排名,不要混为一谈
还有一个高频误区。很多人拿git shortlog -sn直接看提交次数,然后对外说"某某贡献排名第一"。这个数字只能说明"谁制造的提交多",不能说明"谁写的代码多"。一个 200 行的小功能可能是一次提交,一个 2000 行的重构也可能是一次提交。真实场景里我见过有人一天提交 30 次,行数并不高;也有人每天晚上合入一次大功能,单次两三千行。你要做的是行数排名,统计维度就得落到 diff 的行数上,而不是提交数量上,这一点先对齐了再看方案。
2. 方案一:插件市场找统计面板,胜在快,败在口径不透明
如果你只是想在 IDEA 里鼠标点两下就得到一张图,最直接的办法是装一个统计类插件。插件市场里搜"code line"、"git statistics"、"code statistics"这类关键词,会出好几个名字大同小异的插件,基本都能覆盖按作者汇总行数的需求。以我常用的操作为例,路径是 Settings -> Plugins -> Marketplace,搜索后安装,重启 IDEA,面板通常出现在底部 Tool Window 栏里,或者右键项目根目录的菜单里多出一个统计入口。
插件的用法很直观:选好你要统计的范围,点刷新,面板里就会列出所有作者,按添加行、删除行或者总变动行排序,有的插件还会画一个简单的贡献占比条。如果只是要一个粗略结果,比如"这个季度谁写得最多",这种方案十分钟就能满足,不需要接触任何命令行。
2.1 插件方案的实际体验边界
但插件方案有几个限制,实测下来必须提醒你。第一是统计范围不可控:很多插件只统计当前检出的分支,切到 dev 分支和留在 master 分支看到的数字会完全不同;也有插件按全部分支统计,但同一笔提交在多个分支里出现,就可能被重复计数。第二是大仓库首次扫描非常慢,拉几万次 commit 的仓库时,面板会卡住几分钟,期间 IDEA 基本不可用。第三是口径不透明:插件默认是否排除空行和注释行,是否把重命名文件按新文件重新计数,不同插件的实现完全不一样,界面上又很少给选项。
我的建议是:插件适合个人快速查看,不适合作为团队结论。如果数字要拿去复盘、汇报或者做周报表,至少先用下面要讲的 Git 命令脚本交叉验证一遍。验证方法也很简单:单独拿一个改动量小的作者,让插件统计他的新增行,再用命令行统计同名作者相同时间范围的新增行,两边对一下。差几十行可能是边界处理问题,差几千行就是口径完全不同。
2.2 插件方案的一个隐藏加分项
有一些统计插件会额外导出文件级别的明细,这是命令行不好看的维度。比如你想知道某个贡献最多的同学主要改了哪些模块,插件面板里点开作者名字,往往能展开文件列表和对应的行数。这对做模块归属分析很有用,命令行脚本要做同样的事,就得把文件名也写进自定义输出格式,规格复杂得多。我的习惯是:日常自查用插件快速出图,团队复盘用命令行拿到统一口径的榜单,两者各管一段。
3. 方案二:Git 命令拉出年度/月度贡献榜,一句 awk 做汇总
如果插件太"黑盒"、你又想完全掌握统计口径,命令行方案值得你花十分钟学会。Git 本身就带了一个非常适合行数统计的参数组合,核心命令是git log --numstat。--numstat是--shortstat的文件级细化版,它会对每次提交逐个输出文件变更的数值信息,格式是"新增行数、删除行数、文件名",中间用制表符分隔。如果文件是二进制,则会输出两个-。
要让这些数字按作者汇总,最可靠的做法是配合--pretty="%an",让每笔提交的数值块前面出现一行作者名,然后用脚本做累加。我第一次用的时候绕了弯路,一开始想着用git diff A..B对比两端差异,后来发现那只能得到两个版本之间的净变化,不是历史提交的累计行数。要统计"历史上每个作者提交的累计行数",必须用git log把每个 commit 的 diff 逐个累加,--numstat就是为这个场景设计的。
3.1 可直接复制的统计命令
下面这段是我在真实项目里用了很久的脚本,保存成rank.sh就能用:
#!/usr/bin/env bash git log --numstat --pretty="%an" "$@" \ | awk ' /^[0-9-]+[ \t]+[0-9-]+[ \t]+/ { if ($1 != "-") { add[name] += $1 del[name] += $2 net[name] += $1 - $2 } next } { name = $0; commits[name]++ } END { printf "%-20s %6s %10s %10s %10s\n", "作者", "提交数", "新增行", "删除行", "净增行" for (n in add) { printf "%-20s %6d %10d %10d %10d\n", n, commits[n], add[n], del[n], net[n] } }' \ | sort -rn -k3运行方式很简单,在项目根目录的终端里执行:
bash rank.sh如果你只想看最近一个月,可以在命令后面追加时间范围参数:
bash rank.sh --since="1 month ago" --until="now"awk 的逻辑是逐行读取git log的输出:碰到"数字 数字 文件路径"这种内容,就把它累计到当前作者名下;碰到作者名行,就更新当前作者的变量,同时给该作者提交数加一。最后按新增行数降序输出。输出效果大概是:
作者名 提交数 新增行 删除行 净增行 某开发者 128 12306 5120 7186 A同学 52 4302 2011 2291 B同学 37 2876 3305 -429看到第三行那种净增为负的情况别慌,通常是他在大范围重构删除旧代码,这种恰恰是有效产出。
3.2 常用过滤参数,想怎么切就怎么切
命令行方案最大的优势就是参数透明,你完全掌控统计边界。这里列几个我实际会用到的组合:
- 只看某个作者的提交:
git log --author="A同学" --numstat --pretty="%an",适合单独核算一个人。 - 排除合并提交:
git log --no-merges --numstat --pretty="%an",后面第 5 节会讲为什么。 - 只看某个子模块或目录:
git log --numstat --pretty="%an" -- src/main/java。 - 排除构建产物和生成文件:
git log --numstat --pretty="%an" -- . ':(exclude)target' ':(exclude)*.lock'。 - 只看某类文件:
git log --numstat --pretty="%an" -- '*.java'。
需要特别提醒的是范围参数的位置。放在$@里再传给git log是最灵活的,但如果你把--numstat和--pretty挪到后面,Git 的路径过滤逻辑可能把统计范围搞乱。稳妥的做法是用我上面给的顺序:先声明--numstat --pretty,随后追加任何过滤参数。
3.3 为什么不建议用 git shortlog 应付这件事
Git 自带git shortlog -sn可以按作者统计提交次数,但如前面所说,它只统计提交数量,不统计行数。有些在线文档会把这当成"贡献者排行"教给新手,放到"提交行数排名"的需求里就容易闹乌龙。还有git log --shortstat会在每个 commit 下方输出X files changed, Y insertions(+), Z deletions(-),看起来能统计行数,但它不带作者维度,要再写一层解析器把作者和数字对应起来。--numstat才是直接从文件级开始汇总的最短路径。
4. 方案三:把统计脚本固化到 IDEA 外部工具里,按月一键出报表
命令行方案解决了口径问题,但每次都敲一遍脚本也挺烦。我的做法是把统计脚本固化成一个独立文件,再注册到 IDEA 的外部工具菜单里,以后点一下按钮就能跑。IDEA 的外部工具支持执行任意系统命令,很适合这种"不常写但时常要用"的场景。
4.1 先做一个带默认参数的可复用脚本
前面那段rank.sh可以再完善一下,把时间范围做成显式参数,不传时默认统计最近一个月:
#!/usr/bin/env bash SINCE=${1:-"1 month ago"} UNTIL=${2:-"now"} echo "统计范围: $SINCE ~ $UNTIL" echo "" git log --since="$SINCE" --until="$UNTIL" --numstat --pretty="%an" \ | awk ' /^[0-9-]+[ \t]+[0-9-]+[ \t]+/ { if ($1 != "-") { add[name] += $1 del[name] += $2 net[name] += $1 - $2 } next } { name = $0; commits[name]++ } END { printf "%-20s %6s %10s %10s %10s\n", "作者", "提交数", "新增行", "删除行", "净增行" for (n in add) { printf "%-20s %6d %10d %10d %10d\n", n, commits[n], add[n], del[n], net[n] } }' \ | sort -rn -k3保存后chmod +x rank.sh,命令行直接执行./rank.sh,默认就跑最近一个月的贡献榜。想调整时间就传两个参数:
./rank.sh "2025-01-01" "2025-12-31" ./rank.sh "1 week ago" "now"4.2 注册成 IDEA External Tool 的完整路径
打开 IDEA 的 Settings -> Tools -> External Tools,点加号新建一个工具。Name 填"代码行数排名",Program 填脚本路径,比如/home/你的用户/scripts/rank.sh,Working Directory 填$ProjectFileDir$。这里有个关键点:一定要让工作目录指向当前项目根目录,Git 命令只有在项目根目录下执行才能拿到正确的提交历史。把环境参数留空,点击 OK 保存之后,Tools 菜单里就会出现"代码行数排名"这个选项。
以后每个月想跑统计的时候,只要在 IDEA 里打开目标项目,点一下 Tools -> 代码行数排名,底部 Terminal 窗口或 Run 窗口就会直接输出结果。这个方法比在系统终端切目录快,也比装插件少一层"不透明口径",我实际体验下来是最顺手的姿势。
4.3 如果还想更省事:配上定时任务
脚本接上参数之后,定时任务也顺手能做了。Linux 或 macOS 的 crontab 里加一行,每月一号早上跑一次并把结果写到文件:
0 9 1 * * cd /path/to/project && /path/to/rank.sh > /tmp/code-rank-$(date +%Y%m).txt 2>&1Windows 用户可以用 Git Bash 跑同一个脚本,再配合"任务计划程序"设置触发时间。这样月度贡献榜就自动生成了,复盘的时候直接打开那个 txt 文件就行。我个人不太建议把定时报表发给全团队,容易变成内耗的 KPI 游戏,但留给自己做阶段回顾,帮助是实实在在的。
4.4 按需扩展:只看模块、排除产物、统一身份
脚本固化之后,想调整统计范围就改最后那行git log命令的参数。最常见的三个扩展需求我顺手列出来:
第一个是只看核心业务代码目录,避免src/main/resources里的配置刷屏,路径过滤追加在命令末尾即可。第二个是排除构建产物,前面提过用:(exclude)target、:(exclude)dist这类写法,实测对 Maven/Gradle 项目非常有用。第三个需求很容易被忽略——先统一 Git 身份。同一台机器上如果某人有时候用name,有时候用Name,或者两个账号邮箱不一样,统计脚本就会把他拆成两个人。建议团队约定统一的user.name,或者至少在做月度报告之前,用git log --pretty="%an <%ae>"把身份列打印出来检查一遍。
5. 这些坑会让数字对不上:合并提交、换行符、身份不一致
方案都跑通之后,最后这部分是最容易翻车的地方。我在帮某团队搭统计工具时踩过不少坑,数字差得离谱的那种,基本都逃不过下面这几个原因。
5.1 合并提交会把行数算到谁头上
git log --numstat默认会把 merge commit 也当成普通提交统计。但 merge commit 的 diff 通常只包含解决冲突时改的那几行,分支主体上的大量变更已经算给了原开发者的提交,所以 merge 作者被加进来的行数其实很少。反过来,如果什么时候有人用了一次大 merge 又把整个分支内容以 diff 形式带进来,就可能出现一次性增加几千行的情况。我在验证时发现,加了--no-merges之后榜单更接近"每个人亲手写的代码量",所以我建议最终报告默认带上--no-merges。
但也有例外:如果一个团队强制用 squash 合并,所有 feature 分支的提交被压成一个 commit,那 squash 出来的那个提交信息里可能带着大量行数,归到了执行合并的人头上,对应分支原作者的行数反而没了。这种场景没有完美的统一口径,只能在每次统计前约定清楚,并在报告里注明是否包含合并提交,不然数据是无法追溯的。
5.2 换行符差异会让整文件"全员重写"
这是最隐蔽、最容易让行数爆炸的坑。如果一个 Java 文件在某个提交里因为编辑器设置从 CRLF 全部变成了 LF,Git 的 diff 会认为这个文件里每一行都删除了一遍、又新增了一遍,行的新增数和删除数瞬间变成文件行数的两倍。一个大文件连续几次换行符切换,榜单数字就会翻着倍涨。遇到这种情况,先检查是不是仓库里混入了换行符变更,可以在.gitattributes里统一声明纯文本文件的换行符规则,或者在 IDEA 的 Editor -> Code Style 里统一行分隔符为 LF。统计之前跑一下git log --numstat | grep -E '^[0-9]+[ \t]+[0-9]+[ \t]+.*\.[xyzw]$'这类命令排查异常文件也好使,核心是别让格式变更伪装成真实工作量。
5.3 空行、注释行、二进制文件的口径差异
同一个仓库,用不同工具统计,差几千行往往就差在空行和注释行上。git numstat统计的是 diff 带来的物理行变化,包含了空行、注释、大括号单独占一行这些内容。某些插件会默认只统计"有效代码行",还有的会把大括号和空行排除在外。两类数字没有谁对谁错,但对比结论时必须标注口径。二进制文件要注意,-在 awk 脚本里被当成 0 处理,不会污染行数,但像打包产物这种文件本就不该出现在代码行数统计里,建议用上一节的排除参数把它们清掉。
5.4 本地统计和远端平台报表对不上,多半是范围不一致
我遇到过不止一次"IDEA 统计和公司平台上显示的数值完全对不上"的疑问。排查到最后,原因基本都是统计范围不同:平台通常只统计合并进主干分支的 MR 相关提交,而本地git log默认统计的是当前分支能追溯到的全部提交,包括本地临时提交、没 push 的提交、rebase 之后被改写过的提交。还有的统计平台只计算包含在有效合并请求里且合入主干后的提交,这样部分改动就被整体排除了。这不是工具 bug,是范围定义不同。想对标平台数据,最简单的做法是先在主干分支上git pull到最新,再用git log --no-merges统计,并且把起始时间对齐到同一个日期。
5.5 行数排名本身的意义边界
把坑都排完之后想说句实在话:行数排名适合用来观察活跃度和趋势,比如这个月谁提交明显变少、某个老模块谁改动最频繁,这些信息对项目管理很有价值。但它不适合直接当绩效结论。擅长重构的资深开发者,可能一个月删除老垃圾代码几千行,净增却是负数;新来的同学疯狂堆业务代码,行数月底数据很漂亮,代码质量却要打问号。统计数字是一面镜子,它只反映"发生了什么",不回答"做得好不好"。我个人的习惯是把它和提交次数、代码评审意见数、问题修复数放在一起看,互相印证,才敢说某个人的产出和状态比较清晰。
最后分享一个我自用的细节:跑完统计之后,我会额外看一眼"删除行"排名。删除行靠前的同学,通常在做重构或者清理技术债,这比看起来增长量更值得在复盘会上提一嘴。统计工具的最终价值不是制造一个榜单,而是帮你把项目里真实的代码走向看明白,剩下的判断,还是得靠人,不是靠数字。