diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 7ab8b9da76d5..5f9142a6c71f 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -755,6 +755,31 @@ variables are parsed in both Windows and Linux/Mac notations. For example > **Note:** Repository paths can also contain wildcards like `*` and `?`. > For details, see the [PHP glob function](https://php.net/glob). +You can configure the way the package's dist reference (which appears in +the composer.lock file) is built. + +There are the following options: +- `null` - reference will be always null. This can help reduce lock file conflicts + in the lock file but reduces clarity as to when the last update happened and whether + the package is in the latest state. +- `config` - reference is built based on a hash of the package's composer.json and repo config +- `auto` (used by default) - reference is built basing on the hash like with `config`, but if + the package folder contains a git repository, the HEAD commit's hash is used as reference instead. + +```json +{ + "repositories": [ + { + "type": "path", + "url": "../../packages/my-package", + "options": { + "reference": "config" + } + } + ] +} +``` + ## Disabling Packagist.org You can disable the default Packagist.org repository by adding this to your diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index bd32d3612d6c..7134499298d7 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -56,6 +56,13 @@ * "symlink": false * } * }, + * { + * "type": "path", + * "url": "../../relative/path/to/package/", + * "options": { + * "reference": "none" + * } + * }, * ] * @endcode * @@ -81,7 +88,7 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn /** * @var mixed[] - * @phpstan-var array{url: string, options?: array{symlink?: bool, relative?: bool, versions?: array}} + * @phpstan-var array{url: string, options?: array{symlink?: bool, reference?: string, relative?: bool, versions?: array}} */ private $repoConfig; @@ -91,14 +98,14 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn private $process; /** - * @var array{symlink?: bool, relative?: bool, versions?: array} + * @var array{symlink?: bool, reference?: string, relative?: bool, versions?: array} */ private $options; /** * Initializes path repository. * - * @param array{url?: string, options?: array{symlink?: bool, relative?: bool, versions?: array}} $repoConfig + * @param array{url?: string, options?: array{symlink?: bool, reference?: string, relative?: bool, versions?: array}} $repoConfig * @param IOInterface $io * @param Config $config */ @@ -171,12 +178,16 @@ protected function initialize() $package['dist'] = array( 'type' => 'path', 'url' => $url, - 'reference' => sha1($json . serialize($this->options)), ); + $reference = $this->options['reference'] ?? 'auto'; + if ('none' === $reference) { + $package['dist']['reference'] = null; + } elseif ('config' === $reference || 'auto' === $reference) { + $package['dist']['reference'] = sha1($json . serialize($this->options)); + } // copy symlink/relative options to transport options $package['transport-options'] = array_intersect_key($this->options, array('symlink' => true, 'relative' => true)); - // use the version provided as option if available if (isset($package['name'], $this->options['versions'][$package['name']])) { $package['version'] = $this->options['versions'][$package['name']]; @@ -194,7 +205,7 @@ protected function initialize() } $output = ''; - if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H'.GitUtil::getNoShowSignatureFlag($this->process), $output, $path)) { + if ('auto' === $reference && is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H'.GitUtil::getNoShowSignatureFlag($this->process), $output, $path)) { $package['dist']['reference'] = trim($output); } diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php index cb1aa0e3f205..bff6fd6b24cc 100644 --- a/tests/Composer/Test/Repository/PathRepositoryTest.php +++ b/tests/Composer/Test/Repository/PathRepositoryTest.php @@ -154,4 +154,50 @@ public function testUrlRemainsRelative() $relativeUrl = str_replace(DIRECTORY_SEPARATOR, '/', $relativeUrl); $this->assertSame($relativeUrl, $package->getDistUrl()); } + + public function testReferenceNone() + { + $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') + ->getMock(); + + $config = new \Composer\Config(); + + $options = array( + 'reference' => 'none', + ); + $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', '*')); + $repository = new PathRepository(array('url' => $repositoryUrl, 'options' => $options), $ioInterface, $config); + $packages = $repository->getPackages(); + + $this->assertGreaterThanOrEqual(2, $repository->count()); + + foreach ($packages as $package) { + $this->assertEquals($package->getDistReference(), null); + } + } + + public function testReferenceConfig() + { + $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') + ->getMock(); + + $config = new \Composer\Config(); + + $options = array( + 'reference' => 'config', + 'relative' => true, + ); + $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', '*')); + $repository = new PathRepository(array('url' => $repositoryUrl, 'options' => $options), $ioInterface, $config); + $packages = $repository->getPackages(); + + $this->assertGreaterThanOrEqual(2, $repository->count()); + + foreach ($packages as $package) { + $this->assertEquals( + $package->getDistReference(), + sha1(file_get_contents($package->getDistUrl() . '/composer.json') . serialize($options)) + ); + } + } }