How to Checkout a Pull Request with actions/checkout: 4 Methods Explained

To checkout a pull request with actions/checkout, set the ref input to ${{ github.event.pull_request.head.sha }} for the HEAD commit, or use ${{ github.head_ref }} for closed events; for forked PRs in unsafe contexts like pull_request_target, you must additionally set allow-unsafe-pr-checkout: true after security review.

The actions/checkout repository is the official GitHub Action for retrieving repository code in GitHub Actions workflows. When configuring workflows to checkout a pull request with actions/checkout correctly, you must understand that the default behavior retrieves the merge commit rather than the actual PR head, which affects how you configure the ref parameter.

Default Behavior: The Merge Commit

Without specifying the ref input, actions/checkout defaults to $GITHUB_SHA. For a pull_request event, this environment variable contains the merge commit that GitHub automatically creates between the PR branch and the base branch. This merge commit represents the state of the code if the PR were merged at that moment.

According to the actions/checkout source code, the default ref value is defined in [action.yml](https://github.com/actions/checkout/blob/main/action.yml) and resolves to the event's reference when unspecified.

Method 1: Checkout the PR HEAD Commit

To retrieve the exact commit from the source branch (the "head" commit) rather than the merge commit, you must explicitly set the ref input to ${{ github.event.pull_request.head.sha }}.

This configuration points the checkout directly to the forked branch's latest commit, bypassing the temporary merge commit entirely. The actions/checkout README documents this specific pattern under the section "Checkout pull request HEAD commit instead of merge commit" [README § PR HEAD].

Method 2: Checkout Forked PRs in Unsafe Contexts

When workflows run on pull_request_target or workflow_run events, the runner executes in the context of the base repository using the repository's GITHUB_TOKEN. By default, the action refuses to checkout code from forked pull requests in these contexts to prevent "pwn request" vulnerabilities where malicious code could access base repository secrets.

According to the actions/checkout source code, the security guard implementation resides in [src/unsafe-pr-checkout-helper.ts](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts). This helper validates whether the PR originates from a fork and throws an error unless you explicitly opt-in by setting allow-unsafe-pr-checkout: true in your workflow configuration.

The input handling logic in [src/input-helper.ts](https://github.com/actions/checkout/blob/main/src/input-helper.ts) reads this boolean flag and passes it to the validation helper. Only after this verification does the action proceed with the git operations.

Method 3: Checkout on Closed Events

For workflows triggered by pull_request events with type closed, you must still specify the ref input because the checkout runs in detached HEAD mode with no active branch reference. Use ${{ github.head_ref }} to reference the source branch name, or specify the exact SHA.

The actions/checkout README documents this requirement in the "Checkout pull request on closed event" section [README § Closed PR].

Implementation Architecture

The checkout process follows this execution flow:

  1. Input Resolution: The workflow passes inputs to the JavaScript entry point, where [src/input-helper.ts](https://github.com/actions/checkout/blob/main/src/input-helper.ts) normalizes the ref and allow-unsafe-pr-checkout values.

  2. Security Validation: If the workflow runs in pull_request_target or workflow_run contexts and the PR originates from a fork, [src/unsafe-pr-checkout-helper.ts](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts) validates the request. It terminates the action with an error unless allow-unsafe-pr-checkout is set to true.

  3. Git Execution: After validation, the action constructs and executes the appropriate git fetch and git checkout commands based on the resolved reference.

  4. Workspace Preparation: The repository is placed under $GITHUB_WORKSPACE, ready for subsequent workflow steps.

Code Examples

Checkout the HEAD commit of a standard PR

- uses: actions/checkout@v7
  with:
    ref: ${{ github.event.pull_request.head.sha }}

Checkout a forked PR in pull_request_target (unsafe context)

- uses: actions/checkout@v7
  with:
    ref: ${{ github.event.pull_request.head.sha }}
    allow-unsafe-pr-checkout: true  # ⚠️ Review security implications first

Checkout a PR on the closed event

on:
  pull_request:
    types: [closed]

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          ref: ${{ github.head_ref }}  # Source branch name

Summary

  • Default behavior: Without ref, the action checks out the merge commit ($GITHUB_SHA), not the PR head.
  • PR HEAD checkout: Use ref: ${{ github.event.pull_request.head.sha }} to get the exact source branch commit.
  • Forked PR security: In pull_request_target or workflow_run contexts, fork PRs are blocked unless allow-unsafe-pr-checkout: true is set, as enforced by [src/unsafe-pr-checkout-helper.ts](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts).
  • Closed events: Explicitly set ref to ${{ github.head_ref }} when handling closed event types.

Frequently Asked Questions

What is the difference between the merge commit and HEAD commit in a PR checkout?

The merge commit is a temporary commit GitHub creates that merges the PR branch into the base branch, representing the post-merge state. The HEAD commit is the exact last commit on the source branch of the pull request. Use ref: ${{ github.event.pull_request.head.sha }} to checkout the HEAD, or leave ref unspecified to get the merge commit.

Why am I getting an error when trying to checkout a forked pull request?

The action blocks checkouts of forked PRs in pull_request_target or workflow_run contexts to prevent privilege escalation attacks. As implemented in [src/unsafe-pr-checkout-helper.ts](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts), you must set allow-unsafe-pr-checkout: true to override this protection, but only after reviewing the security implications of exposing base repository secrets to fork code.

Can I checkout a pull request after it has been closed?

Yes, but you must explicitly specify the ref input because the closed event runs in detached HEAD mode. Use ref: ${{ github.head_ref }} to checkout the source branch name, or provide the specific SHA. This pattern is documented in the README's closed event section.

Is it safe to use allow-unsafe-pr-checkout: true?

This setting is safe only when you have reviewed the code in the pull request and trust the changes, as it allows forked PR code to run in a context that has access to your repository's secrets. The [src/unsafe-pr-checkout-helper.ts](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts) file implements this guard specifically to prevent accidental exposure of sensitive tokens to potentially malicious fork code.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →