How actions/checkout Resolves Refs to Commits for Different Workflow Events

The actions/checkout action determines the exact commit SHA to checkout by analyzing the GitHub event context and running git rev-parse to resolve branch names, tags, or pull request refs into concrete 40-character hashes.

The actions/checkout action is essential for cloning repositories in GitHub Actions workflows, but before it can safely fetch code, it must resolve abstract references like branch names or PR numbers into specific commit SHAs. This resolution process varies depending on whether the workflow was triggered by a push, pull request, scheduled event, or manual dispatch. Understanding how actions/checkout resolves refs to commits according to the actions/checkout source code helps you debug checkout failures and optimize fetch performance.

The Core Resolution Architecture

The resolution flow is implemented in src/ref-helper.ts and consumed by src/input-helper.ts when the checkout step prepares the repository. The architecture separates context-aware ref extraction from the actual Git operations that convert references into immutable commit SHAs.

The ref-helper module contains the primary logic for interpreting GitHub's webhook payload, while input-helper orchestrates user-provided inputs like ref and fetch-depth with the context-derived values. The src/main.ts file serves as the entry point that wires these components together to perform the actual checkout.

Event-Specific Ref Resolution Logic

actions/checkout applies different resolution strategies based on github.context.eventName. Each event type provides the ref from a specific context field, which the action then converts to a SHA using git rev-parse.

Push Events

For standard push events, the action uses github.ref (e.g., refs/heads/main). The ref string is passed directly to git rev-parse, which returns the SHA of the tip of that branch. This same logic applies to tag pushes, where github.ref contains refs/tags/v1.2.3 and resolves to the commit the tag points at.

Pull Request Events

Pull request events require special handling. The action prefers github.event.pull_request.head.sha, which represents the exact commit at the PR's head.

If fetch-depth is greater than 1, the action may also fetch the merge commit at refs/pull/<number>/merge. When users explicitly request ref: refs/pull/<number>/merge, the action resolves that merge commit SHA instead of the PR head.

Workflow Dispatch Events

For workflow_dispatch triggers, the action checks for github.event.inputs.ref first. If the user supplied an explicit ref input, that string is taken verbatim (accepting branch names, tags, or full refs). If no input is provided, the action falls back to the same logic as push events using github.ref.

Scheduled and Repository Dispatch Events

Events like schedule or repository_dispatch provide the commit SHA directly in github.sha. Since this value is already a concrete 40-character hash, no further resolution via git rev-parse is required.

The Resolution Algorithm

The actual transformation from ref to commit SHA follows a strict algorithm implemented across several functions in src/ref-helper.ts.

getRef() – Context Extraction

The getRef() function reads the event payload from github.context.payload and extracts the appropriate ref based on github.context.eventName. For pull requests, it specifically checks payload.pull_request?.head?.sha, while for other events it selects the corresponding context field.

resolveRef() – SHA Conversion

Once a ref is identified, resolveRef() invokes git rev-parse --verify <ref> through the GitCommandManager class in src/git-command-manager.ts. This command converts symbolic references like main or v1.2.0 into exact commit hashes. If the ref cannot be resolved, the function catches the error and re-throws it with a descriptive message indicating which reference failed validation.

determineRef() – Input Handling

The determineRef() function combines explicit user inputs with context-derived refs. If the user provides a ref input in the workflow YAML, that value takes precedence. If the input is empty or omitted, the function uses the context-derived value from getRef().

Ref Sanitization and Validation

Before any Git operations occur, the helper sanitizes the ref string by trimming whitespace and normalizing shortcuts like heads/ or tags/ into full ref paths. The action validates the string against REF_REGEX to prevent injection attacks and ensure the ref conforms to Git's reference format.

Fetch Depth Handling

When fetch-depth is set to 1, the action performs a shallow fetch of only the needed ref. For pull request merge commits, the action may need to fetch multiple depths (both the merge commit and the PR head). If a shallow fetch does not contain the requested SHA, the action automatically falls back to a full fetch to ensure the commit is available.

Practical Code Examples

Configure actions/checkout to handle different ref resolution scenarios:


# Default behavior – resolves to the tip of the pushed branch

steps:
  - uses: actions/checkout@v4

# Explicit tag resolution

steps:
  - uses: actions/checkout@v4
    with:
      ref: 'v2.5.0'

# Workflow dispatch with user input

on:
  workflow_dispatch:
    inputs:
      branch:
        description: 'Branch to checkout'
        required: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.inputs.branch }}

# Shallow fetch of PR merge commit

steps:
  - uses: actions/checkout@v4
    with:
      fetch-depth: 1
      # For PR events, still resolves refs/pull/${{ github.event.number }}/merge

Summary

  • src/ref-helper.ts contains the core logic for extracting refs from GitHub context and resolving them to SHAs via git rev-parse.
  • Event type determines the source: push events use github.ref, pull requests use payload.pull_request.head.sha, and scheduled events use github.sha directly.
  • Explicit ref inputs override context-derived values when provided in the workflow configuration.
  • Sanitization occurs via REF_REGEX and whitespace trimming to prevent injection attacks.
  • Fetch depth optimization automatically adjusts between shallow and full fetches to ensure the resolved commit is available.

Frequently Asked Questions

What happens if I provide an invalid ref to actions/checkout?

The action calls git rev-parse --verify <ref> through the GitCommandManager in src/ref-helper.ts. If Git cannot resolve the reference, the action catches the error and throws a clear message indicating that the ref could not be found, preventing the workflow from proceeding with an ambiguous state.

How does actions/checkout decide between PR head and merge commit?

By default, actions/checkout resolves to the PR head SHA from github.event.pull_request.head.sha. However, if you explicitly set ref: refs/pull/<number>/merge or if the action needs to fetch additional history with fetch-depth greater than 1, it will resolve and checkout the merge commit instead.

Why does fetch-depth affect which commits are available?

When fetch-depth is 1, the action performs a shallow fetch that only includes the resolved SHA. If that SHA is a merge commit that requires additional parent commits to be present, the action may fail to find it. In this case, actions/checkout falls back to fetching the full history to ensure the commit exists in the local repository.

Can actions/checkout resolve lightweight tags and annotated tags differently?

Yes. When you provide a tag name like v1.2.0 via the ref input, the action passes it to git rev-parse, which resolves lightweight tags directly to the commit SHA and annotated tags to the commit object they point to. Both return the correct commit SHA for checkout regardless of tag type.

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 →