.. _build_sparse:

================
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.
