TFS to SVN Conversion with History

Recently, the team I was on wanted to move our source code from Team Foundation Server to SVN for reasons that had almost nothing to do with source control directly. This would be a pretty simple operation, but we didn't want to lose access to the history when the operations team turned off the machine running TFS. We couldn't find any direct paths for moving history from TFS to SVN, so instead we chose to use git as an intermediary.

Here's how we did it:

Step 1 - Install the tools

Install msysgit:
Install git-tfs:

Step 2 - Pull TFS history to local git repository

git tfs clone <tfs-project-url> "$/<tfs-project-name>/<tfs-project-folder>" <localfolder>
git tfs clone http://tfs-001:8080/tfs/projectroot "$/Reference Architecture/ReferenceArchitecture" RefArch

Step 3 - Initialize git-svn

* User Lower Case - Caps will make it fail! *
Change directory into newly created folder
git svn init -s https://<svnserver>/svn/<project>/
git svn fetch
example: git svn init -s https://svnserver/svn/svnrefarch/

Step 4 - Copy TFS history to SVN

git rebase --onto <target-branch> <initial-commit-hash-id> master
example: git rebase --onto trunk aa3f2e9 master

There are likely to be conflicts during the rebase operation. For each conflict, add the conflicted files and continue.

git add .
git rebase --continue

Step 5 - Commit to SVN

git svn dcommit
Verify the history in SVN matches the history in TFS.

Conclusion: git is awesome

As I said, we had reasons we needed to put our source in SVN, but baring those, I would recommend that you just stop at step 2, get a private github account and push your source there. I have been using git exclusively since the February and have found it to be a great tool.


  1. When I finally got around to reading the git book, I realized I would never go back to CVS/SVN.

  2. Thanks for this, nice and easy where tfs2svn wasn't working so well.

    One thing to add, if you're like me you use existing private keys and putty. Set the GIT_SSH environment var to plink and then set up the host in putty.

  3. Thanks for sharing this, David! It saved us a lot of time.

    The only downside of this method is the lack of user mapping. When we committed from Git to SVN repo, all revisions arrived as authored by the user who did the migration. Fortunately, the number of revisions was small and all were fresh in developers' memory. Otherwise, it could be a showstopper...

    1. That's a good point. We weren't concerned about that because we pair programmed most of the time and had shared code ownership anyway, but that could be a big problem for a lot of teams.

  4. Hi, David. Thanks for the great article. I'm trying to migrate a TFS project right now but I always fail at step 4 - rebasing.

    When I run the command, it only says it's rewinding the the head but then nothing happens and all my local data gets wiped out.

    I haven't used git much before so it's confusing to me. I don't get what I am missing. Any ideas why the rebase fails?

    1. A caveat: I'm using Git-TF instead of git-tfs since the latter doesn't support TFS 2012.

    2. I'm sorry to hear that you're having trouble with TFS 2012. I won't be of my help, unfortunately. After doing this migration, I haven't looked at TFS since.

    3. David, just wanted to let you know that I've managed to solve the problem. There were two things I did wrong in the first place:
      1. I should've run clone with --deep. Otherwise only the latest changeset is cloned. This is something to keep in mind if you use Git-TF.
      2. Apparently, I didn't use the correct "initial-commit-hash-id". When I put the hash of the last rev that I got from the fetch output, the rebase started working for me.

  5. I know this is old in internet years, but I'm hoping that you could provide some insight on my issue.

    I'm getting the following error:

    C:\path\new\folder>git rebase --onto trunk a28c02ae master
    fatal: Needed a single revision
    invalid upstream a28c02ae

    In C:\path\new\folder\.git\svn\refs\remotes\trunk, I have the following file:

    I tried the entire hash as well, with the same result.

    I was thinking I needed a git hash instead, so I did git log which gave me this hash:

    I tried its short form (git log -pretty=format:"%h") and got this:

    C:\path\new\folder>git rebase --onto trunk 019e457 master
    fatal: Needed a single revision
    Does not point to a valid commit: trunk

    1. Found the fix... it's actually the initial svn checkout id that is required. Works now.

    2. Where did you locate the initial svn checkout ID? Nothing is shown for me.

    3. Got mine working, for future googlers:

      git rebase --onto origin/trunk master

      Was what I needed, just putting trunk resulted in "Needed a single revision".

    4. That doesn't work for me. I keep getting the error "Needed a single revision".

      And now, I can't get at the initial svn checkout id anymore. I was using this: git show-ref trunk.

      Seems my notes are messed up...

    5. Fixed. You really need that trailing slash on that git svn init! Seems to work again now and notes were amended.

  6. This comment has been removed by the author.

  7. what should be value of Target-branch attribute and how we can get the value of the ?

  8. what is "initial-commit-hash-id" in "git rebase --onto target-branch initial-commit-hash-id master" command and how can we get the value of this?


Post a Comment

Popular posts from this blog

Architecture at different levels of abstraction