如何使用 Github API (或 Octokit) 检查分支是否已经合并到主分支?

6

我想通过Github API/Octokit检查一个特性分支是否已经合并到我的主分支。我没有找到任何关于这个话题的文档或帖子。这是可能的吗?有人知道哪些帖子涉及这个话题吗?

3个回答

5
这是使用Octokit相当于git log master..my-cool-branch的操作 - 你想查看在你的分支中是否有任何提交不在master中:
var head = "my-cool-branch";
var baseBranch = "master";
var compareBaseToHead = await client.Repository.Commit.Compare(owner, repo, baseBranch, head);

if (compareBaseToHead.TotalCommits == 0)
{
    Console.WriteLine("Branch {0} has been merged into {1}", head, baseBranch);
}
else
{
    Console.WriteLine("Branch {0} has NOT been merged into {1}", head, baseBranch);
}

2
我刚刚完成了一个包含此类检查的脚本。我的脚本的总体目标是查看我们组织中的所有存储库,并清理已合并的分支。该脚本是用Groovy编写的,使用kohsuke's github-api java client。Java客户端缺少一个功能,我有一个PR开放。在合并该PR并发布新版本之前,您必须省略分支保护检查或者像我一样(自己拉取代码并添加功能后构建)。
检查分支是否已合并且没有新提交可合并的逻辑在cleanupBranches方法中。基本上,我循环遍历给定存储库的所有关闭的pull requests(PR)。如果已合并该pull request,则将其头分支与存储库的默认分支进行比较(不是所有存储库都命名为master)。如果该pull request的头分支没有0个提交未合并到默认分支,则可以删除该分支。
我把整个脚本都包含了进来,只是为了提供背景信息。你会注意到我已经更改了一些配置属性的默认值。
@GrabResolver(name='binrepo', root='https://binrepo.myCompany.com')
@Grab(group='org.kohsuke', module='github-api', version='SNAPSHOT')

import org.kohsuke.github.*

// store some things here so they are available everywhere
class Config {
  static Boolean doDelete = Boolean.valueOf(System.getProperty('branch.cleanup.doDelete', 'false'))
  static String  orgName = System.getProperty('branch.cleanup.gitOrgName', 'myOrg')
  static String  gitApiUrl = System.getProperty('branch.cleanup.gitApiUrl', 'https://git.myCompany.com/api/v3')
  static String  apiKey
}

def executeOnShell(String command, boolean log = false) {
  File workingDir = new File(System.properties.'user.dir')
  def process = new ProcessBuilder(addShellPrefix(command))
      .directory(workingDir)
      .redirectErrorStream(true)
      .start()
  if (log) {
    process.inputStream.eachLine { println it }
  }

  process.waitFor();
}

def addShellPrefix(String command) {
  def commandArray = new String[3]
  commandArray[0] = "sh"
  commandArray[1] = "-c"
  commandArray[2] = command
  return commandArray
}

def allRepos(GHOrganization org, String...repoPrefixes) {
  println "Fetching all repositories under the ${Config.orgName} org that match prefix(es) ${repoPrefixes}"
  return org.getRepositories().entrySet().findAll{ entry ->
    if (repoPrefixes) {
      return repoPrefixes.any{ prefix -> entry.key.startsWith(prefix) }
    } else {
      return true
    }
  }
}

def cleanupBranches(repo) {
  def defaultBranchName = repo.getDefaultBranch()
  def defaultBranch = repo.getBranch(defaultBranchName)
  def deletedBranchNames = []
  def branchesByName = repo.getBranches().entrySet().findAll{ !it.key.equals(defaultBranchName) && !it.value.isProtected() }.collectEntries{[it.key, it.value]}
  def pullRequests = repo.queryPullRequests().base(defaultBranchName).state(GHIssueState.CLOSED).list().withPageSize(100).each{ pr ->
    // loop thru all pull requests that have been closed and also merged
    if (pr.isMerged()) {
      def branch = branchesByName.get(pr.getHead().getRef())
      if (branch) {
        // the branch still exists and has been merged by this PR
        // make sure it doesn't have any unmerged commits
        def compare = repo.getCompare(defaultBranch, branch)
        if (compare.getTotalCommits() == 0) {
          // branch has been merged and there are 0 commits since merge. delete it
          println "Branch ${repo.getName()}/${branch.getName()} has 0 commits not merged to ${defaultBranchName}. Delete it. PR ${pr.getNumber()} : ${pr.getTitle()}"
          if (Config.doDelete) {
            deleteBranch(repo, branch)
          }

          // remove from internal map of branches since the branch has now been deleted in git
          branchesByName.remove(branch.getName())
          deletedBranchNames.push "${repo.getName()}/${branch.getName()}"
        }
      }
    }
  }
  return deletedBranchNames
}

def deleteBranch(repo, branch) {
  // use a simple curl here because the kohsuke library way of doing it requires 2 api calls when just 1 will do here  
  String cmd = "curl -X DELETE -H \"Authorization: token ${Config.apiKey}\" ${Config.gitApiUrl}/repos/${Config.orgName}/${repo.getName()}/git/refs/heads/${branch.getName()}"
  executeOnShell(cmd)
}

if (args.size() < 1) {
  println "Usage: cleanupRepoBranches.groovy <oauthToken> <optionalRepo-name>"
  System.exit(1)
}

Config.apiKey = args[0]

def branchesDeleted = []
def errors = []
GitHub github = GitHub.connectToEnterprise(Config.gitApiUrl, Config.apiKey)
if (args.size() > 1) {
  String repoName = args[1]
  GHRepository repo = github.getRepository("${Config.orgName}/${repoName}")
  branchesDeleted = cleanupBranches(repo)
} else {
  def repoPrefixes = System.getProperty('branch.cleanup.repoPrefixes', 'pref-,pref2-').split(',')
  def answer = System.console().readLine "You have not specified a repoName. If you proceed, this script will list ${Config.doDelete ? 'and delete ' : ''}all branches with a merged pull request and 0 commits left to merge for all repos starting with ${repoPrefixes.join(', ')} in the ${Config.orgName} org. Are you sure? (y/n) "
  if (answer == 'y') {
    println 'ok! here we go!'
    allRepos(github.getOrganization(Config.orgName), repoPrefixes).each { entry ->
      try {
        branchesDeleted += cleanupBranches(entry.value)
      } catch (Exception e) {
        errors.push([ message: "Error processing branches for ${entry.key} repo", ex: e ])
      }
    }
  }
}
println "${branchesDeleted.size()} Branches deleted..."
branchesDeleted.each{ branch -> println branch }
println "${errors.size()} errors..."
errors.each{ error ->
  println error.message
  error.ex.printStackTrace()
  println
}

1

如果您已经在分支上打开了一个拉取请求,那么这是最快速和最准确的答案。如果没有,您需要查看将该分支与主分支进行比较,我会在另一个回复中详细说明。 - Brendan Forster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接