Fix handling of reference updates and root references

main
Nils Adermann 5 years ago
parent 9053d74282
commit f1e4ccbe1d

@ -62,9 +62,10 @@ class LocalRepoTransaction
// do we need to update?
if ($package->getVersion() != $localPackageMap[$package->getName()]->getVersion()) {
$operations[] = new Operation\UpdateOperation($source, $package);
} else {
// TODO do we need to update metadata? force update based on reference?
} elseif ($package->isDev() && $package->getSourceReference() !== $localPackageMap[$package->getName()]->getSourceReference()) {
$operations[] = new Operation\UpdateOperation($source, $package);
}
unset($removeMap[$package->getName()]);
} else {
$operations[] = new Operation\InstallOperation($package);
unset($removeMap[$package->getName()]);

@ -108,7 +108,7 @@ class LockTransaction
}
// TODO additionalFixedRepository needs to be looked at here as well?
public function getNewLockNonDevPackages(array $rootForceReferences)
public function getNewLockNonDevPackages()
{
$packages = array();
foreach ($this->decisions as $i => $decision) {
@ -117,14 +117,6 @@ class LockTransaction
if ($literal > 0) {
$package = $this->pool->literalToPackage($literal);
if (!isset($this->unlockableMap[$package->id]) && !($package instanceof AliasPackage) && !($package instanceof RootAliasPackage)) {
echo "rootRef? ".$package->getName()."\n";
// TODO can we really just do this for all of them here? What if we're doing a partial update, should this change anyway?
if (isset($rootForceReferences[$package->getName()])) {
echo "rootRef! ".$package->getName()."\n";
$package->setSourceReference($rootForceReferences[$package->getName()]);
}
$packages[] = $package;
}
}
@ -135,6 +127,7 @@ class LockTransaction
public function getNewLockDevPackages()
{
// TODO this is empty?
$packages = array();
return $packages;
}

@ -14,11 +14,11 @@ namespace Composer\DependencyResolver;
use Composer\Package\AliasPackage;
use Composer\Package\BasePackage;
use Composer\Package\Package;
use Composer\Package\PackageInterface;
use Composer\Repository\AsyncRepositoryInterface;
use Composer\Repository\ComposerRepository;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Repository\LockArrayRepository;
use Composer\Repository\PlatformRepository;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\MultiConstraint;
@ -31,6 +31,7 @@ class PoolBuilder
private $isPackageAcceptableCallable;
private $filterRequires;
private $rootAliases;
private $rootReferences;
private $aliasMap = array();
private $nameConstraints = array();
@ -46,16 +47,17 @@ class PoolBuilder
$this->filterRequires = $filterRequires;
}
public function buildPool(array $repositories, array $rootAliases, Request $request)
public function buildPool(array $repositories, array $rootAliases, array $rootReferences, Request $request)
{
$pool = new Pool($this->filterRequires);
$this->rootAliases = $rootAliases;
$this->rootReferences = $rootReferences;
// TODO do we really want the request here? kind of want a root requirements thingy instead
$loadNames = array();
foreach ($request->getFixedPackages() as $package) {
// TODO can actually use very specific constraint
$loadNames[$package->getname()] = null;
$loadNames[$package->getName()] = null;
}
foreach ($request->getJobs() as $job) {
switch ($job['cmd']) {
@ -94,7 +96,7 @@ class PoolBuilder
foreach ($packages as $package) {
if (call_user_func($this->isPackageAcceptableCallable, $package->getNames(), $package->getStability())) {
$newLoadNames += $this->loadPackage($package, $key);
$newLoadNames += $this->loadPackage($request, $package, $key);
}
}
}
@ -132,7 +134,7 @@ class PoolBuilder
if ($repository instanceof PlatformRepository ||
$repository instanceof InstalledRepositoryInterface) {
foreach ($repository->getPackages() as $package) {
$this->loadPackage($package, $key);
$this->loadPackage($request, $package, $key);
}
}
}
@ -146,7 +148,7 @@ class PoolBuilder
return $pool;
}
private function loadPackage(PackageInterface $package, $repoIndex)
private function loadPackage(Request $request, PackageInterface $package, $repoIndex)
{
$index = count($this->packages);
$this->packages[] = $package;
@ -156,8 +158,18 @@ class PoolBuilder
$this->aliasMap[spl_object_hash($package->getAliasOf())][$index] = $package;
}
// handle root package aliases
$name = $package->getName();
// we're simply setting the root references on all versions for a name here and rely on the solver to pick the
// right version. It'd be more work to figure out which versions and which aliases of those versions this may
// apply to
if (isset($this->rootReferences[$name])) {
// do not modify the references on already locked packages
if (!$request->isFixedPackage($package)) {
$this->setReferences($package, $this->rootReferences[$name]);
}
}
if (isset($this->rootAliases[$name][$package->getVersion()])) {
$alias = $this->rootAliases[$name][$package->getVersion()];
if ($package instanceof AliasPackage) {
@ -194,5 +206,19 @@ class PoolBuilder
return $loadNames;
}
private function setReferences(Package $package, $reference)
{
$package->setSourceReference($reference);
// only bitbucket, github and gitlab have auto generated dist URLs that easily allow replacing the reference in the dist URL
// TODO generalize this a bit for self-managed/on-prem versions? Some kind of replace token in dist urls which allow this?
if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $package->getDistUrl())) {
$package->setDistReference($reference);
$package->setDistUrl(preg_replace('{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i', $reference, $package->getDistUrl()));
} elseif ($package->getDistReference()) { // update the dist reference if there was one, but if none was provided ignore it
$package->setDistReference($reference);
}
}
}

@ -52,7 +52,7 @@ class Request
$package = $package->getAliasOf();
}
$this->fixedPackages[] = $package;
$this->fixedPackages[spl_object_hash($package)] = $package;
if (!$lockable) {
$this->unlockables[] = $package;
@ -80,6 +80,11 @@ class Request
return $this->fixedPackages;
}
public function isFixedPackage(PackageInterface $package)
{
return isset($this->fixedPackages[spl_object_hash($package)]);
}
public function getPresentMap()
{
$presentMap = array();

@ -39,6 +39,7 @@ use Composer\Package\CompletePackage;
use Composer\Package\Link;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Package\Package;
use Composer\Repository\RepositorySet;
use Composer\Semver\Constraint\Constraint;
use Composer\Package\Locker;
@ -375,25 +376,21 @@ class Installer
// to the version specified in the lock
if ($this->updateWhitelist) {
foreach ($lockedRepository->getPackages() as $lockedPackage) {
// TODO should this really be checking acceptability here?
if (!$this->isUpdateable($lockedPackage) && $repositorySet->isPackageAcceptable($lockedPackage->getNames(), $lockedPackage->getStability())) {
// need to actually allow for metadata updates at all times, so we want to fix the most recent prefered package in the repo set instead
$packages = $repositorySet->findPackages($lockedPackage->getName(), new Constraint('=', $lockedPackage->getVersion()));
$lockedPackage = isset($packages[0]) ? $packages[0] : $lockedPackage;
// in how far do we need to reset requirements here, theoretically it's the same version so nothing should have changed, but for a dev version it could have?
// TODO add reason for fix?
$request->fixPackage($lockedPackage);
}
}
}
// TODO reenable events
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request);
$pool = $repositorySet->createPool($request);
// TODO ensure that the solver always picks most recent reference for dev packages, so they get updated even when just a new commit is pushed but version is unchanged
// should already be solved by using the remote package in all cases in the pool
// solve dependencies
$solver = new Solver($policy, $pool, $this->io);
@ -426,7 +423,7 @@ class Installer
$platformDevReqs = $this->extractPlatformRequirements($this->package->getDevRequires());
$updatedLock = $this->locker->setLockData(
$lockTransaction->getNewLockNonDevPackages($this->package->getReferences()),
$lockTransaction->getNewLockNonDevPackages(),
$lockTransaction->getNewLockDevPackages(),
$platformReqs,
$platformDevReqs,
@ -504,6 +501,8 @@ class Installer
}
}
$this->io->write('foo');
if ($doInstall) {
// TODO ensure lock is used from locker as-is, since it may not have been written to disk in case of executeOperations == false
return $this->doInstall($localRepo, true);
@ -703,7 +702,7 @@ class Installer
$this->fixedRootPackage->setRequires(array());
$this->fixedRootPackage->setDevRequires(array());
$repositorySet = new RepositorySet($rootAliases, $minimumStability, $stabilityFlags, $rootConstraints);
$repositorySet = new RepositorySet($rootAliases, $this->package->getReferences(), $minimumStability, $stabilityFlags, $rootConstraints);
$repositorySet->addRepository(new InstalledArrayRepository(array($this->fixedRootPackage)));
$repositorySet->addRepository($platformRepo);
if ($this->additionalFixedRepository) {
@ -793,28 +792,7 @@ class Installer
return $normalizedAliases;
}
private function updatePackageUrl(PackageInterface $package, $sourceUrl, $sourceType, $sourceReference, $distUrl)
{
$oldSourceRef = $package->getSourceReference();
if ($package->getSourceUrl() !== $sourceUrl) {
$package->setSourceType($sourceType);
$package->setSourceUrl($sourceUrl);
$package->setSourceReference($sourceReference);
}
// only update dist url for github/bitbucket/gitlab dists as they use a combination of dist url + dist reference to install
// but for other urls this is ambiguous and could result in bad outcomes
if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $distUrl)) {
$package->setDistUrl($distUrl);
$this->updateInstallReferences($package, $sourceReference);
}
if ($this->updateWhitelist && !$this->isUpdateable($package)) {
$this->updateInstallReferences($package, $oldSourceRef);
}
}
// TODO do we still need this function?
private function updateInstallReferences(PackageInterface $package, $reference)
{
if (!$reference) {
@ -910,7 +888,7 @@ class Installer
}
}
$repositorySet = new RepositorySet(array(), 'dev');
$repositorySet = new RepositorySet(array(), array(), 'dev');
$repositorySet->addRepository($lockRepo);
$seen = array();

@ -29,6 +29,8 @@ class RepositorySet
{
/** @var array */
private $rootAliases;
/** @var array */
private $rootReferences;
/** @var RepositoryInterface[] */
private $repositories = array();
@ -40,9 +42,10 @@ class RepositorySet
/** @var Pool */
private $pool;
public function __construct(array $rootAliases = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
public function __construct(array $rootAliases = array(), array $rootReferences = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
{
$this->rootAliases = $rootAliases;
$this->rootReferences = $rootReferences;
$this->acceptableStabilities = array();
foreach (BasePackage::$stabilities as $stability => $value) {
@ -151,7 +154,7 @@ class RepositorySet
{
$poolBuilder = new PoolBuilder(array($this, 'isPackageAcceptable'), $this->filterRequires);
return $this->pool = $poolBuilder->buildPool($this->repositories, $this->rootAliases, $request);
return $this->pool = $poolBuilder->buildPool($this->repositories, $this->rootAliases, $this->rootReferences, $request);
}
// TODO unify this with above in some simpler version without "request"?

@ -930,7 +930,7 @@ class SolverTest extends TestCase
protected function createSolver()
{
$this->solver = new Solver($this->policy, $this->repoSet->createPool($this->request), $this->repoInstalled, new NullIO());
$this->solver = new Solver($this->policy, $this->repoSet->createPool($this->request), new NullIO());
}
protected function checkSolverResult(array $expected)

@ -3,10 +3,10 @@ Update updates URLs for updated packages if they have changed
a/a is dev and gets everything updated as it updates to a new ref
b/b is a tag and gets everything updated by updating the package URL directly
c/c is a tag and not whitelisted and gets the new URL but keeps its old ref
c/c is a tag and not whitelisted and remains unchanged
d/d is dev but with a #ref so it should get URL updated but not the reference
e/e is dev and newly installed with a #ref so it should get the correct URL but with the #111 ref
e/e is dev but not whitelisted and gets the new URL but keeps its old ref
f/f is dev but not whitelisted and remains unchanged
g/g is dev and installed in a different ref than the #ref, so it gets updated and gets the new URL but not the new ref
--COMPOSER--
{
@ -161,8 +161,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
},
{
"name": "c/c", "version": "1.0.0",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/newc", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/newc/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
@ -179,8 +179,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
},
{
"name": "f/f", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/newf", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/newf/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
@ -208,6 +208,6 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
--RUN--
update a/a b/b d/d g/g
--EXPECT--
Installing e/e (dev-master 1111111)
Updating a/a (dev-master 1111111) to a/a (dev-master 2222222)
Installing e/e (dev-master 1111111)
Updating g/g (dev-master 0000000) to g/g (dev-master 1111111)

@ -0,0 +1,213 @@
--TEST--
Update mirrors updates URLs for all packages if they have changed without updating versions
a/a is dev and gets everything updated as it updates to a new ref
b/b is a tag and gets everything updated by updating the package URL directly
c/c is a tag and not whitelisted and gets the new URL but keeps its old ref
d/d is dev but with a #ref so it should get URL updated but not the reference
e/e is dev and newly installed with a #ref so it should get the correct URL but with the #111 ref
e/e is dev but not whitelisted and gets the new URL but keeps its old ref
g/g is dev and installed in a different ref than the #ref, so it gets updated and gets the new URL but not the new ref
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{
"name": "a/a", "version": "dev-master",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/a/newa", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/zipball/2222222222222222222222222222222222222222", "type": "zip" }
},
{
"name": "b/b", "version": "2.0.3",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" }
},
{
"name": "c/c", "version": "1.0.0",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/c/newc", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/c/newc/zipball/2222222222222222222222222222222222222222", "type": "zip" }
},
{
"name": "d/d", "version": "dev-master",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/d/newd", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/d/newd/zipball/2222222222222222222222222222222222222222", "type": "zip" }
},
{
"name": "e/e", "version": "dev-master",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/e/newe", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/e/newe/zipball/2222222222222222222222222222222222222222", "type": "zip" }
},
{
"name": "f/f", "version": "dev-master",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/f/newf", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/f/newf/zipball/2222222222222222222222222222222222222222", "type": "zip" }
},
{
"name": "g/g", "version": "dev-master",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/g/newg", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/g/newg/zipball/2222222222222222222222222222222222222222", "type": "zip" }
}
]
}
],
"require": {
"a/a": "dev-master",
"b/b": "2.0.3",
"c/c": "1.0.0",
"d/d": "dev-master#1111111111111111111111111111111111111111",
"e/e": "dev-master#1111111111111111111111111111111111111111",
"f/f": "dev-master",
"g/g": "dev-master#1111111111111111111111111111111111111111"
}
}
--INSTALLED--
[
{
"name": "a/a", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/a", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip" }
},
{
"name": "b/b", "version": "2.0.3",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/b", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip" }
},
{
"name": "c/c", "version": "1.0.0",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" }
},
{
"name": "d/d", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/d", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip" }
},
{
"name": "f/f", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip" }
},
{
"name": "g/g", "version": "dev-master",
"source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/g", "type": "git" },
"dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip" }
}
]
--LOCK--
{
"packages": [
{
"name": "a/a", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/a", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "b/b", "version": "2.0.3",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/b", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "c/c", "version": "1.0.0",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "d/d", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/d", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "f/f", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "g/g", "version": "dev-master",
"source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/g", "type": "git" },
"dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip" },
"type": "library"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}
--EXPECT-LOCK--
{
"packages": [
{
"name": "a/a", "version": "dev-master",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/a/newa", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/zipball/2222222222222222222222222222222222222222", "type": "zip" },
"type": "library"
},
{
"name": "b/b", "version": "2.0.3",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" },
"type": "library"
},
{
"name": "c/c", "version": "1.0.0",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/newc", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/newc/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "d/d", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/newd", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "e/e", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/e/newe", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/e/newe/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "f/f", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/newf", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/newf/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "g/g", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/g/newg", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/g/newg/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"a/a": 20,
"d/d": 20,
"e/e": 20,
"f/f": 20,
"g/g": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}
--RUN--
update a/a b/b d/d g/g
--EXPECT--
Installing e/e (dev-master 1111111)
Updating a/a (dev-master 1111111) to a/a (dev-master 2222222)
Updating g/g (dev-master 0000000) to g/g (dev-master 1111111)
Loading…
Cancel
Save