Recently I have been working on an application that basically has a github bot (aka user) fork a repo, commit some files, and submit a PR against someone’s repo. When it came down to actually making a new git commit through the github API, I had quite a hard time. I figured it out with some help from a ruby tutorial, and now I’m going to show you how to do it.

Getting things going

Before we start, we need 2 things.

  1. Install the Octokit nuget package.
  2. Get a credential token
    1. You can get a token for a logged in user via oauth for .net
    2. Or you can get a token for yourself (personal access token) by going to settings=>personal access tokens at github

The Code

Ok so I’m going to walk you through the code…don’t want to wait? scroll to the bottom (or click here).

Forking the target repo

The first thing we need our user to do is create our github client object.

1
2
3
4
5
6
//Create our Client
var client = new GitHubClient(new ProductHeaderValue("OurProgramName"))
{
Credentials = new Credentials("YourCredentialTokenHere")
};

Then we should fork our target repo

1
2
var fork = await client.Repository.Forks.Create(owner, repoName, new NewRepositoryFork() { });

Afterwards we need to get the last commit for the default branch. You can replace fork.DefaultBranch with an actual branch name if you wish.

1
2
3
4
5
6
var refs = await client.GitDatabase.Reference.GetAll(fork.Owner.Login, fork.Name);
//Get the latest commit for the default branch
var lastestCommit = await client.GitDatabase.Reference.Get(fork.Owner.Login, fork.Name, "heads/" + fork.DefaultBranch);
//Get the commit object for the "latest" commit in the default branch
var baseCommit = await client.GitDatabase.Commit.Get(fork.Owner.Login, fork.Name, lastestCommit.Object.Sha);

Now we need to make the data, and the commit.

1
2
3
4
5
6
7
8
//Create the data
var blob = await client.GitDatabase.Blob.Create(fork.Owner.Login, fork.Name, new NewBlob() { Content = "I Am The Body of the file", Encoding = EncodingType.Utf8 });
var genTree = new NewTree() { BaseTree = baseCommit.Tree.Sha };
genTree.Tree.Add(new NewTreeItem() { Type = TreeType.Blob, Mode = "100644", Sha = blob.Sha, Path = "OurNewFile.md" });
var newTree = await client.GitDatabase.Tree.Create(fork.Owner.Login, fork.Name, genTree);
//make the commit
var commitSha = await client.GitDatabase.Commit.Create(fork.Owner.Login, fork.Name, new NewCommit("Commit Message Huzzahhh", newTree.Sha, refs.First().Object.Sha));

This line creates the content of the file you wish to add (note the Content property)

1
2
var blob = await client.GitDatabase.Blob.Create(fork.Owner.Login, fork.Name, new NewBlob() { Content = "I Am The Body of the file", Encoding = EncodingType.Utf8 });

…and this line puts it into OurNewFile.md in a new tree (which I honestly don’t know much about)

1
2
genTree.Tree.Add(new NewTreeItem() { Type = TreeType.Blob, Mode = "100644", Sha = blob.Sha, Path = "OurNewFile.md" });

Then we actually make the commit

1
2
var commitSha = await client.GitDatabase.Commit.Create(fork.Owner.Login, fork.Name, new NewCommit("Commit Message Huzzahhh", newTree.Sha, refs.First().Object.Sha));

Now we have to update our fork with our new commit

1
2
await client.GitDatabase.Reference.Update(fork.Owner.Login, fork.Name, $"heads/{fork.DefaultBranch}", new ReferenceUpdate(commitSha.Sha) { });

Then submit the PR!!!!

1
2
3
//submit the pr
await client.PullRequest.Create(owner, repoName, new NewPullRequest("Add New File!!!!", $"{fork.Owner.Login}:{fork.DefaultBranch}", fork.DefaultBranch) { Body = "OMG I'm A PR!!!!" });

The actual code huzzah!!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public async Task CommitThings(string owner, string repoName)
{
//Create our Client
var client = new GitHubClient(new ProductHeaderValue("OurProgramName"))
{
Credentials = new Credentials("YourCredentialTokenHere")
};
//get the repos for our token
var repos = await client.Repository.GetAllForCurrent();
//if we already have this repo we should delete it (that way we get a fresh copy)
var existingRepo = repos.FirstOrDefault(a => a != null && a.Name.Equals(a.Name));
if(existingRepo != null)
{
await client.Repository.Delete(existingRepo.Owner.Login, existingRepo.Name);
}
//create the fork
var fork = await client.Repository.Forks.Create(owner, repoName, new NewRepositoryFork() { });
//Get all references
var refs = await client.GitDatabase.Reference.GetAll(fork.Owner.Login, fork.Name);
//Get the latest commit for the default branch
var lastestCommit = await client.GitDatabase.Reference.Get(fork.Owner.Login, fork.Name, "heads/" + fork.DefaultBranch);
//Get the commit object for the "latest" commit in the default branch
var baseCommit = await client.GitDatabase.Commit.Get(fork.Owner.Login, fork.Name, lastestCommit.Object.Sha);
//Create the data
var blob = await client.GitDatabase.Blob.Create(fork.Owner.Login, fork.Name, new NewBlob() { Content = "I Am The Body of the file", Encoding = EncodingType.Utf8 });
var genTree = new NewTree() { BaseTree = baseCommit.Tree.Sha };
genTree.Tree.Add(new NewTreeItem() { Type = TreeType.Blob, Mode = "100644", Sha = blob.Sha, Path = "OurNewFile.md" });
var newTree = await client.GitDatabase.Tree.Create(fork.Owner.Login, fork.Name, genTree);
//make the commit
var commitSha = await client.GitDatabase.Commit.Create(fork.Owner.Login, fork.Name, new NewCommit("Commit Message Huzzahhh", newTree.Sha, refs.First().Object.Sha));
await client.GitDatabase.Reference.Update(fork.Owner.Login, fork.Name, $"heads/{fork.DefaultBranch}", new ReferenceUpdate(commitSha.Sha) { });
//submit the pr
await client.PullRequest.Create(owner, repoName, new NewPullRequest("Add New File!!!!", $"{fork.Owner.Login}:{fork.DefaultBranch}", fork.DefaultBranch) { Body = "OMG I'm A PR!!!!" });
}