Sparse Checkouts

The Firefox repository is large: over 230,000 files. That many files can put a lot of strain on machines, tools, and processes.

Some version control tools have the ability to only populate a working directory / checkout with a subset of files in the repository. This is called sparse checkout.

Various tools in the Firefox repository are configured to work when a sparse checkout is being used.

Sparse Checkouts in Mercurial

Mercurial 4.3 introduced experimental support for sparse checkouts in the official distribution (a Facebook-authored extension has implemented the feature as a 3rd party extension for years).

To enable sparse checkout support in Mercurial, enable the sparse extension:

[extensions]
sparse =

The sparseness of the working directory is managed using hg debugsparse. Run hg help debugsparse and hg help -e sparse for more info on the feature.

When a sparse config is enabled, the working directory only contains files matching that config. You cannot hg add or hg remove files outside the sparse config.

Warning

Sparse support in Mercurial 4.3 does not have any backwards compatibility guarantees. Expect things to change. Scripting against commands or relying on behavior is strongly discouraged.

In-Tree Sparse Profiles

Mercurial supports defining the sparse config using files under version control. These are called sparse profiles.

Essentially, the sparse profiles are managed just like any other file in the repository. When you hg update, the sparse configuration is evaluated against the sparse profile at the revision being updated to. From an end-user perspective, you just need to activate a profile once and files will be added or removed as appropriate whenever the versioned profile file updates.

In the Firefox repository, the build/sparse-profiles directory contains Mercurial sparse profiles files.

Each sparse profile essentially defines a list of file patterns (see hg help patterns) to include or exclude. See hg help -e sparse for more.

Mach Support for Sparse Checkouts

mach detects when a sparse checkout is being used and its behavior may vary to accommodate this.

By default it is a fatal error if mach can’t load one of the mach_commands.py files it was told to. But if a sparse checkout is being used, mach assumes that file isn’t part of the sparse checkout and to ignore missing file errors. This means that running mach inside a sparse checkout will only have access to the commands defined in files in the sparse checkout.

Sparse Checkouts in Automation

hg robustcheckout (the extension/command used to perform clones and working directory operations in automation) supports sparse checkout. However, it has a number of limitations over Mercurial’s default sparse checkout implementation:

  • Only supports 1 profile at a time

  • Does not support non-profile sparse configs

  • Does not allow transitioning from a non-sparse to sparse checkout or vice-versa

These restrictions ensure that any sparse working directory populated by hg robustcheckout is as consistent and robust as possible.

run-task (the low-level script for bootstrapping tasks in automation) has support for sparse checkouts.

TaskGraph tasks using run-task can specify a sparse-profile attribute in YAML (or in code) to denote the sparse profile file to use. e.g.:

run:
    using: run-command
    command: <command>
    sparse-profile: taskgraph

This automagically results in run-task and hg robustcheckout using the sparse profile defined in build/sparse-profiles/<value>.

Pros and Cons of Sparse Checkouts

The benefits of sparse checkout are that it makes the repository appear to be smaller. This means:

  • Less time performing working directory operations -> faster version control operations

  • Fewer files to consult -> faster operations

  • Working directories only contain what is needed -> easier to understand what everything does

Fewer files in the working directory also contributes to disadvantages:

  • Searching may not yield hits because a file isn’t in the sparse checkout. e.g. a global search and replace may not actually be global after all.

  • Tools performing filesystem walking or path globbing (e.g. **/*.js) may fail to find files because they don’t exist.

  • Various tools and processes make assumptions that all files in the repository are always available.

There can also be problems caused by mixing sparse and non-sparse checkouts. For example, if a process in automation is using sparse and a local developer is not using sparse, things may work for the local developer but fail in automation (because a file isn’t included in the sparse configuration and not available to automation. Furthermore, if environments aren’t using exactly the same sparse configuration, differences can contribute to varying behavior.

When Should Sparse Checkouts Be Used?

Developers are discouraged from using sparse checkouts for local work until tools for handling sparse checkouts have improved. In particular, Mercurial’s support for sparse is still experimental and various Firefox tools make assumptions that all files are available. Developers should use sparse checkout at their own risk.

The use of sparse checkouts in automation is a performance versus robustness trade-off. Use of sparse checkouts will make automation faster because machines will only have to manage a few thousand files in a checkout instead of a few hundred thousand. This can potentially translate to minutes saved per machine day. At the scale of thousands of machines, the savings can be significant. But adopting sparse checkouts will open up new avenues for failures. (See section above.) If a process is isolated (in terms of file access) and well-understood, sparse checkout can likely be leveraged with little risk. But if a process is doing things like walking the filesystem and performing lots of wildcard matching, the dangers are higher.