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:
Install git-tfs: https://github.com/git-tfs/git-tfs
example:
git tfs clone http://tfs-001:8080/tfs/projectroot "$/Reference Architecture/ReferenceArchitecture" RefArch
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/
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
Verify the history in SVN matches the history in TFS.
Here's how we did it:
Step 1 - Install the tools
Install msysgit: http://code.google.com/p/msysgit/Install git-tfs: https://github.com/git-tfs/git-tfs
Step 2 - Pull TFS history to local git repository
git tfs clone <tfs-project-url> "$/<tfs-project-name>/<tfs-project-folder>" <localfolder>
example:
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> masterexample: 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 dcommitVerify the history in SVN matches the history in TFS.
When I finally got around to reading the git book, I realized I would never go back to CVS/SVN.
ReplyDeleteThanks for this, nice and easy where tfs2svn wasn't working so well.
ReplyDeleteOne 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.
Thanks for sharing this, David! It saved us a lot of time.
ReplyDeleteThe 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...
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.
DeleteHi, David. Thanks for the great article. I'm trying to migrate a TFS project right now but I always fail at step 4 - rebasing.
ReplyDeleteWhen 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?
A caveat: I'm using Git-TF instead of git-tfs since the latter doesn't support TFS 2012.
DeleteI'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.
DeleteDavid, 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:
Delete1. 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.
I know this is old in internet years, but I'm hoping that you could provide some insight on my issue.
ReplyDeleteI'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:
.rev_map.a28c02ae-bed3-4c3b-bddb-c79b100fc565
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:
019e4579861124cbe5e00734380d95c6a0b6567d
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
Found the fix... it's actually the initial svn checkout id that is required. Works now.
DeleteWhere did you locate the initial svn checkout ID? Nothing is shown for me.
DeleteGot mine working, for future googlers:
Deletegit rebase --onto origin/trunk master
Was what I needed, just putting trunk resulted in "Needed a single revision".
That doesn't work for me. I keep getting the error "Needed a single revision".
DeleteAnd 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...
Fixed. You really need that trailing slash on that git svn init! Seems to work again now and notes were amended.
DeleteThis comment has been removed by the author.
ReplyDeletewhat should be value of Target-branch attribute and how we can get the value of the ?
ReplyDeletewhat 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?
ReplyDelete