Azure Devops API:通过 Sha1 前缀、分支名称或标记名称查找 Git 提交?

Azure Devops API: Find a Git commit by Sha1 prefix, branch name or tag name?

在本地 Git 安装中,我可以使用“git show”来查找特定提交,给定 SHA1 哈希前缀、分支名称或标记名称。

如何使用 TFS/Azure Devops API 实现相同的目标?

我已经尝试了 Microsoft.TeamFoundation.SourceControl.WebApi.GitHttpClientBase.[GetCommitsAsync][1](),但是对于 SearchCriteria.ItemVersion and CompareVersion,我需要提供一个 VersionType(分支、标签或提交),所以不像 git-show我已经必须知道我有什么样的标识符。此外,如果是“Commit”,它必须是一个完整的 SHA1 哈希;前缀不行。此外,我想找到一个提交,而不是整个列表。

并且 Microsoft.TeamFoundation.SourceControl.WebApi.GitHttpClientBase.GetCommitAsync() 仅适用于完整的 SHA1 哈希,不适用于前缀、分支或标记。

是否有任何 API 调用等同于“git show”?

如果版本很重要,它是 TFS 服务器 2019 (Dev17.M153.5)。

Azure DevOps 在 UI 的提交页面上有一个搜索框。将其与部分散列一起使用可提供以下查询条件。它使用 GET Commit REST method:

  {
    "fromCommitId": "b86c400000000000000000000000000000000000",
    "toCommitId": "b86c4fffffffffffffffffffffffffffffffffff",
    "skip": 0,
    "maxResultCount": 25
  }

或者将 searchCriteria.itemVersion.version 设置为确切的名称,将 searchCriteria.itemVersion.versionType 设置为 branchtagcommit 以搜索特定的提交,以防您知道全名(分支、标签或完整的提交 ID)。

我使用 Chrome 开发工具来捕获请求。最坏的情况是短哈希不是唯一的,它将 return 多个选项。

对于分支和标签,您还可以 use the refs API,它有一个开始选项和一个包含选项来查找您要查找的引用。

示例:

GET https://dev.azure.com/fabrikam/_apis/git/repositories/{repositoryId}/refs?filter=heads/&filterContains=replacer&api-version=6.0

搜索 filter=heads/ 分支和 filter=tags/ 标签,或者将主过滤器留空以搜索两者。

理论上这个 API 可以 return 多个对象,因为它使用 startsWith 作为 filtercontains 作为 filterContains .

根据 Jesse 的提示,我想出了这个实现:

    public async Task<GitCommit> GetCommitClever(string id)
    {
        return await GetCommitFromTag(id)
            ?? await GetCommitFromBranch(id)
            ?? await GetCommitFromHashPrefix(id);
    }

    private async Task<GitCommit> GetCommit(string commitId)
    {
        return await m_gitClient.GetCommitAsync(commitId, m_repo.Id).ConfigureAwait(false);
    }

    private async Task<GitCommit> GetCommitFromTag(string id)
    {
        var refs = await m_gitClient.GetRefsAsync(m_repo.Id, filter: "tags/" + id).ConfigureAwait(false);
        if (refs == null || refs.Count != 1)
        {
            return null;
        }

        var tag = await m_gitClient.GetAnnotatedTagAsync(m_repo.ProjectReference.Id, m_repo.Id, refs[0].ObjectId).ConfigureAwait(false);
        return await GetCommit(tag.TaggedObject.ObjectId);
    }

    private async Task<GitCommit> GetCommitFromBranch(string id)
    {
        var refs = await m_gitClient.GetRefsAsync(m_repo.Id, filter: "heads/" + id).ConfigureAwait(false);
        if (refs == null || refs.Count != 1)
        {
            return null;
        }

        return await GetCommit(refs.Single().ObjectId).ConfigureAwait(false);
    }

    private async Task<GitCommit> GetCommitFromHashPrefix(string id)
    {
        if (!System.Text.RegularExpressions.Regex.IsMatch(id, @"\A\b[0-9a-fA-F]+\b\Z"))
        {
            throw new ArgumentException($"Branch or tag '{id}' not found.");
        }

        GitQueryCommitsCriteria crit = new GitQueryCommitsCriteria()
        {
            FromCommitId = id.PadRight(40, '0'),
            ToCommitId = id.PadRight(40, 'f')
        };
        var refs = await m_gitClient.GetCommitsAsync(m_repo.Id, crit, top: 2).ConfigureAwait(false);
        if (refs == null || refs.Count == 0)
        {
            throw new ArgumentException($"Commit '{id}' not found.");
        }
        if (refs.Count > 1)
        {
            throw new ArgumentException($"Commit id '{id}' not unique.");
        }

        return await GetCommit(refs.Single().CommitId).ConfigureAwait(false);
    }

    // Using these private members:
    private GitHttpClient m_gitClient;
    private GitRepository m_repo;

还有改进的余地;例如如果你有一个标签“foobar”和一个分支“foo”,那么通过 id“foo”获取提交将 return 标签而不是更好匹配的分支。但我只需要有限的聪明,就这样吧。