Azure DevOps API:获取 Pull Request 的所有文件(源和目标)

Azure DevOps API: Get all files (source and target) of Pull Request

我搜索了很多,在这里找到了几个线程(例如 , ,等等),但我找不到以下任务的答案: 使用 Azure DevOps API 检索特定 PR 的所有文件的内容更改(基本上是前后文件)。

我可以找到一个 PR,可以遍历更改、迭代、提交(以各种组合),但我无法下载每个版本或文件的第一个和最后一个版本(应该有我可以在 DevOps 的 PR 中查看之前和之后的一种方式。

任何提示 where/how 我可以检索某个文件的两个版本 commit/change/iteration?

非常感谢!

干杯, 乌多

感谢所有提示。看来我设法找到了一个寺庙来拉它。请随时纠正我的方法。

这是完整的 PowerShell 文件:

[CmdletBinding()]
param (
    [int]
    $pullRequestId,
    [string]
    $repoName
)

# --- your own values ---
$pat = 'your-personal access token for Azure DevOps'
$urlOrganization = 'your Azure DevOps organization'
$urlProject = 'your Azure DevOps project'
$basePath = "$($env:TEMP)/pullRequest/" # a location to store all the data
# --- your own values ---

if (!$repoName)
{
    $userInput = Read-Host "Please enter the repository name"
    if (!$userInput)
    {
        Write-Error "No repository name given."
        exit
    }
    $repoName = $userInput
}

if (!$pullRequestId)
{
    $userInput = Read-Host "Please enter the PullRequest ID for $($repoName)"
    if (!$userInput)
    {
        Write-Error "No PullRequest ID given."
        exit
    }
    $pullRequestId = $userInput
}

$prPath = "$($basePath)$($pullRequestId)"
$sourcePath = "$($basePath)$($pullRequestId)/before"
$targetPath = "$($basePath)$($pullRequestId)/after"

# --- helper methods ---
function CleanLocation([string]$toBeCreated)
{
    RemoveLocation $toBeCreated
    CreateLocation $toBeCreated
    if (!(Test-Path $toBeCreated))
    {
        Write-Error "Path '$toBeCreated' could not be created"
    }
}

function CreateLocation([string]$toBeCreated)
{
    if (!(Test-Path $toBeCreated)) { New-Item -ErrorAction Ignore -ItemType directory -Path $toBeCreated > $null }
}

function RemoveLocation([string]$toBeDeleted)
{
    if (Test-Path $toBeDeleted) { Remove-Item -Path $toBeDeleted -Recurse -Force }
}

function DeleteFile([string]$toBeDeleted)
{
    if (Test-Path $toBeDeleted) { Remove-Item -Path $toBeDeleted -Force }
}
# --- helper methods ---

# --- Azure DevOps helper methods ---
function GetFromDevOps($url)
{
    $patToken = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($pat)"))
    $repoHeader = @{ "Authorization" = "Basic $patToken" }
    $response = $(Invoke-WebRequest $url -Headers $repoHeader)
    if ($response.StatusCode -ne 200)
    {
        Write-Error "FAILED: $($response.StatusDescription)"
    }
    return $response
}

function JsonFromDevOps($url)
{
    $response = GetFromDevOps $url
    return ConvertFrom-Json -InputObject $response.Content
}

function DownloadFromDevOps($url, $filename)
{
    $patToken = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($pat)"))
    $repoHeader = @{ "Authorization" = "Basic $patToken" }
    $ProgressPreference = 'SilentlyContinue'
    Invoke-WebRequest $url -Headers $repoHeader -OutFile $filename
    $ProgressPreference = 'Continue'
}

function DownloadAndSaveFile($itemUrl, $outputPath, $path, $overwrite)
{
    $outFile = "$outputPath$($path)"
    $fileExists = Test-Path $outFile
    if (!$fileExists -or $overwrite)
    {
        $outPath = [System.IO.Path]::GetDirectoryName($outFile)
        CreateLocation $outPath
        if ($fileExists)
        {
            Write-Host "    overwriting to $outputPath"
        }
        else
        {
            Write-Host "    downloading to $outputPath"
        }
        DownloadFromDevOps $itemUrl $outFile
    }
}

function DownloadFiles($changes, $beforePath, $beforeCommitId, $afterPath, $afterCommitId)
{
    foreach ($change in $changes)
    {
        $item = $change.item

        if ($item.isFolder)
        {
            continue;
        }
        $path = $item.path
        $originalPath = $change.originalPath
        if (!$path)
        {
            $path = $change.originalPath
        }
        $displayPath = $path ?? $originalPath
        $changeType = $change.changeType
        Write-Host "[$($changeType)] $($displayPath)"
        if (($changeType -eq "edit, rename"))
        {
            $itemUrl = "$($urlRepository)/items?path=$($originalPath)&versionDescriptor.version=$($beforeCommitId)&versionDescriptor.versionOptions=0&versionDescriptor.versionType=2&download=true"
            DownloadAndSaveFile $itemUrl $beforePath $originalPath
        }
        # just get the source/before version
        if ($changeType -eq "delete")
        {
            $itemUrl = "$($urlRepository)/items?path=$($originalPath)&versionDescriptor.version=$($beforeCommitId)&versionDescriptor.versionOptions=0&versionDescriptor.versionType=2&download=true"
            DownloadAndSaveFile $itemUrl $beforePath $originalPath
            DeleteFile "$($afterPath)$($originalPath)"
        }
        if ($changeType -eq "edit")
        {
            $itemUrl = "$($urlRepository)/items?path=$($path)&versionDescriptor.version=$($beforeCommitId)&versionDescriptor.versionOptions=0&versionDescriptor.versionType=2&download=true"
            DownloadAndSaveFile $itemUrl $beforePath $path
        }
        if (($changeType -eq "add") -or ($changeType -eq "edit") -or ($changeType -eq "edit, rename"))
        {
            $itemUrl = "$($urlRepository)/items?path=$($path)&versionDescriptor.version=$($afterCommitId)&versionDescriptor.versionOptions=0&versionDescriptor.versionType=2&download=true"
            DownloadAndSaveFile $itemUrl $afterPath $path $true
        }
        $validChangeTypes = @('add', 'delete', 'edit', 'edit, rename')
        if (!($validChangeTypes.Contains($changeType)))
        {
            Write-Warning "Unknown change type $($changeType)"
        }
    }
}
# --- Azure DevOps helper methods ---

$urlBase = "https://dev.azure.com/$($urlOrganization)/$($urlProject)/_apis"
$urlRepository = "$($urlBase)/git/repositories/$($repoName)"
$urlPullRequests = "$($urlRepository)/pullRequests"
$urlPullRequest = "$($urlPullRequests)/$($pullRequestId)"
$urlIterations = "$($urlPullRequest)/iterations"

CleanLocation $prPath

$iterations = JsonFromDevOps $urlIterations
foreach ($iteration in $iterations.value)
{
    # the modified file
    $srcId = $iteration.sourceRefCommit.commitId
    # the original file
    $comId = $iteration.commonRefCommit.commitId
    $comId = $iteration.targetRefCommit.commitId

    $urlIterationChanges = "$($urlIterations)/$($iteration.id)/changes"
    $iterationChanges = JsonFromDevOps $urlIterationChanges

    DownloadFiles $iterationChanges.changeEntries $sourcePath $comId $targetPath $srcId
}