【问题标题】:Azure Devops API: Find a Git commit by Sha1 prefix, branch name or tag name?Azure Devops API:通过 Sha1 前缀、分支名称或标记名称查找 Git 提交?
【发布时间】:2022-01-17 13:47:05
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

    标签: git azure-devops tfs azure-devops-rest-api azure-repos


    【解决方案1】:

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

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

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

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

    对于分支和标签,您也可以use the refs API,它有一个 start-with 和一个 contains 选项来查找您正在寻找的 ref。

    例子:

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

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

    理论上,此 API 可以返回多个对象,因为它使用 startsWith 表示 filtercontains 表示 filterContains

    【讨论】:

    • 我还没有尝试过是否可以在请求中提供 versionType 多次以一次搜索多个内容。
    • 或者你是否可以省略versionType来搜索所有类型。
    • 感谢@jessehouwing 提供这些提示!它们提供了足够的信息,因此我可以实现变通功能。完成后我会在这里分享。
    【解决方案2】:

    使用 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" 提交将返回标签而不是更好匹配的分支。但我只需要有限的聪明,这样就可以了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-16
      • 2019-10-27
      • 2018-10-27
      • 2015-05-14
      • 2022-01-19
      • 2021-07-25
      • 2013-09-10
      • 2021-06-27
      相关资源
      最近更新 更多