소스 검색

The update allow list is now generated while building the pool

This reduces code complexity while making partial updates more
predictable. This also allows composer require to successfully run a
partial update for a new package with transitive dependency updates.
Nils Adermann 5 년 전
부모
커밋
01fe92905a

+ 10 - 3
src/Composer/Command/UpdateCommand.php

@@ -13,6 +13,7 @@
 namespace Composer\Command;
 
 use Composer\Composer;
+use Composer\DependencyResolver\Request;
 use Composer\Installer;
 use Composer\IO\IOInterface;
 use Composer\Plugin\CommandEvent;
@@ -145,6 +146,13 @@ EOT
         $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
         $apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader');
 
+        $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
+        if ($input->getOption('with-all-dependencies')) {
+            $updateAllowTransitiveDependencies = Request::UPDATE_TRANSITIVE_ROOT_DEPENDENCIES;
+        } elseif ($input->getOption('with-dependencies')) {
+            $updateAllowTransitiveDependencies = Request::UPDATE_TRANSITIVE_DEPENDENCIES;
+        }
+
         $install
             ->setDryRun($input->getOption('dry-run'))
             ->setVerbose($input->getOption('verbose'))
@@ -158,9 +166,8 @@ EOT
             ->setApcuAutoloader($apcu)
             ->setUpdate(true)
             ->setUpdateMirrors($updateMirrors)
-            ->setUpdateWhitelist($packages)
-            ->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
-            ->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
+            ->setUpdateAllowList($packages)
+            ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
             ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
             ->setPreferStable($input->getOption('prefer-stable'))
             ->setPreferLowest($input->getOption('prefer-lowest'))

+ 69 - 105
src/Composer/DependencyResolver/PoolBuilder.php

@@ -12,6 +12,7 @@
 
 namespace Composer\DependencyResolver;
 
+use Composer\IO\IOInterface;
 use Composer\Package\AliasPackage;
 use Composer\Package\BasePackage;
 use Composer\Package\Package;
@@ -36,32 +37,41 @@ class PoolBuilder
     private $rootAliases;
     private $rootReferences;
     private $eventDispatcher;
+    private $io;
 
     private $aliasMap = array();
     private $nameConstraints = array();
     private $loadedNames = array();
     private $packages = array();
     private $unacceptableFixedPackages = array();
+    private $updateAllowList = array();
+    private $skippedLoad = array();
+    private $updateAllowWarned = array();
 
-    private $unfixList = array();
-
-    public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, EventDispatcher $eventDispatcher = null)
+    public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, EventDispatcher $eventDispatcher = null, IOInterface $io = null)
     {
         $this->acceptableStabilities = $acceptableStabilities;
         $this->stabilityFlags = $stabilityFlags;
         $this->rootAliases = $rootAliases;
         $this->rootReferences = $rootReferences;
         $this->eventDispatcher = $eventDispatcher;
+        $this->io = $io;
     }
 
     public function buildPool(array $repositories, Request $request)
     {
         if ($request->getUpdateAllowList()) {
-            $this->unfixList = $request->getUpdateAllowList();
+            $this->updateAllowList = $request->getUpdateAllowList();
+            $this->warnAboutNonMatchingUpdateAllowList($request);
 
             foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
-                if (!$this->isUpdateable($lockedPackage)) {
+                if (!$this->isUpdateAllowed($lockedPackage)) {
                     $request->fixPackage($lockedPackage);
+                    // remember which packages we skipped loading remote content for in this partial update
+                    $this->skippedLoad[$lockedPackage->getName()] = true;
+                    foreach ($lockedPackage->getReplaces() as $link) {
+                        $this->skippedLoad[$link->getTarget()] = true;
+                    }
                 }
             }
         }
@@ -85,7 +95,7 @@ class PoolBuilder
                 || $package->getRepository() instanceof PlatformRepository
                 || StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $package->getNames(), $package->getStability())
             ) {
-                $loadNames += $this->loadPackage($request, $package);
+                $loadNames += $this->loadPackage($request, $package, false);
             } else {
                 $this->unacceptableFixedPackages[] = $package;
             }
@@ -94,6 +104,7 @@ class PoolBuilder
         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
             if (isset($this->loadedNames[$packageName])) {
+                $this->rootRequireNotUpdated[$packageName] = true;
                 continue;
             }
 
@@ -120,7 +131,6 @@ class PoolBuilder
                 if ($repository instanceof PlatformRepository || $repository === $request->getLockedRepository()) {
                     continue;
                 }
-
                 $result = $repository->loadPackages($loadNames, $this->acceptableStabilities, $this->stabilityFlags);
 
                 foreach ($result['namesFound'] as $name) {
@@ -189,7 +199,7 @@ class PoolBuilder
         return $pool;
     }
 
-    private function loadPackage(Request $request, PackageInterface $package)
+    private function loadPackage(Request $request, PackageInterface $package, $propagateUpdate = true)
     {
         $index = count($this->packages);
         $this->packages[] = $package;
@@ -205,6 +215,7 @@ class PoolBuilder
         // apply to
         if (isset($this->rootReferences[$name])) {
             // do not modify the references on already locked packages
+            // TODO what about unfix on allow update?
             if (!$request->isFixedPackage($package)) {
                 $package->setSourceDistReferences($this->rootReferences[$name]);
             }
@@ -229,9 +240,16 @@ class PoolBuilder
             $require = $link->getTarget();
             if (!isset($this->loadedNames[$require])) {
                 $loadNames[$require] = null;
-            }
-            if (isset($request->getUpdateAllowList()[$package->getName()])) {
-
+            // 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)) {
+                    $this->unfixPackage($request, $require);
+                    $loadNames[$require] = null;
+                } elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $require) && !isset($this->updateAllowWarned[$require]) && $this->io) {
+                    $this->updateAllowWarned[$require] = true;
+                    $this->io->writeError('<warning>Dependency "'.$require.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>');
+                }
             }
 
             $linkConstraint = $link->getConstraint();
@@ -252,121 +270,67 @@ class PoolBuilder
     }
 
     /**
-     * Adds all dependencies of the update whitelist to the whitelist, too.
-     *
-     * 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 or $whitelistAllDependencies is true.
+     * Checks if a particular name is required directly in the request
      *
-     * @param RepositoryInterface $lockRepo        Use the locked repo
-     *                                             As we want the most accurate package list to work with, and installed
-     *                                             repo might be empty but locked repo will always be current.
-     * @param array               $rootRequires    An array of links to packages in require of the root package
-     * @param array               $rootDevRequires An array of links to packages in require-dev of the root package
+     * @return bool
      */
-    private function whitelistUpdateDependencies($lockRepo, array $rootRequires, array $rootDevRequires)
+    private function isRootRequire(Request $request, $name)
     {
-        $rootRequires = array_merge($rootRequires, $rootDevRequires);
+        $rootRequires = $request->getRequires();
+        return isset($rootRequires[$name]);
+    }
 
-        $skipPackages = array();
-        if (!$this->whitelistAllDependencies) {
-            foreach ($rootRequires as $require) {
-                $skipPackages[$require->getTarget()] = true;
+    /**
+     * Checks whether the update allow list allows this package in the lock file to be updated
+     * @return bool
+     */
+    private function isUpdateAllowed(PackageInterface $package)
+    {
+        foreach ($this->updateAllowList as $pattern => $void) {
+            $patternRegexp = BasePackage::packageNameToRegexp($pattern);
+            if (preg_match($patternRegexp, $package->getName())) {
+                return true;
             }
         }
 
-        $installedRepo = new InstalledRepository(array($lockRepo));
-
-        $seen = array();
-
-        $rootRequiredPackageNames = array_keys($rootRequires);
-
-        foreach ($this->updateWhitelist as $packageName => $void) {
-            $packageQueue = new \SplQueue;
-            $nameMatchesRequiredPackage = false;
-
-            $depPackages = $installedRepo->findPackagesWithReplacersAndProviders($packageName);
-            $matchesByPattern = array();
-
-            // check if the name is a glob pattern that did not match directly
-            if (empty($depPackages)) {
-                // add any installed package matching the whitelisted name/pattern
-                $whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$');
-                foreach ($lockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) {
-                    $matchesByPattern[] = $installedRepo->findPackages($installedPackage['name']);
-                }
+        return false;
+    }
 
-                // add root requirements which match the whitelisted name/pattern
-                $whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName);
-                foreach ($rootRequiredPackageNames as $rootRequiredPackageName) {
-                    if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) {
-                        $nameMatchesRequiredPackage = true;
-                        break;
+    private function warnAboutNonMatchingUpdateAllowList(Request $request)
+    {
+        if ($this->io) {
+            foreach ($this->updateAllowList as $pattern => $void) {
+                foreach ($request->getLockedRepository()->getPackages() as $package) {
+                    $patternRegexp = BasePackage::packageNameToRegexp($pattern);
+                    if (preg_match($patternRegexp, $package->getName())) {
+                        continue 2;
                     }
                 }
-            }
-
-            if (!empty($matchesByPattern)) {
-                $depPackages = array_merge($depPackages, call_user_func_array('array_merge', $matchesByPattern));
-            }
-
-            if (count($depPackages) == 0 && !$nameMatchesRequiredPackage) {
-                $this->io->writeError('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
-            }
-
-            foreach ($depPackages as $depPackage) {
-                $packageQueue->enqueue($depPackage);
-            }
-
-            while (!$packageQueue->isEmpty()) {
-                $package = $packageQueue->dequeue();
-                if (isset($seen[spl_object_hash($package)])) {
-                    continue;
-                }
-
-                $seen[spl_object_hash($package)] = true;
-                $this->updateWhitelist[$package->getName()] = true;
-
-                if (!$this->whitelistTransitiveDependencies && !$this->whitelistAllDependencies) {
-                    continue;
-                }
-
-                $requires = $package->getRequires();
-
-                foreach ($requires as $require) {
-                    $requirePackages = $installedRepo->findPackagesWithReplacersAndProviders($require->getTarget());
-
-                    foreach ($requirePackages as $requirePackage) {
-                        if (isset($this->updateWhitelist[$requirePackage->getName()])) {
-                            continue;
-                        }
-
-                        if (isset($skipPackages[$requirePackage->getName()]) && !preg_match(BasePackage::packageNameToRegexp($packageName), $requirePackage->getName())) {
-                            $this->io->writeError('<warning>Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly whitelisted. Ignoring.</warning>');
-                            continue;
-                        }
-
-                        $packageQueue->enqueue($requirePackage);
-                    }
+                if (strpos($pattern, '*') !== false) {
+                    $this->io->writeError('<warning>Pattern "' . $pattern . '" listed for update does not match any locked packages.</warning>');
+                } else {
+                    $this->io->writeError('<warning>Package "' . $pattern . '" listed for update is not locked.</warning>');
                 }
             }
         }
     }
 
     /**
-     * @param  PackageInterface $package
-     * @return bool
+     * Reverts the decision to use a fixed package from lock file if a partial update with transitive dependencies
+     * found that this package actually needs to be updated
      */
-    private function isUpdateable(PackageInterface $package)
+    private function unfixPackage(Request $request, $name)
     {
-        foreach ($this->unfixList as $pattern => $void) {
-            $patternRegexp = BasePackage::packageNameToRegexp($pattern);
-            if (preg_match($patternRegexp, $package->getName())) {
-                return true;
+        // remove locked package by this name which was already initialized
+        foreach ($this->packages as $i => $loadedPackage) {
+            if ($loadedPackage->getName() === $name && $loadedPackage->getRepository() === $request->getLockedRepository()) {
+                $request->unfixPackage($loadedPackage);
+                unset($this->packages[$i]);
             }
         }
 
-        return false;
+        unset($this->skippedLoad[$name]);
+        unset($this->loadedNames[$name]);
     }
 }
 

+ 22 - 4
src/Composer/DependencyResolver/Request.php

@@ -23,13 +23,16 @@ use Composer\Semver\Constraint\ConstraintInterface;
  */
 class Request
 {
+    const UPDATE_ONLY_LISTED = 0;
+    const UPDATE_TRANSITIVE_DEPENDENCIES = 1;
+    const UPDATE_TRANSITIVE_ROOT_DEPENDENCIES = 2;
+
     protected $lockedRepository;
     protected $requires = array();
     protected $fixedPackages = array();
     protected $unlockables = array();
     protected $updateAllowList = array();
     protected $updateAllowTransitiveDependencies = false;
-    protected $updateAllowTransitiveRootDependencies = false;
 
     public function __construct(LockArrayRepository $lockedRepository = null)
     {
@@ -52,15 +55,20 @@ class Request
         $this->fixedPackages[spl_object_hash($package)] = $package;
 
         if (!$lockable) {
-            $this->unlockables[] = $package;
+            $this->unlockables[spl_object_hash($package)] = $package;
         }
     }
 
-    public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies, $updateAllowTransitiveRootDependencies)
+    public function unfixPackage(PackageInterface $package)
+    {
+        unset($this->fixedPackages[spl_object_hash($package)]);
+        unset($this->unlockables[spl_object_hash($package)]);
+    }
+
+    public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies)
     {
         $this->updateAllowList = $updateAllowList;
         $this->updateAllowTransitiveDependencies = $updateAllowTransitiveDependencies;
-        $this->updateAllowTransitiveRootDependencies = $updateAllowTransitiveRootDependencies;
     }
 
     public function getUpdateAllowList()
@@ -68,6 +76,16 @@ class Request
         return $this->updateAllowList;
     }
 
+    public function getUpdateAllowTransitiveDependencies()
+    {
+        return $this->updateAllowTransitiveDependencies !== self::UPDATE_ONLY_LISTED;
+    }
+
+    public function getUpdateAllowTransitiveRootDependencies()
+    {
+        return $this->updateAllowTransitiveDependencies === self::UPDATE_TRANSITIVE_ROOT_DEPENDENCIES;
+    }
+
     public function getRequires()
     {
         return $this->requires;

+ 21 - 39
src/Composer/Installer.php

@@ -142,9 +142,8 @@ class Installer
      * @var array|null
      */
     protected $updateMirrors = false;
-    protected $updateWhitelist = null;
-    protected $whitelistTransitiveDependencies = false;
-    protected $whitelistAllDependencies = false;
+    protected $updateAllowList = null;
+    protected $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
 
     /**
      * @var SuggestedPackagesReporter
@@ -199,8 +198,8 @@ class Installer
         gc_collect_cycles();
         gc_disable();
 
-        if ($this->updateWhitelist && $this->updateMirrors) {
-            throw new \RuntimeException("The installer options updateMirrors and updateWhitelist are mutually exclusive.");
+        if ($this->updateAllowList && $this->updateMirrors) {
+            throw new \RuntimeException("The installer options updateMirrors and updateAllowList are mutually exclusive.");
         }
 
         // Force update if there is no lock file present
@@ -352,16 +351,11 @@ class Installer
             $lockedRepository = $this->locker->getLockedRepository(true);
         }
 
-        if ($this->updateWhitelist) {
+        if ($this->updateAllowList) {
             if (!$lockedRepository) {
                 $this->io->writeError('<error>Cannot update only a partial set of packages without a lock file present.</error>', true, IOInterface::QUIET);
                 return 1;
-            }/*
-            $this->whitelistUpdateDependencies(
-                $lockedRepository,
-                $this->package->getRequires(),
-                $this->package->getDevRequires()
-            );*/
+            }
         }
 
         $this->io->writeError('<info>Loading composer repositories with package information</info>');
@@ -395,11 +389,11 @@ class Installer
         }
 
         // pass the allow list into the request, so the pool builder can apply it
-        if ($this->updateWhitelist) {
-            $request->setUpdateAllowList($this->updateWhitelist, $this->whitelistTransitiveDependencies, $this->whitelistAllDependencies);
+        if ($this->updateAllowList) {
+            $request->setUpdateAllowList($this->updateAllowList, $this->updateAllowTransitiveDependencies);
         }
 
-        $pool = $repositorySet->createPool($request, $this->eventDispatcher);
+        $pool = $repositorySet->createPool($request, $this->eventDispatcher, $this->io);
 
         // solve dependencies
         $solver = new Solver($policy, $pool, $this->io, $repositorySet);
@@ -618,7 +612,7 @@ class Installer
                 $request->requireName($link->getTarget(), $link->getConstraint());
             }
 
-            $pool = $repositorySet->createPool($request, $this->eventDispatcher);
+            $pool = $repositorySet->createPool($request, $this->eventDispatcher, $this->io);
 
             // solve dependencies
             $solver = new Solver($policy, $pool, $this->io, $repositorySet);
@@ -1138,41 +1132,29 @@ class Installer
      * @param  array     $packages
      * @return Installer
      */
-    public function setUpdateWhitelist(array $packages)
+    public function setUpdateAllowList(array $packages)
     {
-        $this->updateWhitelist = array_flip(array_map('strtolower', $packages));
+        $this->updateAllowList = array_flip(array_map('strtolower', $packages));
 
         return $this;
     }
 
     /**
-     * Should dependencies of whitelisted packages (but not direct dependencies) be updated?
+     * Should dependencies of packages marked for update be updated?
      *
-     * This will NOT whitelist any dependencies that are also directly defined
-     * in the root package.
+     * Depending on the chosen constant this will either only update the directly named packages, all transitive
+     * dependencies which are not root requirement or all transitive dependencies including root requirements
      *
-     * @param  bool      $updateTransitiveDependencies
+     * @param  int      $updateAllowTransitiveDependencies One of the UPDATE_ constants on the Request class
      * @return Installer
      */
-    public function setWhitelistTransitiveDependencies($updateTransitiveDependencies = true)
+    public function setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
     {
-        $this->whitelistTransitiveDependencies = (bool) $updateTransitiveDependencies;
-
-        return $this;
-    }
+        if (!in_array($updateAllowTransitiveDependencies, array(Request::UPDATE_ONLY_LISTED, Request::UPDATE_TRANSITIVE_DEPENDENCIES, Request::UPDATE_TRANSITIVE_ROOT_DEPENDENCIES), true)) {
+            throw new \RuntimeException("Invalid value for updateAllowTransitiveDependencies supplied");
+        }
 
-    /**
-     * Should all dependencies of whitelisted packages be updated recursively?
-     *
-     * This will whitelist any dependencies of the whitelisted packages, including
-     * those defined in the root package.
-     *
-     * @param  bool      $updateAllDependencies
-     * @return Installer
-     */
-    public function setWhitelistAllDependencies($updateAllDependencies = true)
-    {
-        $this->whitelistAllDependencies = (bool) $updateAllDependencies;
+        $this->updateAllowTransitiveDependencies = $updateAllowTransitiveDependencies;
 
         return $this;
     }

+ 3 - 2
src/Composer/Repository/RepositorySet.php

@@ -16,6 +16,7 @@ use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\PoolBuilder;
 use Composer\DependencyResolver\Request;
 use Composer\EventDispatcher\EventDispatcher;
+use Composer\IO\IOInterface;
 use Composer\Package\BasePackage;
 use Composer\Package\Version\VersionParser;
 use Composer\Repository\CompositeRepository;
@@ -185,9 +186,9 @@ class RepositorySet
      *
      * @return Pool
      */
-    public function createPool(Request $request, EventDispatcher $eventDispatcher = null)
+    public function createPool(Request $request, EventDispatcher $eventDispatcher = null, IOInterface $io = null)
     {
-        $poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences, $eventDispatcher);
+        $poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences, $eventDispatcher, $io);
 
         foreach ($this->repositories as $repo) {
             if (($repo instanceof InstalledRepositoryInterface || $repo instanceof InstalledRepository) && !$this->allowInstalledRepositories) {

+ 2 - 2
tests/Composer/Test/Fixtures/installer/github-issues-4795.test

@@ -14,7 +14,7 @@ dependency of one the requirements that is whitelisted for update.
                 { "name": "a/a", "version": "1.0.0" },
                 { "name": "a/a", "version": "1.1.0" },
                 { "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } },
-                { "name": "b/b", "version": "1.1.0", "require": { "a/b": "~1.1" } }
+                { "name": "b/b", "version": "1.1.0", "require": { "a/a": "~1.1" } }
             ]
         }
     ],
@@ -49,9 +49,9 @@ dependency of one the requirements that is whitelisted for update.
 update b/b --with-dependencies
 
 --EXPECT-OUTPUT--
-<warning>Dependency "a/a" is also a root requirement, but is not explicitly whitelisted. Ignoring.</warning>
 Loading composer repositories with package information
 Updating dependencies
+<warning>Dependency "a/a" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>
 Nothing to modify in lock file
 Writing lock file
 Installing dependencies from lock file (including require-dev)

+ 1 - 1
tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test

@@ -70,7 +70,7 @@ Update with a package whitelist only updates those packages and their dependenci
     "platform-dev": []
 }
 --RUN--
-update whitelisted/pkg-* --with-dependencies
+update whitelisted/pkg-* foobar --with-dependencies
 --EXPECT--
 Upgrading dependency/pkg (1.0.0 => 1.1.0)
 Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)

+ 58 - 0
tests/Composer/Test/Fixtures/installer/update-whitelist-warns-non-existing-patterns.test

@@ -0,0 +1,58 @@
+--TEST--
+Verify that partial updates warn about using patterns in the argument which have no matches
+--COMPOSER--
+{
+    "repositories": [
+        {
+            "type": "package",
+            "package": [
+                { "name": "a/a", "version": "1.0.0" },
+                { "name": "b/b", "version": "1.0.0" },
+                { "name": "b/b", "version": "1.1.0" }
+            ]
+        }
+    ],
+    "require": {
+        "a/a": "~1.0",
+        "b/b": "~1.0"
+    }
+}
+
+--INSTALLED--
+[
+    { "name": "a/a", "version": "1.0.0" },
+    { "name": "b/b", "version": "1.0.0" }
+]
+
+--LOCK--
+{
+    "packages": [
+        { "name": "a/a", "version": "1.0.0" },
+        { "name": "b/b", "version": "1.0.0" }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "dev",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}
+--RUN--
+update b/b foo/bar baz/* --with-dependencies
+
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+<warning>Package "foo/bar" listed for update is not locked.</warning>
+<warning>Pattern "baz/*" listed for update does not match any locked packages.</warning>
+Lock file operations: 0 installs, 1 update, 0 removals
+  - Upgrading b/b (1.0.0 => 1.1.0)
+Writing lock file
+Installing dependencies from lock file (including require-dev)
+Package operations: 0 installs, 1 update, 0 removals
+Generating autoload files
+
+--EXPECT--
+Upgrading b/b (1.0.0 => 1.1.0)

+ 10 - 3
tests/Composer/Test/InstallerTest.php

@@ -12,6 +12,7 @@
 
 namespace Composer\Test;
 
+use Composer\DependencyResolver\Request;
 use Composer\Installer;
 use Composer\Console\Application;
 use Composer\IO\BufferIO;
@@ -279,14 +280,20 @@ class InstallerTest extends TestCase
             $updateMirrors = $input->getOption('lock') || count($filteredPackages) != count($packages);
             $packages = $filteredPackages;
 
+            $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
+            if ($input->getOption('with-all-dependencies')) {
+                $updateAllowTransitiveDependencies = Request::UPDATE_TRANSITIVE_ROOT_DEPENDENCIES;
+            } elseif ($input->getOption('with-dependencies')) {
+                $updateAllowTransitiveDependencies = Request::UPDATE_TRANSITIVE_DEPENDENCIES;
+            }
+
             $installer
                 ->setDevMode(!$input->getOption('no-dev'))
                 ->setUpdate(true)
                 ->setDryRun($input->getOption('dry-run'))
                 ->setUpdateMirrors($updateMirrors)
-                ->setUpdateWhitelist($packages)
-                ->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
-                ->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
+                ->setUpdateAllowList($packages)
+                ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
                 ->setPreferStable($input->getOption('prefer-stable'))
                 ->setPreferLowest($input->getOption('prefer-lowest'))
                 ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'));