Git workflows with git-tfs

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:

  • Clone
  • Push
  • Pull

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:

  1. You’re not communicating with TFS source control for nearly every operation
  2. Work perpetually offline, forever and ever
  3. No workspaces
  4. No local file locking (all that “files are readonly” stuff)
  5. Merges use Git’s algorithm and Git’s merge tools
  6. 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:

  1. The master branch represents commits in TFS
  2. All commits locally are done in local topic branches

Let’s dig in to how these rules apply in practice.

Git-tfs workflow

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.

Motivations

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:

image

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.

Summary

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.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in git, TFS. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Corey Kaylor

    It’s a great tool when migrating from TFS also.

    • Magnus

      Except it only created a master branch with all commits in. Is it possible to get the commits done in different branches in TFS so they end up in different Git branches?

  • http://gabemoothart.myopenid.com/ Gabe Moothart

    From your workflow description it sounds like git-tfs will “bundle” all of your local commits into one tfs commit, rather than replaying them in tfs the way that git-svn does. Is that correct?

    • Anonymous

      Yes, that’s right. I’m not familiar with git-svn, but I don’t want my commits replayed to TFS. Each commit is supposed to be deployable, and my local commits inside a topic branch certainly aren’t.

      • Dave

        What about in the other direction?  If there have been multiple (assumed deployable) commits to TFS from other developers since the last time you updated your local copy, wouldn’t it be better to run “git-tfs fetch && git rebase master tfs/default” than “git checkout master && git-tfs pull” ?

    • http://twitter.com/rogue_code Arild Fines

      git tfs rcheckin  (new in 0.12) will replay the git checkins.

  • http://twitter.com/ntcoding Nick

    Hey Jimmy – type this in and see what happens “git tfs ct”

    • Anonymous

      Yah, the checkin-tool alias, is that what you’re talking about?

      I always struggle whether or not to use aliases in blog posts…

      • http://twitter.com/ntcoding Nick

        Indeed. I wasn’t quite sure if you knew about it or not.  -1 for me

  • Jay Smith

    I have been using

  • Jay

    Oops, too quick on the post.  I have been using git-tfs at my work for local workflows. I love it but haven’t used some of the other check commands.  I will give them a try now. Thanks for the post.

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #942

  • http://twitter.com/keith_bloom Keith Bloom

    Thanks for a great post, it is always useful seeing someone share their workflow.

    I am curious to know what you do about the source control bindings that Visual Studio complains about whenever the solution is opened.

  • http://twitter.com/christensena Alan Christensen

    Thanks I’ve been using git tfs for a while but some aspects of your workflow are an improvement on what I’m currently doing

  • http://www.outsource-website-development.com outsourcing web developers

    this one is my favorite post. I think it will help me a lot in my further studies and research. Very well written I appreciate & must say good job.

  • http://jay.smith.myopenid.com/ Jay Smith

    Do take the TFS integration for the solution offline while developing or has that not been an issue?

    • Anonymous

      Yeah, I work permanently offline. Otherwise those annoying messages come up.
      When starting with this, do a clean clone with git-tfs, that way you can just ditch your old workspace altogether.

      • http://jay.smith.myopenid.com/ Jay Smith

        I am moving over project by project, and still need online for some.  I found a Visual Studio Extension that allows me to take the solution offline.  Made it much easier to deal with in a mixed approach. 

        I hope to have them all done soon and then I can go permanently offline.

  • http://www.facebook.com/profile.php?id=64700429 Anthony Mastrean

    How do you handle TFS branches? (like I asked in this SO question http://stackoverflow.com/questions/5129959)

    • Anonymous

      Man, I’ll let you know when I get there. Hopefully soon. I’m going to try multiple git-tfs remotes, we’ll see.

  • http://twitter.com/TesterGiles Giles Continuous

    I’m happy to see that git-tfs has matured to the point of being usable and stable. Going to give it another go!

  • Mario Pareja

    Do you check .gitignore into TFS? If not, how do you deal with this?

    • Anonymous

      Yep, I do. If I never use TFS source control directly, I never have to worry about TFS’s understanding of ignored files.

    • MD Unicorn

      I also ignore the “.gitignore” file itself, so I don’t need to commit it into source control and it also doesn’t pollute the commit window.
      I really don’t need to commit “.gitingore” because I’m the only one in my company who is using git-tfs, and also setting up the “.gitignore” file is quite easy.

  • Jay

    When I tried to follow your flow on the get changes from my branch into master checkout TopicBranch, then rebase master didn’t get my branch changes, does there need to be a merge TopicBranch in there somewhere?

    • Anonymous

      No, I don’t think so, I rebase topic branch on top of master, and it should bring the topic branch up to date against master.

  • Pingback: Searching for a git Workflow « Mark Alan Evans

  • http://twitter.com/rstackhouse Robert Stackhouse

    Nice post. I too have been using a DVCS for years, and have recently had to work with TFS. Only recently started working with git though. Had to google “git rebase”. Had seen it before, but hadn’t really investigated it. The Rebasing section in the chapter on Intermdiate Usage on book.git-scm.com does a good job explaining it. 

    Is there a particular reason you’re using rebase instead of merge? Fewer headaches?

    Hadn’t yet heard of gated check-ins for TFS either. I’m not the one administering TFS either though.

    Good stuff. Thanks for posting.

    • Anonymous

      I like rebase for topic branches as it keeps a cleaner history and is a bit easier to manage changes (personally anyway).

      • Anonymous

        Is that essentially like doing a merge with the –no-ff trigger enabled? 

        • Anonymous

          Yep, I believe so.

        • SD

          No it’s not. It’s quite contrary. –no-ff flag suspresses possibility of fast-forward, which essentialy just moves your current HEAD to merged branch. Effect of fast forward is linear history without additional merge commit, so it’s quite similar to rebase, while merge –no-ff will always result in non-linear history with added merge commit.

  • Diego

    Why do I end up with the same commit message appearing twice in my git log? Is this just a side-effect of working with git-tfs?

    • Anonymous

      Yeah, git-tfs tries to keep git commits separate from TFS commits. It makes a TFS commit, then pulls the TFS commit down as a merge. Those git commits you do locally are just that – local. git-tfs makes sure that it has git commits for all upstream TFS commits.

      • Diego

        Thanks for the reply. It was just something that stuck out since I’d not noticed something similar the times I’ve used git-svn.

  • http://twitter.com/rstackhouse Robert Stackhouse

    I used git config –add –global alias.tfsin “tfs checkintool –build-default-commen
    t” to map the checkintool command to “git tfsin” because the other was just way too much typing.

    • Anonymous

      Nice. Added.

      • http://twitter.com/rstackhouse Robert Stackhouse

        I have been informed that “tfs rcheckin” will achieve the same functionality as “tfs checkintool –build-default-comment” without the pop-up box.

        • Anonymous

          Ha, interesting! That would definitely work!

          We always have to associate w/ work items, which is a lot easier with the dialog box. Thanks for the tip!

  • Anonymous

    Great article, thanks for the info.

  • http://www.facebook.com/geoff.niehaus Geoff Niehaus

    Change “git tfs checkintool –build-default-comment” to “git tfs checkintool -w 9999 –build-default-comment” where 9999 is your workitem task to associate to the checkin.  Saves you an extra step if associating a workitem to a checkin is part of your workflow. 

  • Alper

    Regarding your comment about git tfs keeping git commits separate than TFS commits, it is not possible to have clean linear git history then. Is that correct? Following your workflow did not result in a linear git history for me. (Git-tfs-0.12.1)

  • Pingback: Hey TFS, git out of my way | Headspring

  • http://www.tatvasoft.co.uk/microsoft-dot-net-development.php Charles Shinn

    Git-Tfs permitted me to decrease my activities with TFS to once in a complete celestial satellite. It absolutely eliminated much of the scrubbing as branching and merging is all done using Git.

  • Leo

    Thanks for the article.  Any idea why we get error “no parent found” when  we git tfs checkin?  We are able to pull down from our 2010 TFS but not push to it. Thank you.

    • Anonymous

      Hmmm not sure, is there any other information you get? You might try creating a github issue for this project just to let them know about what you’re seeing.

    • Anonymous

       It may be because you are using the –build-default-comment switch, which was removed recently. It has become the default behavior and is no longer needed as a switch.

      • http://twitter.com/edeevans Ed Evans

        Yes, that fixed it for me.
        Thanks!

  • Anonymous

    Hi,

    We have switched the options for CheckinTool to build a commit message by default, and the option has been removed. Could you please update your post to reflect this?

    Thanks!

  • Pingback: Git alias to delete all local branches | Pedro Reys

  • http://twitter.com/iLemming Ag

    is there any way to diff after “git tfs fetch”? how do you do that, git doesn’t see tfs branches as remotes. And if there’s no way, what’s the use of “git tfs fetch”?

    • MD Unicorn

      I’m using Git Extenstions, and it shows the tfs branches as remotes.

  • Karrtik Iyer

    Hi,
    How can I achieve undo pending changes of a particular file using git-tf commands?

    • jbogard

      You don’t need to use a particular git-tf command, just the normal git command will work (git checkout path_to_file)

      • Karrtik Iyer

        Thanks, this worked, however this does not fetch the latest version of that file from the TFS repository, if I undo a change in a file I expect its latest version to be downloaded from the TFS repository, how do I achieve this?

        • jbogard

          Right, a checkout just resets things back to where they were before, it doesn’t then pull in latest changes. A reset doesn’t reset, and THEN go update the file to latest. That’s two separate operations (even in TFS, it’s a two-step process, Revert/Undo Pending Changes and Get Latest).

          So “git checkout” to reset the file to BEFORE you made changes, then “git tf pull” to pull in latest changes.

          • Karrtik Iyer

            Ok, i can combine both, what is the git command to undo all the commits done on a file, my scenario is :
            1) I modify a file once to add string ABC
            2) commit the above file using (git commit) but haven’t used any git-tf command to check in the file.
            3) I modify the same file again to add string DEF
            4) now I do git hard reset it undoes the changes in step 3,however it still contains ABC string done in step 1.
            How can I undo the change ABC without having to remember any commit id’s?
            Hope my question is clear.
            Thanks a lot for your help so far.

          • jbogard

            Ah, so you’ve committed then!

            So, no, you don’t have to remember the commit hash. You’re wanting to reset your local master to where it was before. The easiest way to do so is “git reset –hard HEAD~1″, which basically means, reset my current HEAD (master) to HEAD-1, which is back 1 commit (HEAD~2 for back 2 commits, etc).

            Just so I make sure I don’t screw anything up, I like to keep SourceTree open to visually see what’s going on. In a distributed version control, especially ones like Hg and Git, where commits are essentially linked lists, having a visual representation of the current state of my local repository really helps.

            SourceTree is a free tool for git/Hg repositories: http://www.sourcetreeapp.com/

          • Karrtik Iyer

            Thanks a bunch, I will try this and get back to you.

  • Pingback: Getting started with Git for the TFS dev (and quick reference) | Xanthene.com

  • Pingback: Porównanie GIT-TF Workflow i GIT-TFS Workflow - Tymoteusz Kęstowicz

  • Pingback: Porównanie GIT-TF Workflow i GIT-TFS Workflow - Tymoteusz Kęstowicz

  • Ivan Saldarriaga

    Hello Jimmy, we’re going to start testing the workflow in TFS that way. It sounds pretty convenient.

    now, we’re concerned about how to keep different code bases into a single TFS project, having independent workflows for each one.

    Everyone recommends to have a single big project into the project collection, with multiple teams inside, differentiated by the area path. That works great at the project planning level, by giving independence at the release calendar, backlog,etc, but can we make it work independently at the repository level? can we have on TFS multiple root folders each one pointing to a different software of different project teams? what do you recommend? thanks very much.

    • jbogard

      This is one of my biggest pet peeves of TFS. Business projects != source code repositories. The idea that these are one and the same is frankly ridiculous.

      Can you create a blank TFS project just for planning purposes and keep source control/deployment/versioning concerns completely separate? That would be my recommendation, but I’m (thankfully) not a TFS expert.

  • http://www.davintryon.com Davin Tryon

    Thanks for the article, I’ve had it open for the past couple of weeks!

    BTW, you might want to add a: “git tfs pull” before the “git branch -d TopicBranch”.

    Otherwise, the two branches are not in sync and I get:
    “error: The branch ‘TopicBranch’ is not fully merged.”