如何快速上手和贡献开源项目
我最近移植和修改了几个开源项目以满足我自己的需求,比方说把 Gesturefy 移植到 Chrome 上、让 KWin 的热区稍微更好用一点、给 Strawberry 音乐播放器适配网易云。
在弄这些的时候博客一直没更新。忙之前感觉没什么好写的,忙的时候没时间写,现在忙完了写一篇总结一下。
明确需求
首先你要问一下自己:是修 BUG 还是加功能?
如果是修 BUG:
- 这个 BUG 算设计缺陷还是代码缺陷?
- 如果是设计缺陷,是不是只有你一个人这么觉得?
- 如果是代码缺陷,那有没有一个 MRE(最小的可重现示例)?
如果是加功能:
- 有没有现成的方案实现?
- 如果确定只能动代码,那这个功能想不想合到上游?
在正式开撸之前你很有必要把这几个问题搞清楚,因为这会很大程度上影响到你接下来要做的事情。
老师我发现了一个 BUG
有没有人抢先一步?
修 BUG 第一件事情不是哼哧哼哧开始看代码,而是去看看软件的最新版是不是已经解决了这个问题。
当今世界大部分开源软件都托管在某个 git 仓库上,不一定是 Github,也可能是 Sourcehunt、Gitlab、Codeberg 等等平台。总之先看主线分支上有没有这个 BUG 相关的 commit,再去看 Pull Request 里有没有人正在解决这个问题。
如果没有的话就去 Issue 板、官方论坛、邮件列表、即时通讯群组等等你能联系到项目组和其他用户的地方看看有没有人反馈,没有的话先反馈。然后再想办法修 BUG。具体怎么修看下面。
真的是 BUG 吗?
很多时候你觉得是 BUG 的东西其实并不是 BUG,而是特性™。aka 这玩意就这么设计的,需要你去当个自适应用户。比方说我最近遇到的一个问题:
KDE 的“热区”(就是鼠标放到屏幕角落或者边缘触发动作)是把多块屏幕当作一整块虚拟屏幕来处理的。什么意思呢?就是假设我有两块屏幕,那么左上角的动作只能在最左边的屏幕触发,反之亦然。我一直以为这是个 BUG,结果翻代码才发现这也是设计的…

代码注释里具体是这么写的:
在整个显示几何体的每个角上都会创建一个像素大小的边缘。无论有多少个屏幕,始终只会有这四个角落边缘。这样设计的动机源自费茨定律(Fitts’s Law),该定律表明:触发屏幕角落是很容易的,但在两个屏幕之间触发一个角落(一个没有视觉边界的一像素目标)则非常困难。
然而当你用一个笔记本外接屏幕的时候就完全不是这么回事了,你的体感就只是角落的动作在你的主屏上无法触发,反而要滑到最远的角落才行。如此一来热区就完全失去了意义。
虽然原作者认为这个是特性,但并不一定没有改变的可能,因为社区一直有人在反馈相关的问题:self.kde - 多屏的屏幕边缘?,所以你也可以尝试修一下,打破默认行为。
上述问题我就修了,最后也被合了 https://invent.kde.org/plasma/kwin/-/merge_requests/8357 。
确实是 BUG
开源社区一般也是一个国际社区,这意味着或许你的用例并不容易在主要维护者那复现,我还是举个例子:微信在我们这是个国民应用,自从出了 Linux 版过后我用的也是很爽。但微信在很多窗管上都有一个 BUG:悬浮窗错位或者弹不出来。
典型的症状包括:搜索框候选项闪一下消失、朋友圈点赞评论选项无法显示、表情弹窗无法弹出… 我们给 labwc 提 Issue 的时候就有一个很显而易见的问题——维护者怎么测试?他们也没办法装个莫名其妙的闭源软件到自己电脑上。

这无可厚非,所以某些时候维护者并不一定愿意修某些 BUG。因此我们最好能提供一个能控制变量的、稳定复现的环境,来让参与进来的所有人都可以复现这个 BUG。
关于这点我觉得 Hugo(一个静态网站生成器)做的很好,我某次在他们的论坛上反馈了一个问题,维护者给我回复要我测试的时候我才发现他有一个专门用来存放测试用例的 git 仓库,一个分支对应一个论坛反馈的帖子。把这个仓库克隆下来,然后切换到对应的分支,工作目录里就包含了重现和测试 BUG 所需的所有文件。


真正的男人会自己手撸软件
自己动手,丰衣足食
如果弄完上面那一轮结果还是不能让人满意,那就只能自己动手了。
不过大侠且慢!我们还是不要直接去读代码,因为很多时候你并不是想知道整个项目是什么样的,你只关心自己的一亩三分地。毕竟我们并没有抱着学习一个项目的目的去看代码,而只是想完成自己需要的功能,漫无目的地去看很容易迷失方向。
在你读代码之前先把文档读了,你至少得知道怎么在你自己的电脑上把项目构建起来,这样你才有能力开始读代码、改代码、跑代码、看日志,如此循环。后面 99% 的时间我们都在这么调试,所以如果你连项目都跑不起来我只能认为你是猴子请来的救兵。
我还要介绍的法宝是:Git 历史,这玩意对于修 BUG 和加功能同等重要,甚至对于我来说必不可少。很多时候你面对源码根本是懵的,如果文档再差点那更是完蛋。
每当这个时候我先会从程序的输出下手,从输出的字符串中找到对应的源码,然后找到对应的文件、接着是模块。然后我会去看和这个模块相关的提交日志,如果你能找到一个和你想做的时候非常相近的提交那就再好不过了!
比方说我给 🍓 音乐播放器适配网易云的时候就是这么做的,我非常幸运的找到一个适配 Spotify 的完整的提交: https://github.com/strawberrymusicplayer/strawberry/commit/5f540a4c08b5f8aacf0b091aed0c980469164b2f

这就意味着我有了一个可以参考的目标系:我可以直接对着这个提交看有哪些地方需要改动、那些文件需要被添加。这可以极大加速我们试错的时间。
如果你碰巧会一点 Git,你还可以用 blame 来看某一段内容是什么时候、被哪些提交更改了的。
到底要不要重复造轮子?
如果造轮子让你感到快乐那就造。
如果不是这样,那就先去搜搜自己的需求有没有现成解决方案。不用局限于搜索引擎,多和 LLM 聊聊,高效治愈幻觉。
上游化?
看个人,我一般会把变更不大的东西推到上游,自己爽的东西自己爽爽就完了()很多时候上游化就是在和维护者扯皮的过程。如果你觉得你的功能很有用就去推,不过 Be humble,没有人有义务接受你的好意。