I'm having trouble performing a simple cherry-pick through the GitHub API. It must be possible, but it's not clear to me how...
I'm developing a chatbot for Slack in C# to help manage Kubernetes environments and GitHub releases, and want to extend it with a hotfix feature. Given an environment, it should create a branch that matches that current release and cherry-pick one or more commit SHA's in there, as supplied by the author of the request through Slack.
All the plumbing is in place. Using the POST /repos/:owner/:repo/git/refs I am able to create the branch that matches the specific release. This means I have both a branch name, commit SHA and tree SHA ready for the next step; cherry-picking one or more commit SHA's into this branch. Using POST /repos/:owner/:repo/git/commits I am able to create a commit, but I'm not sure which tree and/or parent to use - which probably causes the issues I encounter calling POST /repos/:owner/:repo/merges , as it returns me status 409 (merge conflict) where locally, of course, it does not.
The only real example I seem to find is https://github.com/tibdex/github-cherry-pick. However, it does not really match my scenario, and I have a hard time understanding the Git inner workings.
My scenario (latest to oldest);
* commit E (current state of `master`)
* commit D
* commit C (deployed to environment)
* commit B
* commit A
In this scenario, I would like to cherry-pick commit E into a new branch off of commit C, creating a set (A, B, C, E) that I can release.
* commit E (current state of `master`)
* commit D
|
| * commit E (new branch, to be deployed)
|/
* commit C (deployed to environment)
* commit B
* commit A
Basically what I need is a GitHub API version of this bash;
git checkout -b {new-branch-name} {sha}
git cherry-pick {sha}
git push main {new-branch-name}
Any help is appreciated!
Here is how I implemented cherry-pick over the Github API, in pseudo-code:
// here is a commit, for example, from a pull request:
listOfCommits = GET /repos/$owner/$repo/pulls/$number/commits
commit = listOfCommits.head // the first one in the list
// Here is the branch we want to cherry-pick to:
branch = GET /repos/$owner/$repo/branches/$branchName
branchSha = branch.commit.sha
branchTree = branch.commit.commit.tree.sha
// Create a temporary commit on the branch, which extends as a sibling of
// the commit we want but contains the current tree of the target branch:
parentSha = commit.parents.head // first parent -- there should only be one
tempCommit = POST /repos/$owner/$repo/git/commits { "message": "temp",
"tree": branchTree,
"parents": [parentSha] }
// Now temporarily force the branch over to that commit
PATCH /repos/$owner/$repo/git/refs/heads/$refName { sha = tempCommit.sha,
force = true }
// Merge the commit we want into this mess:
merge = POST /repos/$owner/$repo/merges { "base": branchName
"head": commit.sha }
// and get that tree!
mergeTree = merge.commit.tree.sha
// Now that we know what the tree should be, create the cherry-pick commit.
// Note that branchSha is the original from up at the top.
cherry = POST /repos/$owner/$repo/git/commits { "message": "looks good!",
"tree": mergeTree,
"parents": [branchSha] }
// Replace the temp commit with the real commit:
PATCH /repos/$owner/$repo/git/refs/heads/$refName { sha = cherry.sha,
force = true }
// Done!
Git experts, please advise, but I believe this accomplishes:
git checkout -b {new-branch-name} {sha}
git cherry-pick {sha}
git push main {new-branch-name}
The accepted answer works, but it isn't clear why.
The following is taken from a comment in my gist of this script (in golang): https://gist.github.com/sashamor/7918b3be758759440feb7825547370f9
GitHub doesn't support creating cherry-pick commits directly with it's API. However, it does support creating commit objects in the git tree. So, we'll create a new cherry-pick commit from first principles.
To create a commit, we need 3 things:
For a cherry-pick commit, that's mostly easy:
The steps below are for creating the tree SHA. Once we have that, we can create the commit.
To get the tree, we can use the merge API. Currently, the GitHub merge API requires two things:
At first glance, a solution seems to be to use merge with:
However, this has the caveat that all commits before the cherry-pick commit, that aren't in the branch to cherry-pick onto, are included in the merge. We don't want this. To fix this, we can "trick" git into only including the cherry-pick commit, by (temporarily) setting the parent of the branch to the parent of the commit we'd like to cherry-pick. That way, when git performs the merge, it detects that the parent of the branch commit we're merging onto matches the parent of the commit we're merging with, and merges a tree of size 1, containing only the cherry-pick commit.
To create this new "tip of tree" commit:
We then create a temporary branch that points to this commit, so we can merge onto it. We can then call merge with:
Finally, we can use this to create the cherry-pick commit. This can be repeated as many times as needed, with the new cherry-pick commit being the base branch each time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With