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

  1. Delete the package directory (rm -rf vendor/vendor/pkg) and reinstall with --prefer-source so a real git checkout is created.
  2. Run 'composer install --prefer-source' to rebuild all vendors from git sources.
  3. If you intentionally use dist, drop the --prefer-source flag so Composer uses ArchiveDownloader instead of GitDownloader.
  4. 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

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


AI-assisted analysis of composer/composer@c435d285c9 (2026-08-07). Data as JSON: /api/errors/5ebd0125dd77ae53. Report an issue: GitHub.