Jan 27, 2010

How to get Git and TFS Working Together

Update: There's now a second approach for doing this that you may want to look at

TFS has a lot going for it and from a team perspective it’s a fantastic tool and something I wouldn’t pass up willingly.  That said it is a little lacking in some areas and one of those is the source control story.  For some time now I’ve been toying with writing a Git-TFS bridge but just haven’t had the time to really get stuck into it.  For that reason I decided to see if I can get Git talking to TFS via the TFS SvnBridge utility.  I know what you’re thinking and yes, it’s a 3-headed monster if ever I’ve seen one, but I’m a brave (or foolhardy) adventurer and I don’t mind the occasional challenge.

As a note I’m not going to go into a lot of detail on some of the Git commands.  This is more about how to get Git and TFS working together, not how to use Git.

Getting started

Download the SvnBridge client from http://svnbridge.codeplex.com/.  Extract the SvnBridge.exe from the zip file and run it.  It will appear in your system tray and will show you the base URL to use for your SVN commands when you mouse over it.

image

To make sure it’s all working try browsing the TFS server via TortoiseSVN (repo-browser). I found this to be a little slow, but it worked fine.  The URL I used in SVN was something like this http://localhost:8080/tfs.server.name/projectName/Trunk

Assuming that’s OK you can then create an empty folder, open a command prompt and set the new folder as your current directory. Now you can clone (i.e. copy) the TFS repository into Git using the command below (as a note I use msysgit for my git work and have the git bin folder in my path).

git svn clone http://localhost:8080/tfs.server.name/projectName/Trunk/SubFolder SubFolder

This admittedly did take quite a while to do.  It was a large source tree and the TFS server is in a remote location but even so, it’s a lot slower than just doing a full get latest from TFS.  Also, be aware that cloning a repository is making a copy of the whole repository, including history. If you just want the latest revisions and you’re not worried about history (it’s in TFS after all) then I’d recommend you use the –r option and supply a recent changeset number (or range).

I also noticed that failing to put the target folder name at the end (even when it’s the same) can result in errors like this so make sure you include it.

Invalid filesystem path syntax: REPORT request failed on '/tfs.server/!svn/vcc/default': Target path does not exist at c:\Program Files (x86)\Git/libexec/git-core/git-svn line 4567

Now go get a coffee and come back.  Once the download completes you should be able to browse your source folder and see everything is there.

We’re all set now, right?

OK, so now we have all the files from TFS locally, and because we used the SvnBridge we don’t have the annoying TFS read-only flag set on our files.  Nice.  We can start working on our project again, right?

Well not quite.  Visual Studio has this nasty “feature” where it puts source control binding information into the solution and project files.  It’s a major pain in the butt and is something you’ll have to work around if you’re in a team environment where not everyone is using this Git/Svn/TFS combo.  We’ll need to manually remove all that binding information from the solution, however we have to think of our team members.  We can’t just edit the solution file directly as other TFS users will/may be relying on it.

Also, before we make this change we’re going to do one other thing.  We’re going to create a local branch and use that as our working folder.

You can either use the Git Gui (see the branch menu) or you can do it via the command line using the git branch or git checkout –b NewBranch commands.  I’ll leave it up to you to choose the method you prefer.

So now we should have both a master and a local branch. Before we proceed just confirm that you are in the correct branch by doing a git branch command (no arguments) at the command line – the current branch should be highlighted.  If you’re using the GUI then the branch name is shown near the top.

Now, to remove the bindings open the solution file in notepad++ (or the text editor of your choice) and find the section that looks like this and simply remove it.

GlobalSection(TeamFoundationVersionControl) = preSolution
    SccNumberOfProjects = 15
    SccEnterpriseProvider = {4CA534B2-18FA-4F9D-95D4-32DDF27D184C}
    SccTeamFoundationServer = http://tfs.server:8080/
    SccLocalPath0 = .
EndGlobalSection

Save the file and then open it with Visual Studio.  When you do, you’ll be told that the source control provider can’t be installed and then presented with a choice.

image

Since you’re in your local branch you can take either option.  I prefer to choose to work uncontrolled so that visual studio doesn’t remove all the source bindings from the project files (which would break things for others on the team) and it means there’s one less thing to worry about when pushing changes back to TFS.  Regardless, once you make a choice the solution will open as expected and you can make the changes you need to.

P.S. If you haven’t already done so, you may want to create a .gitignore file to ignore those pesky artefacts that get created during development.  There’s an example of one here - http://gist.github.com/233903.  Just put it in the root of your source tree (the folder with the .git subfolder).  You may also want to commit it to your repository so you can track any changes.

Now we’re cooking! Let’s update TFS!

OK so now we’re all set.  Sweet! Go ahead and do your normal development work.  It’s OK.  You’re allowed to :-)

When you’ve made changes you can commit them to Git using either the command line or the Git Gui (which I tend to use) and do any of the other normal things you would do when using Git.

When you’re ready we can now push your changes up to the TFS server.  First we’ll want to push our local branch changes to the master branch (remember to be aware of changes in the solution file).  You may also want to do a git rebase (example) on your local branch to consolidate all your small changes into a single commit before merging to master in preparation for pushing to TFS.

The workflow is basically: git rebase (optional) –> git checkout master  –> git merge –> push to TFS

And the command to commit to TFS is simply

git svn dcommit

Git will push all your local changes to the server and if everything is OK you should be able to switch to team explorer and see the changes in there.  Nice :-)

How do I do a “Get Latest”?

I almost forgot, you will no doubt want to do a “get latest” at some point.  From the command prompt simply do this:

git svn fetch

And it will get the latest changes for you and update your local git repository.  Note that git won’t just get the latest change, it will actually get all the individual changesets from TFS so that the local Git history is up to date.  This is slightly different from TFS where the local workspace is just being brought up to the current changeset.

Pros and Cons?

So, what are the benefits and where do things fall over?

Well, obviously the biggest benefit is that you are now able to use git and work in a truly offline manner from TFS.  You can branch locally and make changes in isolation of others without messing things up for anyone other than yourself, and the speed when working locally is blindingly fast (no over the wire communication with TFS)

On the downside SvnBridge is quite slow and the process to get code in and out of TFS involves more steps than clicking a few buttons in Visual Studio. 

Also, with Git holding all the history, you’ll chew up quite a bit more disk space locally.  I know disk is cheap but in many organisations developers are forced to work with puny boxes and miniscule amounts of space so it may be an issue.

Caveat Emptor:  I haven’t done a lot of work this way as yet and there are many things I haven’t come across as yet (check in policies blocking a commit for instance) so your mileage may vary. You have been warned :-)

If you do try this, I’d be keen to hear how you get along with it.  Good luck!