How fetch-tags Works in actions/checkout: Source Code Deep Dive
The fetch-tags input in actions/checkout controls whether Git tags are fetched during repository checkout by conditionally adding the ref-spec +refs/tags/*:refs/tags/* to the fetch operation based on the Boolean value parsed in src/input-helper.ts.
The actions/checkout action is the standard method for cloning repositories in GitHub Actions workflows. While the fetch-tags input appears simple, its implementation involves sophisticated ref-spec manipulation in the TypeScript source. This article examines how the action processes this input, from YAML parsing in src/input-helper.ts to the Git command generation in src/git-source-provider.ts.
Parsing the fetch-tags Input in src/input-helper.ts
The lifecycle begins in src/input-helper.ts, where the getInputs() function processes workflow configuration. The action reads the fetch-tags input as a string and converts it to a Boolean by comparing the uppercase value to 'TRUE'.
// src/input-helper.ts
result.fetchTags =
(core.getInput('fetch-tags') || 'false').toUpperCase() === 'TRUE';
This conversion means any value other than true or TRUE defaults to false, including empty strings or omitted inputs.
Ref-Spec Construction Logic in src/ref-helper.ts
The core logic resides in src/ref-helper.ts, which defines the tagsRefSpec constant and the getRefSpec() function. This function determines whether to include tag references based on the fetchTags parameter and the nature of the requested ref.
// src/ref-helper.ts
const tagsRefSpec = '+refs/tags/*:refs/tags/*';
function getRefSpec(ref, commit, fetchTags) {
// …
if (fetchTags) {
result.push(tagsRefSpec); // always fetch tags
}
// …
else if (!upperRef.startsWith('REFS/')) {
// unqualified ref – fetch tags only if fetchTags is false
if (!fetchTags) {
result.push(`+refs/tags/${ref}*:refs/tags/${ref}*`);
}
}
}
When fetch-tags is set to true, the action always appends +refs/tags/*:refs/tags/* to the fetch ref-specs. When false (the default), tags are only fetched for unqualified refs—plain tag names that do not start with refs/.
Fetch Execution and Tag Verification in src/git-source-provider.ts
In src/git-source-provider.ts, the orchestration layer calls getRefSpec() with the settings from the input helper, then executes the fetch:
// src/git-source-provider.ts
const refSpec = getRefSpec(settings.ref, settings.commit, settings.fetchTags);
await git.fetch(refSpec, fetchOptions);
After fetching, the action validates tag integrity using testRef() to ensure fetched tags point to the commits that triggered the workflow. This prevents race conditions where a tag moves between the trigger event and the checkout operation.
Practical Workflow Examples
Here are common patterns for using fetch-tags in .github/workflows/:
# .github/workflows/checkout-tags.yml
name: Checkout with tags
on: [push]
jobs:
demo:
runs-on: ubuntu-latest
steps:
# 1️⃣ Default – tags are NOT fetched
- uses: actions/checkout@v4
# 2️⃣ Explicitly fetch all tags (useful for release workflows)
- uses: actions/checkout@v4
with:
fetch-tags: true # <-- forces +refs/tags/*:refs/tags/*
# 3️⃣ Fetch only a single tag (unqualified ref)
- uses: actions/checkout@v4
with:
ref: v1.2.3 # unqualified tag name; tags fetched automatically
In the first step, only the branch commit is fetched. The second step pulls every tag in the repository. The third step automatically adds tag-specific ref-specs because v1.2.3 is an unqualified ref, even without setting fetch-tags: true.
Summary
- Input parsing:
src/input-helper.tsconvertsfetch-tagsfrom string to Boolean using case-insensitive comparison to'TRUE'. - Ref-spec logic:
src/ref-helper.tsconditionally includes+refs/tags/*:refs/tags/*whenfetchTagsis true, or adds specific tag patterns for unqualified refs when false. - Default behavior: Tags are not fetched unless the ref is an unqualified tag name or
fetch-tags: trueis explicitly set. - Security:
src/git-source-provider.tsvalidates fetched tags against the original commit usingtestRef()to prevent moving tag attacks. - Performance: Omitting
fetch-tagsreduces network traffic and speeds up shallow clones by excluding tag references.
Frequently Asked Questions
What is the default value of fetch-tags in actions/checkout?
By default, fetch-tags is false when omitted from the workflow configuration. The input parser in src/input-helper.ts defaults the value to 'false' before converting it to a Boolean, meaning tags are not fetched unless explicitly requested or the ref is an unqualified tag name.
Does fetch-tags: true fetch all tags or only specific ones?
Setting fetch-tags: true fetches all tags in the repository. According to src/ref-helper.ts, this setting causes the action to include +refs/tags/*:refs/tags/* in the fetch ref-specs, which pulls every tag reference. To fetch only a specific tag without pulling all tags, use the ref input with an unqualified tag name like v1.0.0 instead of setting fetch-tags.
When should I use fetch-tags: true versus leaving it as the default?
Use fetch-tags: true when your workflow needs access to the complete tag history, such as release automation, changelog generation, or version comparison scripts. Leave it as the default (false) for standard CI builds where only the specific commit matters, as this reduces fetch time and network bandwidth by skipping tag objects.
How does actions/checkout verify fetched tags after checkout?
After executing git.fetch(), the action calls testRef() in src/git-source-provider.ts to validate that any fetched tag still points to the expected commit SHA. This verification prevents the checkout from succeeding if a tag was moved between the workflow trigger and the fetch operation, ensuring reproducible builds.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →