Skip to content
Jan 12 / davidalpert

Working with TFS branches in git using git-tfs

I’m pretty excited about git. I’m also pretty excited about git-tfs. Let me show you one of the reasons why.

Let’s say that I have a TFS Team Project with a Source folder and three copies of my source tree named Dev, Main, and Release: image

Main was the first source folder. Dev was created by branching off Main, and Release was also created by branching off Main. That makes Dev one child of Main and Release a second child, setting up a merge path of making changes in the Dev branch, merging them into the Main branch, and then merging them into the Release branch.

Right clicking on any of the source branches in Visual Studio’s Source Control Explorer and selecting the View Hierarchy command

image

will show us a picture of these relationships:

One thing I love about working with git is how easy, cheap, and fast it is to make local branches, try several ideas, and mash them all together before checking back into the original repository. Another thing that git makes really easy is moving changesets back and forth between branches.

When I started using the git-tfs bridge I would have created three separate local git repos for this team project, one for each branch. The problem with this approach is that git doesn’t easily recognize the common code between these repos. I have found that while moving changesets between different git repositories with the same histories is fairly straightforward, moving changesets between git repositories or timelines that do not explicitly share a common ancestor commit (according to git) is more difficult.

Enter git-tfs clone –with-branches.

Cloning from TFS –with-branches

Working with the master branch of git-tfs from github you now have the ability to clone several TFS branches into local git branches.

prompt> git tfs clone --with-branches http://tfsserverurl:8080/tfs/ $/Project/Folder/ pathToLocalRepo

This will:

  • initialize a local git repo;
  • add a git-tfs remote called ‘default’ and a local branch called ‘master’ and pull down all the changesets on the branch you requested, adding them one by one to the new git repository.
    • each TFS changeset is pulled down one at a time into a local workspace tucked inside the repository’s .git folder.
  • the –with-branches flag will then query that ‘default’ TFS remote for a list of all related TFS branchs, list them, and then loop through them fetching all the divergent changesets on each branch.

This last step is pretty magical for a git junkie like me.

Look at the command-line output from running that clone –with-branches command against the team project with three branches that I described above:

Even more magical is to crack open gitk and take a look at what the local git repo looks like after running this git-tfs clone command.

prompt> gitk --all &

There are my TFS branches, all linked up in git to the proper parent changesets and everything!

Now I can import an entire TFS branched source tree into git and work locally, batching changes back into TFS when I’m ready to share my code and I can shuffle changes from one TFS branch to another using the full power of git’s toolset.

Caveats

As the code that manages TFS branches in git-tfs is still in it’s early stages, a couple of caveats apply.

  • The speed of this approach will be heavily limited by the connection to TFS so it may be significantly slower than a plain git clone.
  • The –with-branches flag will cause git-tfs to try and pull down all the changesets in a TFS branch hierarchy – if that is a large number of changesets this full clone may take a long time or consume a large amount of disk space.
  • The current code runs a ‘git gc’ command periodically to clean up and re-pack the git repo, but it may not surface this on the console output without the –d or –debug flag, appearing to hang instead – I plan to work on that.
  • The current logic tries to find the oldest changeset on a child branch and link it in git to it’s parent changeset on the parent branch.
    • long-running branches with large files (like the one I’m working on right now at the office) may be problematic; without identifying TFS merges and creating git merges to match (I’m looking into that) long-running TFS branches come into git as long-running git branches, not sharing nearly as much history as they do in reality. This appears cause git to slow down and consume a large amount of memory, probably because it has to process changes across much more history than should be required for a given operation.
    • the current code may pick up the wrong changeset when finding a root in the parent branch
      https://github.com/git-tfs/git-tfs/issues/284

Don’t let these issues scare you off, however – it’s awesome!

When all is said and done I’m absolutely thrilled to see TFS branches pulled down into a git repo and very excited to be helping the git-tfs project to evolve.

Grab the code, try out the emerging branching support, and let us know how it works for you.

  • Philippe

    Good article!
    I would like to insist on one point. You could do the clone and the initialization of all the branches in 2 steps :
    - the clone (without the parameter –with-branches)
    - use the init-branch command with the –all parameter : git tfs init-branch –all

    Good pick too for the problem with the git garbage collector! Perhaps we could disable it at the beginning and restore it after!