# How actions/checkout Handles Git Submodules: Implementation and Configuration

> Discover how the actions/checkout action expertly manages Git submodules using three configurable modes. Learn implementation and configuration details to synchronize your submodules effectively.

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

---

**The actions/checkout action handles Git submodules through three configurable modes that execute native Git commands to synchronize and update submodule references after cloning the parent repository.**

The `actions/checkout` action is the standard method for checking out code in GitHub Actions workflows. When your repository contains Git submodules, understanding exactly how actions/checkout handles submodules ensures your CI/CD pipelines correctly fetch all required dependencies.

## Parsing the Submodule Input

The process begins in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts), where the action parses the `submodules` input and normalizes it into two distinct settings: a boolean `submodules` flag and a `nestedSubmodules` recursion flag.

```typescript
// src/input-helper.ts (lines 127-138)
const submodulesString = (core.getInput('submodules') || '').toUpperCase()
if (submodulesString == 'RECURSIVE') {
  result.submodules = true
} else if (submodulesString == 'TRUE') {
  result.submodules = true
}
core.debug(`submodules = ${result.submodules}`)
core.debug(`recursive submodules = ${result.nestedSubmodules}`)

```

This logic supports three distinct behaviors:

- **`false`** (default): Submodules are ignored entirely
- **`true`**: Top-level submodules are fetched non-recursively
- **`recursive`**: All submodules including nested ones are fetched recursively

## The Submodule Checkout Flow

After the main repository fetch completes, [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) orchestrates the submodule operations. The action only proceeds with submodule handling when `settings.submodules` evaluates to true.

```typescript
// src/git-source-provider.ts (lines 74-97)
if (settings.submodules) {
  core.startGroup('Setting up auth for fetching submodules')
  await authHelper.configureGlobalAuth()
  core.endGroup()

  core.startGroup('Fetching submodules')
  await git.submoduleSync(settings.nestedSubmodules)
  await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
  await git.submoduleForeach(
    'git config --local gc.auto 0',
    settings.nestedSubmodules
  )
  core.endGroup()
}

```

The flow executes three critical Git operations in sequence:

1. **`submoduleSync`**: Aligns submodule URLs with the parent repository configuration using `git submodule sync`
2. **`submoduleUpdate`**: Checks out the specific commits referenced by the parent repository using `git submodule update --init --force`
3. **`submoduleForeach`**: Disables automatic garbage collection within each submodule by running `git config --local gc.auto 0`

## Git Command Implementation

The actual Git operations are delegated to `GitCommandManager` in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts). This wrapper abstracts the native Git CLI calls into typed methods that respect the `fetch-depth` and `recursive` parameters:

- **`submoduleSync(recursive: boolean)`**: Executes `git submodule sync` to update remote URLs
- **`submoduleUpdate(fetchDepth: number, recursive: boolean)`**: Runs `git submodule update --init --force` with optional `--depth` and `--recursive` flags
- **`submoduleForeach(command: string, recursive: boolean)`**: Iterates through submodules executing the provided command

These methods ensure that shallow fetching and recursive traversal are handled according to the workflow inputs.

## Authentication and Credential Persistence

Submodules often require the same authentication as the parent repository. When `persist-credentials` is set to true, the action propagates authentication configuration to each submodule after fetching:

```typescript
// src/git-source-provider.ts (lines 92-96)
if (settings.persistCredentials) {
  core.startGroup('Persisting credentials for submodules')
  await authHelper.configureSubmoduleAuth()
  core.endGroup()
}

```

This call to `configureSubmoduleAuth()` in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) ensures that subsequent Git operations within submodules—such as pushing changes back to remote servers—can authenticate using the same token or SSH key configured for the primary repository.

## Edge Cases and Limitations

The implementation includes specific safeguards for edge cases:

- **REST API Fallback**: When Git is not available on the runner (forcing a fallback to the REST API), submodule support is explicitly disabled and the action throws an error rather than attempting partial submodule handling
- **Worktree Cleaning**: The `submoduleStatus` helper in [`src/git-directory-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-directory-helper.ts) detects existing submodule states to determine whether the worktree requires cleaning before checkout, preventing conflicts with orphaned submodule directories

## Configuration Examples

Configure submodule handling in your workflow using the `submodules` input:

```yaml

# Fetch only top-level submodules

- uses: actions/checkout@v7
  with:
    submodules: true

```

```yaml

# Recursive submodule checkout with full history

- uses: actions/checkout@v7
  with:
    submodules: recursive
    fetch-depth: 0

```

```yaml

# Disable submodules (default behavior)

- uses: actions/checkout@v7

```

```yaml

# Persist credentials for submodule operations

- uses: actions/checkout@v7
  with:
    submodules: recursive
    persist-credentials: true

```

## Summary

- **actions/checkout supports three submodule modes**: `false` (default), `true` (top-level only), and `recursive` (nested submodules)
- **Implementation spans four key files**: [`input-helper.ts`](https://github.com/actions/checkout/blob/main/input-helper.ts) for parsing, [`git-source-provider.ts`](https://github.com/actions/checkout/blob/main/git-source-provider.ts) for orchestration, [`git-command-manager.ts`](https://github.com/actions/checkout/blob/main/git-command-manager.ts) for Git CLI execution, and [`git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/git-auth-helper.ts) for credential propagation
- **Execution sequence**: Sync URLs, update commits, disable GC—executed only after the parent repository fetch completes
- **Authentication automatically propagates** to submodules when `persist-credentials` is enabled, ensuring private submodule repositories remain accessible
- **Shallow fetching** via `fetch-depth` applies to submodules when specified, optimizing clone performance for large repositories

## Frequently Asked Questions

### What happens if I don't specify the submodules input?

By default, `actions/checkout` ignores submodules entirely. The `submodules` input defaults to `false`, meaning the action clones only the parent repository without fetching any submodule content. This minimizes clone time and network usage for workflows that do not require external dependencies.

### Does actions/checkout support recursive submodules?

Yes. Set `submodules: recursive` to fetch all submodules including nested ones. According to the source code in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts), this passes the `--recursive` flag to `git submodule update`, ensuring that submodules within submodules are also initialized and updated.

### How does authentication work for private submodule repositories?

The action reuses the same authentication mechanism used for the parent repository. When `persist-credentials` is true, [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) configures authentication for each submodule via `configureSubmoduleAuth()`. This injects the same GitHub token or SSH key into each submodule's Git configuration, allowing access to private repositories without additional secrets.

### Can I use a shallow fetch with submodules?

Yes. When you specify `fetch-depth` (for example, `fetch-depth: 1`), the action passes this depth parameter to `submoduleUpdate()` in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts). However, shallow submodules may cause issues if subsequent build steps require full git history within those submodules. Use `fetch-depth: 0` for full history when needed.