# How Non-Cone Mode Sparse Checkout Works in actions/checkout

> Understand how non-cone mode sparse checkout in actions/checkout bypasses Git's cone-mode algorithm using legacy pattern matching for flexible repository checkouts.

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

---

**Non-cone mode sparse checkout manually enables `core.sparseCheckout` and writes patterns directly to the `.git/info/sparse-checkout` file, bypassing Git's native cone-mode algorithm to support legacy pattern matching unavailable in the default implementation.**

The `actions/checkout` repository supports sparse checkout to clone only specific portions of a monorepo. While **cone mode** (the default since Git 2.25) offers superior performance, the action preserves **non-cone mode** for workflows requiring complex glob patterns or negative exclusions. When `sparse-checkout-cone-mode` is set to `false`, the action reverts to manually editing Git's internal sparse-checkout configuration rather than invoking modern Git commands.

## How Non-Cone Mode Differs from Cone Mode

Cone mode uses the `git sparse-checkout set` command, which optimizes pattern matching by assuming patterns represent directory cones (prefix matches). This limits patterns to directory names and simple wildcards but executes faster.

**Non-cone mode** removes these restrictions by writing patterns directly to `.git/info/sparse-checkout`. This allows complex patterns like `**/build/` or `!**/test/` but requires the legacy sparse-checkout implementation where Git evaluates every pattern against every path.

## Implementation Details in the Source Code

The action implements non-cone mode through a three-stage pipeline in the TypeScript source.

### Parsing the Cone Mode Flag in [`input-helper.ts`](https://github.com/actions/checkout/blob/main/input-helper.ts)

The boolean input `sparse-checkout-cone-mode` defaults to `true` for backward-compatible performance. In [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 118–121), the input helper parses this flag:

```typescript
result.sparseCheckoutConeMode =
  (core.getInput('sparse-checkout-cone-mode') || 'true').toUpperCase() ===
  'TRUE'

```

If the workflow sets `sparse-checkout-cone-mode: false`, this value becomes `false`, triggering the legacy path.

### Routing to Non-Cone Mode in [`git-source-provider.ts`](https://github.com/actions/checkout/blob/main/git-source-provider.ts)

The provider logic in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) (lines 261–266) acts as the decision router based on the parsed setting:

```typescript
if (settings.sparseCheckoutConeMode) {
    await git.sparseCheckout(settings.sparseCheckout)
} else {
    await git.sparseCheckoutNonConeMode(settings.sparseCheckout)
}

```

When **non-cone mode** is selected, the code invokes `sparseCheckoutNonConeMode` instead of the standard `sparseCheckout` method.

### The Manual Implementation in [`git-command-manager.ts`](https://github.com/actions/checkout/blob/main/git-command-manager.ts)

The core logic resides in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts) within the `sparseCheckoutNonConeMode` function. This method manually enables sparse checkout and appends patterns to Git's configuration:

```typescript
async sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void> {
    await this.execGit(['config', 'core.sparseCheckout', 'true'])
    const output = await this.execGit(['rev-parse', '--git-path', 'info/sparse-checkout'])
    const sparseCheckoutPath = path.join(this.workingDirectory, output.stdout.trimRight())
    await fs.promises.appendFile(sparseCheckoutPath, `\n${sparseCheckout.join('\n')}\n`)
}

```

This implementation:
1. Enables `core.sparseCheckout` via Git config
2. Resolves the path to `.git/info/sparse-checkout` using `rev-parse --git-path`
3. Appends each pattern on a new line to the sparse-checkout file

### Version Guarding

Before executing either path, [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) validates that the runner's Git version meets `MinimumGitSparseCheckoutVersion`. If the installed Git predates sparse-checkout support, the action disables the feature entirely regardless of mode selection.

## Workflow Configuration Examples

To enable **non-cone mode sparse checkout**, explicitly set `sparse-checkout-cone-mode: false` in your workflow:

```yaml
- uses: actions/checkout@v7
  with:
    sparse-checkout: |
      src/
      docs/
      !**/test/
    sparse-checkout-cone-mode: false

```

For comparison, the default cone-mode configuration (which uses `git sparse-checkout set` internally) looks like this:

```yaml
- uses: actions/checkout@v7
  with:
    sparse-checkout: |
      src/
      docs/
    sparse-checkout-cone-mode: true  # Default behavior

```

The non-cone example supports negative patterns (`!**/test/`) that cone mode would reject, while the cone example limits you to directory prefixes.

## Summary

- **Non-cone mode** in `actions/checkout` implements legacy sparse-checkout by directly manipulating `.git/info/sparse-checkout` rather than using `git sparse-checkout set`.
- The `sparseCheckoutNonConeMode` function in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts) handles the manual configuration by enabling `core.sparseCheckout` and appending patterns to the sparse-checkout file.
- This mode activates only when `sparse-checkout-cone-mode: false` is specified, falling back from the default cone-mode algorithm implemented in `sparseCheckout`.
- Use non-cone mode when you require complex glob patterns or negative exclusions unsupported by Git's cone-mode parser.

## Frequently Asked Questions

### What is the difference between cone mode and non-cone mode sparse checkout?

**Cone mode** uses Git's native `sparse-checkout set` command optimized for directory-prefix patterns, offering faster performance but limited pattern syntax. **Non-cone mode** manually writes patterns to `.git/info/sparse-checkout`, supporting complex globs and negative patterns at the cost of slower path evaluation.

### When should I use non-cone mode instead of the default cone mode?

Use non-cone mode when your sparse-checkout patterns include negative exclusions (e.g., `!**/node_modules/`), complex wildcards (e.g., `**/build/`), or globstar patterns that fall outside cone-mode's directory-prefix assumptions. For simple directory filtering, cone mode remains the recommended default.

### How do I enable non-cone mode in my GitHub Actions workflow?

Set `sparse-checkout-cone-mode: false` in your `actions/checkout` step configuration. The action then routes to `sparseCheckoutNonConeMode` in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts), which manually configures `core.sparseCheckout` and writes your patterns directly to the repository's sparse-checkout file.

### Does non-cone mode work with all Git versions?

No, sparse checkout (in either mode) requires Git version 2.25 or later. The action checks `MinimumGitSparseCheckoutVersion` in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) before attempting any sparse-checkout operations, silently disabling the feature if the runner's Git is too old.