实战:合并两个独立开发的 Git 分支,并遇到"fatal: refusing to merge unrelated histories",一步步分析原因并给出解决方案。
目录
[二、为什么会报错 "unrelated histories"?](#二、为什么会报错 “unrelated histories”?)
三、强制合并:--allow-unrelated-histories
[5.1 通过PyCharm工具解决冲突](#5.1 通过PyCharm工具解决冲突)
[5.2 手动解决冲突(适合文件少的情况)](#5.2 手动解决冲突(适合文件少的情况))
[5.3 放弃合并,回到合并前状态](#5.3 放弃合并,回到合并前状态)
[5.4 使用合并策略自动解决(-X ours / -X theirs)](#5.4 使用合并策略自动解决(-X ours / -X theirs))
[5.5 只选取特定文件或代码](#5.5 只选取特定文件或代码)
一、分支合并
在 GitLab 上有一个项目,包含两个分支:
-
tianjin-cb/sop_cip_maf_v1.4.0_260610 -
longwan-cb/sop_cip_maf_v1.3.8_260605
需求:将 longwan-cb/... 分支的代码合并到 tianjin-cb/... 分支中。
操作步骤很简单:
bash
# 克隆目标分支
git clone -b tianjin-cb/sop_cip_maf_v1.4.0_260610 https://gitlab.xxx.com/xxx/sop_engine.git
# 进入目录
cd sop_cip_maf_v1.4.0_260610
# 获取远程所有分支
git fetch origin
# 尝试合并另一个分支
git merge origin/longwan-cb/sop_cip_maf_v1.3.8_260605
如果出现错误:
fatal: refusing to merge unrelated histories

**二、**为什么会报错 "unrelated histories"?
Git 默认要求合并的两个分支有共同的祖先提交 。如果两个分支是基于完全不同的初始提交创建的(比如分别从两个独立的仓库导入,或者用 --orphan 创建的孤立分支),Git 认为它们没有历史关联,直接合并可能会引入不相关的代码,因此拒绝执行。
常见场景:
-
两个分支分别从不同的远程仓库拉取
-
其中一个分支是用
git checkout --orphan创建的 -
重新初始化了仓库并强制推送,导致历史割裂
在我这个案例中,两个分支很可能由不同的人在不同的时间点基于不同的基线创建,导致 Git 无法找到共同的 commit。
三、强制合并:--allow-unrelated-histories
如果确认需要合并这两个历史无关的分支,可以使用 --allow-unrelated-histories 选项强制 Git 执行合并。
bash
git merge origin/longwan-cb/sop_cip_maf_v1.3.8_260605 --allow-unrelated-histories
此时 Git 会尝试合并,但往往会遇到大量冲突,因为两个分支的文件结构和内容可能完全不同。
四、合并冲突的现场
运行上述命令后,终端输出类似:
Auto-merging .gitignore
CONFLICT (add/add): Merge conflict in .gitignore
Auto-merging sop_engine/app/api/v1/sop_validator.py
CONFLICT (add/add): Merge conflict in sop_engine/app/api/v1/sop_validator.py
...
Automatic merge failed; fix conflicts and then commit the result.
冲突类型为 add/add,表示两个分支都新增了同名文件,但内容不同。Git 不知道应该保留哪一个版本,所以标记为冲突,需要人工裁决。
五、解决冲突的几种方案
5.1 通过PyCharm工具解决冲突
使用PyCharm工具打开项目文件,会看到提示合并及其所存在的冲突,
点击那个绿色箭头你就可以看到所有存在更改和冲突的文件:

双击文件你可以进行冲突解决,最左侧的是当前你打开的版本,最右侧的为你要合并进行的版本,中间的为需要应用的版本。

5.2 手动解决冲突(适合文件少的情况)
对每个冲突文件,打开编辑,删除 <<<<<<<、=======、>>>>>>> 标记,保留最终需要的代码。然后:
bash
git add <已解决的文件>
git commit -m "Merge longwan branch into tianjin branch"
缺点:冲突文件多(本例中 40+ 个)时非常耗时,且容易出错。
5.3 放弃合并,回到合并前状态
如果觉得手动解决不现实,可以放弃本次合并:
bash
git merge --abort
5.4 使用合并策略自动解决(-X ours / -X theirs)
- 保留当前分支(tianjin)的版本:遇到冲突时自动采用当前分支的文件
bash
git merge origin/longwan-cb/... --allow-unrelated-histories -X ours
- 完全采用被合并分支(longwan)的版本:
bash
git merge origin/longwan-cb/... --allow-unrelated-histories -X theirs
这种方式不会弹出冲突编辑,直接按策略选择。但注意:它会丢弃另一方的所有修改,可能不符合预期。
5.5 只选取特定文件或代码
如果并不需要完整合并整个分支,而是希望将另一个分支的某些文件或某些提交 应用到当前分支,建议放弃 merge,改用:
- 复制单个文件:
bash
git checkout origin/longwan-cb/sop_cip_maf_v1.3.8_260605 -- path/to/file.py
git add path/to/file.py
git commit -m "Import file.py from longwan branch"
- 挑选特定提交 (
cherry-pick):
bash
git log origin/longwan-cb/... # 查看需要哪些commit
git cherry-pick <commit-hash>
- 查看差异后再决定:
bash
git diff origin/longwan-cb/... --stat
git diff origin/longwan-cb/... -- <具体文件>
总结
| 场景 | 推荐方案 |
|---|---|
| 两个分支本应共享历史 | 使用 --allow-unrelated-histories 合并一次,以后保持共同祖先 |
| 只需另一个分支的少量文件 | git checkout 或 cherry-pick |
| 两个分支已完全独立(不同基线) | 不要合并,考虑重构或保持独立 |
| 测试合并效果 | 先在临时分支上尝试:git checkout -b test-merge,再执行合并 |
