Files
Gavin Mak ead4b2d7aa sync: Implement fetchcmd for standard Git layouts
Introduce support for `repo.fetchcmd` configuration, allowing users to
specify a custom command to fetch project data instead of
`_RemoteFetch`.

When `repo.fetchcmd` is specified, `repo` will execute it instead of
`_RemoteFetch` during the network half of sync. Note that
`repo.fetchcmd` requires `repo.uselocalgitdirs` to be enabled.

The command is executed in a subshell with project-context environment
variables, including the new `REPO_TREV` (target revision resolved to a
commit hash) and `REPO_PROJECT_FETCH_URL`.

After execution, `repo` verifies that the target commit is available and
that the tracking ref and FETCH_HEAD are correctly updated.

Tested with:
```
> ~/git-repo/repo init -u https://android-review.googlesource.com/platform/manifest \
    --repo-url file:///usr/local/google/home/gavinmak/git-repo \
    --groups developers \
    --no-repo-verify \
    --use-local-gitdirs
...

> git config --file .repo/manifests.git/config repo.fetchcmd 'mkdir -p $REPO_PATH && cd $REPO_PATH && if [ ! -d .git ]; then git init && git remote add aosp $REPO_PROJECT_FETCH_URL; fi && git fetch aosp $REPO_TREV && git reset --hard $REPO_TREV && mkdir -p .git/refs/remotes/aosp && echo $REPO_TREV > .git/refs/remotes/aosp/main && echo $REPO_TREV > .git/FETCH_HEAD'

> ~/git-repo/repo sync -j32
warning: repo is not tracking a remote branch, so it will not receive updates; run `repo init --repo-rev=stable` to fix.
You are currently enrolled in Git submodules experiment (go/android-submodules-quickstart).  Use --no-use-superproject to override.

Syncing: 100% (4/4), done in 1m15.686s
Finalizing sync state...
repo sync has finished successfully.
```

Bug: 513329573
Change-Id: I754d3f3c78e86fdeee1a72115297a75b571bc497
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/583883
Reviewed-by: Becky Siegel <beckysiegel@google.com>
Tested-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Gavin Mak <gavinmak@google.com>
2026-06-30 13:20:15 -07:00

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

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 revision resolved to a full commit hash.

Contract

Postconditions on exit 0

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

  1. git cat-file -e REPO_TREV succeeds (the commit must exist in the object store).
  2. The mapped local tracking ref (e.g. refs/remotes/REPO_REMOTE/<branch> for a branch revision, or the tag ref itself for a tag) must point to REPO_TREV.
  3. FETCH_HEAD must point to REPO_TREV.
  4. The commit graph from REPO_TREV must be reachable far enough to compute merge bases with local branches.

Invariants

  • The command should be idempotent; fetching the same REPO_TREV twice should be a no-op.
  • Only FETCH_HEAD and refs/remotes/* 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.
  • repo verifies the tracking ref and target reachability after exit 0. Any mismatch is treated as a failure.