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

> actions/checkout resolves refs to commits for workflow events by parsing GitHub event context and using git rev-parse to find the exact commit SHA.

- Repository: [GitHub Actions/checkout](https://github.com/actions/checkout)
- Tags: internals
- Published: 2026-07-16

---

**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`](https://github.com/actions/checkout/blob/main/src/ref-helper.ts)** and consumed by **[`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/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`](https://github.com/actions/checkout/blob/main/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`](https://github.com/actions/checkout/blob/main/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`](https://github.com/actions/checkout/blob/main/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:

```yaml

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

steps:
  - uses: actions/checkout@v4

```

```yaml

# Explicit tag resolution

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

```

```yaml

# 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 }}

```

```yaml

# 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`](https://github.com/actions/checkout/blob/main/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`](https://github.com/actions/checkout/blob/main/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.