一、为什么需要这套流程?原理是什么?
1. 传统模式的问题(你之前习惯的 commit → push)
-
如果你和同事都在同一个仓库的主分支(如
main)上直接 push,后 push 的人必须先解决冲突,很容易覆盖别人的代码。 -
没有代码审查(Code Review),任何错误都可能直接上线。
-
无法进行持续集成(CI)自动测试,问题难以提前发现。
2. Fork + PR 模式的优点
-
Fork(派生):将主仓库完整复制一份到你自己的账号下,相当于你的"私人工作区"。你可以在自己的 fork 里任意修改,不会影响主仓库。
-
分支(Branch):每个新功能或修复都在独立的分支上开发,避免不同任务之间的代码混杂。
-
Pull Request(PR) :向原仓库发起合并请求。它提供了代码审查 和自动化测试的触发点。只有经过审查和测试的分支才能合并到主分支。
-
保护主分支 :主分支(如
main)只接受经过 PR 合并的代码,始终保持稳定可发布状态。
3. 完整流程的逻辑链条
text
1. 主仓库(上游) ← 2. Fork(你的副本) ← 3. Clone 到本地
4. 创建新分支 → 5. 开发 + 测试 → 6. Push 到你的远程 fork
7. 发起 PR → 8. 团队成员 Review → 9. 根据反馈修改 → 10. 合并
完整操作步骤(以 GitHub 为例)
1. Fork (派生,分支)原仓库
-
打开上游仓库页面,点击右上角 Fork 按钮
-
选择 fork 到你的账号下
2. 克隆你的 fork 到本地
bash
git clone https://github.com/你的用户名/仓库名.git
cd 仓库名
cd 仓库名就是让你 进入这个仓库的根目录,让后续所有 Git 命令能够正确运行添加上游仓库地址(
git remote add upstream ...)的本质是:修改当前仓库的配置 (写入.git/config文件)。
只有当你身处该仓库的根目录时,
git remote命令才知道要修改哪个仓库的配置。如果你没 cd 进去,Git 找不到仓库,自然无法添加 upstream
即:
cd 仓库名是为了让你站在 Git 仓库的"内部",这样所有 Git 命令才能知道要操作哪个仓库,添加 upstream 地址是仓库级别的配置,必须先 cd 进去。
3. 添加上游仓库地址(用于同步)
bash
git remote add upstream https://github.com/原仓库拥有者/仓库名.git
git remote -v # 验证:origin 是你的 fork,upstream 是原仓库
背景知识
当你 clone 你自己的 fork 时,Git 会自动创建一个默认的远程仓库,名字叫 origin,指向 你的 fork (你的用户名/仓库名.git)。
但你的本地仓库并不认识 原始项目(上游仓库)的地址,所以你需要手动告诉它。
第一个命令:
给你的本地仓库添加一个指向原始项目仓库 的远程地址,并给它起个名字叫
upstream。这条命令的组成部分
git remote add→ 添加一个远程仓库记录
upstream→ 你给这个远程仓库起的名字(可以任意,但社区约定俗成用upstream)
https://github.com/原仓库拥有者/仓库名.git→ 原始项目的真实地址执行完后,你的本地仓库就知道:
origin是 我的 fork
upstream是 原作者的项目二、为什么需要添加
upstream?因为原始项目(上游仓库)会不断更新(比如别人合并了新的 PR)。
如果没有
upstream,你就无法从原始项目拉取最新代码,你的 fork 会逐渐落后,将来你提交 PR 时就会产生很多冲突。有了
upstream,你可以随时运行:bash
git fetch upstream # 拉取原始项目的最新代码 git merge upstream/main # 将原始项目的 main 分支合并到你的本地 main从而保持你的代码和原始项目同步。
第二个命令:
git remote -v
-v是--verbose的缩写,意思是 显示所有远程仓库的详细地址。执行后会输出类似:
text
bashorigin https://github.com/你的用户名/仓库名.git (fetch) origin https://github.com/你的用户名/仓库名.git (push) upstream https://github.com/原仓库拥有者/仓库名.git (fetch) upstream https://github.com/原仓库拥有者/仓库名.git (push)验证的目的:
确认
upstream确实添加成功,并且 URL 写对了(没有拼错用户名或仓库名)。确认
origin仍然指向你自己的 fork(确保后续 push 不会推到错误的地方)。
4. 创建新分支(基于上游最新 main)
bash
bash
git fetch upstream # 获取上游最新代码
git checkout -b my-feature upstream/main # 创建并切换到新分支
一、
git fetch upstream这条命令的作用是:从你添加的
upstream远程仓库(即原始项目仓库)下载所有最新的内容(新提交、分支、标签等),但不会合并到你当前的任何本地分支中。执行后,Git 会在本地生成一组"远程跟踪分支",比如
upstream/main、upstream/develop等。这些分支代表了上游仓库各个分支的最新状态。为什么需要这一步?
因为你的本地仓库原本只知道
origin/main(你自己的 fork 的主分支)和可能旧的本地main。如果不先fetch,你的本地就没有upstream/main这个"指针",也就无法基于上游最新代码创建新分支。它并不是"存为一个文件",而是更新 Git 内部的一个指针,指向上游main分支最新提交的位置。可以认为:执行后,
upstream/main就代表了上游main的最新状态。
git fetch upstream 就是去上游仓库看了一眼,把"上游各分支现在长什么样"的信息记在本地的笔记本里(即 upstream/main 等指针),但你的工作区(你正在编辑的代码文件)不会有任何变化
这个"笔记本"就是 .git/refs/remotes/upstream/ 下的文件
二、
git checkout -b my-feature upstream/main这条命令做了两件事:
checkout -b my-feature:创建一个新分支 ,名字叫my-feature,并立即切换到这个分支。
upstream/main:指定了新分支的起点 (基准),也就是基于上游仓库的main分支当前的最新状态来创建。等价于:
bash
bashgit branch my-feature upstream/main # 创建分支,但不切换 git checkout my-feature # 切换到该分支为什么不能用
git checkout -b my-feature main?
如果你用
main(本地main)作为起点,而你的本地main可能已经落后于上游(因为别人合并了 PR 你没有同步),那么你创建的新分支就会基于一个过时的版本,后续提交 PR 时会产生额外的合并冲突。用
upstream/main可以确保你的新分支直接基于上游仓库的最新代码,这样将来提交 PR 时,差异最干净,冲突最少。三、整个流程的逻辑链条
text
bash1. git fetch upstream → 将上游的最新状态同步到本地(存为 upstream/main) 2. git checkout -b my-feature upstream/main → 基于刚刚获取的上游 main 分支,创建一个新的本地分支这样,你的
my-feature分支就等同于从上游的 main 分出来的 ,就像是你在上游仓库直接创建了一个分支一样。之后再开发、提交、push 到origin(你自己的 fork),然后发起 PR,PR 的目标分支就是上游的main,GitHub 会自动发现你的分支是基于上游最新代码的,不会有额外的合并提交
总结
fetch 刷新笔记本里的 upstream/main 指针,checkout -b 则读取这个指针作为新分支的起点
,即上游仓库 main 分支的最新代码
5. 开发 + 本地测试
-
使用编辑器修改代码
-
运行项目要求的测试 (如
npm test、pytest等),确保全部通过
6. 提交并推送到你的 fork
bash
bash
git add .
git commit -m "feat: 添加某个功能"
git push origin my-feature # 推送到你的 fork 仓库的对应分支
git push origin my-feature:
推送到你自己的远程 fork 仓库 (即origin指向的那个 GitHub 仓库)。这个仓库是你的副本,你有完全的写权限
git push origin my-feature:将本地my-feature分支上的提交推送到你自己的远程 fork 仓库 (即origin指向的那个 GitHub 仓库)。这个仓库是你的副本,你有完全的写权限。
问题问"提交到自己的远程私有仓库?"
如果原仓库是公开的,你的 fork 默认也是公开的(不是私有),但只有你有写权限。
如果你手动把 fork 设为私有,那才是私有仓库。
通常我们称它为"你自己的远程仓库"(或者"你的 fork"),不一定私有,但归你控制。
推送之后 ,你的
my-feature分支就会出现在 GitHub 上你自己的 fork 仓库里,然后你就可以去发起 Pull Request(从origin/my-feature到upstream/main)。现在只剩下最后一步"发起 PR"和"审查合并"。如果你现在就想尝试,可以实际 push 一次,然后在 GitHub 上点 "Compare & pull request" 按钮
7. 创建 Pull Request
-
打开你的 fork 仓库页面(GitHub)
-
你会看到一个提示:"my-feature had recent pushes",旁边有 Compare & pull request 按钮
-
点击 Compare & pull request 按钮
-
填写 PR 标题和描述(说明改动内容、测试情况)
-
点击 Create pull request
8. 代码审查与修改
-
维护者或团队成员在 PR 下评论,提出修改意见
-
你回到本地,切换到该分支:
bash
bash
git checkout my-feature
# 根据意见修改代码,再次本地测试
git add .
git commit -m "fix: 根据 review 意见修改"
git push origin my-feature # PR 会自动更新
- 重复直到审查通过
9. 合并
-
当所有人都 审查通过approve(批准)且所有自动化测试(CI)绿色通过后,维护者会点击 Merge pull request 按钮,将你的分支合并进主分支。
-
你的代码正式合并到上游主分支
-
合并后,你可以删除自己的远程分支(可选),并同步更新本地仓库。
10. 同步你的 fork(可选但推荐)
bash
bash
git checkout main
git pull upstream main # 拉取上游最新 main
git push origin main # 同步到你的 fork
git branch -d my-feature # 删除本地分支
git push origin --delete my-feature # 删除远程分支
关键要点提醒
-
不要直接在上游仓库 push → 使用 fork + PR
-
每个功能一个独立分支 → 保持隔离,方便回滚
-
提交前必须本地测试 → 避免浪费 CI 资源
-
PR 内可以多次 push → 自动更新,无需重新建 PR
-
定期同步 upstream → 减少合并冲突
二·、一些小提示
-
为什么要先 fork 而不是直接 clone 原仓库?
因为你没有原仓库的写入权限,fork 让你有自己可写的一份拷贝,然后通过 PR 提出贡献。
-
为什么要在新分支开发?
如果直接用 main 分支,当你提交 PR 后又想开发另一个功能,两个功能会混在一起。分支保持了功能隔离。
-
测试一定要在本地跑:这是你描述中强调的"自己跑一下测试",避免 CI 流水线浪费资源。
-
Review 后再更改:PR 评论系统会自动组织多次修改的会话,最终合并前会确保所有讨论都 resolved。
三、你可以尝试的练习
-
找一个你喜欢的开源项目(或创建一个测试仓库),按照上面的步骤模拟一次 PR 流程。
-
你甚至可以和自己协作:用两个 GitHub 账号,或者让一个朋友扮演 reviewer。
-
使用
git log --oneline --graph查看分支演变,加深理解。
最后
Fork 和 clone 整个仓库只做一次。之后每天加新模块,只需要"同步上游 → 切新分支 → 开发 → push → 开 PR → 等合并"。
其中 第 1-3 步 (主仓库 → Fork → Clone 到本地 + 添加 upstream)只需要在刚开始接触项目时做一次 。
之后的每一次新增小模块、修 bug 或加功能,只需要重复 第 4-10 步中的一部分。
一、一次性初始化(只做一次)
当你第一次给某个项目贡献代码时,需要做这些准备:
-
Fork 主仓库(在 GitHub 网页上点一下按钮)
-
Clone 你的 fork 到本地
-
添加 upstream 远程地址(
git remote add upstream ...)
做完这三步,你的本地环境和远程 fork 就都准备好了。以后不用再做。
二、每天新增模块时的日常流程(可以简化成 6 步)
假设你已经完成了上面的初始化。现在你每天要开发一个新模块(新功能或修复),需要重复的步骤是:
-
同步上游最新代码(避免冲突)
bash
git fetch upstream git checkout main git merge upstream/main git push origin main -
基于最新的 main 创建新分支
bash
git checkout -b feat/新模块名 main -
在分支上开发 + 本地测试
-
提交并 push 到自己的远程 fork
bash
git add . git commit -m "feat: 添加新模块" git push origin feat/新模块名 -
发起 PR(在 GitHub 网页上点按钮)
-
根据 review 反馈修改 (只需继续 push 到同一个分支,PR 自动更新)
→ 等待维护者合并
不需要重新 fork,不需要重新 clone,不需要重新添加 upstream。
三、对比表格(更清晰)
| 步骤 | 描述 | 初次接触项目时 | 每天新增模块时 |
|---|---|---|---|
| 1 | 主仓库 ← Fork | ✅ 需要(一次) | ❌ 不需要 |
| 2 | Clone 到本地 | ✅ 需要(一次) | ❌ 不需要 |
| 3 | 添加 upstream | ✅ 需要(一次) | ❌ 不需要 |
| 4 | 创建新分支 | ✅ 每次都需要 | ✅ 每次都需要 |
| 5 | 开发 + 测试 | ✅ 每次都需要 | ✅ 每次都需要 |
| 6 | Push 到你的远程 fork | ✅ 每次都需要 | ✅ 每次都需要 |
| 7 | 发起 PR | ✅ 每次都需要 | ✅ 每次都需要 |
| 8 | Review | ✅ 每次都需要 | ✅ 每次都需要 |
| 9 | 根据反馈修改 | ✅ 每次都需要 | ✅ 每次都需要 |
| 10 | 合并 | ✅ 每次都需要 | ✅ 每次都需要 |
即
第二次、第三次......开发新模块时,你只需要做:
text
4. git fetch upstream
git checkout -b new-feature upstream/main
5. 开发 + 测试
6. git add . && git commit -m "..." && git push origin new-feature
7. 创建 PR
8. 根据 review 修改
9. 等待合并
10. (合并后) git checkout main && git pull upstream main && git push origin main