Professional Team Foundation Server 2013 (2013)
Branching and Merging
What's in this chapter?
· Understanding branching terminology and concepts
· Getting to know common branching strategies
· Using the branching and merging tools with TFVC
· Using the branching and merging tools with Git
Branching and merging in version control can open up a whole world of possibilities for improving development productivity through parallelization. Yet, for many developers, branching and merging are slightly scary and full of uncertainty. Because of a lack of good tooling in the past, many developers still shy away from branching and merging, despite Team Foundation Server having good support for both. At the other extreme, some people who see all the great branching and merging functionality available can go a little crazy with their newly found power. Overuse of branching and merging can impact developer productivity and reduce the maintainability of their repository as a result.
With the addition of Git as a supported version control repository in Team Foundation Server 2013, you now have two different methods of branching and merging available to your teams.
No matter which side of the spectrum you find yourself on, this chapter explains the fundamental principles behind the important branching and merging strategies, and it provides some key guiding principles to help you apply them to your organization's needs. This chapter highlights the branching and merging tooling available with Team Foundation Server 2013, and then concludes by walking you through the application of this tooling with some examples.
Differences Between TFVC and Git When Branching and Merging
As you saw in Chapter 5, Team Foundation Server supports a centralized version control system, Team Foundation Version Control (TFVC), and a distributed version control system, Git. Some of the main differences between these two technologies lies in how they approach the idea of a “source of truth” for your versioned items.
In centralized version control repositories, the source of truth is the repository located within the server. The local client can have one or more copies of the source code on disk in the form of multiple workspaces, but local clients don't have the ability to keep historical versions nor do they actually have access to those historical versions if they are disconnected from the server. This means that all branching and merging support comes from the server and not the local client.
In distributed version control systems there is no single “source of truth.” Every team member has a full copy of the entire history of the source tree plus any branches they are working on. In addition, they have the same branching and merging facilities as the server does. As such, each client has the ability to perform branching and merging locally as well as on the Team Foundation Server copy of the repository. This feature is extremely flexible and powerful, which can also make it difficult to be successful if you aren't familiar with how branching and merging work.
One of the biggest differences you will initially encounter is how your source repository is visualized within Team Foundation Server. Team Foundation Version Control–based repositories are visualized and manipulated through Source Control Explorer and Team Explorer. This allows for visual manipulation of the repository and visualization of the branch relationships and changeset history, which you will see later in this chapter.
While you can use Team Explorer to manage commits, branching, and merging in your Git-based work, you do not have the same tooling to visualize branch relationships, changesets, and merge history.
Because these models are different, we will make note of which repository type is applicable to each topic when there is a difference.
Lots of terms and concepts are peculiar to the world of branching and merging. The following sections provide some definitions and context for those basic terms and concepts.
As stated in Chapter 5, a branch is a copy of a set of files in a different part of the repository that allows two or more teams of people to work on the same part of a project in parallel. When you create a branch in Team Foundation Version Control in Team Foundation Server 2013, it doesn't actually create new copies of all those files on the server. It just creates a record pointing to them—one reason why creating a new branch containing thousands or even millions of files can be done quickly.
When using Git for version control, you will be able to create branches locally. When you feel the code is ready, you can share them with other team members and the Git repository hosted within your Team Project in Team Foundation Server.
A merge takes the code in two branches and combines them into one codebase. For example, if you had two teams of developers working on two branches, and you wanted to bring the changes together, you would merge them. If the changes consisted simply of edits to different files in the branches, the merge would be simple—but it can get more complicated, depending on what was edited in both branches.
For example, if the same line of the same file was edited in both branches, the person performing the merge must make a decision as to which change should win. In some circumstances, this will result in a hybrid merge, where the combination of the intent behind the two changes requires a different result than the text in those versions being combined. When you branch using centralized version control, Team Foundation Version Control keeps track of the relationship between branches, as shown in Figure 10.1.
Figure 10.1 The relationship between the source and target branches
The branch containing your changes that you want to merge is called the source branch. The branch you want to merge the changes into is the target branch. The common ancestor between them is the base version. When you merge, you can select a range of changes in the source branch to merge into the target branch.
When you branch using distributed version control, Git keeps track of the relationship between the branches. Because each developer has a full copy of the Git repository, Git can search for a common ancestor when performing a Merge. This makes merging between local branches and remote copies of the repository very easy.
If the same file has been edited in both the source and target branches, Team Foundation Server may flag this as a conflict. In Team Foundation Server 2012 RTM, the merge experience for centralized version control was simplified, but if the same file had been edited in both branches, it was flagged as a conflict, even if the changes are to completely different sections of the file. With the 2012.1 Update, the merge tools now check to see if the changes to the same file are actually overlapping and will only generate a conflict in this case.
For certain changes (such as a file that was edited in two different places), Team Foundation Server can make a good guess about what should happen (you want to see a file containing the changes from both places). This is called an automerge. In Team Foundation Server 2012, the number and type of conflicts that can be automerged were increased from earlier releases. And, unlike earlier releases, Team Foundation Server will automerge the file for you if it is safe to do so but allow you to review those changes to ensure that the desired merge behavior has been performed. For example, if two different bugs were fixed, you probably want both changes. However, if the two changes were just fixing the same bug in two different ways, perhaps a different solution is in order. In most cases, where the development team has good communication, the changes are a result of different changes being made to the file. Automerge usually does a great job of merging them together, making it easy for the developer to validate the changes.
There can also be many cases where the actual outcome is unclear, so automerging is not available. For example, if you deleted the file in one branch and edited it in another, do you want to keep the file with the changes or have it removed? The person performing the merge is responsible for deciding the correct conflict resolution based on an understanding of the code and communicating with the team members who made the conflicting changes to understand their intent.
As with life in general, conflict is never good in version control. Making the decision about the correct conflict resolution in version control can be a complex and time-consuming process. Therefore, it is best to adopt a branching strategy that minimizes the likelihood of conflicts occurring. However, conflicts will occur, and Team Foundation Server provides the tooling to deal with them, so conflicts should not be feared.
When you branch a folder in Team Foundation Version Control, the relationships between those branches form a standard hierarchical relationship. The source of the branch is the parent, and the target of the branch is the child, as shown in Figure 10.2. Children who have the same parent are called sibling branches.
Figure 10.2 Hierarchical relationship in branches
In Git, all branches in a repository are related at some point in their past, so the hierarchy of branches is not relevant.
A baseless merge occurs when two arbitrary branches in centralized version control merge without reference to a base version. This is sometimes necessary if the source code was originally imported in a flat structure without the branch relationship being in place, or if you want to merge between a branch and another branch not a direct parent or child (for example, Branch A1 and Branch B1 in Figure 10.2).
Because no base version is being used to compare against, the probability of the server detecting conflicts occurring between the two branches is much higher. For example, if a file were renamed in one branch and edited in the other, it will show up as a file delete conflicting with the file edit, and then a file add that gives no hint as to which file it was related to, or that there was an edit intended for this file in the other branch. For this reason, baseless merges are discouraged with Team Foundation Server 2013 and a warning will appear whenever a baseless merge operation is selected in Visual Studio. Standard merging (one with a base version) through the Visual Studio or Eclipse clients are the encouraged method—and only one branch up or down (a parent to a child or vice versa) is allowed. Therefore, your branching model should attempt to constrain most merges between parent and child branches to minimize the amount of baseless merging required.
In Git, all branches in a repository have a common ancestor somewhere in their history. Git is very good at finding these common ancestors. This means that every merge is considered a standard merge and there is no concept of a baseless merge in Git.
Forward integration (FI) occurs when you merge code from a parent branch to the child branch. Reverse integration (RI) occurs when you merge code from a child branch to the parent branch. The terms FI and RI are specific to centralized version control repositories. They are often thrown around quite freely during a branching debate, so it is important to understand what they mean. If you are doing feature development in branches, it is common to use FI at various points during the feature development cycle, and then to use RI at the end. See the section “Feature Branching” later in this chapter for more information.
In a decentralized version control repository the language used is relative to the branch you are working with. There is no parent or child, so the notion of Forward or Reverse is irrelevant. Instead, when you request changes from another branch, you are performing a Pull operation. When we send changes to another branch, you are performing a Push.
Common Branching Strategies
Depending on the organization of your team, and the software that you need to develop, you can adopt numerous branching strategies, all with various pros and cons. However, just as every strategy in chess is made up of simple moves, every branching strategy uses one or more combinations of some basic techniques. This section details some of the basic techniques, how they are used, and why.
When developing your own branching strategy, you should take into account the needs of your organization. In all likelihood, you may adopt a strategy that combines one or many of the basic techniques described here.
When looking at any strategy for branching and merging, you should keep in mind the following important rules:
· Prefer simplicity over control.
· Branch only when you really need to. (You can branch after the fact if you need to.)
· If you ever want to merge two branches together, keep the time between the branch and the merge to a minimum.
· Ensure that your branch hierarchy matches the path you intend your merges to follow.
For additional guidance on branching and merging with Team Foundation Server, see the “Visual Studio Team Foundation Server Branching and Merging Guide” project on CodePlex at http://vsarbranchingguide.codeplex.com/. This guidance is created by a community of Visual Studio ALM Rangers, and it combines the knowledge of Microsoft engineers and consultants with Microsoft Most Valued Professionals (MVPs) and other technical specialists in the community. The guidance also includes hands-on labs, along with a set of diagrams. Although the guidance caters to very complex branching and merging requirements, it can also be a useful starting point when creating your own branching plan.
It may be counterintuitive, but the simplest branching technique is to not branch at all. This should always be your default position. Do not branch unless you need to. Remember, you are using a version control tool that tracks changes over time. You can branch at any point in the future from any point in the past. This gives you the luxury of not having to create a branch on the server “just in case.” You create branches only when you need them. This strategy does not preclude your team from creating local branches for their work if you are using a Git-based team project. In that case, your team members will simply push their changes to the server from their local branches when they feel their work is ready.
In Team Foundation Version Control, all branching is performed on the server, so there are things you can do to prepare yourself to make branching easier in the future if you decide you need a branch.
Figure 10.3 illustrates the most important thing that you should do if you think you might possibly need to branch in the future. When you first create your Team Foundation Version Control–based team project in Team Foundation Server, create a folder called Mainand check it in. Then, right-click the folder in Source Control Explorer and select Branching and Merging Convert to Branch to get to the screen shown in Figure 10.4. This gives you an easy point to branch from in the future, and it also makes you think about the areas of your source code that live in the same branch together, which will help you in the future if you ever do decide to branch.
Figure 10.3 A branch called Main
Figure 10.4 Convert Folder to Branch screen
With no branching, you have only one branch of code to work in for all teams. This technique works great when you have small teams working on the same codebase, developing features for the same version of the application and supporting only one version of the application at a time. At some point, no matter how complex your branching strategy evolves to support your business needs, you need at least one stable area that is your main (or mainline) code. This is a stable version of the code that will be used for the build that you will create, test, and deploy.
However, during stabilization and test periods, while you are getting ready to release, it may be necessary for the team to not check in any new code into the codebase (undergo a code freeze). With smaller teams working on a single version, this does not impact productivity because the people who would be checking in code are busy testing to ensure that the application works, as well as getting ready for deployment.
With this technique, there is no way to start work on something new before the final build of the current version has been performed. The code freeze period, therefore, can be very disruptive because there is no way to start work on the next version until the current one has shipped. It's these times when other strategies become useful for teams of any size, even a team of one.
Branch per Release
For teams that employ branching, the most common branching technique is branch per release. With this technique, the branches contain the code for a particular release version, as shown in Figure 10.5.
Figure 10.5 Branch per release
Development starts in the Main branch. After a period of time, when the software is considered ready, a branch is made to the V1 branch, and the final builds are performed from it. It is then released into production (with the code in the final production build getting a label to indicate which versions of which files were in that version). Meanwhile, development of new features for version 2 (V2) continues on the Main branch.
Say some bugs are discovered in production that must be addressed, and a small change is necessary to reflect how the business needs something to work. However, the development group does not want to include all the work for V2 that has been going on in the Mainbranch. Therefore, these changes are made in the V1 branch, and builds are taken from it. Any bug fixes or changes that must also be included in the next version (to ensure the bug is still fixed in that next release) are merged back (reverse-integrated) into the Mainbranch. If a bug fix was already in the Main branch, but needed to go into V1, it might simply be merged (forward-integrated) into it. At a certain point, the build is determined to be good, and a new V1.1 build is performed from the V1 branch and deployed to production.
During this time, development on the next version can continue uninterrupted without the risk of features being added into the code accidentally and making their way into the V1.X set of releases. When it is decided that V2.0 is ready to go out the door, the mainline of code is branched again to create the V2 branch, and then the V2.0 build is created from the new branch. Work can continue on the next release in the Main branch, but it is now easy to support and release new builds to customers running on any version that you want to keep supporting.
Branch per release is very easy to understand and allows many versions to be supported at a time. It can be extended to multiple supported releases very easily, and it makes it trivial to view and compare the code that was included in a particular version of the application. Branch per release is well-suited to organizations that must support multiple versions of the code in parallel—such as a typical software vendor.
However, for a particular release, there is still no more parallelism of development than in a standard “no branching” strategy. Also, if the organization must support only two or three versions at a time (the latest version, the previous version, and, perhaps, the version currently being tested by the business), this model can lead to a number of stale branches. While having lots of old, stale branches doesn't impact the performance of Team Foundation Server, or even cause any significant additional storage requirements, it can clutter the repository and make it difficult to find the versions you are interested in—especially if the organization frequently releases new versions. If this is the case, you may want to move old branches into an Archive folder, and have only the active branches (the versions that the development team are currently supporting) in the Releases folder.
Code Promotion Branching
An alternative to branch per release is code-promotion branching (or promotion-level branching). This technique involves splitting the branches into different promotion levels, as shown in Figure 10.6.
Figure 10.6 Code promotion branching
As before, development starts with just the Main branch. When the development team is ready to test the application with the business, it pushes the code to the Test branch (also often called the QA branch). While the code is being tested, work on the next development version is carried out in the Main branch. If any fixes are required during testing, they can be developed on the Test branch and merged back into the Main branch for inclusion in the next release. Once the code is ready to release, it is branched again fromTest to Prod. When the next release cycle comes along, the same is done again. Changes are merged from Main to Test, and then Test to Prod.
Code-promotion branching works well in environments that have a single version running in production but have long test-validation cycles that do not involve all of the development team. This allows development to continue on the next version in Main while test and stabilization of the build occurs in the Test branch. It also makes it trivial for the development team to look at the code currently on each system. Finally, the branch structure makes it easy to create an automated build and deployment system using Team Foundation Build that can automatically update the QA/Test environment as code is pushed to the QA branch.
For more information on the build capabilities of Team Foundation Server 2013, see Part IV of this book.
The previous branching strategies all involve a single team working on the system in its entirety as it works toward a release. All features for that release are developed in parallel, and the build can be deployed only when all features in flight have been completed and tested. However, in large systems, or systems that require very frequent deployment (such as a large commercial website), feature branching (or branch per feature), as shown in Figure 10.7, can be useful.
Figure 10.7 Feature branching
Feature branching is used when a project requires multiple teams to be working on the same codebase in parallel. In Figure 10.7, you see four feature teams working in separate branches (F1, F2, F3, and F4). Note that in a real branching structure, the feature branches themselves would likely have meaningful names such as FlightSelling, InsuranceExcess, or whatever shorthand is used by the project to refer to the feature under development. The Main branch is considered “gold code,” which means that no active development goes on directly in this branch. However, a feature must be reverse-integrated into this branch for it to appear in the final release build and for other teams to pick it up.
Initially, F1 is started with a branch from Main. But, while it is being developed, a second and third team start F2 and F3, respectively. At the end of development of the feature, F1 is merged back into the Main branch, and the F1 branch is deleted. Then that team starts on feature F4. The next feature to finish is F3, followed by F2. At each point, once the feature is merged into the Main branch, a new version of the software is released to the public website. But only one version is ever supported at any time.
Feature branching allows for a large amount of parallel development. However, this comes at the cost of delaying the pain of integrating each team's changes until the feature is complete, and you are merging the feature branch back into Main branch. For example, inFigure 10.7, when merging the F2 branch, all changes and inevitable conflicts introduced by features F1, F2, F3, and F4 must be analyzed and resolved.
The longer a period of time that code is separated into branches, the more independent changes occur and, therefore, the greater the likelihood of merge conflicts. To minimize conflicts, and to reduce the amount of integration debt building up, you should do the following:
· Keep the life of a feature short. Features should be as short as possible and should be merged back into the Main branch as soon as possible.
· Take integrations from the Main branch regularly. In the example shown in Figure 10.7, when F1 is merged back into Main, the feature teams still working on their features should merge those changes into their feature branches at the earliest possible convenient point.
· Organize features into discrete areas in the codebase. Having the code related to a particular feature in one area will reduce the amount of common code being edited in multiple branches and, therefore, reduce the risk of making conflicting changes during feature development. Often, the number of teams that can be working in parallel is defined by the number of discrete areas of code in the repository.
When using feature branching, the whole team doesn't necessarily have to be involved. For example, one or two developers might split off from the rest of the team to go work on a well-isolated feature when there is a risk of the merge not being possible (they are working on a proof of concept), or when it is decided that the current release should not wait for that particular feature to be implemented.
Implementing Branching Strategies in Centralized Version Control
So far, this chapter has covered a lot of the theory behind branching. This section puts that theory into action as it walks you through implementing a branching strategy using the branch tools available with Team Foundation Server 2013 and a Team Foundation Version Control–based Team Project.
For this example, you'll look at a fictional organization called Tailspin Toys that has installed Team Foundation Server and is using the version control functionality. Say that you are a member of the internal IT team, which supports an order-fulfillment intranet site critical to the operation of the business. The team has only one version of the site in production at any one time. However, because of the criticality of the software, the IT team has lengthy test cycles involving a series of experts from the business to ensure that the software is working as required.
The IT team has a single team project called IT and a single ASP.NET web application checked into the team project root folder at $/IT/Orders. They also have an automated build set up in Team Foundation Server.
The team has some issues when it comes to managing sources. The development process is plagued by problems and inefficiencies. There are significant periods when developers are forbidden from checking in to the repository while getting ready for a release. The delays cause the developers to end up creating large shelvesets filled with changes that become unmanageable.
Occasionally, urgent bugs are required to be fixed in the production codebase. This is done by the developer getting the label that represents the production codebase, adding the fix, building it on a local machine, and manually pushing the modified files out to production. Ensuring that the correct files are pushed to production and the source code fix is added back into version control is a manual process that has caused some problems. There have been instances where fixes to production were missing when the next version rolled out and had to be repeated again.
But, luckily, there are some people in the development organization who recognize the problems and want to come up with a branching plan to alleviate some of them. You have been selected to roll out this plan.
After some careful consideration, the team decides that a code-promotion strategy fits their organization quite well. Figure 10.8 shows the plan that the organization has decided to adopt.
Figure 10.8 Example branch strategy
The code will consist of the following three branches, as suggested by the code-promotion branching strategy:
· Main—The main development effort is conducted here. This is the branch from which the regular continuous integration build is performed, and where new features are developed.
· QA—The code will live here while it is being tested by the business. Because these test periods can be lengthy, new code development will carry on in the Main branch. Any fixes or modifications to the version under test will be performed directly on the QA branch and reverse-integrated back into Main. An automated build will be created that will run early in the morning during the week. The results of that build will be pushed to the QA web server daily for testing by the business the following day.
· Prod—This represents the code currently running in production. Code normally goes from Main to QA into Prod. A build is also created for this branch so that urgent hotfixes can be checked in and repeatedly built. Urgent hotfixes like this are very much the exception, though. If an urgent hotfix is performed, a baseless merge is performed to push that fix back into Main. Note that the results of the Prod build are first deployed to a test environment to ensure that they work as expected before manually running a script that pushes the code to production.
Figure 10.9 shows the current codebase.
Figure 10.9 Current codebase in Source Control Explorer
The first thing you want to do is to move the code currently at the root of the team project in version control into a Main branch. This will be the most disruptive of the changes because it will require the build to be reconfigured, and team members to re-sync their workspaces. So, you decide to do this late one night, a few weeks before the IT team is due to push a release to the test team.
To move the code into a branch, you right-click the Orders folder containing the solution and select Move. Then you manually enter a path of $/IT/Main/Orders in the Move dialog box shown in Figure 10.10. Note that the Main folder does not have to exist at this point. Moving the files to that location will cause Team Foundation Server to create the parent folder.
Figure 10.10 Entering a path in the Move dialog box
As soon as this is done and checked in, you edit the build definition's workspace so that it looks at only the Orders Source Control Explorer folder under the Main folder, as shown in Figure 10.11.
Figure 10.11 Editing the build definition's working folders
You also modify the Process for the build to remove the solution file from the old location, and add it in again at the new location, as shown in Figure 10.12. You then manually queue a new build to ensure that everything is working well. Everything works, so you send an e-mail notifying the team of the change to version control, and you go home for the evening.
Figure 10.12 Modifying the Process for the build
Now, as an aside, note that the source is in the correct path, but the Main folder is not yet a branch. In Team Foundation Server, branches are a first-class entity in version control. They are represented by a different icon and have additional metadata such as Owner, Description, and Branch Relationships. To convert a folder to a branch, you right-click the folder in Source Control Explorer and select Branching and Merging Convert to Branch. This displays the Convert Folder to Branch dialog box, as shown in Figure 10.13.
Figure 10.13 Convert Folder to Branch dialog box
Note that to convert a folder to a branch, you must have the Manage Branch permission in Team Foundation Server. Also, once you have converted a folder to a branch, no folders above or below it may be a branch.
If people had already created new branches from the Main folder, you would want to ensure that the check box shown in Figure 10.13 is selected because this will also convert those folders to branches. But this does not apply in our Tailspin Toys example.
In the future, if you ever need to convert a branch back to a regular folder, go to Visual Studio and select File Source Control Branching and Merging Convert to Folder.
Now get back to the example implementation. You come in the next morning and start to get the branches set up. You perform the Convert to Branch operation on Main as described previously, and the source tree is now as shown in Figure 10.14.
Figure 10.14 Main as a branch folder
When the build is ready to be released to the QA team, instead of invoking the code freeze period that used to be enforced, you take the latest version of code and branch it to create the QA branch. You do this by right-clicking the Main branch and selecting Branching and Merging Branch, which displays the Branch dialog box for a branch (see Figure 10.15).
Figure 10.15 Branch dialog box for the Main Branch
In this dialog box, you enter the full path that you would like to create, which, in this example, is $/IT/Releases/QA. If the Releases folder does not already exist, it will be created automatically as part of this operation. As shown in Figure 10.15, there is a warning that this will be committed to the repository as part of a single transaction.
This behavior is slightly different from that experienced when branching a folder or file. When you branch a folder or file in the Visual Studio or Eclipse clients, it is assumed that you are making a copy of the file in your local workspace as well. Figure 10.16 shows an example of the Branch dialog box when a file is selected.
Figure 10.16 Branch dialog box when a file is selected
If you had selected a folder outside an existing branch, you would also get the option to convert the folders to a full branch in Team Foundation Server—but you do not have to. This is a subtle point. While branches are first-class objects in Team Foundation Server, you can branch any folder or file to another place in the repository. This is a great way to copy areas of the repository to a different part of the repository, but make the history of changes that occurred in the old location easily accessible in the new one. In Team Foundation Server, a rename is actually implemented under the covers as a simultaneous branch and a delete of the source location.
In the instance of branching a file or folder, this is done as a two-phase operation. The branch changes are made in your workspace, and then you check these in.
However, in the majority of instances, you want to branch an entire path in version control. Usually, you will not be making changes to the files or performing validation before check-in.
So, performing these in a single atomic transaction is a much more efficient use of server resources. (This is functionally equivalent to the tf branch command line with the /checkin option supplied.) Therefore, you perform the branch as indicated in Figure 10.15 and the source tree is now as shown in Figure 10.17.
Figure 10.17 QA branch created
A new build definition (called Orders QA) is created for the QA branch, with a scheduled trigger of 6 a.m., Monday to Friday. That way, a fresh build is ready and waiting for the test team each morning if changes have been made to the QA branch during the day.
Chapter 18 provides more information on creating build definitions.
Dealing with Changesets
During initial testing, you notice a small bug with the stylesheet on Internet Explorer 6 on Windows XP. None of the development team was old-fashioned enough to be running this configuration, but it is still commonly found in the company, so the team decides to create a fix for it.
The modification is made to the Site.css file in the QA branch and checked in as changeset 6. The next scheduled build (Orders QA_20131231.1) picks up this change and adds it to the code running in the test environment. Once the fix has been verified, it must be merged into the Main branch.
For merges like this, it is best if the merge is performed as soon as possible, and by the developer that made the change. That way, it is fresh in his or her mind and isn't forgotten, or the fix misunderstood. The testing team has set a policy that the related bug cannot move to the Closed state until an urgent fix has been merged into the Main branch—which is a sensible policy.
To merge that code, the developer right-clicks the source branch (in this case, the QA branch) and selects Branching and Merging Merge. This displays the Merge Wizard dialog box, as shown in Figure 10.18.
Figure 10.18 Source Control Merge Wizard
The developer opts to merge selected changesets to ensure that only the change the developer is aware of is picked up. The developer checks that the target branch has been identified as Main, and then clicks Next. This displays the changesets selection page.
On this page, you can select a single changeset or a continuous range of changesets that you want to merge. In the case of the example testing team, it has just the one changeset it is interested in (6), so the developer selects that and clicks Next, as shown in Figure 10.19. This provides a final confirmation page and, when the developer clicks Finish, the merge is performed. The pending changes page now looks like Figure 10.20.
Figure 10.19 Changeset merge range selection page
Figure 10.20 Displaying the results of a merge
The stylesheet file currently has a pending merge on it. At this point, it is good practice to compare the current version of the file with the latest version to ensure that the change you are making is still the correct one, as shown in Figure 10.20. In this case, it is, so the developer associates the changeset with the original bug, checks in the merge, and then marks the bug as Done.
At this point, if you right-click the file in Source Control Explorer and select View History, you will see the History for the file, as shown in Figure 10.21 (once the tree nodes have been expanded).
Figure 10.21 History for the file
In Figure 10.21, you can see the merge of the changes back into Main at changeset 7. By expanding the node, you can see the changes made to that file in the source branch (in this case, the edit of the file in the QA branch in changeset 6). Then, further back in history, you can see the rename (move) of the file when the code was moved under the Main folder. Finally, if you expand that rename node, you can see all the history of the file before it was in the current branch structure.
Another way to visualize this change and see that it made it into the correct branches is to right-click changeset 7 in the History view and select Track Changeset. This displays the Select Branches dialog box (see Figure 10.22), which allows you to select which branches you would like to view.
Figure 10.22 Select Branches dialog box inside the Track Changeset view
For the example scenario, the developer selected the Check All Visible check box and clicked the Visualize button. Initially, this sequence will show a hierarchical view of branches, which are colored according to which branches the changes in changeset 7 made it into. If you were to look at Figure 10.23 in color, you would see that everything showed up green to indicate that everything was good.
Figure 10.23 Branches shown in hierarchical view
An alternative visualization is available by clicking the Timeline Tracking button, as highlighted in Figure 10.23. This displays the changes in a familiar timeline style view, as shown in Figure 10.24. Again, if this were in color, you would see that all the branches are green, which means that the code made it to where it should be.
Figure 10.24 Timeline Tracking view
Back at Tailspin Toys, the IT product has undergone a bunch more testing on the QA branch, and development continues in the Main branch. At the end of the testing period, it is decided that the application is working properly, so the build created with the stylesheet fix in changeset 7 (build Orders QA_20131231.1) is deployed to production.
However, all is not well. Once deployed to production, the Chief Information Officer (CIO) of the company notices an incorrect footer file at the bottom of the main page. The page still contains text that reads, “My ASP.NET MVC Application.” While this doesn't affect functionality in any way, the CIO would like the issue fixed ASAP because she is about to demo the application to the board of directors.
It's a small, low-risk fix. In days gone by, this would be exactly the sort of thing for which a member of the IT team would jump into the production environment and just fix it. However, it's exactly the sort of change that can be forgotten about back in the development branch. So, to ensure that the change is not overlooked, the team decides to do it in version control using the new branch plan.
First, they must create the Prod branch. There are two ways to do this. One is to create the branch from the label applied as part of the build process. Another is to branch by the changeset that included the required fix. Now take a brief look at both methods and see which is more appropriate for this example scenario.
Branch from Label
As previously discussed, it is possible to create branches after the fact by right-clicking in Source Control Explorer and selecting Branching and Merging Branch as well as from the tf branch command line.
In the Branch from QA dialog box, select Label from the Branch Version drop down, as shown in Figure 10.25, and then click the ellipsis ( … ) button to find the label created by the build process. (By default, each build labels the files included in that build with the build number.) Enter the target branch name of $/IT/Releases/Prod and click Branch.
Figure 10.25 Branch by Label in Visual Studio
To do the same thing from the command line, the developer opens up a Developer Command Prompt for VS 2013 and enters the following command:
tf branch $/IT/Releases/QA $/IT/Releases/Prod /version:L”
Orders QA_20121231.21@$/IT” /checkin
Whichever way you perform a branch by label, the advantage is that it will branch only the files included in the specified label, and that label was created automatically by the build process to include only the files in the workspace definition of the build at the time the build was performed.
The major downside is that, as stated in Chapter 6, labels in Team Foundation Server are editable. Someone with appropriate permissions could have edited the label and removed or included certain key files after the label was created by the build process. This is unlikely in the example Tailspin environment, but it is possible.
Branch from Changeset
From the build report shown in Figure 10.26, you can see the build associated with changeset 6 was successful. As discussed in Chapter 6, the changeset represents a unique (immutable) point in time in the version control repository. Therefore, if you were to branch from changeset 6, this would include the files at the exact state that they were in when the build was performed.
Figure 10.26 Build report
The team decides to branch by changeset 6 so as to include all changes up until changeset 6 in the QA branch when creating the Prod branch. To do this, the developer right-clicks the QA branch in Source Control Explorer and selects Branching and Merging Branch. The developer then changes the “Branch from Version” to changeset 6, and sets the Target Branch Name to be $/IT/Releases/Prod.
Once the branch is created, the version control repository then looks like Figure 10.27.
Figure 10.27 Prod branch created
If you were to right-click the Main branch and select Branching and Merging View Hierarchy, you could see a visualization of the current branch structure, as shown in Figure 10.28. If you hover the mouse over each branch, you see a tooltip with the additional metadata about that branch, including any description that you entered.
Figure 10.28 Current branch hierarchy
At this point, the developer can now create a fix in the Prod branch. The developer edits the offending cshtml file and checks it in as changeset 11. The developer then creates a build and deploys this to production. Now you must ensure that the fix is in the appropriate branches so that it also gets included in the future releases.
To do this, you right-click the Prod branch, and select View History. Then, you right-click the changeset and select Track Changeset. As before, you select the Check All Visible check box and click Visualize. The change will show in green in the Prod branch only, as represented by the bottom box in Figure 10.29.
Figure 10.29 Change being visualized for the changeset
To merge this change into Main, the developer now has two choices: a ladder merge or a baseless merge. If you find that during your branch process you frequently must perform baseless merges or merges through other branches (ladder merges), this is a good indication that the model is no longer optimized for the typical circumstances encountered in your environment, and you may want to revise it.
However, in the Tailspin scenario, making ad hoc changes to production is very much an exception case. So, the IT team wants to optimize the branch plan for the usual case of a change starting in Main, getting promoted on to QA, and then to Prod. So the developer must use a ladder merge or a baseless merge to go from Prod to Main.
As shown in Figure 10.29, the team has a change in Prod. To get that fix into Main using standard merges, the developer must first merge it into the QA branch and then, from there, into Main. This is because in Team Foundation Server, a standard merge can flow from parent to child, or vice versa.
To merge the changes, from the Tracking Changeset view shown in Figure 10.29, the developer uses the mouse to drag and drop the Prod branch up to the QA branch. This will display the standard Merge Wizard shown earlier in Figure 10.18. The developer clicks the Selected changesets radio button and clicks Next to display the changeset selection page shown earlier in Figure 10.19.
On this page, the developer would select the desired changeset and click Finish. The developer then checks in the merged file, and clicks the Rerun button in the Tracking Changeset view to show the change in the QA branch. Finally, the developer drags and drops theQA branch to the Main branch and repeats the process through the Merge Wizard.
In this particular example, because of when the change occurred in production, it actually would have been possible to get the change into Main in this way. However, if the change had been required when there was a different (newer) version of the software in the QAbranch, you may have not wanted to merge the changes in this way. Instead, you could have opted to do a baseless merge directly into Main, and then the change would make it back up to the QA branch with the next release to the test team.
Now take a look at how to plug in that option for the Tailspin Toys example scenario.
To discourage baseless merges, the simple drag-and-drop approach is not available inside Visual Studio 2013. Instead, the developer must right-click the Prod branch and select Merge. The Source Control Merge Wizard (refer to Figure 10.18) is displayed with the available parent or child target paths shown in the drop down. In your example, $/IT/Releases/QA would be the only option shown. But to perform a baseless merge, press the Browse button and then select the Main branch. The merge dialog box then shows a warning (as shown in Figure 10.30) that a baseless merge is going to be performed.
The same action could be performed from the command line using a command such as the following:
tf merge /baseless /recursive /version:11
Figure 10.30 Baseless merge warning
In your example, as in the case with many active development environments, development has been ongoing by the rest of the team and additional check-ins have occurred in the Main branch. Therefore, it is highly likely that a conflict will occur. As discussed previously, Team Foundation Server 2013 will automatically attempt to merge those conflicts for you, if possible. For example, if a developer was editing on part of the file in the Main branch, and the change occurred to an unrelated part of the file in the Prod branch, these changes would be merged automatically even though this is a baseless merge. Sadly, in your example scenario, someone has renamed the _Layout.cshtml file in the Main branch to _MainLayout.cshtml. If you had performed the ladder merge, as described in the previous section, Team Foundation Server would have been able to use the common base version to detect the rename operation and merge the changes into the file with the new filename. However, as this is a baseless merge (with no common base version for comparison), Team Foundation Server can use only the current state of the two branches when making its calculations. It therefore has no way to determine that the rename on _Layout.cshtml occurred after a point in time in which the branch that ended getting branched to Prod was performed. Therefore, Team Foundation Server thinks that you want to add _Layout.cshtml back into Main, but it knows that a file used to exist by this name and is clever enough to check with you first to see if that is what you really wanted to do by showing you the conflict dialog box, as shown in Figure 10.31.
Figure 10.31 Resolving baseless merge conflicts
In this instance, you can see that something strange has happened. To dig into things a bit further, you right-click the conflict and look at the Target History. There you can see that _Layout.cshtml was renamed to _MainLayout.cshtml. Most of the options that Team Foundation Server presents requires you to restore the original _Layout.cshtml file, which is not what you want. Therefore, you undo the pending merge change on _Layout.cshtml from the Pending changes page in Team Explorer and try again.
This time, you now know that you want to merge the changes of the specific file _Layout.cshtml in the Prod branch with the file _MainLayout.cshtml in the Main branch. To do this, you go into Source Control Explorer and right-click the _Layout.cshtml file in the Prodbranch and select Branching and Merging Merge. Now you are just merging the one file. In the Target Branch, you press the Browse button and select the _MainLayout.cshtml file that you want your changes to be merged into, as shown in Figure 10.32.
Figure 10.32 A baseless merge of a single file
This will again show the Resolve conflicts dialog box, but this time you will be presented with the option to Merge Changes in Merge Tool. Selecting this option will show the improved integrated merge tool in Visual Studio 2013, as shown in Figure 10.33.
Figure 10.33 Resolving conflicts inside Visual Studio 2013
The change from the source version on the left-hand side is selected and any additional changes necessary to correctly merge the file are performed on the contents in the Results pane at the bottom on the merge tool. The developer then presses the Accept Merge button to inform Team Foundation Server that the conflict on that file has been resolved.
If desired, external diff and merge utilities can be configured in Visual Studio under Tools Options Source Control Visual Studio Team Foundation Server Configure User Tools.
To find out more about configuring external diff and merge utilities for use with Visual Studio, see http://aka.ms/ExternalDiffMerge. James Manning has a blog post detailing the configuration parameters necessary for many of the common tools athttp://aka.ms/ExternalDiffMergeEx.
The developer can now check in the merge by using the command line or Visual Studio. Following is the command to execute a check-in from the command line:
tf checkin /recursive /noprompt $/IT/Main
For more information on using the tf merge command to perform merge operations (including additional examples), see the MSDN documentation at http://aka.ms/tfmerge. For more information about past merges from the command line for a known source and destination branch, see the Help documentation for the tf merges command on MSDN (http://aka.ms/tfmerges) or type tf help merges at a Developer Command Prompt.
Tracking Change through Branches
As you have seen thus far, the branch visualization tooling in Visual Studio 2013 provides some powerful capabilities for viewing your branch hierarchy and tracking the progress of changes through it. Using the View Hierarchy functionality, you can immediately see the relationships of the branches in your source tree, and navigate to their locations in the repository. By selecting Track Changeset for a changeset in the History view, you see into which branches that change has been made, and you can even merge the change into other branches by dragging and dropping between branches.
The Tracking Changeset visualization has some additional features not always displayed in simple examples, such as those presented here. Figure 10.34 shows an example from a more complex branch hierarchy.
Figure 10.34 Complex branch hierarchy in Tracking Changeset visualization
In the example shown in Figure 10.34, the original change occurred in the FeatureB branch as changeset 86. This was reverse-integrated into the FeatureInt branch as a standard merge in changeset 87. That change was then merged into Main. But not all files were copied over as part of the merge, as the cross-hatching and the asterisk next to changeset 88 indicates. This should instantly be an area to investigate which files were checked in and why. Double-clicking the branch will show the changeset details to begin the investigation.
Then, rather than a standard merge back down from Main into the V2.0 branch, you can see that three baseless merges have occurred to get all the changes into that branch (changesets 89, 90, and 91). Finally, a single baseless merge took all the code into the V3.0branch. Figure 10.34 shows that the changes have yet to make it into the FeatureA branch or into the V1.0 and V1.1 branches. Clicking the Timeline Tracking button displays the timeline view for changeset 86, as shown Figure 10.35.
Figure 10.35 Complex Timeline Tracking view
This view does not show the relationships between branches (the hierarchy) but instead shows the merges as they happened. The sequence of events around the partial merges into Main and V2.0, and the subsequent full merge into V2.0, are therefore much more clearly represented. Hovering over each branch provides additional metadata, including its owner and description.
Implementing Branching Strategies in Git
When discussing the implementation of branching strategies in a Git-based world, you have to separate the discussion into server-side local branching activities. Many of the strategies and implementations already discussed were based on the fact that the Team Foundation Version Control–based repositories perform all of their branching on the server. Local copies of those branches are implemented as separate folders within your Workspace or even as separate Workspaces on your machine. With Git, you have the full power of branching and merging both locally and between the local and remote repositories.
No Branching Strategy
As described earlier, this strategy is the simplest to use. It is simply a single branch residing within the team project. Team members can branch and merge locally for any purpose, but they all must Push and Pull from the single Master branch on the Team Foundation Server.
To implement this strategy, start by cloning the Master branch from the team project into your local Git repository, as shown in Figure 10.36.
Figure 10.36 Clone a team project repository.
Now you have a local master that is considered a published branch, as shown in Figure 10.37.
Figure 10.37 Local master is Published
This branch is your link back to the team project and the starting point for all of your local branches. Now you can click on the New Branch link, which opens up a section where you can name your branch. You're expanding the HelloWorld app's pool of languages so call the branch ExpandLangs, as shown in Figure 10.38.
Figure 10.38 Create a branch
Notice also that you have your local master branch as the source and the Checkout branch check box is selected. Click on the Create Branch button and your branch is created, checked out, and you are switched to it as denoted by the bold type in Figure 10.39. Notice that the ExpandLangs branch is Unpublished. This means that it doesn't have a counterpart on the remote repository.
Figure 10.39 New branch
Now you can implement our feature and make as many commits as you like to the branch. When your work is complete on the branch, you can go back to the Branches page in Team Explorer, right-click on the ExpandLangs branch, and select View History, which will show that you have changes that aren't in your local master, as shown in Figure 10.40.
Figure 10.40 ExpandLangs branch history
To merge those changes back to Master, you click the Merge link in the Branches page, which expands the Merge section. Here, you select ExpandLangs as your source and Master as your target and click the Merge button, as shown in Figure 10.41.
Figure 10.41 Merge ExpandLangs to local Master
Now you have your local Master branch updated. You can now Push your local changes up to the team project's repository by switching to the Master branch and then navigating to the Unsynced Changes page, selecting your Commit, and clicking the Push link, as shown in Figure 10.42.
Figure 10.42 Push changes from a local branch to remote repository
Feature Branching Strategy
Feature branching in Git has been the subject of many discussions since its release. The main thing to understand is that the reason for feature branching is the same regardless of the version control tool used; only the implementation will differ.
In Git, you already use a local feature branch to implement your code locally. But what happens if you are working with a couple of colleagues on that feature? You will either all have to have your own local branch of Main and share code between each of your local repositories or you need some central place to share code.
For this to work, you can create a branch locally and then publish that branch back to the team project so your colleagues can Pull the branch locally and work on it.
Let's assume that you need a feature branch to add an About page to your application. You're working with two other developers so you need a common feature branch. You've already got a local copy of Master, as shown in Figure 10.37. You can create a local branch from Master called AboutPage, as shown in Figure 10.43.
Figure 10.43 Create an AboutPage branch from Master.
Then you right-click on the AboutPage branch and select Publish Branch, as shown in Figure 10.44. This will make the branch available to your colleagues in your team project repository, as shown in Figure 10.45.
Figure 10.44 Publish branch
Figure 10.45 Published branch in team project
Now you can each make changes locally and then commit them to your local AboutPage branch. When you're ready, you can go to the Unsynced Commits page, select your commits, and click the Push link to push them to the AboutPage branch in the team project, similar to Figure 10.42. Looking back at the repository in Web Access, you can now see in Figure 10.46 that the difference between the AboutPage branch and Master is your commit.
Figure 10.46 Pushed commit in AboutPage branch
Now your colleague Tatiana needs to create a local branch from the AboutPage branch in the team project. She will go to her Branches page in Team Explorer and click the New Branch link. In the New Branch section, she will change the source drop-down from masterto origin/AboutPage, as shown in Figure 10.47. This will default the name of the local branch to AboutPage, which is fine. She then clicks the Create Branch button to create the branch.
Figure 10.47 Creating a local AboutPage branch
She now opens the branch and makes changes to the application in one or more local commits. When she is done, she pushes her changes to the team AboutPage branch in the team project as you did earlier.
When you go to your Unsynced Changes page, you can click the Fetch button in the Incoming Commits section to show her commits. You can then Pull those commits into your local branch by clicking the Pull link, as shown in Figure 10.48.
Figure 10.48 Pulling a team member's Commits
Your entire team can keep doing these steps until the entire feature is complete. You can then merge the AboutPage branch with Master to deliver your work to the next release.
As you can tell from this chapter, the branch and merge capabilities of Team Foundation Server not only allow for some complex software configuration management scenarios, but also provide the tooling to help understand what is happening with changes in your version control repository.
While conflicts are always going to happen with any parallel development process, Visual Studio 2013 and Team Foundation Server 2013 have tooling to help resolve and manage merge conflicts to make branching and merging easier.
With the arrival of Git as a first-class version control repository choice within Team Foundation Server, you now have the ability to perform branching and merging locally, as well as on the server.
The chapter looked at the terminology used in branching, discussed some common branching techniques, and then provided a detailed walkthrough of implementing a basic branching strategy in an organization using the tools provided for Team Foundation Server 2013. Finally, this chapter examined the changeset tracking functionality available in Team Foundation Version Control to determine to which branches a particular change has propagated.
Chapter 11 builds on the knowledge gained so far in this book, and provides some best-practice guidance over a few scenarios common across development teams (such as how to organize the structure of the repository, manage third-party dependencies, and manage the dependencies for internally developed libraries, such as common framework code). Chapter 11 also looks at the practicalities of using Team Foundation Server to manage Team Foundation Server artifacts, such as process templates, custom build assemblies, custom tools, and so on.