ARTICLE · 1310318
如何在 GitHub 上读懂源码
上一章找到了项目,点进仓库,又是另一道坎:目录这么多,先打开哪个?
从第一个文件一路往下翻,很容易读了半小时,还不知道刚才那段代码负责什么。换个起点:先挑一个自己用过的功能,看看它在项目里经过了哪些地方。
这次用 Python 的 Requests 做例子,只追一个问题:调用requests.get()之后,请求交给了谁?
练习采用公开仓库 github.com/psf/requests 中 v2.32.3 对应的源码提交,完整提交号为 0e322af87745eff34caffe4df68456ebc20d9068。在浏览器地址栏输入 github.com/psf/requests/tree/ 再接上这个提交号,就能进入该版本。选它是为了方便对照,并非推荐安装这个旧版本。本文核对了该版本的源码文本;快捷键、符号跳转和 Blame 的网页交互未实际操作。
先从 README 找到一个可追的问题
仓库首页先看 README。此时不用把安装、部署、贡献指南全读完,先确认项目做什么,以及用户从哪里调用它。
Requests 的 README 里就有 import requests 和 requests.get(...)。它给了我们一个明确起点:get。再看目录,src/requests/ 放着库的源码,tests/ 和 docs/ 分别是寻找测试与说明的地方。这些位置可以在上述固定提交的仓库根目录核对。
目录名只能提供线索。换个项目,源码可能在 lib/,也可能直接放在根目录。先从 README 的调用示例反推入口,通常比猜哪个文件最重要更省力。
知道文件名,用 t 找;知道行号,用 l 跳
在仓库的 Code 文件浏览页面,按 t 可以唤出文件查找器。它用来找文件路径,不是搜索文件里的所有内容。不用快捷键,也可以找页面上的Go to file。
先找 src/requests/init.py。在该版本的这个文件里,可以看到包从 .api 导入了 get 等名字。也就是说,用户写的 requests.get(),还要往 api.py 里看。
再打开 src/requests/api.py,该版本第 62 行就是 def get,函数定义从这里开始。收到别人给的行号时,可以在代码文件页按小写 l,输入行号跳过去,比如这里输入 62。l 适用于看具体代码文件的页面;仓库首页没有代码行可跳。
单字符快捷键不响应时,先退出搜索框或评论框的输入状态,再试;焦点留在输入框里,按键可能只会输入字符。GitHub 的无障碍设置也能关闭字符快捷键。按 ? 可查看当前页面提供的快捷键,不必硬记一套在所有页面通用的按法。依据见文末资料索引 1、2。
读入口时,先跟住那一次调用
api.py 里的 get 很短:它把请求方法、地址和参数交给同文件的 request。继续看 request,会看到它创建一个 Session,再调用 session.request(...)。这两步都可以在该版本的 src/requests/api.py 里直接对照。
读到这里,先记下:
requests.get→ api.py 中的 get→ api.py 中的 request→ Session.request
这已经回答了一部分问题:公开接口收下参数后,把工作交给 Session。暂时不需要逐个研究旁边的 post、put、delete。
接下来打开同一提交下的 src/requests/sessions.py,找到 Session 类里的 request。这个方法准备请求、整理环境设置,再把准备好的请求交给 self.send(...),最后返回响应。
可以把笔记补成 Session.request → Session.send。今天先停在这里:这是一段静态阅读得到的调用关系,还没有追到网络传输底层,也没有实际发出 HTTP 请求。
跳到定义,也要看看谁在用它
在支持的代码文件里,点击可识别的函数或方法名,可以借助符号面板找定义和引用。定义帮助回答“它做什么”,引用帮助回答“谁在什么情况下用它”。
例如看 request,不能只凭名字就认定找到同一个函数。api.py 有模块级函数,Session 类也有同名方法,要连着文件、类和调用对象一起判断。读引用结果时,挑一个具体调用方看上下文,比盯着命中数量更有用。
GitHub 的符号导航支持 Python、JavaScript、TypeScript 等一组语言;官方还列出了活跃分支、仓库少于十万个文件等条件。它不能替代运行时调试,动态调用也不能仅凭导航结果下结论。在这个固定提交页面上若没有出现跳转入口,就按已知路径打开文件,查找名字;找不到交互入口不等于源码没有定义。支持范围见文末资料索引 3。
上一章的 Code Search 适合找线索;这一章拿到线索后,要把定义、调用方和上下文接起来。搜索语法再熟,也不能省掉这一步。
文件来回切换多了,再打开 github.dev
追到第三、第四个文件,想把几个文件留在标签页里对照,可以在 GitHub 仓库或 Pull Request 页面按 .,打开 github.dev,也就是浏览器里的 VS Code。它需要登录;私有仓库仍需访问权限,登录或使用 github.dev 不会增加对上游仓库的写权限。没有写权限时,需按项目贡献流程通过 Fork/PR 提出修改。本次练习只说明用法,不要求登录或修改文件。
它适合阅读和轻量编辑,但没有配套计算环境,不能像本地开发环境那样使用集成终端、构建或运行项目,也不能在这里启动项目调试。可用扩展也限于支持网页环境的部分。免费边界和能力说明见文末资料索引 4。
真要运行、调试项目,可以另行考虑本地环境或 Codespaces。Codespaces 提供远程计算环境,个人账号有每月包含额度,超额使用可能收费;计算和存储分别计量,停止运行后保留的存储仍计入用量。创建前先看自己的额度、预算和付费设置,别把它与免费的网页编辑器混在一起。费用说明见文末资料索引 5。
看到疑问,再去翻 Blame 和提交
顺着调用读下去,可能会冒出一个更具体的问题:“这几行是什么时候改成这样的?”
打开目标文件的Blame,找到关心的代码行,再点对应的提交说明,查看改动前后。如果提交关联了 PR 或 Issue,可以继续读讨论。Blame 是进入历史的入口,提交差异和讨论才可能补上改动背景。文件历史说明见文末资料索引 6。
Blame 显示的是代码行在所查看版本中的最近归属提交,不能直接拿来认定整个函数的首次作者,也不能据此断言设计原因。同一个函数的不同代码行可能来自不同提交;仓库若配置了忽略修订,视图还可能跳过指定提交。看到姓名和日期后,仍要打开提交核对;找不到原因,就把原因留作待确认。
这里留一个练习:在上述固定提交的 api.py 中,挑 get 函数的一条实际代码行,打开 Blame,记录行号、归属提交、作者、日期,再读提交差异,写一句“这次实际改了什么”。若涉及忽略修订提示,也一并记录。本次未成功取得该 Blame 页面的内容,因此不提供虚构的姓名、日期或提交号。
分享笔记前,在文件页按 y 把地址换成固定提交的永久链接,再选中有关代码行,方便别人回到同一版本。仅复制分支链接,日后代码变化可能让行号失去原来的含义。永久链接说明见文末资料索引 7。
读完这一轮,笔记里应当有:用户调用入口在哪、经过哪些函数、还没看懂的地方在哪里。再找一段相关测试,看它用什么输入、期望什么结果,就能继续检验自己的理解。
下一章会从一个范围小、能验证的修改开始,走一遍首个 Pull Request:先读贡献说明,再准备修改、检查差异,把改动理由讲清楚。
资料索引
练习源码:
仓库 github.com/psf/requests,固定提交 0e322af87745eff34caffe4df68456ebc20d9068(v2.32.3 对应源码),涉及文件 src/requests/init.py、src/requests/api.py、src/requests/sessions.py。
以下均为 GitHub 官方文档(GitHub Docs),可在 GitHub Docs 站内或搜索引擎中按文档名称、关键词查到:
《Keyboard shortcuts》,关键词:GitHub keyboard shortcuts 《Managing accessibility settings》,关键词:GitHub character key shortcuts 《Navigating code on GitHub》,关键词:GitHub symbol navigation 《The github.dev web-based editor》,关键词:github.dev web-based editor 《GitHub Codespaces billing》,关键词:GitHub Codespaces billing 《Viewing and understanding files》,关键词:GitHub blame view 《Getting permanent links to files》,关键词:GitHub permanent link















































































