想象你走进一家米其林餐厅,看中了一道招牌菜。你不可能直接冲进厨房抢过主厨的锅铲,而是礼貌地要一份菜谱副本,带回自家厨房慢慢研究。你在自己的灶台上试做、改良,最后端着成品走到主厨面前:“我按您的菜谱改了一版,您尝尝?”主厨和几位帮厨试吃、点评,点头后,你的配方才被正式收录进总菜谱。这就是GitHub协作为开源项目贡献代码的全过程:Fork是拿菜谱,Branch是开辟实验台,Commit是给每次尝试贴标签,PR是端菜上桌,Review是围炉试吃,Merge是主厨盖章。这篇教程,就是带你走过这条“主厨菜谱实验室”的完整流水线。

岗位关联

对AI从业者而言,参与开源项目是简历上最亮的加分项。掌握GitHub Workflow与Fork/PR/Review,你就能把“我学过模型”升级为“我贡献过模型”,让技术能力被真实项目验证。

目录
  1. 可视化演示
  2. Fork操作:把菜谱带回家
  3. 分支管理:开辟实验台
  4. 提交规范:贴好菜品标签
  5. PR流程:端菜上桌请主厨品尝
  6. CodeReview:围炉试吃与改进
  7. 协作规范:后厨的规矩
  8. 成长路径:从帮厨到掌勺
  9. 社区礼仪:灶台上的温度
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

演示Fork、分支管理、PR提交与Code Review完整工作流,理解开源协作规范与成长路径。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Fork分支:协作起点 Fork仓库 分支提交 Fk→Po 本地开发

Fork操作:把菜谱带回家

你走进一家 AI“米其林餐厅”——比如 Hugging Face 的开源模型仓库,看中了招牌菜 transformers。你不能直接改主厨的配方,只能先复印一份完整的菜谱带回自己家。在 GitHub 上,这个动作就叫 Fork:把别人的仓库完整复制到你的账户命名空间下,从此你拥有一份随时可以折腾的私人副本。

这一份副本有什么用?它给了你完全独立的写权限。你可以在自己的菜谱上任意涂改、试错,即使把厨房炸了,也不会影响主厨的招牌菜。但你需要和“主厨的厨房”保持联系:origin 指向你的副本,upstream 指向原仓库。这样,主厨菜谱更新时,你才能把新配方同步回自家厨房,避免自己一直基于老配方做菜。

技术上的标准开局是先点 GitHub 上的 Fork 按钮,再到本地执行 git clone 把自己副本拉到电脑,然后添加 upstream 远程地址。之后每次开工前先 git fetch upstream 并合并,让本地与上游保持同步。这就像帮厨每天早上先检查主厨的菜谱更新,再开始自己的工作。

回到餐厅比喻:每次开工前先同步菜谱,就像帮厨上班第一件事是确认主厨今天的菜谱改了什么。同步干净,后面做菜才不混乱。

Sync=upstream−local=0\displaystyle \text{Sync} = \text{upstream} - \text{local} = 0
这个公式表示:本地副本的提交位置与上游主分支的提交位置之差为零,意味着你既没落后也没超前,处于完全同步状态,可以安心开工。
  1. 同步状态\text{Sync} 代表本地 Fork 与上游仓库的同步程度,数值为 0 表示完美同步。
    Sync\text{Sync}
  2. 上游提交\text{upstream} 指上游主仓库最新的提交编号(commit hash),相当于主厨菜谱的最新版本号。
    upstream\text{upstream}
  3. 本地提交\text{local} 指你本地 Fork 仓库当前所处的提交编号,相当于你手里的菜谱版本号。
    local\text{local}
  4. 差为零= 0 表示两个版本完全一致,可以放心创建新分支而不会冲突。
    =0= 0

分支管理:开辟实验台

你在自家厨房准备改良主厨的招牌菜,但不想把所有食材堆在一个台面上——万一卤汁洒了,整个厨房都遭殃。分支就是你在自家厨房里单独开辟的“实验台”:主台面始终保留最稳定的菜谱,实验台只放你正在尝试的新配方。

技术概念:分支是 Git 中从主提交线上分出的平行时间线。创建分支的命令是 git checkout -b(或新版 git switch -c)。在贡献开源时,绝不直接在 main 上开发,而是为每个功能建独立分支。命名往往遵循 feature/xxx、bugfix/xxx、docs/xxx 的规范,也可以用 Issue 编号,比如 feature/123-add-tokenizer,让维护者一眼看出这个分支要干什么。

分支创建后,你在这个实验台上完成多个提交,最后把整个分支推送到自己的 Fork 仓库:git push origin feature/123-add-tokenizer。这样,后续的 PR 只需要从这个分支发出,主分支保持清洁。类比厨房:你在“实验台”上反复试做,主台面始终干净,最后整道菜连同标签一起端出去。

回到厨房:实验台有清晰的标签,你才不会把“番茄炒蛋”误当成“松露炒蛋”。分支命名就是实验台的标签,规范、可读,维护者才愿意来试吃。

B=branch(Hmain,prefix/description)\displaystyle B = \text{branch}(H_{\text{main}}, \text{prefix}/\text{description})
这个公式说明:一个功能分支(B)从主分支的最新位置(H_main)分出,并用“类型前缀/功能描述”来命名。
  1. 分支命名B 表示新建的功能分支,是后续 PR 的唯一来源。
    BB
  2. 基线位置H_{\text{main}} 表示主分支当前的头部提交,分支从这里起跳。
    HmainH_{\text{main}}
  3. 命名规则\text{prefix}/\text{description} 是分支命名模板,如 feature/123-add-tokenizer。
    prefix/description\text{prefix}/\text{description}

提交规范:贴好菜品标签

每完成一次小实验,你都要在菜碗上贴一张纸条,写清楚“我加了什么、为什么加”。Git 的 Commit 就是这张纸条,包含两部分:提交信息(message)和它对应的改动内容。一个优秀的提交信息,能让几个月后的维护者(甚至你自己)一眼看明白当时的意图。

开源常见的提交规范是 Conventional Commits:type(scope): subject。type 说明改动类型——feat 是加新功能,fix 是修 Bug,docs 是改文档;scope 说明影响范围,比如 api、trainer;subject 是简短描述,比如 fix(api): correct timeout。此外,原子提交 要求每次都只做一件逻辑上的事,不要 “改了三处文件、却说只是格式微调”。

同时,规范的 PR 会在提交信息中关联 Issue 编号,例如 docs: add quickstart (#456)。维护者可以通过引用直接跳转到问题详情。Commit 就像厨房里的标签系统:标签混乱,后面端菜上桌时没人知道你做了什么,主厨不敢试吃。

标签写得好,主厨才知道这道菜该放哪一类、解决了什么。同理,规范的提交信息让维护者在 Review 时可以逐条对照,快速判断改动是否合理。

C=(type,subject),∣C∣=1 logical change\displaystyle C = \left( \text{type}, \text{subject} \right), \quad |C| = 1 \text{ logical change}
这个公式表示:一次提交由类型和简短描述组成,并且一个提交只包含一个明确的逻辑改动。
  1. 提交结构C = (\text{type}, \text{subject}) 说明每次提交都需要有类型前缀和主题描述。
    C=(type,subject)C = \left( \text{type}, \text{subject} \right)
  2. 原子性|C| = 1 \text{ logical change} 强调一个提交只做一件事,避免把无关改动混在一起。
    ∣C∣=1 logical change|C| = 1 \text{ logical change}

PR流程:端菜上桌请主厨品尝

当你在实验台上完成新菜,就该端着它走向主厨的厨房——这一步是 Pull Request(PR)。你从自己 Fork 的分支向原仓库的主分支发出合并请求,等于对主厨说:“这是按您菜谱改良的版本,请审查。” 上游维护者不会看到你家里的整个过程,只看到你精心端上桌的这道菜。

创建 PR 时,要写清描述模板:这个改动解决了什么问题、改了什么、如何测试。常见做法是关联一个 Issue,让维护者在背景中了解问题脉络。如果主分支在你开发期间前进了,你的 PR 可能出现冲突——这就像主厨的菜谱改了两版,你基于旧版做的菜需要重新调味。解决冲突需要把上游最新代码合并进你的分支:git fetch upstream + git merge upstream/main,或在本地 git rebase 后强制推送。

PR 创建后,自动化机器人(CI 检查)会先跑测试,像自动洗碗机一样先确认餐具是否合格。只有测试通过、没有冲突,维护者才会开始人肉试吃——Review。

所以,端菜上桌不是终点。只有同时满足三个条件,主厨的印章才会落下,你的菜才正式进入总菜谱。

Merged=CI∧Approved∧¬Conflict\displaystyle \text{Merged} = \text{CI} \land \text{Approved} \land \neg \text{Conflict}
这个逻辑式说明:一次 PR 要真正被合并进主仓库,必须同时满足三个条件:自动检查通过、评审人批准、以及没有代码冲突。
  1. 合并结果\text{Merged} 是一个布尔值,代表 PR 是否被合入上游主分支。
    Merged\text{Merged}
  2. 自动检查\text{CI} 代表持续集成检查是否通过,相当于自动洗碗机和品控机器。
    CI\text{CI}
  3. 评审批准\text{Approved} 代表至少一位维护者点击了 Approve 按钮。
    Approved\text{Approved}
  4. 无冲突\neg \text{Conflict} 表示当前分支与上游之间没有代码冲突。
    ¬Conflict\neg \text{Conflict}

CodeReview:围炉试吃与改进

主厨收到你端来的菜,不会马上盖章,而是叫上几位资深帮厨一起围在炉边试吃。有人尝了一口说:“酱汁偏咸,需要减一勺盐。” 有人说:“火候不错,但摆盘可以更精致。” 这些意见就是 Code Review 评审意见。每一句都对着代码的具体位置,而不是笼统的评价。

作为贡献者,你要做的是虚心接受每一条意见,回到本地分支修改代码,再次提交并推送。推送后,PR 会自动更新,评审人会在同一页面看到最新版本。这个过程往往要循环好几轮:request changes → 修改 → 重新请求 → 最终 Approve。注意:不要用 git push --force 去覆盖那些评审人已经看过的提交历史,除非你是在交互式 rebase 中压缩整合 review 小修复。否则评审人无法追踪你对意见的回应,就像你把菜撤回去重炒却不告诉主厨改了什么。

当评审人给出的“问题列表”全部解决后,他们会点击 Approve。所有重要的意见都得到回应,PR 才算拿到“通行证”。

就像试吃团每个人都点头后,主厨才会松口:“行,这道菜可以进菜单。” Review 不只是挑刺,更是保障整个菜品质量的协作仪式。

Approve=⋀i=1nRi\displaystyle \text{Approve} = \bigwedge_{i=1}^{n} R_i
这个公式表示:评审通过的最终状态,是所有评审意见(R_1 到 R_n)都被解决后的“逻辑与”结果。
  1. 最终批准\text{Approve} 是 PR 能否合并的关键标志。
    Approve\text{Approve}
  2. 全部解决\bigwedge_{i=1}^{n} 表示从第 1 条到第 n 条意见全部满足,缺一条都不行。
    ⋀i=1n\bigwedge_{i=1}^{n}
  3. 单条意见R_i 是第 i 条评审意见是否已解决,值为真表示解决。
    RiR_i

协作规范:后厨的规矩

任何餐厅的后厨都有铁的规矩:刀具用完归位、台面随时擦净、一次只炒一道菜。开源协作也有类似的惯例,遵守它们会让你的贡献被维护者另眼相看。

第一条,一次只提一个 PR。如果你同时提三个 PR,每个 PR 各改了几处不同模块,维护者很难集中精力审查,就像帮厨同时炒三口锅,最后锅锅烧糊。第二条,保持分支与上游同步。开发周期超过一天的 PR,要定期 git fetch upstream && git merge upstream/main,减少最终冲突范围。第三条,遵守 CONTRIBUTING 指南。大项目通常有贡献者手册,规定代码风格、测试要求、Commit 格式,先读再动手,相当于上岗前先看后厨守则。

这些规范背后是一个朴素概率:合并的概率是每个环节符合期望概率的乘积。任何一环掉链子,整体成功率都会显著下降。厨师长不怕你慢,怕的是你不守规矩。

所以,守规矩不是束缚,而是让每一道菜都稳定达到米其林标准的保障。主厨信任你,才愿意一次次给你试做机会。

Pmerge=∏i=1kpi\displaystyle P_{\text{merge}} = \prod_{i=1}^{k} p_i
这个公式表示:一次 PR 被合并的总概率,等于每个协作规范环节遵守概率的乘积。任何一个环节的失败都会拉低整体成功率。
  1. 合并概率P_{\text{merge}} 表示一次 PR 最终被合入上游的概率。
    PmergeP_{\text{merge}}
  2. 连乘符号\prod_{i=1}^{k} 表示把 k 个环节的通过率相乘,每个环节都更接近 1,整体才更高。
    ∏i=1k\prod_{i=1}^{k}
  3. 单项规范p_i 是第 i 个规范(如 PR 粒度、同步频率、CONTRIBUTING)被遵守的程度。
    pip_i

成长路径:从帮厨到掌勺

每个米其林主厨都从削土豆开始。开源贡献也一样:别一上来就想改核心模型代码,先在 Issue 列表里找带有 good first issue 或 help wanted 标签的问题,这是给新人的“入门练习刀工”。

起步时,最容易上手的是文档:修正拼写错误、补充示例、翻译 README。文档属于低风险改动,维护者审批快,你能在最短时间内走完整个 Fork-PR-Review 流程,建立信心和流程肌肉记忆。之后逐步尝试小 Bug 修复,再到独立实现一个中等功能。每次 PR 后,积极回应评审意见,积累良好信用。

长期参与会带来质变:当你持续贡献、熟悉代码库,维护者可能邀请你成为协作者,获得直接推送权限,甚至成为模块维护者。这条“从帮厨到副厨再到掌勺”的路径,靠的不是单次惊艳,而是持续的可靠输出。

掌勺不是一天练成的。每一次干净利落的 PR,都在为你的“主厨资格”档案添一笔。

Rep=∑t=1TAcceptedt\displaystyle \text{Rep} = \sum_{t=1}^{T} \text{Accepted}_t
这个公式表示:你在社区中的声誉等于一段时间内所有被接受的贡献之和。数量不是唯一因素,但持续被合入的 PR 是最硬的积累。
  1. 贡献声誉\text{Rep} 代表你在社区中积累的信誉资产。
    Rep\text{Rep}
  2. 求和符号\sum_{t=1}^{T} 表示把所有时间点上的贡献累加起来。
    ∑t=1T\sum_{t=1}^{T}
  3. 被接受的贡献\text{Accepted}_t 是第 t 个被维护者合并的 PR,每个括号都是一次“合入”的信任投资。
    Acceptedt\text{Accepted}_t

社区礼仪:灶台上的温度

再高级的餐厅,服务员的微笑也至关重要。开源社区由真人维护,他们也是下班后打开电脑审阅你的代码。因此,沟通的温度直接决定你能否获得第二次机会。

在 Issue 和 PR 评论中,保持友善、具体、礼貌。不说 “你写的这是什么垃圾”,而说 “这里我不太理解,比如当输入为空时,函数会返回什么?” 维护者如果一周没回复,不要每天催,耐心等待即可——他们可能在平衡工作与生活的间隙处理社区事务。对于别人的评论,即使不认同,也先感谢对方花时间,再给数据或用例说明理由。

这些行为共同构成“社区信任度”。它不是一个可以测量的硬指标,却会在维护者心里形成“此人靠谱、沟通顺畅”的印象,直接影响你的 PR 被打包处理的速度。

主厨愿意回你的消息、认真看你的 PR,不只是因为你代码写得好,更因为和你共事舒服。

Trust=f(kindness,patience,reliability)\displaystyle \text{Trust} = f\left(\text{kindness}, \text{patience}, \text{reliability}\right)
这个公式表示:维护者对你的信任,是友善、耐心和可靠三者共同作用的结果。
  1. 信任值\text{Trust} 表示维护者对你这个贡献者整体的信任水平。
    Trust\text{Trust}
  2. 函数关系f(\cdot) 表示一个综合评价模型,三项输入会共同影响输出。
    f(⋯ )f\left( \cdots \right)
  3. 三个维度\text{kindness}(友善)、\text{patience}(耐心)、\text{reliability}(可靠),就像菜品的色、香、味,缺一不可。
    kindness,patience,reliability\text{kindness}, \text{patience}, \text{reliability}

AI 应用场景

场景一:给 Hugging Face 的模型库修文档

阶段:模型开源协作 / 文档维护

解决的问题:传统方式是你读完文档后在本地自嗨,但拼写错误、示例过时会影响全球使用者。通过 Fork-PR 流程,你可以把修改提交回上游,让整个社区受益,同时为求职简历留下可追踪的贡献记录。

具体案例:你发现 transformers 的某个中文文档中 temperature 参数示例有误。于是 Fork 仓库、新建 docs/fix-temperature-example 分支、提交修改、推送后创建 PR,关联 Issue #12345。维护者一天后 Approve 并合并,你的名字出现在贡献者列表里。

场景二:AI 企业内部的 Code Review 流水线

阶段:MLOps 持续集成 / 团队协作

解决的问题:企业内部多模型服务端代码合入主分支前,靠一人审查容易漏掉数据泄漏或评估逻辑错误。引入 GitHub Flow 的 PR 与 Review 机制,相当于把“主厨试吃”制度化,让至少两位工程师审阅每次改动。

具体案例:算法工程师在 feature/fast-tokenizer 分支上优化分词速度,提交 PR 后 CI 自动跑单元测试与基准评估;两位同事在 PR 评论区提出“请补充长文本用例”,作者补完后重新推送,全部 Approve 后合并。

场景三:作为维护者运营开源 AI 项目

阶段:开源生态治理 / 社区运营

解决的问题:开源项目收到大量质量参差的 PR,维护者如果没有评审规范,容易耗尽精力。使用 Issue 标签、PR 模板和 Review 规则,可以像餐厅主厨调配帮厨一样高效分配注意力。

具体案例:你维护一个 stable-diffusion-utils 工具库,在新手 PR 下用友好语气标注“感谢贡献,请补充测试”,并触发 CI 检查;对有效改动及时 Approve,对不合规范的 PR 给出具体修改建议。三个月后项目贡献者数量翻倍。

Python 演示

python53 行
class Commit:    def __init__(self, message):        self.message = message class Branch:    def __init__(self, name, base=None):        self.name = name        self.commits = []        self.base = base     def add_commit(self, message):        self.commits.append(Commit(message)) class Repository:    def __init__(self, name):        self.name = name        self.branches = {"main": Branch("main")}     def fork(self):        forked = Repository(self.name + "-fork")        return forked class PullRequest:    def __init__(self, source_branch, target_repo):        self.source = source_branch        self.target = target_repo        self.reviewed = False        self.approved = False     def review(self, approve):        self.reviewed = True        self.approved = approve     def merge(self):        if self.approved:            self.target.branches["main"].commits.extend(self.source.commits)            return True        return False def main():    upstream = Repository("openai/whisper")    contributor = upstream.fork()    branch = contributor.branches["main"]    branch.add_commit("fix: correct language detection")    pr = PullRequest(branch, upstream)    pr.review(approve=True)    if pr.merge():        print("合并成功,首次贡献完成!")    else:        print("PR 未通过评审,等待反馈。") if __name__ == "__main__":    main()
目的:用面向对象的方式模拟 GitHub Workflow 的核心对象——仓库、分支、提交、PR 与合并,帮助你理解每个概念之间的关系。
  1. class Branch分支类,保存该分支上的提交列表,对应“实验台”。
  2. def add_commit(self, message)给分支添加一条提交记录,对应贴标签动作。
  3. def fork(self)从上游仓库建立一个空副本,对应 GitHub 的 Fork 按钮。
  4. class PullRequestPR 类,连接源分支与目标仓库,对应“端菜上桌”。
  5. pr.review(approve=True)模拟评审人点下 Approve。
  6. pr.merge()只有 approved 为真时才把提交复制进目标仓库,体现 Merged = Approved ∧ ... 的逻辑。

常见误区

误区我刚 Fork 完就急着开发,反正那是自己的副本,等开发完再同步也不迟。

正解主厨的菜谱每天都在变,你如果基于三个月前的版本做菜,端上桌时可能发现主料都换了。正确做法是开工前先 git fetch upstream 并合并,开发过程中定期同步。尤其是开发周期超过一周时,同步频率要更高。

为何常见:新手以为 Fork 是“快照”,忽视了它是“会持续漂移的副本”。Git 的分布式特性让本地副本不会自动更新,于是冲突越积越多。

误区我每次就直接在 main 分支上改,改完直接提 PR,不是也可以吗?

正解在 main 上开发意味着所有功能改动混在同一时间线上,无法独立管理。正确做法是每个功能建一个分支,分支命名带前缀和描述,随时可以并行多个实验台,一个坏了不影响另一个。

为何常见:个人项目里大多数人直接在 main 上提交,习惯了单线操作。但开源协作是多人并行,分支是强制的基本单位。

误区写 “update file“ 或 ”fix” 就够了,改动内容反正都在 diff 里。

正解diff 只能看到改了什么,看不到为什么改。Commit 信息是给未来维护者的便签,建议用 type(scope): subject 描述意图,如 fix(trainer): reduce learning rate for stability。这样评审人在 PR 列表里就能快速判断优先级。

为何常见:写清楚原因需要额外思考,新手急着提交,认为“代码会说话”。实际上代码只表达“如何”,不表达“为何”。

误区评审人提了好多意见,我直接在本地 rebase 压缩成一个漂亮 commit,然后 force push 上去,这样历史很干净。

正解force push 会重写已经公开的提交历史,评审人之前写下的逐行评论将对应不到代码。正确做法是:在分支上追加新的 commit 回应意见,让评审人看到“这条意见已被修复”的轨迹。除非维护者明确要求整合历史,否则不要强制推送。

为何常见:追求完美历史的洁癖在新手中很常见,但忽略了 Review 是一个过程,历史本身也是评审的记录。

误区反正都改了,一起提个 PR,省得维护者看多个请求。

正解一个 PR 应该只解决一个逻辑问题。比如“修 Bug A”和“顺手重构模块 B”不应合并。维护者可能想合入 Bug 修复,却不想引入重构风险。你强行捆绑,会导致整个 PR 被搁置。

为何常见:新手把 PR 当作“打包上传”,没有理解 PR 是“一次可独立决策的变更提案”。餐厅里,主厨一次只品尝一道菜,而不是杂烩拌饭。

面试题

Q1. 在 GitHub 上 Fork 一个仓库后,你得到的是什么?

单选题 · easy

  • A.原仓库的管理权限
  • B.原仓库的独立副本← 正确答案
  • C.直接推送原仓库
  • D.原仓库的只读镜像

答案:B。Fork 是在你的命名空间下创建一份完整的独立副本,你可以随意写。它既不是管理权限,也不是直接推送原仓库,更不是只读镜像。就像复印菜谱回家,你在自家副本上可以任意涂改。

Q2. 在提交 PR 之前,为了保证协作规范,你应该怎么做?

单选题 · easy

  • A.直接修改主分支
  • B.创建独立功能分支← 正确答案
  • C.删除自己的副本
  • D.等待维护者联系

答案:B。每个功能都应该在独立分支上开发,这样能让 PR 保持单一职责,也方便并行工作。直接修改主分支会混入无关改动,删除副本和等待联系都不符合协作流程。

Q3. Code Review 中收到“请修改后再提交”的意见,最合适的做法是?

单选题 · mid

  • A.忽略意见并完成合并
  • B.删除原 PR 再重新提交
  • C.在分支继续提交后推送← 正确答案
  • D.复制到新仓库再提 PR

答案:C。Review 是一个迭代过程。你应在本地同一分支上继续提交修复,并推送到 Fork 仓库,PR 会自动更新。删除 PR 或复制到新仓库会丢失评论上下文,忽略意见更是大忌。

Q4. 以下哪项最不利于 PR 被合并?

单选题 · mid

  • A.提供清晰描述
  • B.附带测试用例
  • C.关联相关 Issue
  • D.掺杂大量无关改动← 正确答案

答案:D。无关改动会让 Review 焦点分散,增加安全风险,维护者很可能直接关闭 PR。清晰描述、测试用例和关联 Issue 都是提升合并概率的正向操作。

Q5. 关于 Fork 的同步,正确的理解是?

单选题 · mid

  • A.Fork 后永远自动同步
  • B.需手动同步上游代码← 正确答案
  • C.只能删除 Fork 重建
  • D.与上游变化完全无关

答案:B。Fork 是静态副本,GitHub 不会自动把上游的新提交推给你。你需要配置 upstream 远程地址,并手动 git fetch upstream 后合并到本地分支。删除重建是最后手段,不是正常同步方式。

Q6. 以下哪些操作会修改提交历史?

多选题 · hard

  • A.rebase -i 压缩提交← 正确答案
  • B.commit --amend 修改提交← 正确答案
  • C.push --force 强推覆盖← 正确答案
  • D.status 查看工作区

答案:ABC。rebase -i 可以压缩、重排提交;commit --amend 会改写最近一次提交;push --force 用本地历史强制覆盖远程分支。status 只查看状态,不改变任何历史。实际操作中,应谨慎对公开的 PR 分支使用这些命令。

Q7. 以下哪些属于 Conventional Commits 约定的提交类型?

多选题 · mid

  • A.feat 新功能← 正确答案
  • B.fix 修复缺陷← 正确答案
  • C.add 添加文件
  • D.docs 更新文档← 正确答案

答案:ABD。Conventional Commits 规定了一批有语义的类型,feat 表示新功能,fix 表示修复,docs 表示文档变更。add 不是标准类型,容易产生歧义,应改用更明确的描述。

Q8. 你发现 PR 里挤进了大量与功能无关的 "Merge branch" 提交,最可能的原因是?

单选题 · hard

  • A.使用了 git revert 回滚
  • B.用 merge 方式同步上游← 正确答案
  • C.用 rebase 交互式压缩
  • D.创建了新分支提 PR

答案:B。用 git merge upstream/main 同步上游时,会产生 merge commit。如果反复同步,PR 中就会出现大量无关的合并节点。改用 git rebase upstream/main 可以重放在干净历史之上。revert、rebase -i 和新建分支都不是这个问题的原因。

Q9. 大型开源项目偏好“小步 PR”,下列哪项不是它的好处?

单选题 · extreme

  • A.便于 Review 聚焦逻辑
  • B.降低引入 Bug 的风险
  • C.减少 CI 运行的时间开销
  • D.限制贡献者的代码数量← 正确答案

答案:D。小步 PR 的好处是评审更聚焦、回归风险更小、冲突更少。它并不是为了“限制贡献者代码量”,而是提倡用多次小 PR 交付同体积代码。硬性限制代码行数既不现实也不符合开源精神。

Q10. 关于 GitHub 分支保护规则,以下哪些说法是正确的?

多选题 · easy

  • A.禁止直接推送 main← 正确答案
  • B.合并必须通过 PR← 正确答案
  • C.至少一位评审通过← 正确答案
  • D.强制所有成员 Fork

答案:ABC。分支保护通常不允许直接推送到 main,要求以 PR 方式合并,并设置一定数量的 Approval 才能合并。它并不强制所有成员都走 Fork,内部协作者可以直接在仓库中建分支提 PR。