# How actions/checkout Validates Moved Tags After Fetching: A Deep Dive into the Verification Logic

> actions/checkout validates moved tags after fetching by comparing commit SHAs. Learn how this verification logic prevents workflow issues.

- Repository: [GitHub Actions/checkout](https://github.com/actions/checkout)
- Tags: deep-dive
- Published: 2026-08-30

---

**When checking out a tag, actions/compare validates that the tag still points to the same commit SHA after fetching by comparing the pre-fetch resolved SHA with a post-fetch rev-parse, aborting the workflow if the tag has moved.**

The `actions/checkout` action is the standard method for cloning repositories in GitHub Actions workflows. When you specify a tag as the checkout target, the action implements a critical safeguard against race conditions where a tag might be repointed to a different commit after the workflow triggers but before the fetch completes.

## Why Tags Require Post-Fetch Validation

In Git, tags are mutable references that can be deleted and recreated or force-updated to point to different commits. The validation logic in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) explicitly addresses the scenario where "the tag was moved after the workflow was triggered" (line 204). Without verification, a workflow could end up executing code from an unexpected commit if a maintainer moved the tag during the job queueing time.

## The Three-Phase Validation Pipeline

The verification process is implemented in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) and follows a strict sequence to ensure integrity.

### Phase 1: Capturing the Expected Commit SHA

Before any network operations begin, the action resolves the requested tag to a specific commit SHA using `git.revParse()`. This creates an immutable baseline representing the state of the repository at the moment the workflow started. The code stores this expected SHA for later comparison.

### Phase 2: Fetching with Tag RefSpecs

When the `fetch-tags` input is set to `true`, the action constructs a specific refspec in [`src/ref-helper.ts`](https://github.com/actions/checkout/blob/main/src/ref-helper.ts) (line 6) as `+refs/tags/*:refs/tags/*`. This refspec forces the fetch operation to update local tag references even if they already exist. The implementation in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts) ensures that tags are explicitly fetched when requested, rather than using the default `--no-tags` behavior.

### Phase 3: Post-Fetch SHA Verification

After the fetch completes, the action performs the critical validation. According to the comments in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts):
- Line 196 notes that "when all history is fetched, the ref we're interested in may have moved to a different..."
- Line 221 explicitly states: "For tags, verify the ref still points to the expected commit."

The implementation resolves the tag a second time using `git.revParse(\`${ref}^{commit}\`)` and compares this fresh SHA against the value captured in Phase 1. If the SHAs differ, the action throws an error:

```bash
Error: Tag <tag> was moved after the workflow was triggered.

```

This immediate failure prevents the workflow from proceeding with potentially incorrect or malicious code.

## Practical Workflow Example

Here is a configuration that enables the moved-tag validation:

```yaml
name: Release Build
on: 
  push:
    tags:
      - 'v*'
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.ref }}
          fetch-tags: true
          fetch-depth: 0

```

If the tag is repointed between the workflow trigger and the checkout execution, the step fails immediately with the moved-tag error, protecting your supply chain.

## Key Implementation Files

- **[`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts)** (lines 196-221): Contains the core verification logic that implements the comment "For tags, verify the ref still points to the expected commit" and performs the SHA comparison.
- **[`src/ref-helper.ts`](https://github.com/actions/checkout/blob/main/src/ref-helper.ts)** (line 6): Defines the tag refspec `+refs/tags/*:refs/tags/*` used when `fetch-tags` is enabled.
- **[`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts)**: Implements the `revParse()` method used to resolve tags to their underlying commit SHAs before and after fetching.
- **[`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts)** (lines 129-132): Processes the boolean `fetch-tags` input to determine whether the validation path should be executed.

## Summary

- **Pre-fetch resolution**: The action captures the expected commit SHA before any network operations to establish a trusted baseline.
- **Explicit tag fetching**: When enabled via `fetch-tags: true`, the action uses the refspec `+refs/tags/*:refs/tags/*` to ensure local tag references are updated.
- **SHA comparison**: After fetching, the action re-resolves the tag and aborts the workflow if the commit SHA has changed from the expected value.
- **Security safeguard**: This validation prevents supply-chain attacks where a tag is moved to malicious code after the workflow starts but before checkout completes.

## Frequently Asked Questions

### What happens if a tag is moved during the checkout process?

The action throws `Error: Tag <tag> was moved after the workflow was triggered.` and fails the step immediately. This error originates in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) when the post-fetch SHA comparison detects a mismatch.

### Does actions/checkout validate moved branches as well?

No. The validation logic specifically targets tags because they are expected to be immutable release references. Branches are inherently mutable, so the action does not perform post-fetch SHA verification for branch refs.

### How can I enable tag validation in my workflow?

Set `fetch-tags: true` in your checkout step inputs. Without this setting, the action uses `--no-tags` during fetch (as implemented in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts)), and the validation logic is never reached because the local tag reference remains unchanged.

### Is this validation performed for both lightweight and annotated tags?

Yes. The validation works for both types because the code uses `git rev-parse <ref>^{commit}` to peel the tag to its underlying commit object. This ensures the SHA comparison is always performed against the actual commit, regardless of whether the tag is lightweight or annotated.