3

So I have a Git repo in Azure DevOps, with a URL like

https://[$server]/tfs/[$company]/[$project]/_git/[$repoName] 

And I can get to individual files in that repo by appending to that something like this:

?path=/[$folder]/[$fileName] 

I am trying to use Powershell to download a specific file from this repo to the corresponding location and filename on my computer, like this:

$sourcePath = "https://[$server]/tfs/[$company]/[$project]/_git/[$repoName]?path=/[$folder]/[$fileName]&download=true" $filePath = "C:\Documents\[$folder]\[$fileName]" Invoke-RestMethod -Uri $sourcePath -Method Get -Headers @{Authorization=("Basic {0}" -f [$AuthInfo])} -OutFile $filePath 

What this is doing is instead of replacing the local file with the file from the repo, it is replacing the contents of the local file with the contents of the response body. I find it odd that all the googling I've been doing says to do it this way, even though the Microsoft article on Invoke-RestMethod actually explains that this is what -OutFile does.

Note I've also tried Invoke-WebRequest....same thing.

So how do I download the actual file from the repo to the local destination I want (or replace the contents of the local destination file with the contents of the repo file)?

In addition, is there a way to specify which branch to get the file from? Maybe I should be using Git powershell commands with this as well somehow? All the other googling I've done about downloading from a git repo comes up with results about GitHub.

Thank you!

3
  • 1
    Here's the git solution, but I'd be interested to see how to do it in powershell too- stackoverflow.com/questions/28375418/… Commented Apr 26, 2021 at 20:20
  • Yeah, @OwlsSleeping - not only is that the Git way, but it looks like it's to download a file to where you have the repo cloned, instead of specifying a location to download it to (which I need to be able to do). Thanks, tho! Commented Apr 26, 2021 at 22:13
  • 1
    This isn't really a Git thing. Git is all about commits as contained in repositories. The fact that commits hold files (each commit being in part an archive of every file) is useful here but the way to get one file from a commit, on a server that has a Git repository, is up to that server. That's why you found all those GitHub-specific answers. You need an Azure-specific answer. Commented Apr 27, 2021 at 0:50

2 Answers 2

3

Use the rest api with the following template:

GET https://{tfs_url}/{collection_name}/{project}/_apis/git/repositories/{repositoryId}/items?path={path}

Check the documentation: Items - Get, Example - Download

I use the following code to copy files (with System.Net.WebClient) in build pipelines:

$user = "" $token = $env:SYSTEM_ACCESSTOKEN $teamProject = $env:SYSTEM_TEAMPROJECT $orgUrl = $env:SYSTEM_COLLECTIONURI $repoName = "REPO_NAME" $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token))) function InvokeGetFileRequest ($GitFilePath, $OutFilePath) { Write-Host "Download file" $GitFilePath "to" $OutFilePath $uriGetFile = "$orgUrl/$teamProject/_apis/git/repositories/$repoName/items?scopePath=$GitFilePath&download=true&api-version=6.1-preview.1" Write-Host "Url:" $uriGetFile $wc = New-Object System.Net.WebClient $wc.Headers["Authorization"] = "Basic {0}" -f $base64AuthInfo $wc.Headers["Content-Type"] = "application/json"; $wc.DownloadFile($uriGetFile, $OutFilePath) } 
Sign up to request clarification or add additional context in comments.

4 Comments

Hmmm...unfortunately that is doing the same thing - the local file's contents is getting replaced with the request's content. Thank you, though!
Could it literally be just because of the fact that my repo is in a "tfs" URL, instead of the kind of "repositories" URL as in your example? Like even though they are both technically Azure DevOps, they behave differently for things like this??
@Andy For TFS should be the same $tfs_coolection_url/$team_project_name/_apis/git/repositories/$repoName/items?scopePath=$GitFilePath&download=true&api-version=5.0. Check the API version that support your TFS: learn.microsoft.com/en-us/rest/api/azure/devops/….
So the issue actually is with the URL I was using, which you clued me in on. Instead of "https://[$server]/tfs/[$company]/[$project]/_git/[$repoName]?path=/[$folder]/[$fileName]", I changed it to "https://[$server]/tfs/[$company]/[$project]/_apis/git/repositories/[$repoName]/items?path=/[$folder]/[$fileName]". And that worked! If you want to update your answer to specify the difference in URL, I'll mark it as the answer. Thank you!!
0

I needed to do this and what I saw here didn't work so I wrote my own:

param( [Parameter(Mandatory=$true)] [string] $GitFilePath, [Parameter(Mandatory=$true)] [string] $OutFilePath, [Parameter(Mandatory=$true)] [string] $RepoName, [string] $token, [string] $orgUrl, [string] $teamProject ) if([String]::IsNullOrEmpty($token)) { if($env:SYSTEM_ACCESSTOKEN -eq $null) { Write-Error "you must either pass the -token parameter or use the BUILD_TOKEN environment variable" exit 1; } else { $token = $env:SYSTEM_ACCESSTOKEN; } } if([string]::IsNullOrEmpty($teamProject)){ if($env:SYSTEM_TEAMPROJECT -eq $null) { Write-Error "you must either pass the -teampProject parameter or use the SYSTEM_TEAMPROJECT environment variable" exit 1; } else { $teamProject = $env:SYSTEM_TEAMPROJECT } } if([string]::IsNullOrEmpty($orgUrl)){ if($env:SYSTEM_COLLECTIONURI -eq $null) { Write-Error "you must either pass the -orgUrl parameter or use the SYSTEM_COLLECTIONURI environment variable" exit 1; } else { $teamProject = $env:SYSTEM_COLLECTIONURI } } # Base64-encodes the Personal Access Token (PAT) appropriately $User='' $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$token))); $header = @{Authorization=("Basic {0}" -f $base64AuthInfo)}; #---------------------------------------------------------------------- Write-Host "Download file" $GitFilePath "to" $OutFilePath $uriGetFile = "$orgUrl/$teamProject/_apis/git/repositories/$repoName/items?scopePath=$GitFilePath&download=true&api-version=6.1-preview.1" Write-Host "Url:" $uriGetFile $filecontent = Invoke-RestMethod -ContentType "application/json" -UseBasicParsing -Headers $header -Uri $uriGetFile $filecontent | Out-File -Encoding utf8 $OutFilePath 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.