The Team Foundation Server (TFS) is a Microsoft product that provides all the necessary features to support the software development process, like source code management, requirements and project management, and build and release processes.
These days, TFS supports two types of source code management systems: Team Foundation Version Control (TFVC) and Git.
On the surface, both systems are very similar. You can commit your code changes, you can view the history of who changed what, and you can revert back to a previous state of your source code.
However, a more in-depth look shows you some crucial differences in the design of the systems—most notably, TFVC offers a centralized repository, while Git builds on a decentralized model.
These days, almost all newly started software projects use Git for source code management. Even Microsoft says that you should use Git for version control unless you have a specific need for centralized version control features in TFVC. Therefore, Git is also default for new projects in TFS.
Initially, TFS supported only TFVC, and support for Git as a source code repository was added later in TFS 2013.
I am sure there are a lot of software projects out there that were started with TFS before Git support was available. Therefore, those projects had to choose TFVC as the source code management system. And as nobody migrated them to Git, those projects have been stuck with TFVC since then.
For example, in my company we have at least half a dozen active software projects, which are using TFVC. For a long time, we haven’t seen the need to migrate them to Git.
If you are used to TFVC and have never really worked with Git, then you don’t know about the small but crucial differences.
There are a lot of benefits when you use Git instead of TFVC as a source code repository. In this post, I will give you four not-so-obvious reasons why those projects should be migrated from TFVC to Git.
The Benefits of Using Git Compared to TFVC
1. Git Supports Joint Work on the Same Feature
Imagine you want to implement a new feature, which requires a new table in the database, some changes in the back-end service, and also some new pages on the front-end. Only if all of these changes are finished will the new feature be complete and ready for production.
These tasks require different skill sets—some database knowledge, experience as a back-end developer, and some front-end skills. Often, these skills are not possessed by a single person and therefore are split among multiple people who have to work together to complete that feature.
This means that multiple people work on the same feature simultaneously and have to share their piece of the work at some point. So everyone holds a piece of the puzzle and only if you put them together will your feature be complete.
Sharing Work With TFVC
If the team uses TFVC, it is quite tricky to share incomplete work easily. Nevertheless, there is a way to create a so-called “shelveset” and share it with another developer.
A shelveset is just a specific version of your code that is not committed to a branch yet but can be shared with another developer. However, if you share a shelveset with your teammate, they will not be able to continue their work based on that shelveset. They can look at it, but cannot integrate their piece of work with it. This is just not possible with TFVC.
The usual workaround is to commit your piece of incomplete work to a branch—let’s call it the “dev” branch. Now your teammate, who is working on the same feature, can fetch it and integrate their piece with yours.
In this scenario, your commit will most likely contain incomplete and probably untested code, because the tests can be performed only when the other pieces of the feature have been developed.
And this is not a nice position to be in—especially for other developers on your team, who are working on a completely different feature. They will fetch your incomplete and untested code as well and therefore might experience some bad behavior of the software—even though they are working on a completely different feature.
Sharing Work With Git
In contrast, teams working with Git usually use some version of the feature branch model. For each feature, you create a separate branch and share that branch only with other developers who are also working on that same feature. Each developer commits to that feature branch, adding their part of the changes necessary to complete the feature.
Only when everyone has added their piece of work, and the feature is tested and complete, will the whole feature be merged to the dev branch. So your dev branch will never contain untested or incomplete code.
Therefore, when multiple developers work together on the same feature, it is very complicated to share incomplete work when using TFVC, but it is straightforward when using Git.
2. Git Allows You to Update an Existing Pull Request
Code reviews are a very important step in the development process, because they ensure better code quality and also make better teams.
Of the different types of code reviews, a lot of development teams use the asynchronous type by default. This review process is supported in Git as well as in TFVC.
In the world of Git, people use so-called “pull requests” to request feedback on a code change.
And TFVC supports “review requests,” which are, to some extent, similar to the pull requests in Git.
Both have the same purpose, which is to share code changes with another developer so they can review the code and give feedback for improvements.
However, there is a very crucial difference between a review request and a pull request, which has a very big impact on the process. Let me explain.
Updating a Review Request With TFVC
In TFVC, the developer working on the coding—or the coder—can send code changes that are not yet committed to a branch, to the reviewer. In essence, the coder is sharing a shelveset with the reviewer.
The reviewer then can make comments to improve the code and mark the review as “Needs work.” After that, the coder makes the necessary changes and has to create another review request.
The big downside here is that this second review request doesn’t have any connection with the first one. This means that the reviewer has to review all the changes, not only the last improvements. So for each improvement, the reviewer gets a new review request and has to review the whole thing again.
Updating a Pull Request in Git
In contrast, in Git it is possible to add improvements changes to an existing pull request.
When you have to make improvements based on the feedback from the reviewer, you commit those changes again to the same branch. These changes then end up in the same pull request and the reviewer has to review only those last improvements.
This fact makes the whole code review process a lot simpler when using Git, especially when the coder is a junior and the reviewer has a lot of suggestions to improve the pull request.
In such a case, you will have multiple code review cycles until the pull request is good enough and the reviewer is happy. In each of these cycles, the coder changes the existing pull request and the reviewer just has to review these changes instead of reviewing everything from scratch.
So, in contrast to the review requests in TFVC, the pull requests in Git support multiple commits. If an existing pull request is changed, another commit is simply added to the pull request and the reviewer needs to review only that additional commit. That’s why Git will save you a lot of time and effort during the review process, and therefore makes the team more productive.
3. Git Allows the Reviewer to Merge
When the reviewer approves the changes made to the code, those changes can be merged to the main line, usually the dev branch. In the Git world, you merge the pull request, while in the world of TFVC, you merge the shelveset. Although both processes seem very similar, there is a crucial difference:
In TFVC, the reviewer marks the review request with “Looks good” to approve the changes. But that’s all they can do. The reviewer is not able to merge the review request to the dev branch by themselves. This has to be done by the coder as a separate step.
As soon as the review request is approved, the coder has to check out the shelveset and then commit the approved changes to the dev branch. This means that, while the coder has probably already started with their new task, they are interrupted to perform that extra step by themselves.
In contrast, in Git the reviewer can merge the pull request immediately when they approve it. The coder doesn’t have to be involved in the merge at all unless there are any merge conflicts, which is more of the exception than the rule.
If you imagine that an experienced coder might produce a couple of pull requests per day, then the simplified Git flow makes the team more productive compared to the workflow with TFVC.
Git saves an unnecessary step for the coder and therefore makes the development workflow simpler.
4. Git Avoids Big Review/Pull Requests
Imagine you are working on a simple feature and you are extending the functionality of an existing class. It is an easy task; you just have to add a new method to that existing class and then you’ll be finished.
However, this additional method adds new responsibilities to that class. Therefore, you decide to rename the class to make it easier for other developers to understand what that class is doing.
Of course, you use the tooling of your integrated development environment (IDE), like Visual Studio, to rename the class. So, it is quite easy and the renaming is performed automatically everywhere in the code where the class is used.
But as the class is used in many different locations in the code, you end up with changes in 20 code files. But only one contains the important logic that needs to be reviewed.
If you have such a scenario while using TFVC, then you create a review request, which contains all the changes in one shelveset. This means the reviewer is forced to review all the files in the same detail, because they do not know where to focus. This is not necessary and wastes a lot of time.
Conversely, in Git you start with a new feature branch. Then you implement the new method and commit those changes. After that, you rename the class and commit again before you finally create the pull request.
When the reviewer is starting the review, they will see two different commits in the pull request. This results in two major benefits.
First of all, by reviewing the commits separately, the reviewer can easily follow the steps the developer took to create the feature.
For instance, if the pull request is really big, let’s say it consists of 10 commits and 30 changed files, then the reviewer can go through each commit one by one to follow the thinking of the developer. That’s of course way easier than having to look at each of the 30 changed files one after another without knowing which changes in different files belong together.
Secondly, the reviewer can also easily figure out which commit is important to focus on for the review.
For instance, if the commit message says, “Rename of class X to Y,” and contains 20 modified files, then the reviewer can safely assume that the rename has been done correctly by the IDE tooling and they do not need to focus the review there.
This means pull requests, which are split up in separate commits, can be reviewed a lot easier. This is because the reviewer can also follow the logical steps of the developer by looking at each commit in chronological order. They can also filter out changes, which have been triggered by the tooling of the IDE.
The fact that a pull request in Git can consist of multiple commits is the biggest benefit of all from my list. It just makes the whole code review task much more efficient and saves a lot of time for the reviewer.
And the lack of support for multiple commits for a big review request in TFVC is a major drawback. As the reviewer has to review the same piece of code over and over again, they don’t pay attention to details anymore and might miss a bug. That’s why even the quality of the code decreases.
This is the most crucial reason why you should move from TFVC to Git.
Moving From TFVC to Git
The good news is that, from a technical perspective, the move from TFVC to Git is quite easy. The bad news, however, is that you need to have your team on board with that decision, but people generally don’t like change very much.
I have covered the technical procedure in a separate blog post, so let’s focus here on the human side of the migration.
As I mentioned earlier, I am currently working on a team that is using TFVC. This is due to historical reasons, as the main coding projects have been started around 10 years ago—at a time when TFS didn’t have support for Git.
And as nobody on my team has ever worked with Git in a former job, they never had to get used to TFVC after working with Git. That’s why I believe that people don’t see these “minor” differences between TFVC and Git.
However, I am in the lucky position that I have worked with Git in my former job and therefore I do see these “minor” differences.
So I am currently in the middle of helping my team to migrate from TFVC to Git. For this migration, we are using the following five-step approach that you can follow in your own migration process as well:
Step 1: Involve People in the Migration
The people on your team should be involved early in the whole process of the migration. If you plan and perform all the tasks without your team, and then finally just drop the results on them, it is very likely that you will get a negative response.
There is the nice stone soup story, or the story of the boiling frog, which basically explains why you should involve your team while the water is still comfortable, as each team member can make their own contribution to the change.
Do the migration for test purposes and give your teammates access to the Git repository (repo) for testing and reviewing. While you still use TFVC, you can already create the Git repo and migrate your code, so everyone can see what the new repo looks like and play around with it.
Step 2: Host a Workshop to Introduce Git Features
Some people on your team probably haven’t worked with Git before. It’s a great idea to facilitate a workshop and show them how to use the most important features of Git. This will make it clear what a developer needs to know in their daily job in order to use Git.
A great tool to visualize the concepts of git is http://git-school.github.io/visualizing-git/.
With this tool you can easily explain how commits are always linked to a parent. You can also show that a branch is just a pointer to a specific commit, and so on.
You can’t expect everyone to spend time learning Git by themselves. Everybody has a lot of other work to do and won’t find the time, so you should plan a dedicated time to get the knowledge actively in the team.
Step 3: Hand Out a Git Cheat Sheet
As there are a few commands that are quite hard to remember, especially if you’re learning Git from scratch, it is a good idea to hand out a list of Git commands that a developer needs to use in their day-to-day work. This way, your teammates can look up important Git instructions easily.
Step 4: Set a Date for the Migration
After the workshop, everyone on your team should have some basic knowledge about Git. But you should give them some time to apply their knowledge on the migrated test repository before you really switch from TFVC to Git in your production repo.
Two weeks should be enough time so that people can play around in the test repo and ask questions in case something is not clear. Ask for their feedback; maybe you missed teaching them some critical parts.
Step 5: Switch to Git When Everyone Feels Confident
A couple of days before the migration date, bring your key people of the team together for a last session. In that session, you can discuss whether they foresee any blocking issues for the go-live. Then you can decide whether you have a “go” or “no go” for the migration.
If you decide it’s a “go”, then you have your key people on board for the migration. And if everything works out as planned, you will switch to Git a couple of days later. After that, you will enjoy the new power of the best version control system out there.
Migrating to Git: Not as Hard as It Seems
The migration from TFVC to Git is not as difficult as you may think. Actually, I was quite surprised by how easy and straightforward it is. By running just a few commands in your console, you can migrate your TFVC repository to a Git repository.
The new Git repo will be within the same TFS Team Project. Therefore, you can keep all your related functionality, like your user stories or builds.
For instance, you don’t need to do anything with your user stories, as they stay in the same TFS Team Project. Of course, you need to adapt your builds a bit to use the new Git repo, but the main parts of your configuration in TFS stay the same.
For testing purposes, you can run the TFVC repo and Git repo simultaneously side by side. When you feel confident that the team has the knowledge to work with Git, then you simply lock the TFVC repository by removing some permissions and continue your development on the migrated Git repository.
As you have seen, there are some major benefits when you migrate from TFVC to Git. The most important one is the fact that you can have multiple commits to a single pull request, making the whole review process much easier and more effective. What you will gain is simply higher code quality.
You can also work easier together on a feature and merge to the main branch only when the feature is tested, reviewed, and builds successfully.
If you already went through the process of migrating from TFVC to Git, please share your experience in the comments. I would be very eager to hear how difficult or smooth the transition went for you.
OK, if your team is still using TFVC, I hope I was able to convince you to make the switch to Git. And hopefully, in a couple of weeks, you and your team can enjoy the benefits of the most powerful source control system on the market.
Stay tuned and HabbediEhre!