How do I checkout a PR from a fork?

Viewed 3696

I'm using github to host some projects and someone forked my repo and submitted a pr, I have never had to test a pr in a fork before. How can I checkout the branch and test it? Can I some how pull it into my repo? Or do I checkout the fork and test the branch that way?

7 Answers

Basically the "upstream" GitHub repository provides direct access to the forks' PR branches (in read-only) so that you can fetch a given PR and test the corresponding code after specifying the appropriate ref, pull/ID/head.

To this aim, I often use the following alias (which can be used for both initial fetch, then subsequent fetches if the PR has been updated−thereby overwriting the pr/ID local branch):

$ git pr
Usage: git pr <id> [<remote>]  # assuming <remote>[=origin] is on GitHub

$ git pr 101  # fetch PR #101 from origin -> branch pr/101

$ git pr 101 upstream # fetch PR #101 from upstream -> branch pr/101

Disclaimer: this definition is not the very last version of the command, see Further automation below.

Here is the one-liner definition, then an expanded form:

$ git config --global alias.pr '!f() { if [ $# -lt 1 ]; then echo "Usage: git pr <id> [<remote>]  # assuming <remote>[=origin] is on GitHub"; else git checkout -q "$(git rev-parse --verify HEAD)" && git fetch -fv "${2:-origin}" pull/"$1"/head:pr/"$1" && git checkout pr/"$1"; fi; }; f'

# git config --global alias.pr '!f() { if [ $# -lt 1 ]; then
  echo "Usage: git pr <id> [<remote>] # assuming <remote>[=origin] is on GitHub";
  else git checkout -q "$(git rev-parse --verify HEAD)" &&
  git fetch -fv "${2:-origin}" pull/"$1"/head:pr/"$1" &&
  git checkout pr/"$1";
  fi; }; f'

Note however that you cannot directly push to these special branches.

If ever you'd like to directly modify the PR (commit+force-push) as a maintainer, you should then add the fork as another remote, as suggested by @yes-siz (provided the PR author authorized such access when opening their PR).

Further automation

It can be noted that the git pr alias above creates a read-only branch, because being able to force-push to the branch that is associated with the PR would require to push to a specific branch within the source fork repository (assuming its owner authorized this access, as mentioned previously).

Fortunately, GitHub's REST API allows one to easily retrieve this information (the GitHub source repository as well as the source ref), so that one can automate this by relying on a simple curl call and a few standard Git commands including git branch --set-upstream-to.

All things put together, we can obtain some handy Bash command:

$ git prw

Facility to fetch a read/write branch from a GitHub repo Pull Request.

Usage:
  git prw <ID> [<REMOTE>] [-f]

Example:
  git prw 390 upstream

Summary:
  If the REMOTE argument is omitted, it defaults to "origin".
  REMOTE must point to a GitHub repository.

  This command checkouts the branch named "pr/ID" (if it doesn't exist
  yet, it fetches the source branch for the PR #ID in the REMOTE repo)
  and sets its upstream branch so that one can force-push to the fork
  (using an SSH URL); it reuses (if applicable) an existing remote
  matching that URL, or creates a remote named REMOTE-fork-for-pr-ID.

  Flag -f overwrites the local branch pr/ID even if it already exists.
  In general, it is a good idea to pass flag -f, unless we already ran
  "git pr ID REMOTE" and did commits in the local branch pr/ID.

  It requires curl <https://curl.se/> and (optionally) jq.

See also:
  - https://stackoverflow.com/a/62432946/9164010
  - https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/checking-out-pull-requests-locally
  - https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork

I published this git-prw command in my git-scripts GitHub repository (given it is not a one-liner script any longer :) and this repository also contains an up-to-date version of the git-pr command.

My scenario (close if not identical to yours) is maintaining a Github repo which others have forked, and submit PRs. Here's my process for reviewing forked PRs:

git remote add <forked-repo-org-or-name> <forked-repo-github-url>

Check the remote was added:

git remote -v

Fetch the forked repo for latest updates:

git fetch <forked-repo-org-or-name>

Checkout the forked PR branch:

git checkout <name-of-pr-branch>

Now you can locally test the branch, explore, and decide whether to merge, comment, etc.

There are a couple different ways but I’ll go over what I would do in this situation

git remote add <whatever you want the remote to be called> <link to the fork>

Here is the documentation. This will allow you to add and check out the remote branch from the fork. When reviewing PRs in the workflow you described I usually do a git clean -dfx (warning: this is a very intensive clean that gets rid of unstaged work you have), git remote add <whatever you want the remote to be called> <link to the fork>, and git checkout <branch name>.

If it’s in your repo already you can see that with git branch -a and simply check it out as you might otherwise.

Since you're using GitHub, my preferred way to do this is using the official GitHub CLI.

https://github.com/cli/cli

Using the GitHub CLI, you can execute

gh pr checkout 101

to checkout pr #101

There's also a lot of other useful commands regarding pull requests in the CLI.

You don't need access to the fork. Just fetch it from the main repo using the PR ref. If this is PR 3, then fetch pull/3/head like this:

git fetch origin pull/3/head:my_new_branch
git checkout my_new_branch

The PR is in your repo. That's the whole point of the fork-and-PR architecture.

So just git fetch and take a look however you like on your machine.

But when the time comes to merge, then yes, you will pull the branch from the fork. Fortunately you can have GitHub do that for you.

Specific answer for clean CI machine minimal checkouts that can work for Github PRs with a source branch from forks or not.

First, some environment variables for clarity:

# This is likely provided by the CI provider. Just the normal clone URL.
GIT_HTTP_URL = "git@github.com:organization/repo.git"
# This is the pull request number. Example: https://github.com/organization/repo/pull/14726
PULL_REQUEST_ID = 14726

Command to run for sparse checkout of PR:

$ git init
$ git remote add origin $GIT_HTTP_URL
$ git fetch --jobs=10 --no-tags --depth=1 origin "refs/pull/$PULL_REQUEST_ID/head:pull/$PULL_REQUEST_ID"
$ git checkout "pull/$PULL_REQUEST_ID"

This takes the PR checkout time (for my fairly large example repo) from ~1 minute to ~10 seconds.

Related