Prep Code to Migrate from TFVC to Github

My development group has traditionally used TFS version control (TFVC) for our source control management.  It hasn’t always been a great experience (especially when we had long lived branches outside of the MAIN branch) but we have been using it successfully for 6+ years.

I recently needed to move a couple of solutions from TFVC to Github and decided to automate the code preparation to make it an easier and more consistent process with PowerShell. 

Pull from TFS

First we use the TFS REST API to pull the source code from TFS after asking the user for their credentials.  The source is downloaded as a compressed zip file.  We pull a fresh copy of the source from TFS so we don’t accidentally grab extra local files during the migration.  This also allows us to compare our old application folder against this new Github-ready folder before we commit to compare the differences.

Function PrepareSourceForGithub {

[string]$tfsCollection = ‘defaultcollection’,

$sourceZip = “”
$sourceZipUrl = http://$tfsUrl/$tfsCollection/_apis/tfvc/items?path=$folderPath&api-version=1.0
Write-Host “Please provide you TFS credentials:”
$creds = Get-Credential
Write-Host ‘Downloading the source. This may take a few minutes …’

Invoke-WebRequest $sourceZipUrl -Credential $creds -Headers @{‘Accept’=’application/zip’} -OutFile $sourceZip

UNZIP Source

So next we need to unzip the code to a local folder.  I was actually surprised that PowerShell doesn’t have a zip cmdlets built in but it wasn’t too hard to figure out with the help of a few blog posts:

function UnzipFile ([string]$file, [string]$destination, [string]$subFolder) {

$shell_app=new-object -com shell.application
$filePath = Get-Item $file
$zip_file = $shell_app.namespace(“$($filePath.FullName)”)

Get-Item $destination | Remove-Item -Recurse -Force
New-Item $destination -ItemType Directory
$destinationPath = Get-Item $destination
$dest = $shell_app.namespace($destinationPath.FullName)$dest.Copyhere($zip_file.items())

Get-ChildItem -Path “$($destinationPath.FullName)/$subFolder/*” | Copy-Item -Destination $destinationPath.FullName -Recurse

Get-Item -Path “$($destinationPath.FullName)/$” | Remove-Item -Force -Recurse


Delete Files/Folders

Next we need to delete files/folders that are specific to TFS source control and other files we don’t want in Github.  So we delete the compiled files in “bin” and “obj”.  We also delete the packages folder at the solution level to get rid of all the binary files that Git doesn’t handle well.

Get-ChildItem -Path $sourcePath -Recurse -Directory -Filter ‘Packages’ | Get-ChildItem | Remove-Item -Force -Recurse

Get-ChildItem -Path $sourcePath -Recurse -Directory -Filter ‘bin’ | Remove-Item -Force -Recurse

Get-ChildItem -Path $sourcePath -Recurse -Directory -Filter ‘obj’ | Remove-Item -Force -Recurse

Get-ChildItem -Path $sourcePath -Recurse -File -Filter ‘*.vspscc’ | Remove-Item -Force

Get-ChildItem -Path $sourcePath -Recurse -File -Filter ‘*.csproj’ | ForEach-Object { RemoveSccNodesFromProjectFile($_.FullName)}

Get-ChildItem -Path $sourcePath -Recurse -File -Filter ‘*.sln’ | ForEach-Object { RemoveSccFromSolutionFile($_.FullName)}

NOTE – If you previously checked in your Nuget packages, you will need an alternate solution after moving to Git.  You can restore via extensions to MSBuild, restore manually locally and/or add a restore step to your build pipeline.

Remove TFS References

There are references to TFS in both the project and solution files which need to be removed. 

function RemoveSccNodesFromProjectFile ([string]$projectFile) {

$xml = Get-Content $projectFile
$xml.SelectNodes(“//*[starts-with(name(), ‘Scc’)]”) | ForEach-Object {$_.RemoveAll()}


function RemoveSccFromSolutionFile ([string]$solutionFile) {

$solutionContents = [Io.File]::ReadAllText($solutionFile)
#Get-Content $solutionFile
$solutionContents = $solutionContents -replace ‘GlobalSection\(TeamFoundationVersionControl\).*\s(.*Scc.*\s)*\sEndGlobalSection’, ”
Set-Content $solutionFile $solutionContents


Add .git files

Technically we didn’t need to delete the files/folders above because they will be ignored via our .gitignore file. I still like to do that so we don’t have extra files hanging around locally. I’m copying the default file that is included when you create a new project Visual Studio and add Git source control from another Github project.

Invoke-WebRequest ‘’ -OutFile “$sourcePath/.gitignore”

Invoke-WebRequest ‘’ -OutFile “$sourcePath/.gitattributes”


With this script we can make a single PowerShell method call with the TFS Server, Team Project Collection name, source path and new repo name to automate the preparation of the code.

PrepareSourceForGithub ‘tfsServer01p:8080/tfs’ ‘Acme’ ‘$/Fx/Services/FeatureFlags’ ‘acme.platform.shared.featureflag’

Next you would open the solution, build and run your tests to ensure everything is working correctly.  Once everything is working you can run the git commands (init/commit) in this folder to commit it to Github.

Here is the full code.

Please leave a comment below if you found this post helpful or have further questions.

Happy Migrating!

2 thoughts on “Prep Code to Migrate from TFVC to Github”

Leave a Reply