Merge pull request #9223 from naderman/partial-update-remove-unused
Partial updates should remove all unused dependenciespull/9286/head
commit
eebae982d5
|
@ -62,6 +62,7 @@ class LockTransaction extends Transaction
|
||||||
|
|
||||||
if ($literal > 0) {
|
if ($literal > 0) {
|
||||||
$package = $pool->literalToPackage($literal);
|
$package = $pool->literalToPackage($literal);
|
||||||
|
|
||||||
$this->resultPackages['all'][] = $package;
|
$this->resultPackages['all'][] = $package;
|
||||||
if (!isset($this->unlockableMap[$package->id])) {
|
if (!isset($this->unlockableMap[$package->id])) {
|
||||||
$this->resultPackages['non-dev'][] = $package;
|
$this->resultPackages['non-dev'][] = $package;
|
||||||
|
|
|
@ -31,13 +31,13 @@ class Pool implements \Countable
|
||||||
protected $packageByName = array();
|
protected $packageByName = array();
|
||||||
protected $versionParser;
|
protected $versionParser;
|
||||||
protected $providerCache = array();
|
protected $providerCache = array();
|
||||||
protected $unacceptableFixedPackages;
|
protected $unacceptableFixedOrLockedPackages;
|
||||||
|
|
||||||
public function __construct(array $packages = array(), array $unacceptableFixedPackages = array())
|
public function __construct(array $packages = array(), array $unacceptableFixedOrLockedPackages = array())
|
||||||
{
|
{
|
||||||
$this->versionParser = new VersionParser;
|
$this->versionParser = new VersionParser;
|
||||||
$this->setPackages($packages);
|
$this->setPackages($packages);
|
||||||
$this->unacceptableFixedPackages = $unacceptableFixedPackages;
|
$this->unacceptableFixedOrLockedPackages = $unacceptableFixedOrLockedPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setPackages(array $packages)
|
private function setPackages(array $packages)
|
||||||
|
@ -181,9 +181,9 @@ class Pool implements \Countable
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isUnacceptableFixedPackage(PackageInterface $package)
|
public function isUnacceptableFixedOrLockedPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
return \in_array($package, $this->unacceptableFixedPackages, true);
|
return \in_array($package, $this->unacceptableFixedOrLockedPackages, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
|
|
|
@ -81,7 +81,7 @@ class PoolBuilder
|
||||||
/**
|
/**
|
||||||
* @psalm-var list<Package>
|
* @psalm-var list<Package>
|
||||||
*/
|
*/
|
||||||
private $unacceptableFixedPackages = array();
|
private $unacceptableFixedOrLockedPackages = array();
|
||||||
private $updateAllowList = array();
|
private $updateAllowList = array();
|
||||||
private $skippedLoad = array();
|
private $skippedLoad = array();
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class PoolBuilder
|
||||||
* have already their maximum required range loaded and can not be
|
* have already their maximum required range loaded and can not be
|
||||||
* extended by markPackageNameForLoading
|
* extended by markPackageNameForLoading
|
||||||
*
|
*
|
||||||
* Packages get cleared from this list if they get unfixed as in that case
|
* Packages get cleared from this list if they get unlocked as in that case
|
||||||
* we need to actually load them
|
* we need to actually load them
|
||||||
*/
|
*/
|
||||||
private $maxExtendedReqs = array();
|
private $maxExtendedReqs = array();
|
||||||
|
@ -129,7 +129,7 @@ class PoolBuilder
|
||||||
|
|
||||||
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
||||||
if (!$this->isUpdateAllowed($lockedPackage)) {
|
if (!$this->isUpdateAllowed($lockedPackage)) {
|
||||||
$request->fixPackage($lockedPackage);
|
$request->lockPackage($lockedPackage);
|
||||||
$lockedName = $lockedPackage->getName();
|
$lockedName = $lockedPackage->getName();
|
||||||
// remember which packages we skipped loading remote content for in this partial update
|
// remember which packages we skipped loading remote content for in this partial update
|
||||||
$this->skippedLoad[$lockedName] = $lockedName;
|
$this->skippedLoad[$lockedName] = $lockedName;
|
||||||
|
@ -140,7 +140,7 @@ class PoolBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
foreach ($request->getFixedOrLockedPackages() as $package) {
|
||||||
// using MatchAllConstraint here because fixed packages do not need to retrigger
|
// using MatchAllConstraint here because fixed packages do not need to retrigger
|
||||||
// loading any packages
|
// loading any packages
|
||||||
$this->loadedPackages[$package->getName()] = new MatchAllConstraint();
|
$this->loadedPackages[$package->getName()] = new MatchAllConstraint();
|
||||||
|
@ -160,12 +160,12 @@ class PoolBuilder
|
||||||
) {
|
) {
|
||||||
$this->loadPackage($request, $package, false);
|
$this->loadPackage($request, $package, false);
|
||||||
} else {
|
} else {
|
||||||
$this->unacceptableFixedPackages[] = $package;
|
$this->unacceptableFixedOrLockedPackages[] = $package;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($request->getRequires() as $packageName => $constraint) {
|
foreach ($request->getRequires() as $packageName => $constraint) {
|
||||||
// fixed packages have already been added, so if a root require needs one of them, no need to do anything
|
// fixed and locked packages have already been added, so if a root require needs one of them, no need to do anything
|
||||||
if (isset($this->loadedPackages[$packageName])) {
|
if (isset($this->loadedPackages[$packageName])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -219,21 +219,21 @@ class PoolBuilder
|
||||||
$this->rootAliases,
|
$this->rootAliases,
|
||||||
$this->rootReferences,
|
$this->rootReferences,
|
||||||
$this->packages,
|
$this->packages,
|
||||||
$this->unacceptableFixedPackages
|
$this->unacceptableFixedOrLockedPackages
|
||||||
);
|
);
|
||||||
$this->eventDispatcher->dispatch($prePoolCreateEvent->getName(), $prePoolCreateEvent);
|
$this->eventDispatcher->dispatch($prePoolCreateEvent->getName(), $prePoolCreateEvent);
|
||||||
$this->packages = $prePoolCreateEvent->getPackages();
|
$this->packages = $prePoolCreateEvent->getPackages();
|
||||||
$this->unacceptableFixedPackages = $prePoolCreateEvent->getUnacceptableFixedPackages();
|
$this->unacceptableFixedOrLockedPackages = $prePoolCreateEvent->getUnacceptableFixedPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
$pool = new Pool($this->packages, $this->unacceptableFixedPackages);
|
$pool = new Pool($this->packages, $this->unacceptableFixedOrLockedPackages);
|
||||||
|
|
||||||
$this->aliasMap = array();
|
$this->aliasMap = array();
|
||||||
$this->packagesToLoad = array();
|
$this->packagesToLoad = array();
|
||||||
$this->loadedPackages = array();
|
$this->loadedPackages = array();
|
||||||
$this->loadedPerRepo = array();
|
$this->loadedPerRepo = array();
|
||||||
$this->packages = array();
|
$this->packages = array();
|
||||||
$this->unacceptableFixedPackages = array();
|
$this->unacceptableFixedOrLockedPackages = array();
|
||||||
$this->maxExtendedReqs = array();
|
$this->maxExtendedReqs = array();
|
||||||
$this->skippedLoad = array();
|
$this->skippedLoad = array();
|
||||||
$this->indexCounter = 0;
|
$this->indexCounter = 0;
|
||||||
|
@ -250,7 +250,7 @@ class PoolBuilder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root require (which was not unfixed) already loaded the maximum range so no
|
// Root require (which was not unlocked) already loaded the maximum range so no
|
||||||
// need to check anything here
|
// need to check anything here
|
||||||
if (isset($this->maxExtendedReqs[$name])) {
|
if (isset($this->maxExtendedReqs[$name])) {
|
||||||
return;
|
return;
|
||||||
|
@ -258,7 +258,7 @@ class PoolBuilder
|
||||||
|
|
||||||
// Root requires can not be overruled by dependencies so there is no point in
|
// Root requires can not be overruled by dependencies so there is no point in
|
||||||
// extending the loaded constraint for those.
|
// extending the loaded constraint for those.
|
||||||
// This is triggered when loading a root require which was fixed but got unfixed, then
|
// This is triggered when loading a root require which was locked but got unlocked, then
|
||||||
// we make sure that we load at most the intervals covered by the root constraint.
|
// we make sure that we load at most the intervals covered by the root constraint.
|
||||||
$rootRequires = $request->getRequires();
|
$rootRequires = $request->getRequires();
|
||||||
if (isset($rootRequires[$name]) && !Intervals::isSubsetOf($constraint, $rootRequires[$name])) {
|
if (isset($rootRequires[$name]) && !Intervals::isSubsetOf($constraint, $rootRequires[$name])) {
|
||||||
|
@ -311,7 +311,7 @@ class PoolBuilder
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// these repos have their packages fixed if they need to be loaded so we
|
// these repos have their packages fixed or locked if they need to be loaded so we
|
||||||
// never need to load anything else from them
|
// never need to load anything else from them
|
||||||
if ($repository instanceof PlatformRepository || $repository === $request->getLockedRepository()) {
|
if ($repository instanceof PlatformRepository || $repository === $request->getLockedRepository()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -344,14 +344,14 @@ class PoolBuilder
|
||||||
// right version. It'd be more work to figure out which versions and which aliases of those versions this may
|
// right version. It'd be more work to figure out which versions and which aliases of those versions this may
|
||||||
// apply to
|
// apply to
|
||||||
if (isset($this->rootReferences[$name])) {
|
if (isset($this->rootReferences[$name])) {
|
||||||
// do not modify the references on already locked packages
|
// do not modify the references on already locked or fixed packages
|
||||||
if (!$request->isFixedPackage($package)) {
|
if (!$request->isLockedPackage($package) && !$request->isFixedPackage($package)) {
|
||||||
$package->setSourceDistReferences($this->rootReferences[$name]);
|
$package->setSourceDistReferences($this->rootReferences[$name]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if propogateUpdate is false we are loading a fixed package, root aliases do not apply as they are manually
|
// if propogateUpdate is false we are loading a fixed or locked package, root aliases do not apply as they are
|
||||||
// loaded as separate packages in this case
|
// manually loaded as separate packages in this case
|
||||||
if ($propagateUpdate && isset($this->rootAliases[$name][$package->getVersion()])) {
|
if ($propagateUpdate && isset($this->rootAliases[$name][$package->getVersion()])) {
|
||||||
$alias = $this->rootAliases[$name][$package->getVersion()];
|
$alias = $this->rootAliases[$name][$package->getVersion()];
|
||||||
if ($package instanceof AliasPackage) {
|
if ($package instanceof AliasPackage) {
|
||||||
|
@ -372,11 +372,11 @@ class PoolBuilder
|
||||||
$linkConstraint = $link->getConstraint();
|
$linkConstraint = $link->getConstraint();
|
||||||
|
|
||||||
if ($propagateUpdate) {
|
if ($propagateUpdate) {
|
||||||
// if this is a partial update with transitive dependencies we need to unfix the package we now know is a
|
// if this is a partial update with transitive dependencies we need to unlock the package we now know is a
|
||||||
// dependency of another package which we are trying to update, and then attempt to load it again
|
// dependency of another package which we are trying to update, and then attempt to load it again
|
||||||
if ($request->getUpdateAllowTransitiveDependencies() && isset($this->skippedLoad[$require])) {
|
if ($request->getUpdateAllowTransitiveDependencies() && isset($this->skippedLoad[$require])) {
|
||||||
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$require])) {
|
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$require])) {
|
||||||
$this->unfixPackage($request, $require);
|
$this->unlockPackage($request, $require);
|
||||||
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
||||||
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $require) && !isset($this->updateAllowWarned[$require])) {
|
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $require) && !isset($this->updateAllowWarned[$require])) {
|
||||||
$this->updateAllowWarned[$require] = true;
|
$this->updateAllowWarned[$require] = true;
|
||||||
|
@ -386,7 +386,7 @@ class PoolBuilder
|
||||||
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We also need to load the requirements of a fixed package
|
// We also need to load the requirements of a locked package
|
||||||
// unless it was skipped
|
// unless it was skipped
|
||||||
if (!isset($this->skippedLoad[$require])) {
|
if (!isset($this->skippedLoad[$require])) {
|
||||||
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
||||||
|
@ -394,14 +394,14 @@ class PoolBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're doing a partial update with deps we also need to unfix packages which are being replaced in case they
|
// if we're doing a partial update with deps we also need to unlock packages which are being replaced in case
|
||||||
// are currently locked and thus prevent this updateable package from being installable/updateable
|
// they are currently locked and thus prevent this updateable package from being installable/updateable
|
||||||
if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) {
|
if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) {
|
||||||
foreach ($package->getReplaces() as $link) {
|
foreach ($package->getReplaces() as $link) {
|
||||||
$replace = $link->getTarget();
|
$replace = $link->getTarget();
|
||||||
if (isset($this->loadedPackages[$replace], $this->skippedLoad[$replace])) {
|
if (isset($this->loadedPackages[$replace], $this->skippedLoad[$replace])) {
|
||||||
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$replace])) {
|
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$replace])) {
|
||||||
$this->unfixPackage($request, $replace);
|
$this->unlockPackage($request, $replace);
|
||||||
$this->markPackageNameForLoading($request, $replace, $link->getConstraint());
|
$this->markPackageNameForLoading($request, $replace, $link->getConstraint());
|
||||||
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $replace) && !isset($this->updateAllowWarned[$replace])) {
|
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $replace) && !isset($this->updateAllowWarned[$replace])) {
|
||||||
$this->updateAllowWarned[$replace] = true;
|
$this->updateAllowWarned[$replace] = true;
|
||||||
|
@ -464,16 +464,16 @@ class PoolBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverts the decision to use a fixed package from lock file if a partial update with transitive dependencies
|
* Reverts the decision to use a locked package if a partial update with transitive dependencies
|
||||||
* found that this package actually needs to be updated
|
* found that this package actually needs to be updated
|
||||||
*/
|
*/
|
||||||
private function unfixPackage(Request $request, $name)
|
private function unlockPackage(Request $request, $name)
|
||||||
{
|
{
|
||||||
// remove locked package by this name which was already initialized
|
// remove locked package by this name which was already initialized
|
||||||
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
foreach ($request->getLockedPackages() as $lockedPackage) {
|
||||||
if (!($lockedPackage instanceof AliasPackage) && $lockedPackage->getName() === $name) {
|
if (!($lockedPackage instanceof AliasPackage) && $lockedPackage->getName() === $name) {
|
||||||
if (false !== $index = array_search($lockedPackage, $this->packages, true)) {
|
if (false !== $index = array_search($lockedPackage, $this->packages, true)) {
|
||||||
$request->unfixPackage($lockedPackage);
|
$request->unlockPackage($lockedPackage);
|
||||||
$this->removeLoadedPackage($request, $lockedPackage, $index);
|
$this->removeLoadedPackage($request, $lockedPackage, $index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,7 +485,7 @@ class PoolBuilder
|
||||||
// as long as it was not unfixed yet
|
// as long as it was not unfixed yet
|
||||||
&& isset($this->skippedLoad[$this->skippedLoad[$name]])
|
&& isset($this->skippedLoad[$this->skippedLoad[$name]])
|
||||||
) {
|
) {
|
||||||
$this->unfixPackage($request, $this->skippedLoad[$name]);
|
$this->unlockPackage($request, $this->skippedLoad[$name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($this->skippedLoad[$name], $this->loadedPackages[$name], $this->maxExtendedReqs[$name]);
|
unset($this->skippedLoad[$name], $this->loadedPackages[$name], $this->maxExtendedReqs[$name]);
|
||||||
|
@ -496,7 +496,7 @@ class PoolBuilder
|
||||||
unset($this->packages[$index]);
|
unset($this->packages[$index]);
|
||||||
if (isset($this->aliasMap[spl_object_hash($package)])) {
|
if (isset($this->aliasMap[spl_object_hash($package)])) {
|
||||||
foreach ($this->aliasMap[spl_object_hash($package)] as $aliasIndex => $aliasPackage) {
|
foreach ($this->aliasMap[spl_object_hash($package)] as $aliasIndex => $aliasPackage) {
|
||||||
$request->unfixPackage($aliasPackage);
|
$request->unlockPackage($aliasPackage);
|
||||||
unset($this->packages[$aliasIndex]);
|
unset($this->packages[$aliasIndex]);
|
||||||
}
|
}
|
||||||
unset($this->aliasMap[spl_object_hash($package)]);
|
unset($this->aliasMap[spl_object_hash($package)]);
|
||||||
|
|
|
@ -229,11 +229,11 @@ class Problem
|
||||||
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.');
|
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$fixedPackage = null;
|
$lockedPackage = null;
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
foreach ($request->getLockedPackages() as $package) {
|
||||||
if ($package->getName() === $packageName) {
|
if ($package->getName() === $packageName) {
|
||||||
$fixedPackage = $package;
|
$lockedPackage = $package;
|
||||||
if ($pool->isUnacceptableFixedPackage($package)) {
|
if ($pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.');
|
return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -253,13 +253,13 @@ class Problem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($fixedPackage) {
|
if ($lockedPackage) {
|
||||||
$fixedConstraint = new Constraint('==', $fixedPackage->getVersion());
|
$fixedConstraint = new Constraint('==', $lockedPackage->getVersion());
|
||||||
$filtered = array_filter($packages, function ($p) use ($fixedConstraint) {
|
$filtered = array_filter($packages, function ($p) use ($fixedConstraint) {
|
||||||
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
|
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
|
||||||
});
|
});
|
||||||
if (0 === count($filtered)) {
|
if (0 === count($filtered)) {
|
||||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
|
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$lockedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,8 @@ class Request
|
||||||
protected $lockedRepository;
|
protected $lockedRepository;
|
||||||
protected $requires = array();
|
protected $requires = array();
|
||||||
protected $fixedPackages = array();
|
protected $fixedPackages = array();
|
||||||
protected $unlockables = array();
|
protected $lockedPackages = array();
|
||||||
|
protected $fixedLockedPackages = array();
|
||||||
protected $updateAllowList = array();
|
protected $updateAllowList = array();
|
||||||
protected $updateAllowTransitiveDependencies = false;
|
protected $updateAllowTransitiveDependencies = false;
|
||||||
|
|
||||||
|
@ -67,22 +68,47 @@ class Request
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark an existing package as being installed and having to remain installed
|
* Mark a package as currently present and having to remain installed
|
||||||
*
|
*
|
||||||
* @param bool $lockable if set to false, the package will not be written to the lock file
|
* This is used for platform packages which cannot be modified by Composer. A rule enforcing their installation is
|
||||||
|
* generated for dependency resolution. Partial updates with dependencies cannot in any way modify these packages.
|
||||||
*/
|
*/
|
||||||
public function fixPackage(PackageInterface $package, $lockable = true)
|
public function fixPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
$this->fixedPackages[spl_object_hash($package)] = $package;
|
$this->fixedPackages[spl_object_hash($package)] = $package;
|
||||||
|
|
||||||
if (!$lockable) {
|
|
||||||
$this->unlockables[spl_object_hash($package)] = $package;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unfixPackage(PackageInterface $package)
|
/**
|
||||||
|
* Mark a package as locked to a specific version but removable
|
||||||
|
*
|
||||||
|
* This is used for lock file packages which need to be treated similar to fixed packages by the pool builder in
|
||||||
|
* that by default they should really only have the currently present version loaded and no remote alternatives.
|
||||||
|
*
|
||||||
|
* However unlike fixed packages there will not be a special rule enforcing their installation for the solver, so
|
||||||
|
* if nothing requires these packages they will be removed. Additionally in a partial update these packages can be
|
||||||
|
* unlocked, meaning other versions can be installed if explicitly requested as part of the update.
|
||||||
|
*/
|
||||||
|
public function lockPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
unset($this->fixedPackages[spl_object_hash($package)], $this->unlockables[spl_object_hash($package)]);
|
$this->lockedPackages[spl_object_hash($package)] = $package;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a locked package fixed. So it's treated irremovable like a platform package.
|
||||||
|
*
|
||||||
|
* This is necessary for the composer install step which verifies the lock file integrity and should not allow
|
||||||
|
* removal of any packages. At the same time lock packages there cannot simply be marked fixed, as error reporting
|
||||||
|
* would then report them as platform packages, so this still marks them as locked packages at the same time.
|
||||||
|
*/
|
||||||
|
public function fixLockedPackage(PackageInterface $package)
|
||||||
|
{
|
||||||
|
$this->fixedPackages[spl_object_hash($package)] = $package;
|
||||||
|
$this->fixedLockedPackages[spl_object_hash($package)] = $package;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unlockPackage(PackageInterface $package)
|
||||||
|
{
|
||||||
|
unset($this->lockedPackages[spl_object_hash($package)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies)
|
public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies)
|
||||||
|
@ -121,6 +147,21 @@ class Request
|
||||||
return isset($this->fixedPackages[spl_object_hash($package)]);
|
return isset($this->fixedPackages[spl_object_hash($package)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLockedPackages()
|
||||||
|
{
|
||||||
|
return $this->lockedPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLockedPackage(PackageInterface $package)
|
||||||
|
{
|
||||||
|
return isset($this->lockedPackages[spl_object_hash($package)]) || isset($this->fixedLockedPackages[spl_object_hash($package)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFixedOrLockedPackages()
|
||||||
|
{
|
||||||
|
return array_merge($this->fixedPackages, $this->lockedPackages);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO look into removing the packageIds option, the only place true is used is for the installed map in the solver problems
|
// TODO look into removing the packageIds option, the only place true is used is for the installed map in the solver problems
|
||||||
// some locked packages may not be in the pool so they have a package->id of -1
|
// some locked packages may not be in the pool so they have a package->id of -1
|
||||||
public function getPresentMap($packageIds = false)
|
public function getPresentMap($packageIds = false)
|
||||||
|
@ -140,15 +181,15 @@ class Request
|
||||||
return $presentMap;
|
return $presentMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUnlockableMap()
|
public function getFixedPackagesMap()
|
||||||
{
|
{
|
||||||
$unlockableMap = array();
|
$fixedPackagesMap = array();
|
||||||
|
|
||||||
foreach ($this->unlockables as $package) {
|
foreach ($this->fixedPackages as $package) {
|
||||||
$unlockableMap[$package->id] = $package;
|
$fixedPackagesMap[$package->id] = $package;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $unlockableMap;
|
return $fixedPackagesMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLockedRepository()
|
public function getLockedRepository()
|
||||||
|
|
|
@ -125,23 +125,25 @@ abstract class Rule
|
||||||
|
|
||||||
public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool)
|
public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool)
|
||||||
{
|
{
|
||||||
if ($this->getReason() === self::RULE_FIXED && $this->reasonData['lockable']) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) {
|
if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) {
|
||||||
if (PlatformRepository::isPlatformPackage($this->reasonData->getTarget())) {
|
if (PlatformRepository::isPlatformPackage($this->reasonData->getTarget())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
if ($request->getLockedRepository()) {
|
||||||
if ($package->getName() === $this->reasonData->getTarget()) {
|
foreach ($request->getLockedRepository()->getPackages() as $package) {
|
||||||
if ($pool->isUnacceptableFixedPackage($package)) {
|
if ($package->getName() === $this->reasonData->getTarget()) {
|
||||||
return true;
|
if ($pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!$this->reasonData->getConstraint()->matches(new Constraint('=', $package->getVersion()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// required package was locked but has been unlocked and still matches
|
||||||
|
if (!$request->isLockedPackage($package)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!$this->reasonData->getConstraint()->matches(new Constraint('=', $package->getVersion()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,15 +152,17 @@ abstract class Rule
|
||||||
if (PlatformRepository::isPlatformPackage($this->reasonData['packageName'])) {
|
if (PlatformRepository::isPlatformPackage($this->reasonData['packageName'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
if ($request->getLockedRepository()) {
|
||||||
if ($package->getName() === $this->reasonData['packageName']) {
|
foreach ($request->getLockedRepository()->getPackages() as $package) {
|
||||||
if ($pool->isUnacceptableFixedPackage($package)) {
|
if ($package->getName() === $this->reasonData['packageName']) {
|
||||||
return true;
|
if ($pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!$this->reasonData['constraint']->matches(new Constraint('=', $package->getVersion()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!$this->reasonData['constraint']->matches(new Constraint('=', $package->getVersion()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,11 +184,22 @@ abstract class Rule
|
||||||
return 'No package found to satisfy root composer.json require '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '');
|
return 'No package found to satisfy root composer.json require '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$packagesNonAlias = array_values(array_filter($packages, function ($p) {
|
||||||
|
return !($p instanceof AliasPackage);
|
||||||
|
}));
|
||||||
|
if (count($packagesNonAlias) === 1) {
|
||||||
|
$package = $packagesNonAlias[0];
|
||||||
|
if ($request->isLockedPackage($package)) {
|
||||||
|
return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion()." and an update of this package was not requested.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.';
|
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.';
|
||||||
|
|
||||||
case self::RULE_FIXED:
|
case self::RULE_FIXED:
|
||||||
$package = $this->deduplicateDefaultBranchAlias($this->reasonData['package']);
|
$package = $this->deduplicateDefaultBranchAlias($this->reasonData['package']);
|
||||||
if ($this->reasonData['lockable']) {
|
|
||||||
|
if ($request->isLockedPackage($package)) {
|
||||||
return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion().' and an update of this package was not requested.';
|
return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion().' and an update of this package was not requested.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,12 +223,10 @@ class RuleSetGenerator
|
||||||
|
|
||||||
protected function addRulesForRequest(Request $request, $ignorePlatformReqs)
|
protected function addRulesForRequest(Request $request, $ignorePlatformReqs)
|
||||||
{
|
{
|
||||||
$unlockableMap = $request->getUnlockableMap();
|
|
||||||
|
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
foreach ($request->getFixedPackages() as $package) {
|
||||||
if ($package->id == -1) {
|
if ($package->id == -1) {
|
||||||
// fixed package was not added to the pool as it did not pass the stability requirements, this is fine
|
// fixed package was not added to the pool as it did not pass the stability requirements, this is fine
|
||||||
if ($this->pool->isUnacceptableFixedPackage($package)) {
|
if ($this->pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +238,6 @@ class RuleSetGenerator
|
||||||
|
|
||||||
$rule = $this->createInstallOneOfRule(array($package), Rule::RULE_FIXED, array(
|
$rule = $this->createInstallOneOfRule(array($package), Rule::RULE_FIXED, array(
|
||||||
'package' => $package,
|
'package' => $package,
|
||||||
'lockable' => !isset($unlockableMap[$package->id]),
|
|
||||||
));
|
));
|
||||||
$this->addRule(RuleSet::TYPE_REQUEST, $rule);
|
$this->addRule(RuleSet::TYPE_REQUEST, $rule);
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ class Solver
|
||||||
throw new SolverProblemsException($this->problems, $this->learnedPool);
|
throw new SolverProblemsException($this->problems, $this->learnedPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LockTransaction($this->pool, $request->getPresentMap(), $request->getUnlockableMap(), $this->decisions);
|
return new LockTransaction($this->pool, $request->getPresentMap(), $request->getFixedPackagesMap(), $this->decisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -633,7 +633,7 @@ class Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($lockedRepository->getPackages() as $package) {
|
foreach ($lockedRepository->getPackages() as $package) {
|
||||||
$request->fixPackage($package);
|
$request->fixLockedPackage($package);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->locker->getPlatformRequirements($this->devMode) as $link) {
|
foreach ($this->locker->getPlatformRequirements($this->devMode) as $link) {
|
||||||
|
@ -651,7 +651,6 @@ class Installer
|
||||||
// installing the locked packages on this platform resulted in lock modifying operations, there wasn't a conflict, but the lock file as-is seems to not work on this system
|
// installing the locked packages on this platform resulted in lock modifying operations, there wasn't a conflict, but the lock file as-is seems to not work on this system
|
||||||
if (0 !== count($lockTransaction->getOperations())) {
|
if (0 !== count($lockTransaction->getOperations())) {
|
||||||
$this->io->writeError('<error>Your lock file cannot be installed on this system without changes. Please run composer update.</error>', true, IOInterface::QUIET);
|
$this->io->writeError('<error>Your lock file cannot be installed on this system without changes. Please run composer update.</error>', true, IOInterface::QUIET);
|
||||||
// TODO actually display operations to explain what happened?
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} catch (SolverProblemsException $e) {
|
} catch (SolverProblemsException $e) {
|
||||||
|
@ -821,9 +820,9 @@ class Installer
|
||||||
{
|
{
|
||||||
$request = new Request($lockedRepository);
|
$request = new Request($lockedRepository);
|
||||||
|
|
||||||
$request->fixPackage($rootPackage, false);
|
$request->fixPackage($rootPackage);
|
||||||
if ($rootPackage instanceof RootAliasPackage) {
|
if ($rootPackage instanceof RootAliasPackage) {
|
||||||
$request->fixPackage($rootPackage->getAliasOf(), false);
|
$request->fixPackage($rootPackage->getAliasOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
$fixedPackages = $platformRepo->getPackages();
|
$fixedPackages = $platformRepo->getPackages();
|
||||||
|
@ -841,7 +840,7 @@ class Installer
|
||||||
|| !isset($provided[$package->getName()])
|
|| !isset($provided[$package->getName()])
|
||||||
|| !$provided[$package->getName()]->getConstraint()->matches(new Constraint('=', $package->getVersion()))
|
|| !$provided[$package->getName()]->getConstraint()->matches(new Constraint('=', $package->getVersion()))
|
||||||
) {
|
) {
|
||||||
$request->fixPackage($package, false);
|
$request->fixPackage($package);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,6 @@ Updating dependencies
|
||||||
Your requirements could not be resolved to an installable set of packages.
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
Problem 1
|
||||||
- locked/pkg is locked to version dev-master and an update of this package was not requested.
|
|
||||||
- locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.
|
|
||||||
Problem 2
|
|
||||||
- locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.
|
- locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.
|
||||||
- locked/pkg is locked to version dev-master and an update of this package was not requested.
|
- locked/pkg is locked to version dev-master and an update of this package was not requested.
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ Partial update from lock file should update everything to the state of the lock,
|
||||||
"require": {
|
"require": {
|
||||||
"a/old": "*",
|
"a/old": "*",
|
||||||
"b/unstable": "*",
|
"b/unstable": "*",
|
||||||
"c/uptodate": "*"
|
"c/uptodate": "*",
|
||||||
|
"e/newreq": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
--LOCK--
|
--LOCK--
|
||||||
|
@ -60,7 +61,6 @@ update b/unstable
|
||||||
{ "name": "a/old", "version": "1.0.0", "type": "library" },
|
{ "name": "a/old", "version": "1.0.0", "type": "library" },
|
||||||
{ "name": "b/unstable", "version": "1.0.0", "type": "library", "require": {"f/dependency": "1.*"} },
|
{ "name": "b/unstable", "version": "1.0.0", "type": "library", "require": {"f/dependency": "1.*"} },
|
||||||
{ "name": "c/uptodate", "version": "1.0.0", "type": "library" },
|
{ "name": "c/uptodate", "version": "1.0.0", "type": "library" },
|
||||||
{ "name": "d/removed", "version": "1.0.0", "type": "library" },
|
|
||||||
{ "name": "e/newreq", "version": "1.0.0", "type": "library" },
|
{ "name": "e/newreq", "version": "1.0.0", "type": "library" },
|
||||||
{ "name": "f/dependency", "version": "1.0.0", "type": "library" }
|
{ "name": "f/dependency", "version": "1.0.0", "type": "library" }
|
||||||
],
|
],
|
||||||
|
@ -77,5 +77,4 @@ update b/unstable
|
||||||
Upgrading a/old (0.9.0 => 1.0.0)
|
Upgrading a/old (0.9.0 => 1.0.0)
|
||||||
Downgrading b/unstable (1.1.0-alpha => 1.0.0)
|
Downgrading b/unstable (1.1.0-alpha => 1.0.0)
|
||||||
Downgrading c/uptodate (2.0.0 => 1.0.0)
|
Downgrading c/uptodate (2.0.0 => 1.0.0)
|
||||||
Installing d/removed (1.0.0)
|
|
||||||
Installing e/newreq (1.0.0)
|
Installing e/newreq (1.0.0)
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
--TEST--
|
||||||
|
Removing a package deletes unused dependencies and does not update other dependencies
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "root/a", "version": "1.0.0", "require": { "dep/c": "*", "dep/d": "*"} },
|
||||||
|
{ "name": "remove/b", "version": "1.0.0", "require": { "dep/c": "*", "dep/e": "*"} },
|
||||||
|
{ "name": "dep/c", "version": "1.0.0" },
|
||||||
|
{ "name": "dep/c", "version": "1.2.0" },
|
||||||
|
{ "name": "dep/d", "version": "1.0.0" },
|
||||||
|
{ "name": "dep/d", "version": "1.2.0" },
|
||||||
|
{ "name": "dep/e", "version": "1.0.0" },
|
||||||
|
{ "name": "unrelated/f", "version": "1.0.0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"root/a": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--LOCK--
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{ "name": "root/a", "version": "1.0.0", "require": { "dep/c": "*", "dep/d": "*"} },
|
||||||
|
{ "name": "remove/b", "version": "1.0.0", "require": { "dep/c": "*", "dep/e": "*"} },
|
||||||
|
{ "name": "dep/c", "version": "1.0.0" },
|
||||||
|
{ "name": "dep/d", "version": "1.0.0" },
|
||||||
|
{ "name": "dep/e", "version": "1.0.0" },
|
||||||
|
{ "name": "unrelated/f", "version": "1.0.0" }
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false
|
||||||
|
}
|
||||||
|
--RUN--
|
||||||
|
update remove/b
|
||||||
|
--EXPECT--
|
||||||
|
Installing dep/d (1.0.0)
|
||||||
|
Installing dep/c (1.0.0)
|
||||||
|
Installing root/a (1.0.0)
|
|
@ -0,0 +1,39 @@
|
||||||
|
--TEST--
|
||||||
|
Removing a package has no effect if another package would require an update in order to find a correct set of dependencies without the removed package
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "root/a", "version": "1.0.0", "require": { "remove/b": "*", "dep/c": "*"} },
|
||||||
|
{ "name": "remove/b", "version": "1.0.0", "require": { "dep/c": "*"} },
|
||||||
|
{ "name": "dep/c", "version": "1.0.0" },
|
||||||
|
{ "name": "dep/c", "version": "1.2.0", "replace": { "remove/b": "1.0.0"} }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"root/a": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--LOCK--
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{ "name": "root/a", "version": "1.0.0", "require": { "remove/b": "*", "dep/c": "*"} },
|
||||||
|
{ "name": "remove/b", "version": "1.0.0", "require": { "dep/c": "*"} },
|
||||||
|
{ "name": "dep/c", "version": "1.0.0" }
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false
|
||||||
|
}
|
||||||
|
--RUN--
|
||||||
|
update remove/b
|
||||||
|
--EXPECT--
|
||||||
|
Installing dep/c (1.0.0)
|
||||||
|
Installing remove/b (1.0.0)
|
||||||
|
Installing root/a (1.0.0)
|
|
@ -62,6 +62,4 @@ Your lock file does not contain a compatible set of packages. Please run compose
|
||||||
- b/requirer is locked to version 1.0.0 and an update of this package was not requested.
|
- b/requirer is locked to version 1.0.0 and an update of this package was not requested.
|
||||||
- b/requirer 1.0.0 requires root/pkg ^1 -> found root/pkg[2.x-dev] but it does not match the constraint.
|
- b/requirer 1.0.0 requires root/pkg ^1 -> found root/pkg[2.x-dev] but it does not match the constraint.
|
||||||
|
|
||||||
Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions.
|
|
||||||
|
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
If a new requirement cannot be installed on a partial update due to replace, there should be a suggestion to use --with-all-dependencies
|
A partial update for a new package yet to be installed should remove another dependency it replaces if that allows installation.
|
||||||
--COMPOSER--
|
--COMPOSER--
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -39,17 +39,6 @@ If a new requirement cannot be installed on a partial update due to replace, the
|
||||||
}
|
}
|
||||||
--RUN--
|
--RUN--
|
||||||
update new/pkg
|
update new/pkg
|
||||||
--EXPECT-EXIT-CODE--
|
|
||||||
2
|
|
||||||
--EXPECT-OUTPUT--
|
|
||||||
Loading composer repositories with package information
|
|
||||||
Updating dependencies
|
|
||||||
Your requirements could not be resolved to an installable set of packages.
|
|
||||||
|
|
||||||
Problem 1
|
|
||||||
- current/dep is locked to version 1.0.0 and an update of this package was not requested.
|
|
||||||
- new/pkg[1.0.0] cannot be installed as that would require removing current/dep[1.0.0]. new/pkg replaces current/dep and thus cannot coexist with it.
|
|
||||||
- Root composer.json requires new/pkg 1.* -> satisfiable by new/pkg[1.0.0].
|
|
||||||
|
|
||||||
Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions.
|
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
Removing current/dep (1.0.0)
|
||||||
|
Installing new/pkg (1.0.0)
|
||||||
|
|
|
@ -45,6 +45,7 @@ Require a new package in the composer.json and updating with its name as an argu
|
||||||
--RUN--
|
--RUN--
|
||||||
update new/pkg --with-dependencies
|
update new/pkg --with-dependencies
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
Removing current/dep-provide (1.0.0)
|
||||||
Removing current/dep (1.0.0)
|
Removing current/dep (1.0.0)
|
||||||
Installing new/pkg (1.0.0)
|
Installing new/pkg (1.0.0)
|
||||||
Installing new/pkg-provide (1.0.0)
|
Installing new/pkg-provide (1.0.0)
|
||||||
|
|
Loading…
Reference in New Issue