octokit / octokit.net

A GitHub API client library for .NET
https://octokitnet.readthedocs.io/en/latest/
MIT License
2.67k stars 1.07k forks source link

How to update a fork with upstream commits #1833

Closed Jericho closed 6 years ago

Jericho commented 6 years ago

Say I compare a fork with its upstream branch and determine that my fork is behind. How can I make sure my branch is up-to-date using octokit.net 0.30.0?

var fork = await _githubClient.Repository.Get("my_user", "the_repo_name").ConfigureAwait(false);
var upstream = fork.Parent;
var compareResult = await _githubClient.Repository.Commit.Compare(upstream.Owner.Login, upstream.Name, upstream.DefaultBranch, $"{fork.Owner.Login}:{fork.DefaultBranch}").ConfigureAwait(false);
if (compareResult.BehindBy > 0)
{
    ... how can I update the fork? ...
}
ryangribble commented 6 years ago

The GitHub API is not the best way to do hardcore git data manipulation stuff. Whilst it does have the rudimentary ability to create blobs/trees, commits and references etc, I dont think it would be that easy to get the exact commits from upstream into your fork.

So if you do want to follow the "traditional pattern" of git fetch/pull from upstream and push to origin, you should look at libgit2 or interoperating a commandline git client.

If you are happy to take a more GitHub based approach (rather than git approach), that WOULD be possible via the API, you could automatically raise a Pull Request from the upstream branch to your fork's branch, then automatically merge it, using the merge option for "rebase merging". I think this would avoid you getting a merge commit on your fork's branch, and thus being transparently up to date with upstream...

Jericho commented 6 years ago

@ryangribble thank you for the suggestions. I would prefer to stick with octokit so your second suggestion is particularly interresting to me. Can you point me to a sample that demonstrates how to create a PR from upstream into my fork, how to automerge, etc.?

shiftkey commented 6 years ago

I haven't tried this myself, but this SO answer suggests you might be able to get away with just using the Git Data API:

  • Get upstream ref /repos/upstream/repo/git/refs/heads/master, and get the hash from it
  • Update your fork PATCH /repos/my/repo/git/refs/heads/master with the same hash

How this might look with Octokit.NET (the code compiles but No Guaranteesâ„¢ on anything else):

var upstreamMaster = await client.Git.Reference.Get("upstream", "repo", "heads/master");
var newSha = upstreamMaster.Object.Sha;
Console.WriteLine($"The master branch on upstream/repo is {newSha}");

var forkMaster = await client.Git.Reference.Update("fork", "repo", "heads/master", new ReferenceUpdate(newSha));
var updatedSha = forkMaster.Object.Sha;
Console.WriteLine($"The master branch on fork/repo is {updatedSha}");
Jericho commented 6 years ago

@shiftkey your suggestion works like a charm and so much simpler that anything I tried so far. Thanks

In case anybody is curious here's my code to update my fork if it's behind the upstream repo:

var fork = await _githubClient.Repository.Get("my_username", "the_repo_name").ConfigureAwait(false);
var upstream = fork.Parent;
var compareResult = await _githubClient.Repository.Commit.Compare(upstream.Owner.Login, upstream.Name, upstream.DefaultBranch, $"{fork.Owner.Login}:{fork.DefaultBranch}").ConfigureAwait(false);
if (compareResult.BehindBy > 0)
{
    var upstreamBranchReference = await _githubClient.Git.Reference.Get(upstream.Owner.Login, upstream.Name, $"heads/{upstream.DefaultBranch}").ConfigureAwait(false);
    await _githubClient.Git.Reference.Update(fork.Owner.Login, fork.Name, $"heads/{fork.DefaultBranch}", new ReferenceUpdate(upstreamBranchReference.Object.Sha)).ConfigureAwait(false);
}
matks commented 4 years ago

@shiftkey your suggestion works like a charm and so much simpler that anything I tried so far. Thanks

In case anybody is curious here's my code to update my fork if it's behind the upstream repo:

var fork = await _githubClient.Repository.Get("my_username", "the_repo_name").ConfigureAwait(false);
var upstream = fork.Parent;
var compareResult = await _githubClient.Repository.Commit.Compare(upstream.Owner.Login, upstream.Name, upstream.DefaultBranch, $"{fork.Owner.Login}:{fork.DefaultBranch}").ConfigureAwait(false);
if (compareResult.BehindBy > 0)
{
  var upstreamBranchReference = await _githubClient.Git.Reference.Get(upstream.Owner.Login, upstream.Name, $"heads/{upstream.DefaultBranch}").ConfigureAwait(false);
  await _githubClient.Git.Reference.Update(fork.Owner.Login, fork.Name, $"heads/{fork.DefaultBranch}", new ReferenceUpdate(upstreamBranchReference.Object.Sha)).ConfigureAwait(false);
}

Just wanted to say thanks for sharing this snippet, this is exactly what I was searching for đŸ˜„