If that’s the case I think you’re looking at it from the wrong angle, so to say. It really a matter of centralized vs. distributed version control.
Here’s how Microsoft actually explains it when you choose version control while creating a new project in Team Foundation Service:
Team Foundation Version Control (TFVC) uses a single, centralized server repository to track and version files. Local changes are always checked in to the central server where other developers can get the latest changes.
Git is a Distributed Version Control System (DVCS) that uses a local repository to track and version files. Changes are shared with other developers by pushing and pulling changes through a remote, shared repository.
The S in TFS does not stand for Source Control, it stands for server, that’s very important to understand.
Git on the other hand does not have a server concept, it has (optionally) one or many shared repositories.
One might actually wonder why Microsoft has made such an effort, believe me it is, to incorporate Git into TFS when they already have a great version control system in place (TF Version Control or TFVC for short)?
I think the answer is actually pretty simple (if we put aside the need for MS to stay up-to-date):
TFS is not a version control system, it is an ALM (application lifecycle management) product that uses a version control component. Git is nowadays a very popular such a version control component so why shouldn’t we be allowed to use it in TFS?
With that introduction I’ll get on to business which is the following.
What this post will cover (its purpose):
- Introduction to Git from the perspective of a TFVC developer. Basic concepts in Git, the things you should know about to put it simple.
- My point is not to show that Git is superior to TFVC, it’s just different and I hope to highlight the big differences.
- Understanding branching, merging and HEAD in Git and why they are such important concepts.
Offline != Distributed
A distributed VCS is much more than offline support. I would actually go so far as to say that a DVCS does not offer online support…think about that for moment.
In a DVCS, like Git, all users have a ‘complete copy’ of the entire repository. It also means that you have ‘full control’ of your own repository.
This might seem a bit confusing but it will become clearer once you start working with branches, promise.
In TFVC if you are offline and try to create a new branch you will get prompted with this error:
That’s because creating a branch is server operation in TFVC. You will also need permission (on the server) to create branches.
When you’re offline using TFVC you can only add/delete/update files and so on.
If you try the same thing using Git it will not be an issue. All commits are always made to your local repository. Let me say that again, all commits are always made to your local repository. And…you have full control of your local repository.
That is probably what’s misleading to some people.
Again you could say that Git is never online and always offline…well kind of anyway?!
Push and Pull?
But wait a minute…if everything is local/offline how can we collaborate on a big project with many developers that need to share code.
Well, all commits are made to the local repository. When you are ready to publish your work (share it with your team) you’d issue a push to a shared repository.
That means that you will push your changes (commits) from your local branch to the shared version of that branch (in a 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’s worth noticing that Git can have more than one shared repository, thus it’s not a central server we’re talking about here. If you do have a shared repository that no one works in (like a shared network folder or something) that repository is said to be bare.
Since everyone will have a complete copy of the repository chances are pretty high that someone will have a ‘backup’ if something happens. (it’s distributed risk so to say)
Snapshots vs. Revisions
In Git all changes (commits) instead result in a snapshot that reflects how the repository looked at the time of the commit.
Let’s see an example:
If we look at the ‘feature 001 done’ commit/snapshot above this is what we see:
- The actual changes which in this example would only be an updated program.cs file.
- All other files where unchanged thus there will be a pointer to a parent commit (in this case the ‘Intial Load’ commit which added all those files). This is for efficiency.
So in order for Git to show us the complete state of the repository it only has to walk the trail and apply each commit from the bottom up, so to say.
In Git a branch is nothing more than a pointer to a commit (or a snapshot). Coming from a VCS that uses branches as copies of the parent that it originated from this can be a bit confusing.
It can also be a bit confusing when you look at the Git history….you never really see any clear signs of branches. They are just so natural in Git.
My favourite way of describing the difference between the TFS and Git branching models is as follows, in TFS – branching is essentially like the TV Show Fringe in that you have to deal with parallel worlds crossing between those world is a difficult process with severe health risks to the person “crossing over”, whereas with Git is more like Doctor Who: branching is essentially dealing with time streams where you can easily go forwards, backwards and even sideways.
In Git, when you create a branch all you really will do is create a pointer to a commit. Nothing is moved, copied, nothing.
Compare the image above (TFVC on the right and Git on the left) after working a while with some branches. TFVC gets quite littered with copies of your source control and you have to introduce some maintenance task that cleans up the branches. (remember, they reside on the server)
Git only has one copy of the source code though and branches are usually only handled locally, you normally never push your local branches anywhere for sharing.
In a TFVC project with many branches you will also have to remember to open the file/s you want to edit from inside the correct folder (can be a bit tricky to remember). In Git, that will never be an issue.
A branch starts by referencing the newest commit, which is usually the latest commit from the source branch, and when you continue to create a new commit in a branch, the branch reference (pointer) is updated to reflect that commit instead.
HEAD is just a reference to the latest commit in the branch you’re currently working on.
Think of it like a DVD (or something) where you can choose different chapters and move back and forward as you please.
So when you switch (checkout in Git-terms) branch all you do is change chapter in the DVD menu (…)
In Git a merge is…wait for it…a commit. But a special type of commit that has multiple parents. Git will, by itself, decide the best way to perform a merge which also makes it a lot easier. (sometimes this just means a fast-forward merge)
Let’s look at an example:
The merge commit (tagged with v1.0.1) has two parents; the hotfix 1.0.1 and one of the previous merge commits (tagged with v1.0.0).
All merge conflicts are handled inside the merge commit as you can see from the merge commit above which caused a conflict.
No changes are made in the parent commits, history is preserved. (compare this to other VC systems where a merge is basically a squash of two branches…and thus scary)
But Git is a command-line tool, right?
Well, it used to be. And sometimes you have to resort to using the command-line but that’s even true with TFVC sometimes.
With that said we nowadays have support in Visual Studio for Git. It’s not as complete as the tools for TFVC…yet. However there’s another tool that I want to shed some light at, SourceTree (from Atlassian):
But Git has no security, right?
Well, I’m no expert in this area but if you have access to the Git repository (you have cloned it) you have full control over your local version.
However it’s pretty easy to setup separate read and write permissions, and Git supports more than one protocol.
Numerous services exist for that very purpose, like (most of them offer a bit more than just permissions though):
|Check-In||Commit + Push|
|Get Latest Version||Pull|
|‘Map Local Path’||Clone|
|Shelve||Stash (only local though)|
|‘Compare Local to Server’||Fetch|
The biggest differences are (from my point of view):
- The miniature file system (snapshot concept) that Git has.
- You have full control over your own repository. Sync to others once you are ready to.
- Branches do not create anything else than a pointer to a commit.
- All commits are local operations.
- Merges are non-destructive.
Lastly, my experience is that Git works better for ‘continues delivery type of development’. It’s just easier to develop things in isolation and integrate often and in smaller portions (feature branching).
TFVC works better when you work with sprints, a ‘one branch per sprint strategy’ that lives over a longer period of time.