laedit Jérémie Bertrand

Commit to GitHub with Octokit.net

on github, octokit-net

GitHub api & Octokit.net

GitHub has an API which amongts many features, can handles commits directly.

Octokit.net is the .net declinaison of octokit, the official GitHub API client.
You can add it to a project through Nuget.

And to use it you just have to instanciate a GitHubClient with a ProductHeaderValue describing your application:

var github = new GitHubClient(new ProductHeaderValue("GithubCommitTest"));

After that you already have access to many operations, like accessing the data of a user:

var user = await github.User.Get("laedit");
Console.WriteLine($"laedit have {user.PublicRepos} public repos");

But in order to commit you have to authenticate yourself, otherwise you can have a "not found" error.

Authentication

There are two ways of doing it, either with login/password or personal access token.
I strongly recommand the personal access token since it has a limited scope and can be revoked easily at any time.

For that:

Once generated, you can use it in the GitHub client:

github.Credentials = new Credentials("personal_access_token");

The code above is only an example, avoid to store your token directly in source code or in a Version Control System.

One file / one line commit

The API allows to dome some one-line commits for operations on single file:

// github variables
var owner = "laedit";
var repo = "CommitTest";
var branch = "master";

// create file
var createChangeSet = await github.Repository.Content.CreateFile(
                                owner,
                                repo,
                                "path/file.txt",
                                new CreateFileRequest("File creation",
                                                      "Hello World!",
                                                      branch));

// update file
var updateChangeSet = await github.Repository.Content.UpdateFile(
                                owner,
                                repo,
                                "path/file.txt",
                                new UpdateFileRequest("File update",
                                                      "Hello Universe!",
                                                      createChangeSet.Content.Sha,
                                                      branch));

// delete file
await github.Repository.Content.DeleteFile(
                                owner,
                                repo,
                                "path/file.txt",
                                new DeleteFileRequest("File deletion",
                                                      updateChangeSet.Content.Sha,
                                                      branch));

All content is automatically converted to base64, preventing to commit any file other than text, like an image.
This limitation will be removed with PR #1488.

Full commit

But it is also possible to acces the whole Git Data and create a more complex commit step by step. So you have a precise control on the git database but it require more API calls.

For example if you want to add a new commit on top of the last commit if the master branch:

  1. Get the SHA of the latest commit of the master branch
var headMasterRef = "heads/master";
// Get reference of master branch
var masterReference = await github.Git.Reference.Get(owner, repo, headMasterRef);
// Get the laster commit of this branch
var latestCommit = await github.Git.Commit.Get(owner, repo, masterReference.Object.Sha);
  1. Create the blob(s) corresponding to your file(s)
// For image, get image content and convert it to base64
var imgBase64 = Convert.ToBase64String(File.ReadAllBytes("MyImage.jpg"));
// Create image blob
var imgBlob = new NewBlob { Encoding = EncodingType.Base64, Content = (imgBase64) };
var imgBlobRef = await github.Git.Blob.Create(owner, repo, imgBlob);
// Create text blob
var textBlob = new NewBlob { Encoding = EncodingType.Utf8, Content = "Hellow World!" };
var textBlobRef = await github.Git.Blob.Create(owner, repo, textBlob);
  1. Create a new tree with:
    • the SHA of the tree of the latest commit as base
    • items based on blob(s) or entirelly new
// Create new Tree
var nt = new NewTree { BaseTree = latestCommit.Tree.Sha };
// Add items based on blobs
nt.Tree.Add(new NewTreeItem { Path = "MyImage.jpg", Mode = "100644", Type = TreeType.Blob, Sha = imgBlobRef.Sha });
nt.Tree.Add(new NewTreeItem { Path = "HelloW.txt", Mode = "100644", Type = TreeType.Blob, Sha = textBlobRef.Sha });

// Other way to add a text file directly
// less API call but the content is automatically converted to base64 so only text can be used
var newTreeItem = new NewTreeItem { Mode = "100644", Type = TreeType.Blob, Content = "Hello Universe!", Path = "HelloU.txt" };
nt.Tree.Add(newTreeItem);

var newTree = await github.Git.Tree.Create(owner, repo, nt);
  1. Create the commit with the SHAs of the tree and the reference of master branch
// Create Commit
var newCommit = new NewCommit("Commit test with several files", newTree.Sha, masterReference.Object.Sha);
var commit = await github.Git.Commit.Create(owner, repo, newCommit);
  1. Update the reference of master branch with the SHA of the commit
var headMasterRef = "heads/master";
// Update HEAD with the commit
await github.Git.Reference.Update(owner, repo, headMasterRef, new ReferenceUpdate(commit.Sha));

Once understood it is not quite complex and it allows to learn how Git works with commit creation.

No comments (for now), but you can reach me on twitter or mastodon.