解决冲突

偶尔,当你从版本库更新、合并文件时,或者切换工作副本至一个不同的 URL 时你会遇到冲突。有两种冲突:

  • 文件冲突

当两名(或更多)开发人员修改了同一个文件中相邻或相同的行时就会发生文件冲突。

  • 树冲突

当一名开发人员移动、重命名、删除一个文件或文件夹,而另一名开发人员也对它们进行了移动、重命名、删除或者仅仅是修改时就会发生树冲突。

文件冲突

当两名或更多的开发人员修改了某个文件的同一行时就会发生冲突。因为 Subversion 对项目一无所知,所以需要由开发人员来解决冲突。文件中冲突区域按此标记

  1. <<<<<<< filename
  2. your changes
  3. =======
  4. code merged from repository
  5. >>>>>>> revision

对于每一个冲突的文件 Subversion 在目录中添加了三个额外的文件:

  • 文件名.扩展名.mine

这是你的文件,在你更新你的工作副本之前存在于你的的工作副本中——也就是说,没有冲突标志。这个文件除了你的最新修改外没有别的东西。

  • 文件名.扩展名.r旧版本

这是在你更新你的工作副本之前的基础版本(BASE revision)文件。也就是说,它是在你做最后修改之前所检出的文件。

  • 文件名.扩展名.r新版本

这个文件是当你更新你的工作副本时,你的 Subversion 客户端从服务器接收到的。这个文件对应于版本库中的最新版本。

你可以用TortoiseSVN → 编辑冲突来运行外部合并工具/冲突编辑器,你也可以使用任何别的编辑器手动解决冲突。你需要决定哪些代码是需要的,做一些必要的修改然后保存。使用诸如TortoiseMerge或者其他流行的合并工具。后者总体上是更简单的选择,因为他们在三窗格面板中显示文件。如果你使用文本编辑器,你需要搜索以<<<<<<<开头的字符串。

然后,执行命令 TortoiseSVN → 已解决 并提交人的修改到版本库。需要注意的是已解决命令并不是真正的解决了冲突,它只是删除了文件 文件名.扩展名.mine文件名.扩展名.r*,允许你提交修改。

如果你的二进制文件有冲突,Subversion不会试图合并文件。本地文件保持不变(完全是你最后修改时的样子),但你会看到文件名.扩展名.r*文件。如果你要撤消你的修改,保留版本库中的版本,请使用 SVN 还原(Revert)命令。如果你要保持你的版本覆盖版本库中的版本,使用已解决命令,然后提交你的版本。

你可以右击父文件夹,选择TortoiseSVN → 已解决…,使用“已解决”命令来解决多个文件。这个操作会出现一个对话框,列出文件夹下所有有冲突的文件,你可以选择将哪些标记成已解决。

属性冲突

当两名或更多的开发人员修改了某个文件的属性时就会发生属性冲突。属性作为文件的一部分,解决属性冲突只能由开发人员来完成。

如果一个更改必须被另一个覆盖,那么就在 使用本地属性解决 和 使用远程属性解决 中选择一个。如果更改想要被合并,那就选择 手工编辑属性,选出所要的属性值然后标记为已解决。

树冲突

当一名开发人员移动、重命名、删除一个文件或文件夹,而另一名开发人员也对它们进行了移动、重命名、删除或者仅仅是修改时就会发生树冲突。有很多种不同的情形可以导致树冲突,而且不同的情形需要不同的步骤来解决冲突。

当一个文件通过 Subversion 在本机删除后,文件也从本机文件系统中删除。因此即使它是树冲突的一部分,却既不能显示冲突的叠加图标也不能通过右键单击来解决冲突。使用检查修改对话框来获得编辑冲突选项。

TortoiseSVN 能够协助找到合并更改的正确位置,但是需要作一些额外的工作来整理冲突。请牢记: 当进行一次更新操作后,工作副本的基础文件将会包括每一个项目在执行更新操作时版本库中的版本。如果你在进行更新后再撤销更改,工作副本将返回到版本库的状态,而不是你开始进行更改前的状态。

本地删除,当更新时有更改进入

  • 开发人员 A 修改 Foo.c 并将其提交至版本库中。

  • 开发人员 B 同时在他的工作副本中将文件 Foo.c 改名为 Bar.c,或者仅仅是删除了 Foo.c 或它的父文件夹。

更新开发人员 B 的工作副本会导致树冲突:

  • 在工作副本中,Foo.c 被删除了,但是被标记为树冲突。

  • 如果冲突是由于更改文件名引起的而不是删除文件引起的,那么 Bar.c 被标记为添加,但是其中却不包括开发人员 A 修改的内容。

开发人员 B 现在必须做出选择是否保留开发人员 A 的更改。在更改文件名的案例中,他可以将 Foo.c 的更改合并到改名后的文件 Bar.c 中去。对于删除文件或文件夹的案例中,他可以选择保留包含开发人员 A 更改内容的项目并放弃删除操作。或什么也不做而直接将冲突标记为已解决,那样他实际上丢弃了开发人员 A 的更改。

冲突编辑对话框在能找到原始文件的重命名后Bar.c就能帮助合并修改。如果有多个文件可能移动了资源,那么这些每个文件的都会有一个按钮供你来选择正确的文件。

本地更改,当更新时有删除进入

  • 开发人员 A 将文件 Foo.c 改名为 Bar.c 并将其提交至版本库中。

  • 开发人员 B 在他的工作副本中修改文件 Foo.c

或者在一个文件夹改名的案例中…

  • 开发人员 A 将父文件夹 FooFolder 改名为 BarFolder 并将其提交至版本库中。

  • 开发人员 B 在他的工作副本中修改文件 Foo.c

更新开发人员 B 的工作副本会导致树冲突。对于一个简单的文件冲突:

  • Bar.c 被当作一个正常文件添加到工作副本中。

  • Foo.c 被标记为添加(包括其历史记录)并且产生树冲突。

对于一个文件夹冲突:

  • BarFolder 被当作一个正常文件夹添加到工作副本中。

  • FooFolder 被标记为添加(包括其历史记录)并且产生树冲突。

Foo.c 被标记为已修改。

开发人员 B 现在需要做出决定是否接受开发人员 A 作出的结构改变并且合并她的更改到新结构下适当的文件中,或者直接放弃开发人员 A 的更改并保留本地文件。

为了将她的本地更改和修正合并,开发者B必须首先找到冲突文件Foo.c在版本库中已被重命名或移动的文件名。可以使用日志对话框来完成。然后使用显示正确文件资源的按钮来解决冲突。

如果开发人员 B 认为 A 的更改是错误的,那么在冲突编辑对话框中她必须选择保留按钮。这样就会标记冲突的文件/文件夹为已解决,但是开发人员 A 的更改需要被手工删除。日志对话框再次帮助追踪哪些文件移动了。

本地删除,当更新时有删除进入

  • 开发人员 A 将文件 Foo.c 改名为 Bar.c 并将其提交至版本库中。

  • 开发人员 B 将 Foo.c 改名为 Bix.c

更新开发人员 B 的工作副本会导致树冲突:

  • Bix.c 被标记为添加(包括其历史记录)。

  • Bar.c 被添加到工作副本中,其状态为‘正常’。

  • Foo.c 被标记为删除并且产生一个树冲突。

要解决这个冲突,开发人员 B 必须找出冲突的文件 Foo.c 经过改名/移动后在版本库中的新文件名是什么。可以使用日志对话框来完成这个任务。

然后,开发人员 B 需要决定 Foo.c 的新文件名中的哪一个需要保留 - 开发人员 A 改的那个还是他自己改的那个。

在开发人员 B 手工解决冲突后,使用冲突编辑对话框中的按钮将树冲突标记为已解决。

本地缺少,当合并时有更改进入

  • 开发人员 A 在主干上工作,修改 Foo.c 并将其提交至版本库中

  • 开发人员 B 在分支上工作,将 Foo.c 改名为 Bar.c 并将其提交至版本库中

合并开发人员 A 的主干更改到开发人员 B 的分支工作副本会导致树冲突:

  • Bar.c 已经存在于工作副本中,其状态为‘正常’。

  • Foo.c 被标记为缺少并产生树冲突。

要解决这个冲突,开发人员 B 要在冲突编辑对话框中标记文件为已解决,这样就会将其从冲突列表中删除。她接下来需要决定是否将缺少的文件 Foo.c 从版本库中复制到工作副本中,是否将开发人员 A 的对 Foo.c 的更改和合并到改名后的 Bar.c 或者是否通过标记冲突为已解决来忽略更改什么事也不做。

注意,如果你将缺少的文件从版本库中复制到工作副本中然后再标记为已解决,你复制下来的文件将被再次删除。你必须先解决冲突。

本地更改,当合并时有删除进入

  • 开发人员 A 在主干上工作,将 Foo.c 改名为 Bar.c 并将其提交至版本库中。

  • 开发人员 B 在分支上工作,修改 Foo.c 并将其提交至版本库中

  • 开发人员 A 在主干上工作,将父文件夹 FooFolder 改名为 BarFolder 并将其提交至版本库中。

  • 开发人员 B 在分支上工作,在她的工作副本中修改 Foo.c

合并开发人员 A 的主干更改到开发人员 B 的分支工作副本会导致树冲突:

  • Bar.c 被标记为添加。

  • Foo.c 被标记为修改并产生树冲突。

开发人员 B 现在需要做出决定是否接受开发人员 A 作出的结构改变并且合并她的更改到新结构下适当的文件中,或者直接放弃开发人员 A 的更改并保留本地文件。

为了将她的本地更改和修正合并,开发者B必须首先找到冲突文件Foo.c在版本库中已被重命名或移动的文件名。可以使用日志对话框来完成。冲突编辑器只显示工作副本的日志,因为它不知道哪个路径在合并中被使用,所以你必须自己弄明白。修改随后必须手动合并,因为没有办法自动化或者简化这个流程。一旦更改被传递,冲突路径就不需要了,可以删除。

如果开发人员 B 认为 A 的更改是错误的,那么在冲突编辑对话框中她必须选择保留按钮。这样就会标记冲突的文件/文件夹为已解决,但是开发人员 A 的更改需要被手工删除。日志对话框再次帮助追踪哪些文件移动了。

本地删除,当合并时有删除进入

  • 开发人员 A 在主干上工作,将 Foo.c 改名为 Bar.c 并将其提交至版本库中。

  • 开发人员 B 在分支上工作,将 Foo.c 改名为 Bix.c 并将其提交至版本库中

合并开发人员 A 的主干更改到开发人员 B 的分支工作副本会导致树冲突:

  • Bix.c 被标记为正常(未修改)状态。

  • Bar.c 被标记为添加(包括其历史记录)。

  • Foo.c 被标记为缺少并且产生树冲突。

要解决这个冲突,开发人员 B 必须找出冲突的文件 Foo.c 经过改名/移动后在版本库中的新文件名是什么。可以使用日志对话框来完成这个任务。

然后,开发人员 B 需要决定 Foo.c 的新文件名中的哪一个需要保留 - 开发人员 A 改的那个还是他自己改的那个。

在开发人员 B 手工解决冲突后,使用冲突编辑对话框中的按钮将树冲突标记为已解决。

其它树冲突

有一些其它情况被标记为树冲突,因为冲突中卷入了文件夹而不是文件。例如你在主干和分支中添加了同名的文件夹然后在合并时就会发生树冲突。如果你想要保留合并目标中的文件夹,只要将冲突标记为已解决即可。如果你想要保留合并源中的文件夹,那么就要先使用 SVN 删除目标中的文件夹然后再合并。如果你需要处理更复杂的情况那就需要手工解决了。