diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php
index 3ba921868..9ba60002a 100644
--- a/src/Composer/Downloader/GitDownloader.php
+++ b/src/Composer/Downloader/GitDownloader.php
@@ -75,7 +75,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
$this->io->writeError(" - Syncing " . $package->getName() . " (" . $package->getFullPrettyVersion() . ") into cache");
$this->io->writeError(sprintf(' Cloning to cache at %s', ProcessExecutor::escape($cachePath)), true, IOInterface::DEBUG);
$ref = $package->getSourceReference();
- if ($this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref) && is_dir($cachePath)) {
+ if ($this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref, $package->getPrettyVersion()) && is_dir($cachePath)) {
$this->cachedPackages[$package->getId()][$ref] = true;
}
} elseif (null === $gitVersion) {
diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php
index f43ba052b..80521ca3b 100644
--- a/src/Composer/Util/Git.php
+++ b/src/Composer/Util/Git.php
@@ -297,9 +297,31 @@ class Git
return true;
}
- public function fetchRefOrSyncMirror(string $url, string $dir, string $ref): bool
+ public function fetchRefOrSyncMirror(string $url, string $dir, string $ref, string $prettyVersion = null): bool
{
if ($this->checkRefIsInMirror($dir, $ref)) {
+ if (Preg::isMatch('{^[a-f0-9]{40}$}', $ref) && $prettyVersion !== null) {
+ $branch = Preg::replace('{(?:^dev-|(?:\.x)?-dev$)}i', '', $prettyVersion);
+ $branches = null;
+ $tags = null;
+ if (0 === $this->process->execute('git branch', $output, $dir)) {
+ $branches = $output;
+ }
+ if (0 === $this->process->execute('git tag', $output, $dir)) {
+ $tags = $output;
+ }
+
+ // if the pretty version cannot be found as a branch (nor branch with 'v' in front of the branch as it may have been stripped when generating pretty name),
+ // nor as a tag, then we sync the mirror as otherwise it will likely fail during install.
+ // this can occur if a git tag gets created *after* the reference is already put into the cache, as the ref check above will then not sync the new tags
+ // see https://github.com/composer/composer/discussions/11002
+ if (null !== $branches && !Preg::isMatch('{^[\s*]*v?'.preg_quote($branch).'$}m', $branches)
+ && null !== $tags && !Preg::isMatch('{^[\s*]*'.preg_quote($branch).'$}m', $tags)
+ ) {
+ $this->syncMirror($url, $dir);
+ }
+ }
+
return true;
}