sync: don't run git ls-remote before fetchcmd on unpinned refs

When a project's revision is an unpinned branch or tag and superprojects
are not used (so self.revisionId is unset), _CustomFetch previously ran
git ls-remote against the Git remote before invoking repo.fetchcmd just
to pre-populate REPO_TREV.

This added a per-project network round-trip before fetchcmd and forced
branch resolution against the Git remote rather than letting fetchcmd
resolve REPO_RREV from its own server (just as standard git fetch does
in _RemoteFetch).

Only populate REPO_TREV before fetchcmd when the target commit SHA is
already known (via self.revisionId from superproject or a SHA-pinned
revisionExpr). When REPO_RREV is an unpinned branch or tag, leave
REPO_TREV empty, let fetchcmd resolve REPO_RREV and update the local
tracking ref and FETCH_HEAD, and verify afterward that the tracking ref
and FETCH_HEAD resolve to the same valid commit in the object store.

Bug: 543101585
Change-Id: I482bf4c022d0e669e5620d4517581ac193983da0
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/640423
Reviewed-by: Brian Gan <brgan@google.com>
Commit-Queue: Gavin Mak <gavinmak@google.com>
Tested-by: Gavin Mak <gavinmak@google.com>
This commit is contained in:
Gavin Mak authored and gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com committed 2026-10-09 14:09:37 -07:00
1 parent 277759b9e1
commit 2e8a3805bb
3 files changed
+156 -57

No files matched your search

+18 -14
View File
@@ -28,7 +28,10 @@ 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.
* `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
@@ -37,22 +40,23 @@ The following environment variable is specific to `repo.fetchcmd`:
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.
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 resolve to a valid commit in the object
store (`git rev-parse --verify <ref>^{commit}`), and must equal `REPO_TREV`
if `REPO_TREV` was non-empty.
2. `FETCH_HEAD` must resolve to the same target commit in the object store
(`git rev-parse --verify FETCH_HEAD^{commit}`).
3. The commit graph from the target commit 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.
* 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 `MetaProject`s (i.e. the internal `repo`
repository itself at `.repo/repo` and the `manifests` repository at