diff --git a/doc/03-cli.md b/doc/03-cli.md index 7a20d742f..1daae9154 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -155,7 +155,8 @@ php composer.phar update vendor/* * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. * **--no-suggest:** Skips suggested packages in the output. -* **--with-dependencies:** Add also all dependencies of whitelisted packages to the whitelist. +* **--with-dependencies:** Add also dependencies of whitelisted packages to the whitelist, except those that are root requirements. +* **--with-all-dependencies:** Add also all dependencies of whitelisted packages to the whitelist, including those that are root requirements. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. @@ -201,8 +202,8 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master * **--no-update:** Disables the automatic update of the dependencies. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--update-no-dev:** Run the dependency update with the `--no-dev` option. -* **--update-with-dependencies:** Also update dependencies of the newly - required packages. +* **--update-with-dependencies:** Also update dependencies of the newly required packages, except those that are root requirements. +* **--update-with-all-dependencies:** Also update dependencies of the newly required packages, including those that are root requirements. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not fulfill these. See also the [`platform`](06-config.md#platform) config option. diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 048baace0..493c6481f 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -131,7 +131,7 @@ EOT ->setApcuAutoloader($apcu) ->setUpdate(true) ->setUpdateWhitelist($packages) - ->setWhitelistDependencies(!$input->getOption('no-update-with-dependencies')) + ->setWhitelistNonRootDependencies(!$input->getOption('no-update-with-dependencies')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ->setRunScripts(!$input->getOption('no-scripts')) ; diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 354c661ad..4ec17bef5 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -47,7 +47,8 @@ class RequireCommand extends InitCommand new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), 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 with explicit dependencies.'), + 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('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-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), @@ -169,7 +170,8 @@ EOT ->setApcuAutoloader($apcu) ->setUpdate(true) ->setUpdateWhitelist(array_keys($requirements)) - ->setWhitelistDependencies($input->getOption('update-with-dependencies')) + ->setWhitelistNonRootDependencies($input->getOption('update-with-dependencies')) + ->setWhitelistAllDependencies($input->getOption('update-with-all-dependencies')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index a05c813ff..986137f16 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -49,7 +49,8 @@ class UpdateCommand extends BaseCommand new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), - new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'), + new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also dependencies of whitelisted packages to the whitelist, except those defined in root package.'), + new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist, including those defined in root package.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), @@ -146,7 +147,8 @@ EOT ->setApcuAutoloader($apcu) ->setUpdate(true) ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $packages) - ->setWhitelistDependencies($input->getOption('with-dependencies')) + ->setWhitelistNonRootDependencies($input->getOption('with-dependencies')) + ->setWhitelistAllDependencies($input->getOption('with-all-dependencies')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 55ec51baf..0bfe450f5 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -125,7 +125,8 @@ class Installer * @var array|null */ protected $updateWhitelist = null; - protected $whitelistDependencies = false; + protected $whitelistNonRootDependencies = false; + protected $whitelistAllDependencies = false; /** * @var SuggestedPackagesReporter @@ -1283,7 +1284,7 @@ class Installer * * Packages which are listed as requirements in the root package will be * skipped including their dependencies, unless they are listed in the - * update whitelist themselves. + * update whitelist themselves or $whitelistAllDependencies is true. * * @param RepositoryInterface $localOrLockRepo Use the locked repo if available, otherwise installed repo will do * As we want the most accurate package list to work with, and installed @@ -1305,8 +1306,10 @@ class Installer } $skipPackages = array(); - foreach ($rootRequires as $require) { - $skipPackages[$require->getTarget()] = true; + if (!$this->whitelistAllDependencies) { + foreach ($rootRequires as $require) { + $skipPackages[$require->getTarget()] = TRUE; + } } $pool = new Pool('dev'); @@ -1351,7 +1354,7 @@ class Installer $seen[$package->getId()] = true; $this->updateWhitelist[$package->getName()] = true; - if (!$this->whitelistDependencies) { + if (!$this->whitelistNonRootDependencies && !$this->whitelistAllDependencies) { continue; } @@ -1652,14 +1655,34 @@ class Installer } /** - * Should dependencies of whitelisted packages be updated recursively? + * Should indirect dependencies of whitelisted packages be updated? + * + * This will NOT whitelist any dependencies that are also directly defined + * in the root package. + * + * @param bool $updateNonRootDependencies + * + * @return Installer + */ + public function setWhitelistNonRootDependencies($updateNonRootDependencies = true) + { + $this->whitelistNonRootDependencies = (bool) $updateNonRootDependencies; + + return $this; + } + + /** + * Should all dependencies of whitelisted packages be updated recursively? + * + * This will NOT whitelist any dependencies that are also defined in the + * root package. * - * @param bool $updateDependencies + * @param bool $updateAllDependencies * @return Installer */ - public function setWhitelistDependencies($updateDependencies = true) + public function setWhitelistAllDependencies($updateAllDependencies = true) { - $this->whitelistDependencies = (bool) $updateDependencies; + $this->whitelistAllDependencies = (bool) $updateAllDependencies; return $this; } diff --git a/tests/Composer/Test/Fixtures/installer/update-with-all-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-with-all-dependencies.test new file mode 100644 index 000000000..f4fbfce9b --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-with-all-dependencies.test @@ -0,0 +1,44 @@ +--TEST-- + +See Github issue #6661 ( github.com/composer/composer/issues/6661 ). + +When `--with-all-dependencies` is used, Composer\Installer::whitelistUpdateDependencies should update the dependencies of all whitelisted packages, even if the dependency is a root requirement. + +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a", "version": "1.0.0" }, + { "name": "a", "version": "1.1.0" }, + { "name": "b", "version": "1.0.0", "require": { "a": "~1.0" } }, + { "name": "b", "version": "1.1.0", "require": { "a": "~1.1" } } + ] + } + ], + "require": { + "a": "~1.0", + "b": "~1.0" + } +} + +--INSTALLED-- +[ + { "name": "a", "version": "1.0.0" }, + { "name": "b", "version": "1.0.0", "require": { "a": "~1.0" } } +] + +--RUN-- +update b --with-all-dependencies + +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Updating dependencies (including require-dev) +Package operations: 0 installs, 2 updates, 0 removals +Writing lock file +Generating autoload files + +--EXPECT-- +Updating a (1.0.0) to a (1.1.0) +Updating b (1.0.0) to b (1.1.0) \ No newline at end of file diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index adac21714..9d900095a 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -224,7 +224,8 @@ class InstallerTest extends TestCase ->setUpdate(true) ->setDryRun($input->getOption('dry-run')) ->setUpdateWhitelist($input->getArgument('packages')) - ->setWhitelistDependencies($input->getOption('with-dependencies')) + ->setWhitelistNonRootDependencies($input->getOption('with-dependencies')) + ->setWhitelistAllDependencies($input->getOption('with-all-dependencies')) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'));