Since Visual Studio and Team Foundation Server support Git as a source control system, there are a lot of conversions going on about when to choose what version control system. TFS is an Application Lifecycle Management platform and contained one version control system called Team Foundation Version Control (TFVC) in the past. Since version 2013, Git is fully integrated into TFS as well as Visual Studio.
When we talk about Git and TFS a lot of people are asking the same question: “Is this some kind of Git-like Microsoft-we-brought-our-own-solution implementation?” The answer is No! It is 100% Git, just in an Enterprise ALM platform.
As you probably know, the main difference between the two source control systems is:
If you think that Git enables you to work work offline, you are right. But so does TFS (at least since version 2012). The common assumption that “Offline” means “Distributed” is completely wrong. But of course you have you have a full offline experience with a distributed version control system. So what does distributed then mean? Simply said, you have a local copy of the repository including the whole history. Or in other words, you are always working with your local repository and all commits are always made to your local repository. So how am I going to collaborate with my colleagues? If you want to share your changes you can push them from your local repository to a shared repository or you pull changes from the shared repository to your local repository.
In contrast to the short explanation of the distributed version control system, what about TFVC. TFVC is a centralized version control system which means that the repository with its history is located on a centralized server. Locally you are working on a representation of a specific version. In TFVC there has been a change with version 2012 when local workspaces were introduced. Before you had the classic “Check-in Check-out” model which means that you check-out each file you are currently editing. After editing, you check-in the files with your commit. The server knows about each file being edited and can also control those actions on a per-file basis. With local workspaces the model was changed to “Edit Commit” which means that you get a specific version of your repository and do all changes locally without checking-out the files. You then work disconnected until you commit the changes back to the server.
So isn’t there any difference in the offline functionality between TFVC and Git? Yes, there is. With local workspaces you can work on you files without any connection to the server. But for committing changes or creating new branches you need to be connected to the server. With Git, you are always working against your local repository which means you commit the changes (and commit and commit…) locally as well as you create branches locally. Only if you push or pull changes or publish your branch to or from a shared repository you need to have a connection to the shared repository.
As I already have written in the basic section, there is no check-out or map local folder with Git. With Git, you simply clone a shared repository or you can also start with your local repository. As the word “clone” implies, you clone the whole repository with the whole history. As you can see in the graphic below, you push your changes to the shared repository. You can also pull changes from the shared repository to your local repository. In Git it is also possible to pull down changes but not merge them into your code, that’s called a fetch. It is also important to know that Git can have more than one shared repository, so it is not always a central server or central shared repository.
So how should we now use those repositories? From a TFS user perspective the centralized-style Git workflow seams to be the best matching. Each developer clones from a single shared repository and pushes and pulls the changes to and from it. Since Git does not allow you to push your changes if someone else has pushed since the the last time you fetched.
Since Git is very flexible, there are other workflows which truly make sense for there porpose. If you are for example contributor to a GitHub project, an Integraion Manager Workflow applies where only the integration manager is allowed to push changes to the blessed repository. The developers request the integrator to pull their changes from their public repository.
Branching and Merging is really easy and fast. Therefore it encourages developers to have multiple local branches that can be entirely independent of each other. The creation, merging, and deletion of branches only takes seconds.
As a TFVC user you are used to the fact that a branch will result in a new folder in your local repository. So switching from branch to branch means that you close your solution or files and open the solution or files from the other local branch location. Git uses repository wide branching and not path based branching. This means that you always stay in your repository folder and by switching the branches you change the file content to the representations of the current branch. Technically speaking, a branch is nothing more than a pointer to a specific commit and not a copy as it is in TFVC.
Some .NET developers may now be concerned. What happens if I switch the from one branch to another with the Visual Studio solution still opened? As written above the file location stays the same because the whole repository representation switches to the new branch. In case of our scenario, the content of the solution file, project files and source code files may change. So we definitely need to reload the solution after switching the branches. As you will notice, there isn’t even a prompt indicating that you have to reload the solution. Why? Visual Studio identifies the branch changes and automatically unloads and loads the solution and project files for you in the background without any notification. Great!
Another important difference to TFVC is that branches do not have to be published. With Git you can easily create branches with are not published to the shared repository and therefore nobody can see them. So if you would like to try something without worrying about sharing and merging, just create a new local branch without publishing it.
The Git data model ensures cryptographic integrity. Every file and commit is checksummed. Therefore it is not possible to change anything without changing the IDs.
With Git you can format and review the commit before actually committing it. Simply said, this more or less means that you do not have to commit all changes, you can define which changes should be included in the commit. This goes together with the VS functionality and TFVC where we have the possibility to exclude a change from the check-in.
We already pointed out several differences between TFVC and Git in the above sections. But to get the point, we will again have a look at it.
First of all we have to have a look at how the source code is actually stored. In centralized version control systems changes are revisions and changes will result in deltas. In Git all changes (commits) are snapshots that reflect how the repository looked at the time of the commit. To be efficient, every commit has a reference to its parent commit. Simply said, to represent the current state of the repository, Git goes through the commit chain and applies them.
As we have already pointed out, branches are very lightweight and nothing more than a pointer to a commit. This is why we are working in the same directory event when switching between branches in Git. In TFVC we have a “copy” which results in a dedicated path on the client side. When working with Git you will probably be confronted with the term “HEAD”. So, what is it? To be able to know on what you are currently working on (your current repository representation), Git stores the pointer to the latest commit of your current branch in HEAD. So when you switch between branches, HEAD will point to another commit.
Merging in terms of Git is also just another commit. This time, the commit has two parent commit references. If a merge conflict occurs, the conflicts are handled inside the merge commit. No changes are mode in the parent commits, the history is preserved.
This question is actually pretty hard to answer in a common way.It depends on many factors. So you have to make your decision by yourself, but we can help you with arguments.
The conclusion of the above points is to use: