From 5ad93959cf674e85296e64714ff0d2006199c64f Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 2 Apr 2020 00:54:49 +0200 Subject: [PATCH] PoolBuilder: On partial update of a new dep with mutual replace, unfix replacer Test also verifies provider does not get uninstalled in partial update for another package name --- .../DependencyResolver/PoolBuilder.php | 14 ++++-- ...pendencies-require-new-replace-mutual.test | 50 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace-mutual.test diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index 55f3b4c9e..305ad16b6 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -67,10 +67,11 @@ class PoolBuilder foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) { if (!$this->isUpdateAllowed($lockedPackage)) { $request->fixPackage($lockedPackage); + $lockedName = $lockedPackage->getName(); // remember which packages we skipped loading remote content for in this partial update - $this->skippedLoad[$lockedPackage->getName()] = true; + $this->skippedLoad[$lockedPackage->getName()] = $lockedName; foreach ($lockedPackage->getReplaces() as $link) { - $this->skippedLoad[$link->getTarget()] = true; + $this->skippedLoad[$link->getTarget()] = $lockedName; } } } @@ -244,7 +245,7 @@ class PoolBuilder // if this is a partial update with transitive dependencies we need to unfix the package we now know is a // dependency of another package which we are trying to update, and then attempt to load it again } elseif ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies() && isset($this->skippedLoad[$require])) { - if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $require)) { + if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$require])) { $this->unfixPackage($request, $require); $loadNames[$require] = null; } elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $require) && !isset($this->updateAllowWarned[$require])) { @@ -273,7 +274,7 @@ class PoolBuilder foreach ($package->getReplaces() as $link) { $replace = $link->getTarget(); if (isset($this->loadedNames[$replace]) && isset($this->skippedLoad[$replace])) { - if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $replace)) { + if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$replace])) { $this->unfixPackage($request, $replace); $loadNames[$replace] = null; // TODO should we try to merge constraints here? @@ -363,6 +364,11 @@ class PoolBuilder } } + // if we unfixed a replaced package name, we also need to unfix the replacer itself + if ($this->skippedLoad[$name] !== $name) { + $this->unfixPackage($request, $this->skippedLoad[$name]); + } + unset($this->skippedLoad[$name]); unset($this->loadedNames[$name]); } diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace-mutual.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace-mutual.test new file mode 100644 index 000000000..0cb5ad97f --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-require-new-replace-mutual.test @@ -0,0 +1,50 @@ +--TEST-- +Require a new package in the composer.json and updating with its name as an argument and with-dependencies should remove packages it replaces which are not root requirements +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*", "mutual/target-provide": "*" } }, + { "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } }, + { "name": "new/pkg", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } }, + { "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } }, + { "name": "new/pkg-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } } + ] + } + ], + "require": { + "current/pkg": "1.*", + "new/pkg": "1.*", + "new/pkg-provide": "1.*" + } +} +--INSTALLED-- +[ + { "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*" } }, + { "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } }, + { "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } } +] +--LOCK-- +{ + "packages": [ + { "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*" } }, + { "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } }, + { "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} +--RUN-- +update new/pkg --with-dependencies +--EXPECT-- +Removing current/dep (1.0.0) +Installing new/pkg (1.0.0) +Installing new/pkg-provide (1.0.0)