Some lesser known Github API functionality
Feb 8, 2014One of our automation tools occasionally needs to interact with our Github repositories. Unfortunately, the current implementation of this tool leaves something to be desired as it requires cloning these repositories to local disk. Changes against these local repositories are then made on local branches, after which these branches get pushed to Github.
However, in order to save on disk space this tool will only ever create a single local copy of each repository. This makes it unsafe to run multiple instances of this tool as multiple instances simultaneously executing sequences of git commands against the same local repositories might lead to these commands inadvertently getting interpolated, thereby leaving the local repositories in an undefined state.
The solution to this complexity was to completely remove the need for local repositories and instead aim to have everything done through the wonderful Github API. This article is a reminder to myself about some API functionality that I found while looking into this.
Checking if a branch contains a commit
While the Github API does not have an explicit call to check whether a given commit is included in a branch, we can nevertheless use the compare call for just this purpose. This call takes two commits as input and returns a large JSON response of comparison data. We can use the status
field of the response to ascertain if a given commit is behind or identical to the HEAD commit of a branch. If so, then the branch contains that commit.
We can use the Ruby octokit gem to implement this as follows.
require 'octokit'
class GithubClient < Octokit::Client
def branch_contains_sha?(repo, branch, sha)
['behind', 'identical'].include?(compare(repo, branch, sha).status)
end
end
Creating a remote branch from a remote commit
Sometimes you’ll want to create a remote branch by branching from a remote commit. We can use the create_reference call to accomplish this. Note that the ref
parameter of this call needs to be set to refs/heads/#{branch}
when creating a remote branch.
require 'octokit'
class GithubClient < Octokit::Client
def create_branch_from_sha(repo, branch, sha)
# create_ref internally transforms "heads/#{branch}" into "refs/heads/#{branch}"
# as mentioned above, this is required by the Github API
create_ref(repo, "heads/#{branch}", sha)
end
end
Setting the HEAD of a remote branch to a specific remote commit
You can even forcefully set the HEAD of a remote branch to a specific remote commit by using the update_reference call. As mentioned earlier, the ref
parameter needs to be set to refs/heads/#{branch}
. Be careful when using this functionality though as it essentially allows you to overwrite the history of a remote branch!
require 'octokit'
class GithubClient < Octokit::Client
def update_branch_to_sha(repo, branch, sha, force = true)
# update_ref internally transforms "heads/#{branch}" into "refs/heads/#{branch}"
# as mentioned earlier, this is required by the Github API
update_ref(repo, "heads/#{branch}", sha, force)
end
end