Merge pull request #8729 from naderman/t/partial-update-always-update-replace
Give a clearer error message explaining how to update a conflicting locked dependencypull/8740/head
commit
51c48b1519
|
@ -66,6 +66,8 @@ class RequireCommand extends InitCommand
|
||||||
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
|
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
|
||||||
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated, except those that are root requirements.'),
|
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated, except those that are root requirements.'),
|
||||||
new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'),
|
new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'),
|
||||||
|
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-dependencies'),
|
||||||
|
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'),
|
||||||
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
|
||||||
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
|
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
|
||||||
new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
|
new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
|
||||||
|
@ -256,9 +258,9 @@ EOT
|
||||||
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
|
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
|
||||||
|
|
||||||
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
|
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
|
||||||
if ($input->getOption('update-with-all-dependencies')) {
|
if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) {
|
||||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
|
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
|
||||||
} elseif ($input->getOption('update-with-dependencies')) {
|
} elseif ($input->getOption('update-with-dependencies') || $input->getOption('with-dependencies')) {
|
||||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
|
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -268,8 +268,8 @@ class PoolBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're doing a partial update with deps and we're not loading an initial fixed package
|
// if we're doing a partial update with deps we also need to unfix packages which are being replaced in case they
|
||||||
// we also need to trigger an update for transitive deps which are being replaced
|
// 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();
|
||||||
|
|
|
@ -92,7 +92,6 @@ class Problem
|
||||||
}
|
}
|
||||||
|
|
||||||
$messages = array();
|
$messages = array();
|
||||||
|
|
||||||
foreach ($reasons as $rule) {
|
foreach ($reasons as $rule) {
|
||||||
$messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool);
|
$messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool);
|
||||||
}
|
}
|
||||||
|
@ -100,6 +99,17 @@ class Problem
|
||||||
return "\n - ".implode("\n - ", $messages);
|
return "\n - ".implode("\n - ", $messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isCausedByLock()
|
||||||
|
{
|
||||||
|
foreach ($this->reasons as $sectionRules) {
|
||||||
|
foreach ($sectionRules as $rule) {
|
||||||
|
if ($rule->isCausedByLock()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a reason descriptor but ignore duplicates
|
* Store a reason descriptor but ignore duplicates
|
||||||
*
|
*
|
||||||
|
|
|
@ -123,6 +123,11 @@ abstract class Rule
|
||||||
|
|
||||||
abstract public function isAssertion();
|
abstract public function isAssertion();
|
||||||
|
|
||||||
|
public function isCausedByLock()
|
||||||
|
{
|
||||||
|
return $this->getReason() === self::RULE_FIXED && $this->reasonData['lockable'];
|
||||||
|
}
|
||||||
|
|
||||||
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array())
|
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array())
|
||||||
{
|
{
|
||||||
$literals = $this->getLiterals();
|
$literals = $this->getLiterals();
|
||||||
|
|
|
@ -36,12 +36,15 @@ class SolverProblemsException extends \RuntimeException
|
||||||
$installedMap = $request->getPresentMap(true);
|
$installedMap = $request->getPresentMap(true);
|
||||||
$text = "\n";
|
$text = "\n";
|
||||||
$hasExtensionProblems = false;
|
$hasExtensionProblems = false;
|
||||||
|
$isCausedByLock = false;
|
||||||
foreach ($this->problems as $i => $problem) {
|
foreach ($this->problems as $i => $problem) {
|
||||||
$text .= " Problem ".($i + 1).$problem->getPrettyString($repositorySet, $request, $pool, $installedMap, $this->learnedPool)."\n";
|
$text .= " Problem ".($i + 1).$problem->getPrettyString($repositorySet, $request, $pool, $installedMap, $this->learnedPool)."\n";
|
||||||
|
|
||||||
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
|
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
|
||||||
$hasExtensionProblems = true;
|
$hasExtensionProblems = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$isCausedByLock |= $problem->isCausedByLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) {
|
if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) {
|
||||||
|
@ -52,6 +55,10 @@ class SolverProblemsException extends \RuntimeException
|
||||||
$text .= $this->createExtensionHint();
|
$text .= $this->createExtensionHint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($isCausedByLock && !$isDevExtraction) {
|
||||||
|
$text .= "\nUse the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions.";
|
||||||
|
}
|
||||||
|
|
||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
--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
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
|
||||||
|
{ "name": "current/dep", "version": "1.0.0" },
|
||||||
|
{ "name": "new/pkg", "version": "1.0.0", "replace": { "current/dep": "1.0.0" } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"current/pkg": "1.*",
|
||||||
|
"new/pkg": "1.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--INSTALLED--
|
||||||
|
[
|
||||||
|
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
|
||||||
|
{ "name": "current/dep", "version": "1.0.0" }
|
||||||
|
]
|
||||||
|
--LOCK--
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
|
||||||
|
{ "name": "current/dep", "version": "1.0.0" }
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": []
|
||||||
|
}
|
||||||
|
--RUN--
|
||||||
|
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 can not be installed as that would require removing current/dep 1.0.0. new/pkg replaces current/dep and can thus not 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 updates and removals for packages currently locked to specific versions.
|
||||||
|
--EXPECT--
|
Loading…
Reference in New Issue