一个下午向同一个仓库提了四个 PR:两个合并,两个被关。被关的那两个,代码本身没问题——诊断是对的,改动是有效的——它们死在动手之前的检查上。
这篇记录那两个 PR 为什么被关,以及我之后给自己加的闸。
被关的两个#
第一个:和别人的 PR 撞车。
我的 PR 关闭时,维护者指向了另一个更早的 PR,说同样的诊断已经被独立提出过了。我去看了,诊断确实一致——包括那个关键的错误定位。
问题在于:我只搜了已合并的改动,没有搜开着的 PR。已合并的代码里没有这个修复,所以我判断"这是个真问题";但开着的 PR 里已经有了,所以我的改动是重复劳动。
第二个:上游已经把根因修掉了。
这个更隐蔽。仓库的 main 分支上确实还有这个 bug,但另一个分支(或者一个还没合并的 PR)已经改了根因。我的修复是在旧版本上做的补丁,一旦上游分支合并,我的改动就会冲突或者失效。
这次的教训是:动手之前要同步上游,看的不只是 main,还要看活跃的分支和 PR。
加的两道闸#
这两次之后,我给自己定了一个硬性前置条件,没走完不许写代码:
闸一:同步上游,而不是同步"我以为的上游"#
git remote add upstream https://github.com/OWNER/REPO.git
git fetch upstream --prune
git log --oneline upstream/main -20然后明确确认自己的起点是最新的:
git merge-base --is-ancestor upstream/main HEAD && echo "已在最新上游之上" || echo "落后了,先 rebase"这一步经常被跳过,因为"我刚 clone 的"。但一个活跃仓库在你读 issue、写代码、跑测试的这几个小时里,可能已经前进了。
闸二:查重复 —— 三类都要查#
# 1. 已合并的 PR
gh search prs --repo OWNER/REPO --state closed "关键词" --limit 20
# 2. 开着的 PR —— 这一条是我漏掉的
gh search prs --repo OWNER/REPO --state open "关键词" --limit 20
# 3. issue,包括已关闭的
gh search issues --repo OWNER/REPO "关键词" --state all --limit 20第 2 条是那次踩坑的直接来源。开着的 PR 是最容易漏、也最容易撞的一类——它既不在主线代码里(所以你测得到 bug),也不在"已完成"里(所以你没搜)。
另外用关键词搜的时候要多换几个词:函数名、错误信息、文件名、issue 编号。只搜一个词几乎必然漏。
剩下的两个:什么样的改动会被合并#
对照被合并的那两个,被合并的改动有几个共同点:
- 有可复现的路径。PR 描述里能让人按步骤复现问题,不需要维护者猜。
- 改动范围最小。没有顺手重构、没有格式化不相关的文件。
- 诊断被独立验证。这一点很值——即使 PR 最终被关闭(比如上游已修),如果你的诊断是对的,维护者会明确说出来。
第 3 点说明一件事:被关不等于白做。但被关两次之后我意识到,与其靠"诊断正确"挽回,不如一开始就多做那两步检查,把 PR 做实。
后来的结果#
按这套流程重做之后:
- career-ops(75k+ Stars) #5069 合入,931 行
- CopilotKit(37k+ Stars) 两个 PR 经维护者评审后合并
这几条都是可核验的事实——PR 编号和行数都能在 GitHub 上查到。
小结#
被关的那两次真正教我的,不是"要更小心",而是一个具体的顺序问题:
先同步上游,再查重复,最后才动手。
这个顺序不能颠倒。代码写得再对,如果上游已经修了、或者别人正在修,这份工作就是零价值——而发现这一点只需要在动手前多花十分钟。