多仓库代码合并实战指南

0 阅读6分钟

背景

在实际开发中,经常会遇到这样的场景:一个项目从主仓库拷贝出去,交给其他团队(或供应商)独立开发新功能,开发完成后需要将成果集成回主仓库。

本文总结了几种常见的合并方案,重点介绍最推荐的「直接合并远程分支」方案,帮助你快速、安全地完成多仓库代码集成。

适用场景

  • 项目 B 是从项目 A 拷贝/fork 出去的,目录结构基本一致
  • 项目 B 在原有基础上新增了页面、模块或子包
  • 需要将项目 B 的成果合并回项目 A
  • 后续项目 B 可能还会持续更新,需要多次同步

方案对比

方案保留 Git 记录后续同步复杂度适用场景
直接合并分支✅ 完整✅ git merge两个仓库结构一致,推荐首选
git subtree✅ 完整✅ subtree pull需要将外部仓库映射到子目录
git submodule✅ 完整✅ submodule update外部仓库需要独立版本管理
手动拷贝文件❌ 丢失❌ 每次手动一次性集成,不再更新

推荐方案:直接合并远程分支

为什么推荐

当两个仓库有相同的代码基础(从同一份代码拷贝出去),Git 能识别共同的文件基础,合并时只会引入增量变更。这和日常合并功能分支的体验完全一致:

  • 零额外工具 — 纯 Git 操作,团队所有人都熟悉
  • 完整保留提交记录 — 外部仓库的每一条 commit 都保留
  • 后续同步最简单git fetch + git merge 一步到位
  • 冲突处理自然 — Git 三路合并,和日常开发体验一致

操作步骤

第一步:添加远程仓库

# 将外部仓库添加为一个新的 remote
git remote add external <外部仓库地址>

# 拉取外部仓库的所有分支信息
git fetch external

external 是自定义的远程名称,可以根据实际情况命名,比如 vendorpartner 等。

第二步:创建集成分支

# 从主分支创建一个独立的集成分支,避免直接影响主分支
git checkout main
git checkout -b feat/integrate-external

第三步:执行合并

# 合并外部仓库的主分支
git merge external/main

如果两个仓库没有共同的 Git 祖先(比如外部仓库是全新 git init 的,而不是 fork),需要加上 --allow-unrelated-histories 参数:

git merge external/main --allow-unrelated-histories

第四步:解决冲突

合并时大概率会在公共配置文件上产生冲突,比如:

  • 路由配置文件
  • package.json / 依赖管理文件
  • 全局样式文件
  • 入口文件

解决冲突的原则:

# 查看所有冲突文件
git diff --name-only --diff-filter=U

# 逐个解决冲突后标记为已解决
git add <冲突文件>

# 全部解决后提交
git commit

第五步:审查与合入

# 审查本次合并引入的所有变更
git diff main..feat/integrate-external --stat

# 确认没问题后,合入主分支(建议通过 MR/PR 流程)
git checkout main
git merge feat/integrate-external

后续同步

当外部仓库有新的更新时,同步非常简单:

# 拉取最新代码
git fetch external

# 创建同步分支
git checkout -b sync/external-$(date +%Y%m%d)

# 合并更新
git merge external/main

# 解决冲突(如果有),审查后合入主分支

Git 会基于上次合并点做增量 diff,只合入新的 commit,冲突会比首次少很多。

关键注意事项

1. 首次合并前做好准备

  • 了解外部仓库改了什么:让对方提供变更清单,或者先 fetch 后用 git log external/main --oneline 查看提交记录
  • 备份当前分支:虽然 Git 操作都可以回退,但创建一个备份分支更安心
git branch backup/before-merge

2. 重点关注公共文件的冲突

外部团队在开发过程中,可能修改了一些不该改的公共文件。合并时要特别审查:

  • 配置文件:路由配置、构建配置、环境变量等
  • 依赖文件:package.json、lock 文件等
  • 全局文件:入口文件、全局样式、公共工具函数等

对于不需要的改动,可以在合并后针对性 revert:

# 查看某个文件在合并中的变更
git diff main -- <文件路径>

# 如果某个文件的改动不需要,恢复为主分支的版本
git checkout main -- <文件路径>
git commit -m "revert: 还原不需要的改动"

3. 处理「不相关历史」的情况

如果外部仓库不是从主仓库 fork 出去的,而是:

  • 把代码打包发过去,对方重新 git init
  • 或者通过其他方式创建的独立仓库

这时两个仓库没有共同祖先,Git 默认会拒绝合并。解决方法:

git merge external/main --allow-unrelated-histories

这种情况下首次合并的冲突会比较多(因为 Git 无法判断哪些文件是「相同的基础」),需要耐心逐个解决。但解决完一次后,后续同步就正常了。

4. 建议的长期工作流

main
 │
 ├── feat/integrate-external       ← 首次合并(通过 MR 审查)
 │
 ├── sync/external-20260428        ← 第二次同步
 │
 └── sync/external-20260515        ← 第三次同步

每次同步都走独立分支 + MR/PR 审查流程,确保主分支始终可控。

5. 集成完成后的清理

如果外部仓库不再需要同步,可以移除远程引用:

git remote remove external

这不会影响已经合并进来的代码和提交记录。

其他方案简述

git subtree

适用于需要将外部仓库映射到主仓库的某个子目录的场景:

# 首次添加
git subtree add --prefix=path/to/subdir external main --squash

# 后续同步
git subtree pull --prefix=path/to/subdir external main --squash

优点是外部代码被隔离在指定目录,缺点是当两个仓库目录结构一致时反而不方便。

git submodule

适用于外部仓库需要保持完全独立、按版本引用的场景:

# 添加子模块
git submodule add <外部仓库地址> path/to/subdir

# 更新子模块
git submodule update --remote

优点是版本管理精确,缺点是团队成员需要额外学习 submodule 的操作流程,clone 时也需要 --recursive

总结

对于「从主仓库拷贝出去开发,再合并回来」这种场景,直接合并远程分支是最简单、最自然的方案。它不引入额外的工具或概念,后续同步成本最低,团队协作也最顺畅。核心就三步:

  1. git remote add — 添加远程仓库
  2. git fetch + git merge — 拉取并合并
  3. 解决冲突,审查合入

掌握这个方法,多仓库合并就不再是难题。