124

I've got a specific commit which I would like to contribute to a repository I have forked on github. I assume the mechanism to do so is a "pull request". However when I try this I can only pull request my whole branch. I do not wish to pull request the other commits as they are not relevant. Any idea how I can do this.

repo I wish to pull request to.

The last commit b50b2e7 is the only commit I wish to pull request. Anyway I can do this or are all commits dependent on each other?

commit I wish to pull request

pontikos
  • 1,433
  • 2
  • 11
  • 7
  • 4
    Related: Some good details on [how the pull requests are different](https://stackoverflow.com/q/6235379/465053) in Git (the software) and GitHub (the web service) – RBT Aug 12 '17 at 03:21

3 Answers3

178

Create a new branch with just that change:

# If you haven't set up your remote yet, run this line:
# git remote add upstream https://github.com/konradjk/exac_browser.git
git fetch --all                                   # Get the latest code
git checkout -b my-single-change upstream/master  # Create new branch based on upstream/master
git cherry-pick b50b2e7                           # Cherry pick the commit you want
git push -u origin my-single-change               # Push your changes to the remote branch

Then create the PR from that branch.

Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
  • What does the second line do? Does it create a branch in upstream? We are usually not authorised to create branches in upstream. So I don't understand the meaning of the second line. – NullByte08 Apr 23 '20 at 19:17
  • 3
    @NullByte08 - `git checkout -b my-single-change upstream/master` creates a new _local_ branch named `my-single-change` _based on_ the upstream `master` branch (meaning it starts out pointing to the same commit as `upstream/master`). As you add additional commits to `my-single-change`, they're only added to your local branch. – Joseph Silber Apr 24 '20 at 18:18
  • So If I create a pull request from the `my-single-change`, will it create it in the upstream/master? and also, suppose we change the upstream to origin in the second line then what difference would it make? – NullByte08 Apr 25 '20 at 20:00
  • 2
    @NullByte08 - `upstream` is usually the original repository, to which you don't have write access. `origin` is usually your own fork, to which you can push whatever you want. `git push -u origin my-single-change` pushes your local `my-single-change` branch to your fork. When you create a PR, you're asking the original repository to pull in your changes from the `my-single-change` branch in your own fork. – Joseph Silber Apr 26 '20 at 16:16
  • 1
    @NullByte08 - If you change the second line from `upstream/master` to `origin/master`, your new branch will be based off your fork's `master`, not the original repository's `master`. If you created the fork just now, then they're probably the same. But if you created the fork a while ago, the original repository's `master` might have additional changes that you don't yet have in your fork's `master`. – Joseph Silber Apr 26 '20 at 16:18
  • So the new branch will have its base(*starting point*) at the branch we mention in the second line. I know about what the `upstream` and `origin` usually refers to. But thanks for the descriptive reply. – NullByte08 Apr 26 '20 at 16:36
  • cherry-pick doesn't save all history up to that point – theonlygusti Jul 29 '21 at 11:36
  • 1
    It seems like if upstream is not setup before the steps, `git fetch --all` should be run after and not before, otherwise you cant create the branch. – viblo Aug 17 '21 at 19:56
42

I had the same error of alwaysCurious, so I did a little digging. 1

The regular case

A - B - C [master]
         \
          D - E - F - G [feature] 

You're working on a project, you use a separate branch (feature) for your committed changes (D-E-F-G) and you want to create a pull request. However you want only some of the commits to be included in the pull request (E and F)

The procedure here is the one from Joseph's answer

# optional: set upstream as remote if it's not
git remote add upstream https://github.com/<upstream_github_username>/<upstream_github_repo_name>.git
# fetch changes
git fetch --all
# create specific branch for your partial pull request
git checkout -b partial-change upstream/master

Now this is how it looks:

          [partial-change]
A - B - C [master]
         \
          D - E - F - G [feature]

Cherry-pick your specific commits and push the changes:

git cherry-pick <hash of commit E>
git cherry-pick <hash of commit F>
git push -u origin partial-change

After fixing any conflict this is where you'll get:

          E1 - F1 [partial-change]
         / 
A - B - C [master]
         \
          D - E - F - G [feature]

The consecutive case

If instead you just want to apply all the consecutive commits up to the last one (or two or three) you can just branch out at the specific commit. For instance here I just want the commits up to E and not the subsequent ones:

git checkout -b partial-consecutive-changes <hash of commit E>
git push -u origin partial-consecutive-changes

A - B - C [master]
         \
          D - E [partial-consecutive-changes]
               \
                F - G [feature]

The rookie mistake

The last procedure can also help you if you just applied consecutive changes to master without using a specific branch for them and now you want to cherry-pick them after. This is relevant if you've forked a project at C and proceeded on master with the other commits. Here I am adding an asterisk to signal that new changes are happening on the fork:

A - B - C - D* - E* - F* - G* [master]

What you shouldn't do is:

git checkout -b partial-change upstream/master
git cherry-pick <hash of commit D>
git cherry-pick <hash of commit E>
git push -u origin partial-change

In this case you're trying to branch out the master at G* and cherry picking the previous commits will get you the warning:

The previous cherry-pick is now empty, possibly due to conflict resolution.

since you're adding the same old commits on the new branch.

What you should do instead is:

git checkout -b partial-change <hash of commit E>
git push -u origin partial-change

A - B - C - D* - E* - F* - G* [master]
                  \
              D* - E* [partial-change]               

After this you're ready to make a pull request with only the selected commits.


Notes:

  1. Here I'm extending this great answer from Schwern.

  2. To get the last n commit hashes it may be useful to use: git log --pretty=oneline --abbrev-commit | head -n

gibbone
  • 2,300
  • 20
  • 20
15

I'm not familiar with cherry-pick and had a problem when I tried Joseph's approach (something about the cherry-pick being empty). I found a work-around that seems to have worked well:

# Create new branch directly from specified commit:
$ git checkout -b my-single-change b50b2e7
$ git push --set-upstream origin my-single-change

You can now select this branch in GitHub and create a pull request.

alwaysCurious
  • 523
  • 5
  • 14
  • 1
    Will it work only if your Specific commit is the first after already pushed to origin? In other words, if you have commits a,b and c, and you create branch from Commit b, commit a will be also included. – Michael Freidgeim Aug 18 '20 at 21:32
  • 3
    I tried the `cherry-pick` as well and initially received the "empty commit" error. The reason for that was my new branch was based off of the branch containing the commit I wanted to `cherry-pick`, so that new branch technically already had the commit. I had to create my branch off a different branch (like `master`), and then was able to successfully `cherry-pick` from `branch-with-commit-I-want` into `branch-with-cherry-pick` – Chris Serra Feb 04 '21 at 21:15
  • this answer doesn't work, it actually pulls all commits before as well – Pencilcheck Sep 20 '22 at 21:27