A place for spare thoughts


How to establish git central repository for working against TFS with git-tfs bridge

Filed under: git, git-tfs — Ivan Danilov @ 17:40

As I described here previously my goal was to establish central git repository and avoid redundant round-trips to TFS whenever possible.

To achieve this now you probably want to follow my advise in the above mentioned article about having user.name=<tfs account name without domain> and same user.email for everyone fetching changes from TFS.
In the latest git-tfs sources this was already fixed, so you don’t need to set this kind of things.

When I established central repository I faced some inconveniences: firstly pattern “checkout master, check central repository for changes already there but not in your local repository, pull new changes from TFS, push to central repository if anything new was pulled, checkout old working branch again, optionally rebase it onto master” was very boring and very repeatable. And the second inconvenience: this pattern should be repeated each time something new appears in TFS. I want to have some scheduled updating so I wouldn’t think about such things as when and how I should update latest version etc.

Well, it is perfect target for automation. So now I want to share my bash script that does these things and saves you several dozens of key-pressing each time you need get changes from TFS.


    # parameter 1 is last exit code
    # parameter 2 is error message that should be shown if error code is not 0
    if [ "${1}" -ne "0" ]; then
        cat '~temp.log'
        echo ${2}
        rm -f '~temp.log' &gt; /dev/null
        exit ${1}
    rm -f '~temp.log' &gt; /dev/null

echo "$(date) Update launched"

if [ ! -z "$(git status --porcelain)" ]; then
    echo "$(date) Your status is not clean, can't update"
    exit 1;

branch_name="$(git symbolic-ref HEAD 2&gt;/dev/null)"

if [ ! "$branch_name" == "master" ]; then
    git checkout master

echo "$(date) Pulling from central repo first to avoid redundant round-trips to TFS..."
git pull &gt; '~temp.log'
check_err $? "Pulling from central repo failed"

echo "$(date) Pulling from TFS..."
git tfs pull -d &gt; '~temp.log'
check_err $? "Pulling from TFS resulted in error";

remote_conflicting_commits="$(git rev-list origin/master ^master)"
if [ ! -z "$remote_conflicting_commits" ]; then
    echo "origin/master has conflicting commits, can't push"
    exit 1;

local_commits_to_push="$(git rev-list master ^origin/master)"
if [ -z "$local_commits_to_push" ]; then
    echo "$(date) Central repo is up-to-date, nothing to push"
    echo "$(date) Pushing updates to central repo"
    git push --tags origin master &gt; '~temp.log'
    check_err $? "Push to central resulted in error";

if [ ! "$branch_name" == "master" ]; then
	git checkout $branch_name
	if [ "$1" == "-r" ]; then
		echo "Rebasing $branch_name on master";
		git rebase master

This script assumes you want master branch to mirror TFS and rebases your working branch onto new changes only if you have specified -r switch. And for the second inconvenience you have just to set-up some dedicated working copy and run this script each, say, five minutes in it. It is very easy to do with built-in Windows Task Scheduler:

  1. Go to Start -> Administrative tools -> Task Scheduler
  2. Click Create Task on the right side
  3. Check radio button ‘Run whether user is logged on or not’
  4. Select Triggers tab, click ‘New’. Set Start=’One time’ and ‘Repeat task every’ whatever, check ‘Enabled’, click OK.
  5. Go to Actions tab, ‘New’. Set Program/script to ‘cmd‘, Add arguments=’/c "sh.exe update >> log.txt"‘ and in the Start in field put path to your working copy, dedicated to central repository updating.
    1. That’s all. Now you should have updates regularly without any efforts. It is important to note though, that script assumes your remote named origin/master and noone pushes conflicting changes to central repo’s master branch.

      And not forget to add /log.txt to .gitignore. Otherwise your updates will just fail to start because of not clean working tree.



  1. I tried this, and so far it works great, but there is one interesting difference. The tags seem to be missing.

    When I run gitk, I see that the bootstrapped check-ins are all tagged with

    After running this script a few times, I don’t see any more tags.

    I changed to the bare repo and noticed that the remotes/tfs/default tag didn’t advance — ie. it’s at the same point as when I bootstrapped.

    Comment by Jeramy — 10/08/2011 @ 21:59

  2. v0.12 has autotag feature. By default it is turned off. New tags are appearing only if it is turned on. Before 0.12 it was not an option, but hardcoded and cannot be turned off. So when you updated to 0.12 – git-tfs does not create more tags. If you want them back – execute `git config tfs.<remote-id>.autotag true`.

    tfs/default advances when you execute `git tfs fetch`, `git tfs pull`, `git tfs checkin`, `git tfs rcheckin` or `git tfs pull`. In short – every time when TFS is contacted for new checkins. If you haven’t set up periodical run of this script in central repo – it wouldn’t advance tfs/default, because git-tfs.exe doesn’t have any chance to run and see that it should be advanced.

    Comment by sparethought — 10/08/2011 @ 22:06

  3. Hi, sparethou, thanks for your great work. I have made some changes based on your scripts so tfs and git can be dual-way synchronized by using windows scheduled task and a work directory specific for this sync scripts (https://github.com/royx/git-tfs/tree/master/scripts). I think it could be useful for others, could you please review it when you are not busy?


    Comment by royx — 26/11/2011 @ 09:30

    • Hi Roy,

      It seems you’re much more versed in scripting that I am πŸ™‚
      I will definitely take a closer look at your scripts. Thank you for the contribution.

      Comment by sparethought — 26/11/2011 @ 13:19

    • I’ve took a quick look at your scripts. It’s a great idea to notify somebody about errors during automatic scheduled script execution via cscript, will definitely use it myself. Thanks!

      Next, about the rest of the script. I intentionally didn’t use automatic rebases because they can fail. Let me clarify how I use the script a bit. I don’t run this script manually. Either I don’t have it scheduled on my dev machine. I have 24/7 server that is executing it each 5 minutes against bare ‘origin’ repository. And we have devs on the other side that are not using git and checking in to TFS directly, so actual conflicts are not rare thing.
      Thus said, if the script faced some troubles – that means somebody should login to server, check what is going on by looking in ‘log.txt’ file, turn auto-update task off, fix things and turn task back on. And that fixing require good understanding of what’s going on and how things work internally. Others just won’t go there to not make things even worse. So if problem occurs at night or at weekend – most probably auto-update will stuck until I get there and fix it. In such circumstances it is not a good idea to make rebase automatic – each conflict will stop the process. So I decided that auto-update would – like git’s “fetch” command – just receive current state of the TFS because it can’t result in conflict. On the other hand we need to checkin to TFS from dev machines manually. But it is not a very big problem.

      It seems reasonable to have such synchronization if all the work is going on the same side with all devs working through git. It’s just not our case unfortunately.

      Comment by sparethought — 04/12/2011 @ 01:00

    • P.S. Just in case you’re curious. During last three months we have auto-update script stuck only once. Seems one of TFS API calls hang because of the network problem – after killing sh.exe process and re-running the task everything went smoothly again.

      Comment by sparethought — 04/12/2011 @ 01:04

    • See https://sparethought.wordpress.com/2012/08/23/my-environment-for-day-to-day-work-with-git-tfs/ I’ve tried to describe it in more details.

      Comment by Ivan Danilov — 23/08/2012 @ 16:25

  4. I think I’m missing something here. I can see how the Bash script works, and I can see how to set it up as a scheduled task, but what I can’t see is where sh.exe came from. Is it part of msysgit, or are you running git commands some other way (like cygwin)?

    Comment by Jeff C. — 17/08/2012 @ 21:03

    • Part of msysgit. I must confess – I don’t know how to install cygwin at all. If I need so much of a linux – I will probably just get VM with real linux πŸ™‚

      Comment by Ivan Danilov — 17/08/2012 @ 21:20

      • I was hoping that was the answer. That should make it pretty easy.

        Comment by Jeff C. — 17/08/2012 @ 22:40

  5. […] should be also pushed to Central‘s master branch. It is simplified version of the script from this post. Also there you can find how to create scheduler task to execute this update script every 5 minutes […]

    Pingback by My environment for day-to-day work with git-tfs « A place for spare thoughts — 23/08/2012 @ 16:23

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

Blog at WordPress.com.

%d bloggers like this: