译:只是路过
Chapter 8: Merge Branches
第八章:合并分支
This is part of an online book called Source Control HOWTO, a best practices guide on source control, version control, and configuration management.
这是一篇名为如何做源码控制的在线书籍的一部分,一本关于源码控制、版本控制、配置管理的最佳实践手册。
What is "merge branches"?
什么是“合并分支”?
Many users find the word "merge" to be confusing, since it seems to imply that we start out with two things and end up with only one. I'm not going to start trying to invent new vocabulary. Instead, let's just try to be clear about what we mean we speak about merging branches. I define "merge branches" like this:
很多用户发现“合并”这个词比较含混,因为看来好像是在暗示从两件事情开始,然后只有一件事情结束。我不打算再去创造新的词汇。只是,我们正试图明白我们在谈及合并分支时我们的真实含义。我这样定义“合并分支”的:
To "merge branches" is to take some changes which were done to one branch and apply them to another branch.
“合并分支”就是将在一个分支完成了的变更应用到另一个分支。
Sounds easy, doesn't it? In practice, merging branches often is easy. But the edge cases can be really tricky.
听来很简单吧?在实践中,合并分支通常是简单的。但是有的突出的案例却真的比较复杂。
Consider an example. Let's say that Joe has made a bunch of changes in $/branch and we want to apply those changes to $/trunk. At some point in the past, $/branch and $/trunk were the same, but they have since diverged. Joe has been making changes to $/branch while the rest of the team has continued making changes to $/trunk. Now it is time to bring Joe back into the team. We want to take all the changes Joe made to $/branch, no matter what those changes were, and we want to apply those changes to $/trunk, no matter what changes have been to $/trunk during Joe's exile.
来考虑一种情况。我们说Joe在$/branch做了一系列变更,然后我们打算将他们应用到$/trunk上。在过去的某个点,$/branch 和$/trunk是一样的,但是他们从那之后分支了。Joe开始在$/branch上进行变更,而团队其余成员在$/trunk继续进行变更。现在是时候带Joe回到团队中来了。我们希望拿到Joe在$/branch做过的所有变更,不管他改的哪里,然后我们要把它们应用到$/trunk上,不管在Joe被隔离到另一个分支后$/trunk上做了哪些变更。
The central question about merge branches is the matter of how much help the source control tool can provide. Let's imagine that our SCM tool provided us with a slider control:
关于合并分支最重要的问题就是源码控制工具可以提供多少帮助。我们假设我们的源码控制工具为我们提供了一个滑块来控制其可以提供多少帮助:
If we drag this slider all the way to the left, the source control tool does all the work, requiring no help at all from Joe. Speaking as a source control vendor, this is the ideal scenario that we strive for. Most of us don't make it. However, here at SourceGear we made the decision to build our source control product on the .NET Framework, which luckily has full support for the kind of technology needed to implement this. The code snippet below was pasted from our implementation of the Merge Branches feature in Vault:
如果我们一下子就拖动这个滑块到最左边,源码控制工具就做所有工作,不需要Joe的任何帮助。作为源码控制工具供应商来讲,这是我们奋斗的理想境界。可是很多我们这样的供应商都没有做到。但是,最近,在SourceGear公司,我们下定决心在.NET架构上构建我们的源码控制产品,在.NET架构上,我们很幸运的得到了所有运行需要用到的技术支持。下面的代码片断就是粘贴自Vault里面执行分支合并功能的:
public void MergeBranches(Folder origin, Folder target)
{
ArrayList changes = GetSelectedChanges(origin);
DeveloperIntention di =System.Magic.FigureOutWhatDeveloperWasTryingToDo(changes);
di.Apply(target);
}
Boy do I feel sorry for all those other source control vendors trying to implement Merge Branches without the class! And to think that so many people believe the .NET Framework is too large. Sheesh!
噢,我很遗憾那些其他试图做分支合并的的源码控制工具供应商没有这个!他们还在试图在没有DeveloperIntention这个类的情况下执行合并分支。然后向那么多人相信的.NET真庞大。晕!
Best Practice: Take responsibility for the merge.
最佳实践:对合并负责。
Successfully using the branching and merging features of your source control tool is first a matter of attitude on the part of the developer. No matter how much help the source control tool provides, it is not as smart as you are. You are responsible for doing the merge. Think of the tool as a tool, not as a consultant.
成功的使用你的源码控制工具的分支和合并特性是同开发人员的态度相关的。不管源码控制工具提供了多少帮助,它都不如你聪明。你要为合并负责。记住工具始终是工具,而不是顾问。
OK, I lied. (Stop trying to add a reference to the System.Magic DLL. It doesn't exist.) The actual truth is that this slider can never be dragged all the way to the left.
好吧,我说谎了。(不要试图添加一个对System.Magic.DLL的引用,它不存在。)这事实是滑块根本不可能被一下就拖到左边。
If we drag the slider all the way to the right, we get a situation which is actually closer to reality. Joe does all the work and the source control tool is no help at all. In essence, Joe sits down with $/trunk and simply re-does the work he did in $/branch. The context is different, so the changes he makes this time may be very different from what he did before. But Joe is smart, and he can figure out The Right Thing to do.
如果我们把滑块一下拉到右边,我们就会遇到接近真相的情形。Joe做了所有的工作,而源码控制工具根本没有给出帮助。实质是,Joe不得不在$/trunk工作,重新将他在$/branch上已经做过的工作再重做一次。情形是不同的,所以他在之前做的变更可能同这次要做的会有很大的差异。但是Joe够聪明,他可以解决滑块右边需要做的事情。
In practice, we find ourselves somewhere between these two extremes. The source control tool cannot do magic, but it can usually help make the merge easier.
在实际当中,我们发现我们自己常常处于两个极端之间。源码控制工具不能想像,但是通常它可以帮助我们把合并变得简单一些。
Since the developer must still take responsibility for the merge, things will go more smoothly if she understands what's really going on. So let's talk about how merge branches works. First I need to define a bit of terminology.
因为开发人员必须对合并负责,如果她知道什么要真正的发生,事情就会比较平稳的发展了。所以我们来讨论如何合并分支的工作。首先我需要定义一点术语。
For the remainder of this chapter I will be using the words "origin" and "target" to refer to the two branches involved in a merge branches operation. The origin is the folder which contains the changes. The target is the folder to which we want those changes to be applied.
在本章的剩余部分我都会用到“源”和“目标”来指明在一个合并分支操作中的两个分支。源是包含了变更的目录。目标是我们打算应用变更到里面的另一个目录。
Note that my definition of merge branches is a one-way operation. We apply changes from the origin to the target. In my example above, $/branch is the origin and $/trunk is the target. That said, there is nothing which prevents me switching things around and applying changes in the opposite direction, with $/trunk as the origin and $/branch as the target, but that would simply be a separate merge branches operation.
注意,我对合并分支的定义是一个单向的操作。我们从源应用变更到目标。我上面的例子里面,$/branch就是源,$/trunk就是目标。那就是说,没有任何事情阻止我经常变换位置,甚至在相反的方向应用变更,用$/trunk做源,用$/branch做目标。但是一个单独的合并分支的操作是简单的。
Conceptually, a merge branches operation has four steps:
就概念而言,一个合并分支操作有四个步骤:
1. Developer selects changes in the origin
2. Source control tool applies some changes automatically to the target
3. Developer reviews the results and resolves any conflicts
4. Commit
1. 开发人员在源中选择目录
2. 源码控制工具自动应用一些变更到目标
3. 开发人员回顾结果并解决冲突
4. 提交
Each of these steps is described a bit more in the following sections.
在下面对每一步有更详细的描述。
1. Selecting changes in the origin
1.在源中选择变更
When you begin a merge branches operation, you know which changes from the origin you want to be applied over in the target. Most of the time you want to be very specific about which changes from the origin are to be merged. This is usually evident in the conversation which preceded the merge:
当你开始一个合并分支操作,你知道你要从源应用到目标的变更。绝大多数时候你需要非常精确的知道从源里面要合并的变更。这通常显然的在合并之前的会话中:
l “Dan让我合并3.0.5中修复的所有缺陷到主干中”
l “Jeff告诉我们需要从主干合并7620号缺陷的修复到维护树里面”
l “Ian之前实验性质重写的featureX准备合并到主干”
One way or another, you need to tell your source control tool which changes are involved in the merge. The interface for this operation can vary significantly depending on which tool you are using. The screen shot below is the point where the Merge Branches Wizard in Vault is asking me to specify which changes should be merged. I'm selecting everything back to the last build label:
无论如何,你要告诉你的源码控制工具在合并中应用哪些变更。这个操作的接口非常强的依赖于你使用的源码工具。下面的截图就是Vault中合并分支的指引,它要求我指定哪些变更需要合并。我在选择自上一个构建标签后的任何一个变更。
2. Applying changes automatically to the target
2.自动应用变更到目标
After selecting the changes to be applied, it's time to try and make those changes happen in the target. It is important here to mention that merging branches requires us to consider every kind of change, not just the common case of edited files. We need to deal with renames, moves, deletes, additions, and whatever else the source control tool can handle.
在选择了应用变更之后,就可以试着对那些目标中的变更进行处理。必须要强调的是合并分支需要我们考虑各种类型的变更,而不是普通的编辑文件。我们需要处理重命名、移动、删除、添加或者凡是源码控制工具可以处理的。
I won't spell out every single case. Suffice it to say that each operation should be applied to the target in the way that Makes Sense. This won't succeed in every situation, but when it does, it is usually safe. Examples:
我不会每个单一的案例都讲那么清楚。使它满足于说明每个操作应该被通过这种有意义的方式应用到目标就可以了。不是在任何情况下都会成功的,但是当它那样做了,它就是安全的。例如:
Bottom line, a source control tool should do all the operations which seem certain to be safe. And even then, the user needs a chance to review everything before the merge is committed to the repository.
底线就是,一个源码控制工具应该做所有看上去可靠、安全的操作。尽管这样,用户需要一个机会在提交到库中的合并进行评估。
Let's consider a simple example from Subversion. I created a folder called trunk, added a few files, and then branched it. Then I made three changes to the trunk:
我们来考虑一下一个Sbuversion的简单的例子。我创建了一个叫做trunk的目录,添加了一点文件,然后对其做了分支。然后我对trunk目录做了三个变更:
Then I asked Subversion to merge all changes between version 2 and 4 of my trunk into my branch:
然后我要求Subversion合并了我的主干的版本2和版本4的所有变更到我的分支:
Subversion correctly detected all three of my changes and applied them to my working copy of the branch.
Subversion正确的检测到了我这三个变更,并且将他们应用到了我的分支的工作拷贝里面。
3. Developer review
3. 开发人员评估
Best Practice: Review the merge before you commit.
最佳实践:在你提交之前评估合并
After your source control tool has done whatever it can do, it's your turn to finish the job. Any conflicts need to be resolved. Make sure the code still builds. Run the unit tests to make sure everything still works. Use a diff tool to review the changes.
在你的源码控制工具完成了它可以做的任何事情之后,就该到你完成这个工作了。任何冲突都需要解决。确信代码仍然被构建。运行单元测试确保代码正常工作。用一个差异比较工具来评估变更。
Merging branches should always take place in a working folder. Your source control tool should give you a chance to do these checks before you commit the final results of a merge branches operation.
合并分支应该始终在一个工作目录中进行。你的源码控制工具应该给你一个机会去在你提交合并分支的最终结果之前检查你的工作。
The final step in a merge branches operation is a review by the developer. The developer is ultimately responsible, and is the only one smart enough to declare that the merge is correct. So we need to make sure that the developer is given final approval before we commit the results of our merge to the repository.
在合并分支操作中最后一步就是开发人员去评估合并分支操作。开发人员是最终的责任人,是唯一有资格宣布合并正确的人。所以我们需要确信在我们提交合并结果到库里面的时候,开发人员给出了一个正式批准。
This is the developer's opportunity to take care of anything which could not be done automatically by the source control tool in step 2. For example, suppose the tree contains a file which is in a binary format that cannot be automatically merged, and that this file has been modified in both the origin and the target. In this case, the developer will need to construct a version of this file which correctly incorporates both changed versions.
可以去考虑任何不会由源码控制工具在第2步里自动完成的工作,这是开发人员的幸运。例如,假设一棵树,包含了一个有二进制格式不能自动合并的文件,而且那个文件在源和目标中都被修改了。在这种情况下,开发人员需要为文件构建一个正确的合并了所有变更的版本。
4. Commit
4.提交
The very last step of a merge branches operation is to commit the results to the repository. Simplistically, this is a commit like any other. Ideally, it is more. The difference is whether or not the source control tool supports "merge history".
合并分支操作的最终的步骤就是提交结果到库里面。非常简单,这是一个类似其他提交的提交。在概念上,它要比其他的提交多点。差别就是不管源码控制工具是否支持“合并历史”。
The benefits of merge history
合并历史的益处
Merge history contains special historical information about all merge branch operations. Each time you do use the merge branches feature, it remembers what happened. This allows us to handle two cases with a bit more finesse:
合并历史包含了关于合并操作的所有特殊的历史信息。每次你用到合并分支特性,它就会记住发生了什么。这就允许我们用多点策略来处理两种情况:
Repeated merge.
重复的合并。
Frequently you want to merge from the same origin to the same target multiple times. Let's suppose you have a sub-team working in a private branch. Every few weeks you want to merge from the branch into the trunk. When it comes time to select the changes to be merged over, you only want to select the changes that haven't already been merged before. Wouldn't it be nice if the source control tool would just remember this for you?
你经常都会要从同一个源合并到同一个目标很多次。那我们假设你有一个小的团队,他们工作在私有分支上。隔几周你就想将分支合并到主干上一下。当又到了选择要合并到主干的变更时,你只想选择之前没有合并过的变更。那源码工具是否好到刚好记住这些你的信息呢?
Merge history allows this and makes things more convenient. The workaround is simply to use a label to mark the point of your last merge.
合并历史就允许这样做,并且很便利。工作区里面会简单的使用一个标签来标示你上一次合并的点。
Merge in both directions.
从两个方向合并
A similar case happens when you have two branches and you sometimes want to merge back and forth in both directions. For example:
一个类似的情况还会在你有两个分支,并且你有时需要从两个不同方向来回合并的时候发生。例如:
1. Create a branch
2. Do some work in both the branch and the trunk
3. Merge some changes from the branch to the trunk
4. Do some more work
5. Merge some changes from the trunk to the branch
1. 创建一个分支
2. 在主干和分支中都做些工作
3. 从分支合并一个变更到主干
4. 做更多的工作
5. 从主干合并一些变更到分支
At step 5, when it comes time to select changes to be merged, you want the changes from step 3 to be ignored. There is no need to merge those changes from the trunk to the branch because the branch is where those changes came from in the first place! A source control tool with a smart implementation of merge history will know this.
在第5步,当需要选择变更进行合并的时候,你希望忽略第3步的变更。那就没有必要从主干合并变更到分支,因为分支是那些变更首先发生的地方!一个聪明的执行合并历史的源码控制工具应该知道这些的。
Not all source control tools support merge history. A tool without merge history can still merge branches. It simply requires the developer to be more involved, to do more thinking.
不是所有的源码控制工具支持合并历史。一个没有合并历史的工具仍然可以合并分支。它简单的要求开发人员更多的关注,进行更多的思考。
In fact, I'll have to admit that at the time of this writing, my own favorite tool falls into this category. We're planning some major improvements to the merge branches feature for Vault 4.0, but as of version 3.x, Vault does not support merge history. Subversion doesn't either, as of version 1.1. Perforce is reported to have a good implementation of merge history, so we could say that its "slider" rests a bit further to the left.
事实上,我得要承认在这次编写的时候,我自己喜欢的工具开始成了这个类型。我们计划在Vault4.0里面提升合并分支的性能,但是在3.X版本,Vault不支持合并历史。Subversion在版本1.1也不支持。Perforce据报道说有一个关于合并历史的好的功能,所以我们可以说那个“滑块”离左边远了一点了。
Summary
概要
I don't want this chapter to be a step-by-step guide to using any one particular source control tool, so I'm going to keep this discussion fairly high-level. Each tool implements the merging of branches a little differently.
我不希望这个章节是一个一步一步教你使用任何一款源码控制工具的手册,所以我将这个讨论保留在一个很高的层面。每个工具都在执行合并分支上有不同。
For some additional information, I suggest you look at Version Control with Subversion, a book from O'Reilly. It is obviously Subversion-specific, but it contains a discussion of branching and merging which I think is pretty good.
要获取更多的信息,我建议你看看O'Reilly出版的Version Control with Subversion。那当然是Subversion的说明书,但是它包含了一个关于分支和合并的讨论,我认为写得很不错。
The one thing all these tools have in common is the need for the developer to think. Take the time to understand exactly how the branching and merging features work in your source control tool.
所有工具共有的一个特点是需要开发人员思考。花些时间来正确理解在你的源码控制工具中分支和合并是怎么回事的。