一、适用场景
你现在的目标,本质上是下面这件事:
- 基于 GitHub 上的开源项目做二次开发。
- 你的代码放在你自己的仓库里。
- 以后开源项目继续更新时,你希望能不定期把它的最新稳定分支同步进来。
- 同步后,再把这些更新合并到你自己的二开分支中。
这类需求最稳妥的做法,不是直接在上游的稳定分支上乱改,而是把"同步上游"和"你自己的改动"拆开管理。
二、推荐的分支与远程设计
强烈建议你采用下面这套结构。
1. 远程仓库的角色
origin:你自己的 GitHub 仓库。upstream:原始开源项目的 GitHub 仓库。
也就是说:
- 你平时
push到的是origin - 你平时拉取开源项目更新时,拉的是
upstream
2. 分支的角色
建议至少维护两个核心分支:
upstream-stable:专门用于同步上游稳定分支,尽量不要在这个分支上写你自己的业务改动。custom-main:你自己的二开主分支,真正用于开发和交付。
如果你还有具体需求开发,可以再从 custom-main 拉出:
feature/loginfeature/paymentfix/order-bug
3. 为什么不要直接在 main 上二开
如果你直接在一个和上游同名的分支上改代码,比如直接在 main 上二开,那么后续同步上游更新时:
- 冲突会更多
- 历史会更乱
- 你会很难区分"哪些是上游改动,哪些是你自己的改动"
- 一旦误操作,容易把你的改动覆盖掉
所以推荐做法是:
- 用一个"纯同步分支"跟上游走
- 用一个"二开分支"承接你的业务改动
三、整体工作流总览
完整流程如下:
- 先 Fork 开源项目到你自己的 GitHub 账号下。
- 克隆你自己的仓库到本地。
- 给本地仓库增加
upstream远程,指向原始开源项目。 - 创建一个
upstream-stable分支,用来跟踪上游稳定分支。 - 基于
upstream-stable创建你的二开分支custom-main。 - 日常开发都在
custom-main或它的功能分支上进行。 - 当上游有新稳定版本时:
- 先把上游更新同步到
upstream-stable - 再把
upstream-stable合并到custom-main
- 先把上游更新同步到
你可以把它理解成:
上游稳定代码 -> upstream-stable -> custom-main -> 你的功能分支
四、首次搭建的详细操作步骤
下面假设:
- 上游开源项目地址:
https://github.com/open-source-owner/demo-project.git - 你的仓库地址:
https://github.com/yourname/demo-project.git - 上游稳定分支:
main
如果对方不是 main,而是 master、release、stable、release/1.0 之类,请把命令里的分支名替换掉。
第 1 步:先 Fork 开源项目
在 GitHub 页面中操作:
- 打开上游项目页面
- 点击右上角
Fork - 选择 Fork 到你自己的账号或组织
完成后,你会得到自己的仓库,比如:
bash
https://github.com/yourname/demo-project.git
说明:
Fork 的本质,是在 GitHub 服务器上复制一份仓库到你自己的名下。
以后你对自己仓库有完全控制权,但仍然可以从原仓库同步更新。
第 2 步:克隆你自己的仓库
bash
git clone https://github.com/yourname/demo-project.git
cd demo-project
命令含义
git clone https://github.com/yourname/demo-project.git
git clone:把远程仓库完整复制到本地。- 后面的 URL:表示你要克隆的仓库地址。
- 克隆完成后,本地会生成一个同名目录,并自动配置一个默认远程叫
origin。
cd demo-project
cd:进入刚刚克隆下来的项目目录。
第 3 步:查看当前远程仓库
bash
git remote -v
命令含义
git remote:查看或管理远程仓库别名。-v:显示更详细的信息,包括远程仓库地址。
你通常会看到类似结果:
bash
origin https://github.com/yourname/demo-project.git (fetch)
origin https://github.com/yourname/demo-project.git (push)
这里说明:
- 拉取代码默认来自
origin - 推送代码默认也推给
origin
第 4 步:添加上游仓库 upstream
bash
git remote add upstream https://github.com/open-source-owner/demo-project.git
然后再次查看:
bash
git remote -v
你应该能看到:
bash
origin https://github.com/yourname/demo-project.git (fetch)
origin https://github.com/yourname/demo-project.git (push)
upstream https://github.com/open-source-owner/demo-project.git (fetch)
upstream https://github.com/open-source-owner/demo-project.git (push)
命令含义
git remote add upstream <仓库地址>
git remote add:添加一个新的远程仓库别名。upstream:这个别名是你自定义的,业内通常把原始开源仓库命名为upstream。<仓库地址>:原始开源项目地址。
这一步之后:
origin代表你的仓库upstream代表原始开源仓库
第 5 步:拉取所有远程分支信息
bash
git fetch --all --prune
命令含义
git fetch:从远程仓库拉取最新提交、分支、标签到本地,但不会自动合并到当前分支。--all:从所有远程仓库拉取,也就是同时拉取origin和upstream。--prune:清理本地已经失效的远程引用,比如远程删掉的分支。
这条命令很安全,因为它只更新本地对远程的"认知",不会直接改你的工作代码。
第 6 步:查看上游有哪些分支
bash
git branch -a
或者更聚焦一点:
bash
git branch -r
命令含义
git branch -a
git branch:查看分支-a:显示本地分支和远程分支
git branch -r
-r:只显示远程分支
你可能会看到:
bash
remotes/origin/main
remotes/origin/dev
remotes/upstream/main
remotes/upstream/release
这时你就可以确定,究竟要跟踪哪一个"稳定分支"。
第 7 步:创建专门同步上游的分支 upstream-stable
如果上游稳定分支是 main,执行:
bash
git checkout -b upstream-stable upstream/main
然后推送到你自己的仓库:
bash
git push -u origin upstream-stable
命令含义
git checkout -b upstream-stable upstream/main
git checkout:切换分支。-b upstream-stable:如果本地没有这个分支,就新建一个叫upstream-stable的分支。upstream/main:表示以远程upstream的main分支作为起点创建。
这条命令执行后的效果是:
- 本地新建一个
upstream-stable - 它的代码内容和
upstream/main一致
git push -u origin upstream-stable
git push:把本地分支推送到远程仓库。-u:建立"上游跟踪关系"。以后你在这个分支上直接git push或git pull,Git 就知道默认对应哪个远程分支。origin upstream-stable:表示把本地upstream-stable推送到你的远程仓库origin。
第 8 步:基于同步分支创建你的二开主分支
bash
git checkout -b custom-main upstream-stable
git push -u origin custom-main
命令含义
git checkout -b custom-main upstream-stable
- 新建并切换到
custom-main - 它的起点是
upstream-stable
这意味着:
- 你的二开分支一开始和上游稳定分支完全一致
- 之后你在
custom-main上写自己的业务改动
git push -u origin custom-main
- 把你的二开主分支推送到你自己的 GitHub 仓库
- 并建立本地
custom-main与远程origin/custom-main的跟踪关系
五、日常开发建议
1. 平时不要直接在 upstream-stable 上开发
这个分支最好只做一件事:
- 接收上游稳定分支的更新
不要在这里开发你的业务代码,否则后续同步时就失去"纯净同步分支"的意义。
2. 日常在 custom-main 或功能分支开发
比如:
bash
git checkout custom-main
git pull
git checkout -b feature/user-center
命令含义
git checkout custom-main
- 切换到你的二开主分支
git pull
- 从当前分支所跟踪的远程分支拉取最新代码并尝试自动合并
- 因为前面用了
-u建立跟踪关系,所以这里默认相当于从origin/custom-main拉取
git checkout -b feature/user-center
- 基于当前分支创建一个功能分支
- 用于隔离某个具体功能开发
开发完成后,再合并回 custom-main。
六、以后如何同步上游最新稳定分支
这是整个流程里最关键的部分。
假设上游稳定分支还是 main。
标准同步流程
第 1 步:抓取上游最新代码
bash
git fetch upstream --prune
命令含义
git fetch upstream:只从upstream这个远程仓库拉取最新信息--prune:删除已经在远程被移除的旧引用
执行后,本地的 upstream/main 就会更新到最新状态,但你当前分支还没有变化。
第 2 步:切换到同步分支
bash
git checkout upstream-stable
命令含义
- 切换到专门同步上游代码的本地分支
第 3 步:把上游稳定分支快进合并到同步分支
bash
git merge --ff-only upstream/main
命令含义
git merge:把另一个分支的提交合并到当前分支--ff-only:只允许"快进合并"
所谓快进合并,意思是:
- 如果你的
upstream-stable没有自己额外的提交 - 并且它只是落后于
upstream/main - 那么 Git 会直接把分支指针向前移动
这个参数非常适合纯同步分支,因为它能保证:
- 不会生成多余的 merge commit
- 如果你误在
upstream-stable上做了自己的提交,它会直接报错,而不是偷偷合进去
如果这一步失败,通常说明:
- 你在
upstream-stable上做过额外提交 - 或这个分支已经不再是一个纯净同步分支
第 4 步:把更新后的同步分支推到你自己的仓库
bash
git push origin upstream-stable
命令含义
- 把本地更新后的
upstream-stable推送到origin - 这样你的 GitHub 仓库里也保存了最新同步结果
第 5 步:切换到你的二开主分支
bash
git checkout custom-main
第 6 步:把同步分支的更新合并到你的二开分支
bash
git merge upstream-stable
命令含义
- 把
upstream-stable的最新内容合并到当前分支custom-main - 如果你的二开分支上有自己的改动,Git 会尝试自动合并
- 如果存在同一位置的改动冲突,就需要你手动解决
这一步是整个"把上游更新带入你的二开项目"的核心步骤。
第 7 步:测试并推送你的二开分支
bash
git push origin custom-main
如果你还有 CI、自动化测试、构建命令,也应该在这一步之前先执行。
例如:
bash
npm install
npm test
npm run build
注意:
真正应该以什么命令测试,取决于你的项目技术栈。
Java 项目可能是
mvn test,Python 项目可能是pytest,Go 项目可能是go test ./...。
七、一套最常用的完整命令模板
以后每次同步时,你基本可以按下面顺序执行。
bash
git fetch upstream --prune
git checkout upstream-stable
git merge --ff-only upstream/main
git push origin upstream-stable
git checkout custom-main
git merge upstream-stable
git push origin custom-main
如果上游稳定分支不是 main,把 upstream/main 替换成对应分支名即可,例如:
bash
git merge --ff-only upstream/release
或者:
bash
git merge --ff-only upstream/release/1.0
八、如果出现冲突,应该怎么处理
当你执行:
bash
git merge upstream-stable
如果 Git 提示冲突,不要慌,这是很常见的。
1. 先看冲突状态
bash
git status
命令含义
git status:查看当前仓库状态- 它会告诉你哪些文件冲突了、哪些文件待提交
2. 打开冲突文件,处理冲突标记
你会看到类似内容:
text
<<<<<<< HEAD
这是你 custom-main 上的代码
=======
这是 upstream-stable 合并进来的代码
>>>>>>> upstream-stable
含义是:
<<<<<<< HEAD到=======:当前分支的内容,也就是你的二开代码=======到>>>>>>> upstream-stable:来自同步分支的内容
你需要手动决定:
- 保留哪一边
- 或把两边合理整合到一起
3. 标记冲突已解决
处理完每个冲突文件后,执行:
bash
git add <冲突文件路径>
比如:
bash
git add src/app.js
git add package.json
命令含义
git add:把修改加入暂存区- 在合并冲突场景下,它还表示"这个文件的冲突我已经解决完了"
4. 完成合并
如果是普通 merge,在所有冲突文件都 git add 完以后,执行:
bash
git commit
命令含义
- 这一步会生成一个合并提交
- Git 会自动给你一个默认的 merge commit message,你也可以自己改
5. 如果想放弃这次合并
bash
git merge --abort
命令含义
- 放弃当前尚未完成的合并
- 尽量回到合并开始前的状态
九、为什么这里推荐 merge,而不是一上来就 rebase
你会经常看到两种做法:
git merge upstream-stablegit rebase upstream-stable
这里更推荐你在"长期维护二开项目"的场景下优先使用 merge。
推荐 merge 的原因
- 更安全,尤其是多人协作时
- 不会重写已经存在的公开提交历史
- 更容易看出"哪一次是同步上游更新"
- 冲突处理完成后,历史语义更清晰
rebase 的特点
- 会把你的提交"重新播放"到新的基线上
- 历史看起来更直
- 但如果分支已经推送给别人使用,再
rebase会带来额外风险
结论
对于大多数二开维护场景:
- 同步上游到同步分支:用
git merge --ff-only - 把同步分支合并到二开分支:优先用
git merge
如果你非常熟悉 Git,而且是纯个人分支,也可以在少数场景下使用 rebase。
十、如果上游所谓"稳定版"不是分支,而是 Tag / Release
有些项目没有长期维护的稳定分支,而是:
- 平时开发在
main - 稳定版本通过
tag发布,比如v1.2.0
这种情况下,你有两种选择。
方案 A:仍然跟踪官方维护的稳定分支
如果官方有:
release/1.0stablelts
优先跟踪这些正式稳定分支。
方案 B:按 Tag 建立你自己的同步基线
比如先抓取标签:
bash
git fetch upstream --tags
查看标签:
bash
git tag
基于某个稳定标签创建同步分支:
bash
git checkout -b upstream-stable v1.2.0
以后官方发布 v1.3.0 时,你可以:
bash
git checkout upstream-stable
git merge v1.3.0
不过从长期维护角度看,Tag 同步通常不如稳定分支自然。
如果项目提供稳定分支,优先跟踪稳定分支。
十一、如何判断哪个才是"最新稳定分支"
你需要先确认上游项目的发布策略,不要想当然把 main 当成稳定版。
通常可以通过下面几种方式判断:
- 看仓库首页默认分支说明
- 看
README.md - 看
CONTRIBUTING.md - 看
Release页面 - 看维护者是否说明:
main是开发分支release/x.y是稳定分支lts是长期支持分支
实战建议
- 如果官方明确说
main可用于生产,那就跟踪main - 如果官方提供
release/stable/lts,优先跟踪这些分支 - 如果只有 Tag/Release,没有稳定分支,就按发布版本做同步
十二、推荐的远程与分支命名规范
为了以后维护不混乱,建议统一命名。
远程命名
origin:你的仓库upstream:原始开源仓库
分支命名
upstream-stable:专门同步上游稳定代码custom-main:你的二开主分支feature/*:新功能开发fix/*:Bug 修复release/*:你自己的发布分支
十三、常用命令速查表
| 命令 | 作用 | 说明 |
|---|---|---|
git remote -v |
查看远程仓库 | 确认 origin 和 upstream 是否配置正确 |
git fetch upstream --prune |
拉取上游最新信息 | 不直接改当前工作区 |
git branch -a |
查看所有分支 | 包含本地和远程分支 |
git checkout upstream-stable |
切换分支 | 切到同步分支 |
git merge --ff-only upstream/main |
快进同步上游 | 适合纯净同步分支 |
git checkout custom-main |
切换到二开分支 | 准备接收上游更新 |
git merge upstream-stable |
合并同步分支 | 把上游更新带入二开分支 |
git status |
查看状态 | 冲突排查最常用 |
git add <file> |
暂存文件 | 冲突解决后标记完成 |
git commit |
提交合并结果 | 生成合并提交 |
git merge --abort |
放弃合并 | 回退到合并前状态 |
git push origin <branch> |
推送分支 | 把结果提交到你的 GitHub |
十四、完整示例:从零到长期维护
下面给你一套可以直接套用的示例。
假设信息
- 上游仓库:
https://github.com/open-source-owner/demo-project.git - 你的仓库:
https://github.com/yourname/demo-project.git - 上游稳定分支:
main - 你的二开分支:
custom-main
首次初始化
bash
git clone https://github.com/yourname/demo-project.git
cd demo-project
git remote add upstream https://github.com/open-source-owner/demo-project.git
git fetch --all --prune
git checkout -b upstream-stable upstream/main
git push -u origin upstream-stable
git checkout -b custom-main upstream-stable
git push -u origin custom-main
以后每次同步
bash
git fetch upstream --prune
git checkout upstream-stable
git merge --ff-only upstream/main
git push origin upstream-stable
git checkout custom-main
git merge upstream-stable
git push origin custom-main
如果你要开发一个新功能
bash
git checkout custom-main
git pull
git checkout -b feature/new-dashboard
开发完后:
bash
git add .
git commit -m "feat: add new dashboard"
git checkout custom-main
git merge feature/new-dashboard
git push origin custom-main
十五、最推荐你的实际落地方式
如果你现在就要开始做,我建议你按下面原则执行:
- 先把原项目 Fork 到你自己的 GitHub。
- 本地同时配置两个远程:
originupstream
- 永远保留一个纯净分支
upstream-stable。 - 永远在
custom-main上做你的二开。 - 每次同步都先更新
upstream-stable,再合并到custom-main。 - 每次合并完上游更新后,先测试,再推送到远程。
这样做的优点是:
- 结构清晰
- 风险小
- 便于长期维护
- 便于多人协作
- 出问题时也容易回溯
十六、一句话总结
最稳妥的二开同步策略就是:
用
upstream跟踪开源项目,用origin存放你自己的仓库;用
upstream-stable专门同步上游稳定分支,用custom-main专门承载你的二开代码;每次同步时,先更新
upstream-stable,再把它合并进custom-main。