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
bar
to file foo - append
baz
to 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.sha
And 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_sha
Contents 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)
)
end
4. 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.