Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Git Data API Awakens #14

Open
JuanitoFatas opened this issue Dec 23, 2015 · 3 comments
Open

The Git Data API Awakens #14

JuanitoFatas opened this issue Dec 23, 2015 · 3 comments
Labels

Comments

@JuanitoFatas
Copy link
Contributor

JuanitoFatas commented Dec 23, 2015

GitHub has a low-level Git Data API. You can do basically everything with Git via this powerful API!

screenshot 2016-08-17 21 47 37

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

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.

@forelabs
Copy link

forelabs commented Oct 9, 2018

"bar" => Base64.decode64(foo.content) + "baz"

should be

"bar" => Base64.decode64(bar.content) + "baz"

@JuanitoFatas
Copy link
Contributor Author

"bar" => Base64.decode64(foo.content) + "baz"

should be

"bar" => Base64.decode64(bar.content) + "baz"

thanks, fixed!

@tkreiner
Copy link

tkreiner commented Jul 8, 2021

Great blog post! Was very helpful to get me started on a project I was working on.

Section 3 and 4 was a little confusing for me since I don't have deep understanding of how git works under the covers. While working on this project, I came across a function in the library that simplifies that whole section down to one line of code. Posting here for others to reference.

Link - http://octokit.github.io/octokit.rb/Octokit/Client/Contents.html#create_contents-instance_method

Sample code:

client.create_contents(repo, "/path/to/file.txt", "Committing new file to branch.", "This is the contents of the file", branch: new_branch_name)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants