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

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

The boolean input sparse-checkout-cone-mode defaults to true for backward-compatible performance. In src/input-helper.ts (lines 118–121), the input helper parses this flag:

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

The provider logic in src/git-source-provider.ts (lines 261–266) acts as the decision router based on the parsed setting:

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

The core logic resides in src/git-command-manager.ts within the sparseCheckoutNonConeMode function. This method manually enables sparse checkout and appends patterns to Git's configuration:

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 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:

- 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:

- 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 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, 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 before attempting any sparse-checkout operations, silently disabling the feature if the runner's Git is too old.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →