Skip to content

Instantly share code, notes, and snippets.

@kamyar1979
Last active July 10, 2024 15:21
Show Gist options
  • Save kamyar1979/8b03c5ca6a33f4db2553c359b7847713 to your computer and use it in GitHub Desktop.
Save kamyar1979/8b03c5ca6a33f4db2553c359b7847713 to your computer and use it in GitHub Desktop.
Converts all TFS projects from TFVC to Git
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
}
@kamyar1979
Copy link
Author

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:

  • Include: Only convert mentioned projects
  • Exclude: Convert all projects but mentioned

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment