Skip to content

feat(run-task): add git sparse checkout support - #1039

Open
ahochheiden wants to merge 3 commits into
taskcluster:mainfrom
ahochheiden:sparse-checkout
Open

feat(run-task): add git sparse checkout support#1039
ahochheiden wants to merge 3 commits into
taskcluster:mainfrom
ahochheiden:sparse-checkout

Conversation

@ahochheiden

@ahochheiden ahochheiden commented Sep 10, 2026

Copy link
Copy Markdown

Fixes #1025

run-task gains sparse checkouts for git clones. The task supplies native git patterns in <REPO>_SPARSE_PATTERNS as a JSON list, and names the profile with --<repo>-sparse-profile. A sparse clone is blobless and applies the patterns before its first checkout, so only the profile's files are written or fetched.

Checkout caches are shared between sparse and full tasks and only ever widen:

  • A full task widens a sparse cache.
  • A sparse task on a full cache runs as-is.
  • A sparse task on a differently sparse cache appends its patterns.

A profile made only of paths uses cone mode with the sparse index.

On the Firefox side this is supported by Bug 2071081, which translates the Mercurial sparse profiles into these patterns, and Bug 2071092, which keys checkout caches on the run-task a task actually runs. Delivering the patterns to git tasks lands after a release with this change is pinned.

@ahochheiden
ahochheiden requested a review from a team as a code owner September 10, 2026 23:08
…ronment

The patterns travel in `<REPO>_SPARSE_PATTERNS` as a JSON list of native git
patterns, so the cloned revision does not need to contain the profile. A JSON
list because generic-worker on Windows sets task environment variables through
a batch `set` line, which cuts a value at its first newline. The option
and the variable have to come together, and a checkout is required for either.
Patterns go through stdin so a name starting with a dash is not taken for an
option. A full task on a sparse cache drops the sparse checkout first. On the
Mercurial path the option goes to robustcheckout as --sparseprofile.

A sparse clone is blobless (--filter=blob:none) and defers its checkout until
the patterns are set, so only files inside the profile are written or fetched.
Git keeps that filter on the origin remote and a fetch only inherits it when
it names that remote, so run-task fetches through origin whenever the head
repository is the repository it cloned, and points origin at the task's
repository first so a cached clone follows a repository that moved. git 2.49
keeps the filter for a fetch by matching URL as well, but that is not
documented and the workers run other versions.

Refs taskcluster#1025
Sparse and full tasks in a worker pool share one checkout cache, and this
change decides what happens when a task finds a cache another kind of task
left behind: the cache only ever grows. A full task on a sparse cache widens
it to a full checkout, and every later task on that worker runs full. A sparse
task on a full cache runs on it as is. A sparse task on a cache with another
sparse profile appends its own patterns with `sparse-checkout add`, so the
cache ends up covering both profiles. Appending is why every translated
pattern has to be positive.

Whatever grows the cache runs after the checkout of the target revision, so a
widen or an addition fetches that revision's files and not the old revision's.
A cache in cone mode is treated as foreign and widened for now. The next
commit starts creating cone caches and takes them over.

Refs taskcluster#1025
Git tests a no cone pattern against every index entry on every checkout. Cone
mode accepts only directories, and in return git matches by prefix and keeps
the index itself small with the sparse index. When every pattern is an
anchored literal path, which is true for twelve of the eighteen profiles,
run-task applies them with `set --cone --sparse-index`. Locally repackage-msi
went from 15 to 1 second on a warm cache.

A path to a file works as a cone directory because cone mode includes the
files of every ancestor directory. The same rule means a cone checkout has a
few hundred more files than the profile names, the files directly inside every
ancestor up to the root. Never fewer, so tasks are unaffected.

Mixing follows the widen only rule. A cone cache takes further cone profiles
with `add`. A glob profile on a cone cache converts it to no cone mode and
keeps every directory it had plus the files of their ancestors, listed with
`git ls-tree` at the target revision, so nothing that was checked out
disappears.

Refs taskcluster#1025

@bhearsum bhearsum left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I need to take a second pass on this with fresh brains; here's a few thoughts to start.

A full task widens a sparse cache.

Does this mean that tasks that use sparse profiles that run after a full checkout is cached are less performant than those that run with nothing cached, or after a different task that used a sparse profile?

Patterns go through stdin so a name starting with a dash is not taken for an
option.

This also avoids the possibility of hitting any limits on the length of arguments, presumably?

"safe.directory",
Path(destination_path).as_posix(),
]
retry_required_command(b"vcs", args, extra_env=env)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moving from being measured, to not being measured. It's arguably more correct this way (git config is not part of the clone), but it does change this metric. Is that on purpose?

args.append("--filter=blob:none")

if shallow or sparse_patterns:
args.append("--no-checkout")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does sparse_patterns imply --no-checkout?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Git partial clone support to run-task

2 participants