composer/composer · error · RuntimeException
The .git directory is missing from '.$path.', see https://ge
Error message
The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information
What it means
Thrown by doUpdate (src/Composer/Downloader/GitDownloader.php:154) when hasMetadataRepository($path) returns false, i.e. is_dir($path.'/.git') fails. Composer cannot run git fetch/checkout update commands against a directory that has no git repository, so it aborts and points the user to the commit-deps docs. It indicates the existing checkout was not created from a git source.
Source
Thrown at src/Composer/Downloader/GitDownloader.php:155
if ($newRef = $this->updateToCommit($package, $path, (string) $ref, $package->getPrettyVersion())) {
if ($package->getDistReference() === $package->getSourceReference()) {
$package->setDistReference($newRef);
}
$package->setSourceReference($newRef);
}
return \React\Promise\resolve(null);
}
/**
* @inheritDoc
*/
protected function doUpdate(PackageInterface $initial, PackageInterface $target, string $path, string $url): PromiseInterface
{
GitUtil::cleanEnv($this->process);
$path = $this->normalizePath($path);
if (!$this->hasMetadataRepository($path)) {
throw new \RuntimeException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information');
}
$cachePath = $this->config->get('cache-vcs-dir').'/'.Preg::replace('{[^a-z0-9.]}i', '-', Url::sanitize($url)).'/';
$ref = $target->getSourceReference();
if (!empty($this->cachedPackages[$target->getId()][$ref])) {
$msg = "Checking out ".$this->getShortHash($ref).' from cache';
$remoteUrl = $cachePath;
} else {
$msg = "Checking out ".$this->getShortHash($ref);
$remoteUrl = '%url%';
if (Platform::getEnv('COMPOSER_DISABLE_NETWORK')) {
throw new \RuntimeException('The required git reference for '.$target->getName().' is not in cache and network is disabled, aborting');
}
}
$this->io->writeError($msg);
View on GitHub (pinned to c435d285c9)
Solutions
- Delete the package directory (rm -rf vendor/vendor/pkg) and reinstall with --prefer-source so a real git checkout is created.
- Run 'composer install --prefer-source' to rebuild all vendors from git sources.
- If you intentionally use dist, drop the --prefer-source flag so Composer uses ArchiveDownloader instead of GitDownloader.
- Restore the missing .git by re-cloning manually into the path if you need to preserve local edits.
Example fix
# before composer update vendor/pkg --prefer-source # .git missing -> error # after rm -rf vendor/vendor/pkg && composer install --prefer-source
Defensive patterns
Strategy: validation
Validate before calling
// Guard before attempting a source update.
if (!is_dir($path . '/.git')) {
throw new \LogicException(
"Cannot update $path as git source: .git is missing (installed via dist?). Reinstall with --prefer-source."
);
} Type guard
function isGitCheckout(string $path): bool {
return is_dir(rtrim($path, '/') . '/.git');
} Try / catch
try {
$downloader->update($initial, $target, $path, $url);
} catch (\RuntimeException $e) {
if (str_starts_with($e->getMessage(), 'The .git directory is missing')) {
// reinstall from source instead of updating
$filesystem->remove($path);
$downloader->download($target, $path);
return;
}
throw $e;
} Prevention
- Pick one of source or dist per package and stick with it.
- Don't manually delete .git directories in vendor/.
- In deploy artifacts, keep .git if you later update via source.
When it happens
Trigger: doUpdate() is invoked for a package whose install path exists but lacks a .git subdirectory. Typically the package was originally installed via --prefer-dist (a zip archive contains no .git), and a later update tries to perform a VCS-style update.
Common situations: Running 'composer update vendor/pkg --prefer-source' after the package was installed via dist; someone manually deleted the .git folder from vendor/; a deploy artifact that strips .git directories; switching source types in composer.json.
Related errors
- The .hg directory is missing from '.$path.', see https://get
- git was not found in your PATH, skipping source download
- Failed to execute ' . implode(' ', $command) . "\n\n" . $thi
- Failed to read package information from ${url} as the path d
- GitDriver requires a usable cache directory, and it looks li
AI-assisted analysis of composer/composer@c435d285c9 (2026-08-07).
Data as JSON: /api/errors/5ebd0125dd77ae53.
Report an issue: GitHub.