The repo.reprojectcmd configuration names a command that repo sync runs instead of Git to move a project's index and worktree to the tree of the target commit. It is the checkout-side counterpart of repo.fetchcmd (see docs/fetch-cmd.md): together they let an external tool take over both the network fetch and the materialization of a project. This is useful on virtualized filesystems that address content by hash, where a tree can be materialized far faster than git checkout can write every file.
The command only materializes the tree. repo then makes the ref write that Git would have made, using git update-ref.
To use this feature, set the following in .repo/manifests.git/config:
[repo] reprojectcmd = "your custom command here" uselocalgitdirs = true
Setting repo.reprojectcmd requires repo.uselocalgitdirs to be set to true.
For reference, this command does with Git what repo would otherwise do itself:
[repo] reprojectcmd = "git -C $REPO_PATH read-tree -m -u $REPO_TREV" uselocalgitdirs = true
The one-tree merge applies the change to the target, keeps local changes to every other path, and refuses to overwrite a modified or untracked file, so it enforces the preconditions below by itself. It also works for a project that has nothing checked out yet.
The command is executed in a subshell, from the root of the client, populated with standard project-context environment variables. For details on standard variables (such as REPO_PROJECT, REPO_PATH, REPO_REMOTE, etc.), see the Environment section in repo help forall or subcmds/forall.py.
The variables the command typically needs are:
REPO_PATH: The project path relative to the root of the client.REPO_TREV: The target revision resolved to a full commit hash. Match this commit's tree.There is no force mode: a project that would need one never reaches the command (see the preconditions below).
repo sync already classifies each project and picks a Git operation. The command replaces the three that are a materialization of a target tree:
repo sync -d.After the command exits 0, repo writes the ref itself: it detaches HEAD at REPO_TREV, or moves the checked out branch to REPO_TREV.
The command is not run:
HEAD already names REPO_TREV.HEAD is ahead of REPO_TREV, where Git's merge would be a no-op.git rebase, which is not a materialization of a target tree.MetaProjects (i.e. the internal repo repository itself at .repo/repo and the manifests repository at .repo/manifests).Before invoking the command, repo ensures that:
HEAD, or is empty on an unborn HEAD).Detecting collisions with untracked files or unstaged working-tree modifications is the responsibility of the reproject command itself (e.g. via git read-tree -m -u $REPO_TREV or a custom virtual filesystem checkout tool). If local changes collide with the target tree, the command must abort with a non-zero exit code. Local modifications and untracked files outside the diff between HEAD and REPO_TREV must be preserved.
After the command exits with status 0, repo expects the following postconditions to be met:
git diff-index --quiet --cached REPO_TREV^{tree} exits 0 (the index matches the target tree).HEAD still names what it did before the command, and its resolved commit object ID has not changed.The command may modify the worktree and the index, and may write project-local Git config. The command must:
HEAD's tree to REPO_TREV's tree and leave every other path alone. Local modifications and untracked files outside that change must survive: the command applies a diff, it does not reset the tree.HEAD and ORIG_HEAD. repo owns every ref write..git/, and not touch anything under .repo/.repo sync --local-only.repo sync exits non-zero.Nested projects are out of scope: a project whose path lies inside another project's path, a <project> nested in another <project> in the manifest, and a submodule discovered with sync-s or --recurse-submodules. repo sync fails if the manifest has one while repo.reprojectcmd is set.