Skip to content

Instantly share code, notes, and snippets.

@berkus
Created January 2, 2014 20:01
Show Gist options
  • Save berkus/8225683 to your computer and use it in GitHub Desktop.
Save berkus/8225683 to your computer and use it in GitHub Desktop.
Working with GitHub pull requests from git

From http://www.somethingorothersoft.com/2012/05/22/pulling-github-pull-requests-with-git/

Fetch all Pull Requests (even closed ones)

Just execute the following commands and you'll be able to fetch all pull requests by simply executing git fetch pr. Pull requests will be stored in a remote as individual remote branches. So a pull request 123 will be accessible as 'pr/123':

git remote add pr https://github.com/upstream/repo.git
git config --local --unset remote.pr.fetch
git config --local --add remote.pr.fetch "+refs/pull/*/head:refs/remotes/pr/*"

Fetch individual pull requests (by number)

We can use shell git alias (i.e. a git alias that can execute any shell command) to fetch individual pull requests and put them under a remote pr. You don't have to follow above steps to create a pr remote:

git config --global alias.fetch-pr '"!f() { git fetch $1 refs/pull/$2/head:refs/remotes/pr/$2; }; f"'

Just to explain what's going on there:

! means a shell alias

f() defines a shell function called "f"

{ git fetch $1 refs/pull/$2/head:refs/remotes/pr/$2; } is the body of the function. It fetched from the remote (first parameter) a specified pull request (second parameter) and places it as though it came from a remote pr.

f invokes the function.

You can run this as follows:

git fetch-pr origin 123
#After running the command you'll be able to see the history
git log pr/123

Rebase individual pull requests into current branch

This is an alias that would fetch the PR and rebase it into the current branch, so that the PR commits look like they have been applied to the HEAD of the current branch:

git config --global alias.rebase-pr '"!f() { git checkout -b pr_$1 pr/$1; git rebase @{-1}; git checkout @{-2}; git rebase @{-1}; }; f"'

Use is like so:

git rebase-pr 123

Note that in order to run rebase-pr, you would have needed to run 'fetch-pr'. rebase-pr is not very conflict friendly (not that conflicts coming from submitters are acceptable). If you do have a conflict and hit git rebase --continue, you end up on a temporary branch pr_NNN. You will then need to switch back to master and rebase pr_NNN. So the sequence of steps would be:

git rebase --continue
git checkout master
git rebase pr_NNN
git branch -D pr_NNN

With these things available in your git toolbox, you should hopefully be able to reduce the friction of the Fork + Pull Model workflow even further.

@idbrii
Copy link

idbrii commented Mar 12, 2018

This is great. Thanks!

git checkout @{-2} wasn't getting me the right branch (I was switching around a lot to test and I think it was getting the previous previous branch). Instead, I used git checkout @{-1}; which makes sense because we switch branches and always want to go back to the last one we were on. I also replaced the final rebase with a ff merge because that made more sense to me. And I added some input error handling and early out on failure:

pr-cherry-pick = "!f() { re='^[0-9]+$'; if [ -z \"$1\" -o -z \"${1##*[!0-9]*}\" ] ; then echo 'Error: PR fetch requires pull request number.\nExample: git pr-rebase 23' ; else ( git checkout -b pr_$1 pr/$1 || ( echo 'Hint: Forget to pr-fetch?' && false ) ) && git rebase @{-1} && git checkout @{-1} && git merge --ff-only @{-1} ; fi; }; f"

I guess if this gets any more complex, I should use hub!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment