三方合并的关键角色

  1. BASE(基线版本)
    • 这是 SVN 在你本地 .svn 目录中存储的文件“参考版本”,通常是你上一次成功 updatecommit 之后的状态。
    • 它代表了“本地和远程共同拥有的、尚未分化之前”的那一个版本。
  2. WORKING(工作副本当前版本)
    • 这是你在本地对文件进行修改后的最新状态。与 BASE 相比,可能新增了一些变动,比如你在第 2 行和第 4 行写了新的内容。
  3. INCOMING 或 REPO(远程版本/合并源版本)
    • 这是从服务器上拉取的最新改动,或者是在分支合并时的另外一条分支修改。与 BASE 相比,可能也有不同之处,比如在第 5 行和第 8 行被他人修改了。

SVN 如何进行自动合并

当你执行 svn updatesvn merge 时,如果不同人的修改并不冲突(也就是修改发生在不同的行或互不干涉的代码块),SVN 会按照以下步骤自动合并

  1. 比较 BASE 与 WORKING(本地差异)
    • SVN 会计算出你本地针对 BASE 做了哪些修改。比如:
      • 在第 2 行和第 4 行有新增或修改。
    • 这些改动可以被视为一个“差异集(diff)”。
  2. 比较 BASE 与 INCOMING(远程差异)
    • SVN 同样计算远程(或合并源)相对于同一个 BASE 做了哪些修改。比如:
      • 在第 5 行和第 8 行有修改。
    • 这也是一个“差异集”。
  3. 判断修改是否冲突
    • SVN 会检查:
      • 你在 BASE 到 WORKING 的差异,是否与 BASE 到 INCOMING 的差异在同一行(或同一区段)出现不同的改动。
    • 如果改动在不同的行(例如本地改第 2、4 行,远程改第 5、8 行),或者虽然在同一文件但在不相互覆盖的区域(例如本地插入是第 3 行后,远程插入是第 7 行后),SVN 就能把它们“无缝”地合并在一起。
  4. 应用合并
    • 先对 BASE 应用本地修改(得到一个过渡版本),再对其应用远程修改。
    • 最终得到一个既包含你本地改动、又包含远程改动的合并后文件
  5. 更新本地基线(BASE)
    • 当自动合并顺利完成后,你的工作副本文件会变成“合并后状态”,SVN 也会更新 .svn 里的 BASE,表示本地现在基于这个新状态继续工作。

具体示例

假设原始文件(BASE)有 10 行:

1
2
3
4
5
6
7
8
9
10
1: line1
2: line2
3: line3
4: line4
5: line5
6: line6
7: line7
8: line8
9: line9
10: line10
  • 本地(WORKING): 修改了第 2 行和第 4 行。
  • 远程(INCOMING): 修改了第 5 行和第 8 行。

三方合并时,SVN 发现第 2、4 行和第 5、8 行并不重叠(不同的行),所以就直接把两边的改动都合并进来,最后自动生成的文件在本地会是:

1
2
3
4
5
6
7
8
9
10
1: line1
2: line2 (local changed)
3: line3
4: line4 (local changed)
5: line5 (remote changed)
6: line6
7: line7
8: line8 (remote changed)
9: line9
10: line10

还是上述文件,再比如:

  • **本地(WORKING):**第3行后插入3行。
  • 远程(INCOMING): 第11行后插入2行。

三方合并时,4~6 行是本地合并进来的;11~12 行是远程合并进来的。整体文件长度从 10 行变成了 15 行,同时保留了双方对各自行段的改动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1: line1
2: line2
3: line3
4: L1 <-- 本地新增
5: L2 <-- 本地新增
6: L3 <-- 本地新增
7: line4
8: line5
9: line6
10: line7
11: R1 <-- 远程新增
12: R2 <-- 远程新增
13: line8
14: line9
15: line10

这个结果包含了全部有效改动,且无冲突。

合并冲突

冲突原因

如果本地和远程都在相同的行有修改,SVN 无法自动决定哪边才是正确的修改,才会出现冲突(会生成 .mine, .rXX, .rYY 文件等)。例如:

  • BASE(第 4 行): System.out.println("Original Base");
  • WORKING(第 4 行): System.out.println("Local changes!");
  • INCOMING(第 4 行): System.out.println("Remote changes!");

因为第 4 行这处的修改“撞”到一起,SVN 就需要你手动选择保留哪个或者合并两者内容。

.suo文件的冲突

.suo 文件(Solution User Options)是 Visual Studio 的用户级别设置文件,通常是二进制格式,根本不适合进行文本差异比对(用 Notepad++ 打开会显示乱码)。这类文件一般只在你本地存储个人或环境相关的配置信息,如断点、调试配置、IDE 窗口布局等,不应该提交到版本库里去。

1. 为什么会出现冲突?

.suo 文件是二进制且易变每次打开/关闭解决方案、调整调试设置或更改 IDE 布局等,.suo 文件都会更新。由于多人协同开发时每个人都有不同的 IDE 环境,.suo 很容易在版本库中反复产生冲突。
文件实质内容无法文本合并
因为 .suo 是二进制,SVN 或 Git 等版本控制系统没法直接对其做出有效的“文本合并”,只能粗暴地提示冲突。你在 Notepad++ 中看到的“乱码”正是二进制数据。

2. 正确的做法

将 .suo 文件移出版本控制
通常我们会将这些用户/机器相关的配置文件(如 .suo、.user、*.userprefs 等)放到忽略列表(svn:ignore、.gitignore 等)里,防止它们被提交到远程仓库。
如果当前版本库里已经有 .suo 文件,建议直接在 SVN 中执行删除 (svn delete),并在 SVN 忽略列表中添加 *.suo,避免后续再被提交。 只保留 .sln / .csproj / .vbproj 等真正需要的项目文件
.sln、.csproj、.vbproj 是项目/解决方案本身的配置信息,一般需要纳入版本管理(会影响实际编译构建)。 .suo、.user 这类仅与个人环境相关,不应入库。
如果一定要处理冲突,若暂时无法删除 .suo,需要先解决当前冲突可以采用“保留任意一方”来强行解决,比如执行: svn resolve --accept mine-full <文件名> (保留本地版本) svn resolve --accept theirs-full <文件名> (保留远程版本) 。最终建议还是通过移除 .suo 来彻底杜绝后续冲突。

小结

  • 自动合并原理:只要本地和远程的修改没有在同一个代码片段/行发生冲突,SVN 就能将其并入同一个文件,形成“自动合并”。
  • 为什么需要“三方合并”:SVN 需要知道在你上一次成功同步(BASE)的基础上,本地和远程分别修改了哪些部分,以便判断他们是否冲突,或可直接拼合。
  • 自动合并成功后:你在本地会直接看到已经合并了双方改动的文件,无需手动处理冲突标记,也无需手动编辑 <<<<<<<=======>>>>>>> 之类的冲突符号。
  • .suo 或者**.user**等文件仅与个人环境相关,不应纳入版本控制。

当 SVN 显示“Auto-merging …”或没有出现冲突提示时,你就可以放心地继续工作,并在需要时 svn commit 将合并后的内容提交到远程仓库。