-
Notifications
You must be signed in to change notification settings - Fork 4
Description
GitHub has a low-level Git Data API. You can do basically everything with Git via this powerful API!
In this tutorial, I am going to walk you through how to use this API with Octokit to change files in one single commit in a new branch and send a Pull Request.
Suppose we want to send a Pull Request for https://github.com/JuanitoFatas/git-playground with these changes:
- append
barto file foo - append
bazto file bar - in one commit with the message "Update foo & bar in a new topic branch "update-foo-and-bar".
This is how you could do it:
0. Install Octokit Gem
$ gem install octokit
1. Prepare Octokit Client
Get an access token, and open irb with octokit required, then create an Octokit client with your token:
$ irb -r octokit
> client = Octokit::Client.new(access_token: "<your 40 char token>")We also prepare two variables to be used later, the repo name and new branch name:
repo = "JuanitoFatas/git-playground"
new_branch_name = "update-foo-and-bar"
2. Create a new Topic Branch
First, let's get the base branch (in this case, master branch) SHA1, so that we can branch from master.
We can use the Octokit#refs method to get the base branch SHA1:
master = client.refs(repo).find do |reference|
"refs/heads/master" == reference.ref
end
base_branch_sha = master.object.shaAnd creates a new branch from base branch via Octokit#create_ref method:
new_branch = client.create_ref(repo, "heads/#{new_branch_name}", base_branch_sha)The tricky part here is that you need to prefix your new branch name with "heads/".
3. Change file contents
First let's use Octokit#contents method with the SHA1 to get existing foo and bar files' content.
foo = client.contents repo, path: "foo", sha: base_branch_sha
bar = client.contents repo, path: "foo", sha: base_branch_shaContents on GitHub API is Base64-encoded, we need to decode and append "bar" to foo file, "baz" to bar file respectively:
require "base64"
# path => new content
new_contents = {
"foo" => Base64.decode64(foo.content) + "bar",
"bar" => Base64.decode64(bar.content) + "baz"
}Creates a new tree with our new files (blobs), the new blob can be created via (Octokit#create_blob method). This new tree will be part of our new “tree”.
new_tree = new_contents.map do |path, new_content|
Hash(
path: path,
mode: "100644",
type: "blob",
sha: client.create_blob(repo, new_content)
)
end4. Create a new commit with changes
Get the current commit first via Octokit#git_commit method:
commit = client.git_commit(repo, new_branch["object"]["sha"])Note that this method is not the same as Octokit#commit method. git_commit is from the low-level Git Data API, while commit is using the Commits API.
Now we get the commit object, we can retrieve the tree:
tree = commit["tree"]Creates a new tree by Octokit#create_tree method with the blobs object we created earlier:
new_tree = client.create_tree(repo, new_tree, base_tree: tree["sha"])The base_tree argument here is important. Pass in this option to update an existing tree with new data.
Now our new tree is ready, we can add a commit onto it:
commit_message = "Update foo & bar"
new_commit = client.create_commit(repo, commit_message, new_tree["sha"], commit["sha"])5. Add commit to the new branch
Finally, update the reference via Octokit#update_ref method on the new branch:
client.update_ref(repo, "heads/#{new_branch_name}", new_commit["sha"])6. Issue Pull Request
Creates a new Pull Request via Octokit#create_pull_request method:
title = "Update foo and bar"
body = "This Pull Request appends foo with `bar`, bar with `baz`."
client.create_pull_request(repo, "master", new_branch_name, title, body)That's it! ✨ See the result here.
Now you can do basically everything with Git via GitHub's Git Data API!
May the Git Data API be with you.
Thanks for reading!
@JuanitoFatas ✏️ Jolly Good Code
About Jolly Good Code
We specialise in Agile practices and Ruby, and we love contributing to open source.
Speak to us about your next big idea, or check out our projects.

