I’ve been working with git-tfs on a project for about a couple of months. Git-tfs is a source control bridge that allows you to work with Git locally, reducing the number of operations to communicate with the TFS source control server to exactly three operations:
Pretty much the same three operations that require connections with normal DVCS work. Having worked with DVCS such as Git and Mercurial for a few years, it’s nice to keep the DVCS paradigm when interacting with a centralized VCS like TFS.
Why use the git-tfs bridge? A few good reasons:
- You’re not communicating with TFS source control for nearly every operation
- Work perpetually offline, forever and ever
- No workspaces
- No local file locking (all that “files are readonly” stuff)
- Merges use Git’s algorithm and Git’s merge tools
- You still see all of the history in TFS
You don’t, however, get remote branches very easily. You can do multiple remotes, but it’s not something I’ve tried out (yet). That being said, I’ve found a couple of workflows adapted to my normal Git workflow work really well when interacting with git-tfs.
Assuming you’ve already cloned, I basically follow two rules:
- The master branch represents commits in TFS
- All commits locally are done in local topic branches
Let’s dig in to how these rules apply in practice.
Since master represents TFS commits (and should represent deployable code), the first thing we do locally on a clean repository is:
git checkout -b TopicBranch
We first create a local branch representing the topic of what our work is going to be. From there, work proceeds as normal:
/* work work work */ git add -A git commit -m "My first fun commit" /* work work work */ git add -A git commit -m "Still going" /* work work work */ git add -A git commit -m "OK now I'm done"
At this point, we’re done with what we’re done locally, and it’s time to integrate our changes. First, we want to make sure we have all upstream commits pulled down before we merge:
git checkout master git tfs pull
The bolded commands are the git-tfs commands. Our master branch is updated with the latest upstream commits from TFS. If any commits came down the pipe, we’ll want to make sure our branch is updated to be based on the latest upstream commits. To do that, we’ll perform a straightforward rebase:
git checkout TopicBranch git rebase master
Our local topic branch is now based against the latest upstream commit. We can now build locally and push our changes up to Team System. I like to use the standard TFS check-in tool to do so, so I use the git-tfs command:
git tfs checkintool --build-default-comment
That last command line flag instructs git-tfs to build a comment based on the comments. The normal TFS check-in dialog pops up, and you can associate work items, add comments and so on. Once you check in, git-tfs pushes the commit up.
If the commit succeeds, git-tfs will pull the commit back down to master, performing a merge commit between your local topic branch and master. Once that’s done, you only need to clean up the local git branches and you’re done:
git checkout master git branch -d TopicBranch
You’re now back to where you started, and can build more software starting the workflow from the beginning.
Why always work in a topic branch and not off master? I’m a big believer in the piece of Continuous Delivery that states that commits in master should always always always not just be potentially deployable commits but actually deployed commits. A clean master branch ensures this is the case.
Working off of master also introduces weird parallel lines of work that make it difficult to understand what the heck is going on:
Not a fan.
Working with gated check-ins
Later versions of TFS have a feature that checkins can be gated so that a build must succeed before the check-in actually gets committed. This functions by a check-in only pushing up a shelveset, and the shelveset then gets committed automatically once the Team Build build succeeds.
If you’re working with gated check-ins, your workflow is slightly different since TFS is now the entity performing the check-in, not you locally. Locally, when you run the checkintool command, you’ll get a “checkin cancelled” message, because your check-in was in essence, delayed.
Our check-in workflow now becomes:
git tfs checkintool --build-default-comment /* Click the "Build Changes" button that pops up */ /* Wait for build to succeed, and the "build success" message to pop up*/ git checkout master git tfs pull git branch -D TopicBranch
The main difference is that git-tfs can’t perform the merge commit of our topic branch locally since git-tfs wasn’t the tool performing the actual commit. We have a couple of choices at the end:
- Make a merge commit manually (weird)
- Let our local topic branch stick around (OK, but also weird to have dangling branches)
- Delete the un-merged topic branch with the –D switch
I prefer the last option, simply because I don’t much care about the local commits, since they don’t show up anywhere upstream. If I had Git remotely, I would do differently.
NOTE: you will need the latest drop of git-tfs to allow this to work. A recent bug was fixed that allowed the other TFS build dialogs to pop up correctly.
If you’re using TFS and know Git, you should stop using TFS directly and use the git-tfs bridge. It works, it’s reliable, and it takes away nearly all of the inefficiencies of working with a centralized VCS like TFS. With this workflow, you’ll keep a sane view of what upstream TFS looks like, and easily manage and isolate local work.