Files
git-repo/docs/fetch-cmd.md
T
Gavin Mak 616396317c sync: verify fetchcmd postconditions without reading objects
After repo.fetchcmd exits, _CustomFetch verified the tracking ref and
FETCH_HEAD with ResolveCommit, i.e. `git rev-parse --verify
<ref>^{commit}`. Peeling to a commit makes Git look the object up: it
opens every pack index in objects/pack/ until it finds the commit, then
reads the commit from its packfile.

fetchcmd exists for object stores that are populated lazily, such as
ABFS, whose `abfs git fetch` registers packfiles as virtual files and
returns at once. There the peel is the first read of the packs the
command registered, and it blocks until they are downloaded. A project
whose revision did not change needs no objects to sync, yet paid that
download whenever fetchcmd registered new packs for it.

Compare the object IDs that the refs record instead (ResolveRef, which
runs the same rev-parse without the peel). Only when they differ peel
both sides to commits, so an annotated tag still matches the commit it
points to. A commit missing from the object store now fails the checkout
that reads it instead of the network half.

On a first sync the checkout reads the commit anyway, so there the
download only moves to the next command that needs the object.

Bug: 543101585
Change-Id: I51248ff1e9674b16e3ff8cee69572dbc8c7a8e99
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/643003
Commit-Queue: Gavin Mak <gavinmak@google.com>
Tested-by: Gavin Mak <gavinmak@google.com>
Reviewed-by: Brian Gan <brgan@google.com>
2026-11-09 15:15:43 -08:00

3.1 KiB

Fetch Command Contract

The repo.fetchcmd configuration allows specifying a custom command to be executed during repo sync to fetch objects, instead of using standard git fetch. This is particularly useful in environments with virtualized filesystems or lazy checkouts where fetching metadata and downloading file contents should be decoupled.

The checkout half of a sync has a counterpart, repo.reprojectcmd; see docs/reproject-cmd.md.

Configuration

To use this feature, set the following in .repo/manifests.git/config:

[repo]
	fetchcmd = "your custom command here"
	uselocalgitdirs = true

Setting repo.fetchcmd requires repo.uselocalgitdirs to be set to true.

Environment Variables

The custom command is executed in a subshell populated with standard project-context environment variables. For details on standard variables (such as REPO_PROJECT, REPO_PATH, REPO_PROJECT_FETCH_URL, etc.), see the Environment section in repo help forall or subcmds/forall.py.

The following environment variable is specific to repo.fetchcmd:

  • REPO_TREV: The target commit hash when the revision is already pinned to a commit (e.g. via a superproject or a commit SHA in the manifest), or empty when REPO_RREV is an unpinned branch or tag to be resolved by the fetch command.

Contract

Postconditions on exit 0

After the fetch command exits with status 0, repo expects the following postconditions to be met:

  1. When the manifest revision names a branch or tag, the mapped local tracking ref (e.g. refs/remotes/REPO_REMOTE/<branch> for a branch, or refs/tags/<tag> for a tag) must name the target commit, and must equal REPO_TREV if REPO_TREV was non-empty. A tag ref may name an annotated tag of the target commit.
  2. FETCH_HEAD must name the same target commit.
  3. The target commit, and the commit graph from it far enough to compute merge bases with local branches, must be readable from the object store. They may be materialized lazily, when first read.

Invariants

  • The command should be idempotent; repeating the same fetch should be a no-op.
  • Only FETCH_HEAD, refs/remotes/*, and refs/tags/* should be modified to preserve repo sync --network-only semantics. HEAD and local branches must not be touched by the fetch command.
  • Dirty worktree state must be preserved.
  • The command is not executed for MetaProjects (i.e. the internal repo repository itself at .repo/repo and the manifests repository at .repo/manifests).

Failure

  • A non-zero exit status aborts the project's sync, and the command's stderr is surfaced to the user.
  • After exit 0, repo checks postconditions 1 and 2 by comparing the object IDs that the refs record (git rev-parse --verify <ref>), so it does not read the object store during the network half of the sync. It peels the IDs to commits only when they differ, as for an annotated tag. Any mismatch is treated as a failure.
  • A target commit missing from the object store fails the checkout that reads it.