在使用 php curl 将文件上传到 Azure DevOps Git 之前如何获取 oldObjectId?

How can I get oldObjectId before uploading a file to Azure DevOps Git using php curl?

问题

在文档 add-a-binary-file 中,您可以看到我们需要一个 oldObjectId 参数来将文件上传到 Azure DevOps git:

{
  "refUpdates": [
    {
      "name": "refs/heads/master",
      "oldObjectId": "1380164a8118686087e38ce91f36b24b58c2df02"
    }
  ],
  "commits": [
    {
      "comment": "Added new image file.",
      "changes": [
        {
          "changeType": "add",
          "item": {
            "path": "/images/people/default.jpg"
          },
          "newContent": {
            "content": "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",
            "contentType": "base64encoded"
          }
        }
      ]
    }
  ]
}

这是我的代码片段:

/**
 * getPushesList
 * @param string $lastCommitUrl
 *
 * @return bool|string
 */
function getPushesList($lastCommitUrl=''){
    global $baseUri;
    global $organization;
    global $project;
    global $repositoryId;
    global $branch;
    global $username;
    global $accessToken;
    $url = $baseUri . '/' . $organization . '/' . $project . '/_apis/git/repositories/' . $repositoryId . '/pushes?api-version=5.1';

    $url = $lastCommitUrl ? $lastCommitUrl : $url;
    $queryArr = [
        'searchCriteria.refName' => 'refs/heads/' . $branch,
        '$top' => 1,
    ];
    $queryStr = http_build_query($queryArr);
    $url .= '&' . urldecode($queryStr);

    $authorization = 'basic ' . base64_encode($username . ':' . $accessToken);
    //$url sample: https://dev.azure.com/kmx3ecup/img_bed/_apis/git/repositories/img_bed/pushes?api-version=5.1&searchCriteria.refName=&searchCriteria.includeRefUpdates=false&$top=1
    // echo $url;exit;
    $ch = curl_init();
    $options = [
        CURLOPT_URL => $url,
        CURLOPT_HTTPHEADER => [
            'authorization: ' . $authorization
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
    ];
    //In case you need a proxy
    // $options[CURLOPT_PROXY] = 'http://127.0.0.1:1087';
    curl_setopt_array($ch, $options);
    $responseJson = curl_exec($ch);
    if(curl_errno($ch)){
        echo 'Error: ' . curl_error($ch);
    }
    $retArr = json_decode($responseJson, true);
    curl_close($ch);
    return $retArr;
}

/**
 * getLastCommitUrl
 * @return mixed|string
 * @throws \GuzzleHttp\Exception\GuzzleException
 */
function getLastCommitUrl(){
    $repoList = getPushesList();
    $lastCommitUrl = '';
    if(is_array($repoList) && isset($repoList['value'][0]['url'])){
        $lastCommitUrl = $repoList['value'][0]['url'];
    }
    return $lastCommitUrl;
}

/**
 * getLastCommitId
 * @return mixed|string
 * @throws \GuzzleHttp\Exception\GuzzleException
 */
function getLastCommitId(){
    $lastCommitUrl = getLastCommitUrl();
    //forty 0
    $lastCommitId = str_repeat('0', 40);
    if($lastCommitUrl){
        $result = getPushesList($lastCommitUrl);
        if(isset($result['commits'][0]['commitId'])){
            $lastCommitId = $result['commits'][0]['commitId'];
        }
    }
    return $lastCommitId;
}

/**
 * create a file in the specified repo and branch
 * @param $uploadFilePath
 * @param $path
 *
 * @return mixed
 */
function create($uploadFilePath, $path){
    global $baseUri;
    global $organization;
    global $project;
    global $repositoryId;
    global $branch;
    global $username;
    global $accessToken;
    $url = $baseUri . '/' . $organization . '/' . $project . '/_apis/git/repositories/' . $repositoryId . '/pushes?api-version=5.1';
    $authorization = 'basic ' . base64_encode($username . ':' . $accessToken);
    //$url sample: https://dev.azure.com/kmx3ecup/img_bed/_apis/git/repositories/img_bed/pushes?api-version=5.1&searchCriteria.refName=&searchCriteria.includeRefUpdates=false&$top=1
    // echo $url;exit;
    $lastCommitId = getLastCommitId();
    // var_dump($lastCommitId);exit;
    $postFields = [
        'refUpdates' => [
            [
                'name'=> 'refs/heads/' . $branch,
                'oldObjectId' => $lastCommitId,
            ],
        ],
        'commits' => [
            [
                'comment' => 'This is a test push',
                'changes' => [
                    [
                        //add, edit, delete
                        'changeType' => 'add',
                        'item' => [
                            'path' => $path,
                        ],
                        'newContent' => [
                            'content' => base64_encode(file_get_contents($uploadFilePath)),
                            'contentType' => 'base64encoded'
                        ]
                    ]
                ],
            ],
        ],
    ];

    $ch = curl_init();
    $options = [
        CURLOPT_URL => $url,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'authorization: ' . $authorization,
            'content-type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode($postFields),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
    ];
    //In case you need a proxy
    // $options[CURLOPT_PROXY] = 'http://127.0.0.1:1087';
    curl_setopt_array($ch, $options);
    $responseJson = curl_exec($ch);
    if(curl_errno($ch)){
        echo 'Error: ' . curl_error($ch);
    }
    curl_close($ch);
    return json_decode($responseJson, true);
}

$baseUri = 'https://dev.azure.com';

$organization = '<YOUR_ORGANIZATION>';
$project = '<YOUR_PROJECT_NAME>';
$repositoryId = '<YOUR_REPOSITORY_ID_OR_REPOSITORY_NAME>';
$branch = 'master';
$username = '<YOUR_USERNAME>';
$accessToken = '<YOUR_ACCESS_TOKEN>';
$uploadFilePath = '/path/to/test.jpg';
$path = '/images/' . date('Y/m/d');
$returnArr = create($uploadFilePath, $path);
var_dump($returnArr) ;

我得到的方式oldObjectId

相关文档:
Get pushes list
Get a specified push
Push changes to repository(upload a file)

问题:是不是我获取lastCommitId的方式不对?

原来oldObjectIdrefsobjectId,你可以试试我下面的代码,对我来说没问题。

/**
 * getRefsList
 * Doc: https://docs.microsoft.com/en-us/rest/api/azure/devops/git/refs/list?view=azure-devops-rest-5.1#refs
 *
 * @param $branch
 * @return bool|string
 */
function getRefsList($branch){
    global $baseUri;
    global $organization;
    global $project;
    global $repositoryId;
    global $username;
    global $accessToken;
    //GET https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repositoryId}/refs?api-version=5.1
    $url = $baseUri . '/' . $organization . '/' . $project . '/_apis/git/repositories/' . $repositoryId . '/refs?api-version=5.1';
    $queryArr = [
        'filter' => 'heads/' . $branch,
    ];
    if(!empty($queryArr)){
        $queryStr = http_build_query($queryArr);
        $url .= '&' . $queryStr;
    }
    // echo($url);exit;
    $authorization = 'basic ' . base64_encode($username . ':' . $accessToken);
    // echo $url;exit;
    $ch = curl_init();
    $options = [
        CURLOPT_URL => $url,
        CURLOPT_HTTPHEADER => [
            'authorization: ' . $authorization
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
    ];
    //In case you need a proxy
    // $options[CURLOPT_PROXY] = 'http://127.0.0.1:1087';
    curl_setopt_array($ch, $options);
    $responseJson = curl_exec($ch);
    if(curl_errno($ch)){
        echo 'Error: ' . curl_error($ch);
    }
    $retArr = json_decode($responseJson, true);
    curl_close($ch);
    $refs = [];
    if(isset($retArr['count']) && $retArr['count'] > 0){
        $refs = $retArr['value'];
    }
    return $refs;
}

/**
 * getObjectIdOfRef
 * @param $branch
 *
 * @return mixed|string
 */
function getObjectIdOfRef($branch){
    $refs = getRefsList($branch);
    $objectId = '';
    if($refs){
        foreach($refs as $ref){
            if($ref['name'] == 'refs/heads/'.$branch){
                $objectId = $ref['objectId'];
                break;
            }
        }
    }
    return $objectId;
}

/**
 * create a file in the specified repo and branch
 * Doc: https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pushes/create?view=azure-devops-rest-5.1
 * @param $uploadFilePath
 * @param $path
 *
 * @return mixed
 */
function create($uploadFilePath, $path){
    global $baseUri;
    global $organization;
    global $project;
    global $repositoryId;
    global $branch;
    global $username;
    global $accessToken;
    $url = $baseUri . '/' . $organization . '/' . $project . '/_apis/git/repositories/' . $repositoryId . '/pushes?api-version=5.1';
    $authorization = 'basic ' . base64_encode($username . ':' . $accessToken);
    //$url sample: https://dev.azure.com/kmx3ecup/img_bed/_apis/git/repositories/img_bed/pushes?api-version=5.1&searchCriteria.refName=&searchCriteria.includeRefUpdates=false&$top=1
    // echo $url;exit;
    $oldObjectId = getObjectIdOfRef($branch);
    // var_dump($oldObjectId);exit;
    $postFields = [
        'refUpdates' => [
            [
                'name'=> 'refs/heads/' . $branch,
                'oldObjectId' => $oldObjectId,
            ],
        ],
        'commits' => [
            [
                'comment' => 'This is a test push',
                'changes' => [
                    [
                        //add, edit, delete
                        'changeType' => 'add',
                        'item' => [
                            'path' => $path,
                        ],
                        'newContent' => [
                            'content' => base64_encode(file_get_contents($uploadFilePath)),
                            'contentType' => 'base64encoded'
                        ]
                    ]
                ],
            ],
        ],
    ];

    $ch = curl_init();
    $options = [
        CURLOPT_URL => $url,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'authorization: ' . $authorization,
            'content-type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode($postFields),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
    ];
    //In case you need a proxy
    // $options[CURLOPT_PROXY] = 'http://127.0.0.1:1087';
    curl_setopt_array($ch, $options);
    $responseJson = curl_exec($ch);
    if(curl_errno($ch)){
        echo 'Error: ' . curl_error($ch);
    }
    curl_close($ch);
    return json_decode($responseJson, true);
}

$baseUri = 'https://dev.azure.com';
$organization = '<YOUR_ORGANIZATION>';
$project = '<YOUR_PROJECT_NAME>';
$repositoryId = '<YOUR_REPOSITORY_ID_OR_REPOSITORY_NAME>';
$branch = 'master';
$username = '<YOUR_USERNAME>';
$accessToken = '<YOUR_ACCESS_TOKEN>';
$uploadFilePath = '/path/to/test.jpg';
$path = '/images/' . date('Y/m/d') . '/' . basename($uploadFilePath);
$retArr = create($uploadFilePath, $path);

$publicUrl = $retArr['repository']['url'] . '/items?path=' . $path . '&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=0&versionDescriptor[version]=' . $branch . '&resolveLfs=true&$format=octetStream&api-version=5.0';

echo $publicUrl;