用 git log --format 与 awk 去重,列出 Git 仓库全部贡献作者及邮箱

原创2026-10-04 11:34:241,448 阅读
文章标签:文档教程知识库

用 git log --format 与 awk 去重,列出 Git 仓库全部贡献作者及邮箱

git log 是仓库全部提交的"账本",但默认输出包含的信息远多于作者本身。本篇指南讲解如何用 --format 精确提取每个提交的 Author Name 与 Author Email,再用 awk 的 !seen[$0]++ 技巧对海量重复记录保序去重,最后配合 --reverse 让作者按"首次提交"的先后顺序呈现。读完本文,你将掌握一套可复制的命令组合,一键生成一份完整、无重复、按时间顺序排列的仓库作者清单。

前置理解:git log 是仓库提交的完整账本

git log 会按时间倒序列出当前分支可达的所有提交记录。要让这份清单真正"完整",有两个前提:

  1. 你处于目标分支(例如 main);
  2. 已经从远程拉取了最新代码(git pull)。

满足这两点后,git log 的输出就是该仓库所有提交的完整列表——这正是"列出全部作者"的数据基础。值得一提的是,作者本人对 git log 的依赖贯穿整个仓库笔记:比如 git/display-all-git-log-entries-in-my-local-timezone.md 讨论用 git config --global log.date rfc-local 让时间戳统一显示为本地时区,git/include-some-stats-in-your-git-log.md 讨论用 --stat 查看每个提交的增删统计,而本主题关注的是其中"作者"这一维度。

用 --format 只输出作者名与作者邮箱

git log 的信息比作者信息丰富得多。可以用 --format 标志把输出收窄到只有作者名和作者邮箱。--format 的完整占位符列表位于 man git-log 的 PRETTY FORMATS 小节(跳过内置格式,往后面翻即可看到)。

本文关心的两个占位符:

占位符 含义
%an Author Name(作者名)
%ae Author Email(作者邮箱)

格式串中可以按任意顺序、任意排版组合这些占位符。例如把作者名和用尖括号包裹的邮箱拼在一起:

❯ git log --format='%an <%ae>'
Zac Jones <zacjones93@gmail.com>
Zac Jones <zacjones93@gmail.com>
Zac Jones <zacjones93@gmail.com>
Zac Jones <zacjones93@gmail.com>
Zac Jones <zacjones93@gmail.com>
Zac Jones <zacjones93@gmail.com>
Zac Jones <zacjones93@gmail.com>
John Lindquist <johnlindquist@gmail.com>
Zac Jones <zacjones93@gmail.com>
...

上面这段输出来自原文档作者参与的一个真实大型公开仓库(egghead-next 项目,数千次提交)。可以看到,尽管输出已精简为"姓名 + 邮箱",但对一个提交量庞大的仓库而言,重复的作者会密密麻麻地刷屏。

用 awk 的 !seen[$0]++ 对作者列表保序去重

面对成百上千条提交,重复作者不可避免。常规去重思路是 sort | uniq(或 sort -u):先用 sort 把相同行聚到一起,再由 uniq 去掉相邻重复。它的致命缺点是会改变原始顺序——所有行会被重新按字母排序,而顺序往往是有意义的。

保序去重的替代方案是 awk 模式 !seen<a href="https://link.gitcode.com/i/4e74bff9c47fffd56a34baf8c451f0d2" target="_blank">$0]++,这也是本仓库中 [unix/deduplicate-list-while-preserving-original-order.md 这篇笔记的核心内容。把它接在 git log --format 的管道后面:

❯ git log --format='%an <%ae>' | awk '!seen[$0]++'
Zac Jones <zacjones93@gmail.com>
John Lindquist <johnlindquist@gmail.com>
Josh Branchaud <jbranchaud@gmail.com>
Vojta Holik <vojta@egghead.io>
Creeland A. Provinsal <cree@egghead.io>
joel <joelhooks@gmail.com>
Creeland <cree@provinsal.com>
...

输出立刻从"刷屏"变成了一份干净的唯一作者清单。

这段 awk 是怎么工作的

!seen[$0]++ 的核心机制如下:

  • $0 是 awk 当前正在处理的一整行文本;
  • seen 是一个自动初始化的关联数组(associative array),以行为键;
  • 对于首次出现的行,seen[$0] 尚不存在,取值 0,被 ! 取反为真值(truthy),该行被打印;
  • 紧接着 ++ 把该键的计数从 0 递增为 1;
  • 之后再次遇到相同行时,seen[$0] 为 1,!1 为假,该行被拒绝打印。

由于 awk 默认的 action 就是打印当前行,最终效果等价于"首次出现即输出、重复出现即丢弃",且保持各行首次出现的原始顺序不变。这与 sort -u 的字母序输出形成鲜明对比。

用 --reverse 让作者按首次提交顺序排列

git log 的默认排序是降序——最新提交在最前。这意味着上面去重后的清单会呈现"最近活跃的贡献者在前"。原文档作者希望看到的恰恰相反:按作者首次向项目提交的时间顺序列出。

--reverse 标志将 git log 的输出顺序反转(从最旧到最新),配合 awk 去重,完整的最终命令是:

❯ git log --reverse --format='%an <%ae>' | awk '!seen[$0]++'
Joel Hooks <joelhooks@gmail.com>
johnlindquist <johnlindquist@gmail.com>
John Lindquist <johnlindquist@gmail.com>
William Johnson <w.alexander.johnson@gmail.com>
depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Evgeniy Nagalskiy <evgeniy.nagalskiy@gmail.com>
Taylor Bell <taylorbell@gmail.com>
Maggie Appleton <maggie.fm.appleton@gmail.com>
...

这行命令现在给出的是:最早参与项目的人在顶部,按他们各自第一次提交的时间先后依次排列。这样的清单非常适合做贡献者荣誉榜、整理 credits、或审计一个仓库都有哪些人来过。

从输出中读出信息:真人作者与机器人账户并存

上面这份真实输出还透露了一个值得注意的细节:清单里出现了 depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> 这类由依赖更新服务创建的机器人账户。它们同样拥有 author name 和 author email,因此会毫无差别地进入 git log 的账本。如果你的目标是"只统计人类贡献者",可以在 awk 阶段追加过滤条件(例如排除邮箱中含 [bot] 的行)进一步净化清单。

仓库中的相关进阶用法

本仓库 git/ 目录下还有多篇与"作者"这一主题直接相关的笔记,可作延伸:

小结

最终可落地的完整命令组合如下:

# 按首次提交顺序列出所有作者(含邮箱),自动去重
git log --reverse --format='%an <%ae>' | awk '!seen[$0]++'

# 只列作者名,不显示邮箱
git log --reverse --format='%an' | awk '!seen[$0]++'

# 最近提交优先(默认顺序)的完整作者清单
git log --format='%an <%ae>' | awk '!seen[$0]++'

三个核心要素缺一不可:--format='%an <%ae>' 负责精准提取作者维度,awk '!seen[$0]++' 负责保序去重,--reverse 负责把时间顺序调整为"最早在前"。更完整的格式化选项,参见 man git-log 的 PRETTY FORMATS 小节。

登录后查看全文
til