Last active
July 10, 2024 15:21
-
-
Save kamyar1979/8b03c5ca6a33f4db2553c359b7847713 to your computer and use it in GitHub Desktop.
Converts all TFS projects from TFVC to Git
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
param ( | |
[Parameter(Mandatory=$true)][string]$Uri, | |
[Parameter(Mandatory=$true)][string]$Token, | |
[Parameter(ParameterSetName = 'Exclude')][string[]]$Exclude, | |
[Parameter(ParameterSetName = 'Include')][string[]]$Include | |
) | |
function Get-AuthHeader { | |
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($Token)")) | |
return "Basic $encodedCreds" | |
} | |
function Update-Powershell { | |
if($PSVersionTable.PSVersion.Major -lt 7) { | |
Invoke-Expression "& { $(Invoke-RestMethod https://aka.ms/install-powershell.ps1) } -UseMSI" | |
} | |
} | |
function Get-Scoop { | |
try | |
{ | |
Write-Output "`e[33mChecking Scoop...`e[0m" | |
scoop | Out-Null | |
} | |
catch [System.Management.Automation.CommandNotFoundException] | |
{ | |
Write-Output "`e[33mScoop not found. Installing...`e[0m" | |
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser | |
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression | |
if(-not ($?)) { | |
Write-Error("Could not install scoop!") | |
return | |
} | |
} | |
} | |
function Get-Git { | |
try | |
{ | |
Write-Output "`e[33mChecking Git...`e[0m" | |
git | Out-Null | |
} | |
catch [System.Management.Automation.CommandNotFoundException] | |
{ | |
Write-Output "`e[33mGit not found. Installing...`e[0m" | |
scoop install git | |
$gitName = Read-Host -Prompt "Enter your name" | |
$gitEmail = Read-Host -Prompt "Enter your email address" | |
git config --global user.name $gitName | |
git config --global user.email $gitEmail | |
} | |
} | |
function Get-GitTfs { | |
try | |
{ | |
Write-Output "`e[33mChecking Git-Tfs...`e[0m" | |
git-tfs | Out-Null | |
} | |
catch [System.Management.Automation.CommandNotFoundException] | |
{ | |
Write-Output "`e[33mGit-Tfs not found. Installing...`e[0m" | |
scoop install git-tfs | |
} | |
} | |
function Find-Project { | |
param ( | |
$name | |
) | |
$fresh = $true | |
Write-Output "`e[36mCheking if previous conversion available...`e[0m" | |
if(Test-Path -Path $name) { | |
Set-Location $name | |
git status | Out-Null | |
if($? -eq 0) { | |
$fresh = false | |
$originUrl = git remote get-url origin | |
Set-Location .. | |
if($remote -ne $originUrl) { | |
Write-Error "`e[31mRemote Git repository does not match`e[0m" | |
throw "Git Repository does not match" | |
} | |
} | |
else { | |
Set-Location .. | |
Remove-Item -Recurse -Force $name | |
} | |
} | |
return $fresh | |
} | |
function Push-GitRepository { | |
param ( | |
$name, | |
$remote | |
) | |
Write-Output "`e[36m Pushing to Server.`e[0m" | |
Set-Location $name | |
git remote add origin "$($remote)" | |
git -c http.extraHeader="Authorization: $(Get-AuthHeader)" push --set-upstream --all origin | |
if($?) { | |
Write-Output "`e[33m $($p.name) Sucessfully pushed.`e[0m" | |
} | |
Set-Location .. | |
} | |
function Convert-Project { | |
param ( | |
$name, | |
$remote | |
) | |
if (Find-Project $name) { | |
Write-Output "`e[36mConverting $($name)...`e[0m" | |
git tfs list-remote-branches $Uri | Where-Object {$_ -match "^ \$/$($name)/(\w*) \[\*\]"} | |
if($Matches -eq $null) { | |
git tfs clone $Uri "$/$($name)" | |
} | |
else { | |
$branchName = $Matches[1] | |
git tfs clone $Uri "$/$($name)/$($branchName)" $name --branches=all | |
} | |
} | |
else { | |
Write-Output "`e[36mPrevious conversion found. Fetching changes $($name)...`e[0m" | |
git tfs fetch | |
} | |
if($?) { | |
Push-GitRepository $name $remote | |
} | |
} | |
function Add-GitRepository { | |
param ( | |
$name | |
) | |
Write-Output "`e[36mCreating Git repository for $($name)`e[0m" | |
$data = @{ | |
name = $($name) | |
project = @{ | |
id = $($p.id) | |
} | |
} | |
$json = $data | ConvertTo-Json | |
return Invoke-RestMethod -Method Post -Headers $headers -Body $json -ContentType "application/json" $gitBaseUri | |
} | |
function Get-Projects { | |
Write-Output "`e[36mGetting TFS project list from $($Uri)`e[0m" | |
$projects = Invoke-RestMethod -Headers $headers "$($Uri)/_apis/projects?api-version=4.1" | |
Write-Output "`e[34mFound $($projects.Count) Projects. Iterating...`e[0m" | |
return $projects.value | |
} | |
Update-Powershell | |
Get-Scoop | |
Get-Git | |
Get-GitTfs | |
foreach($p in Get-Projects) { | |
if(($null -ne $Include) -and -not ($p.name -in $Include)) { continue } | |
if(($null -ne $Exclude) -and ($p.name -in $Exclude)) { continue } | |
$gitBaseUri = "$($Uri)/$($p.id)/_apis/git/repositories?api-version=4.1" | |
Write-Output "`e[36mChecking if Git repository exists for $($p.name)`e[0m" | |
$headers = @{ | |
Authorization = Get-AuthHeader | |
} | |
$repoList = Invoke-RestMethod -Headers $headers $gitBaseUri | |
if($repoList.Count -eq 0){ | |
$repo = Add-GitRepository $p.name | |
} | |
else { | |
$repo = $repoList.value[0] | |
} | |
Convert-Project $p.name $repo.webUrl | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Converts the whole TFVC repositories into Git along with history.
After converting the project locally, the script creates a git repository within TFS and pushed the result.
This script tries to install required tools. You must create token using TFS web interface.
It is recommended to make the token full access.
. tfs-to-git\tfs-to-git.ps1 -Uri http://<your-tfs-url>/tfs/DefaultCollection -Token <tfs token>
You can use two optional parameters: