diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index 0a48708d2..da251d214 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -119,6 +119,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $url = realpath($url); } $this->doUpdate($initial, $target, $path, $url); + + $exception = null; break; } catch (\Exception $exception) { if ($this->io->isDebug()) { diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index 9c851fe9a..1f88c7895 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -325,6 +325,57 @@ class GitDownloaderTest extends TestCase $downloader->update($packageMock, $packageMock, $this->workingDir); } + public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover() + { + $expectedFirstGitUpdateCommand = $this->winCompat("git remote set-url composer '' && git fetch composer && git fetch --tags composer"); + $expectedSecondGitUpdateCommand = $this->winCompat("git remote set-url composer 'git://github.com/composer/composer' && git fetch composer && git fetch --tags composer"); + + $packageMock = $this->getMock('Composer\Package\PackageInterface'); + $packageMock->expects($this->any()) + ->method('getSourceReference') + ->will($this->returnValue('ref')); + $packageMock->expects($this->any()) + ->method('getSourceUrls') + ->will($this->returnValue(array('/foo/bar', 'https://github.com/composer/composer'))); + $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); + $processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no"))) + ->will($this->returnValue(0)); + $processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($this->winCompat("git remote -v"))) + ->will($this->returnValue(0)); + $processExecutor->expects($this->at(2)) + ->method('execute') + ->with($this->equalTo($expectedFirstGitUpdateCommand)) + ->will($this->returnValue(1)); + $processExecutor->expects($this->at(4)) + ->method('execute') + ->with($this->equalTo($this->winCompat("git --version"))) + ->will($this->returnValue(0)); + $processExecutor->expects($this->at(5)) + ->method('execute') + ->with($this->equalTo($this->winCompat("git remote -v"))) + ->will($this->returnValue(0)); + $processExecutor->expects($this->at(6)) + ->method('execute') + ->with($this->equalTo($expectedSecondGitUpdateCommand)) + ->will($this->returnValue(0)); + $processExecutor->expects($this->at(7)) + ->method('execute') + ->with($this->equalTo('git branch -r')) + ->will($this->returnValue(0)); + $processExecutor->expects($this->at(8)) + ->method('execute') + ->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir))) + ->will($this->returnValue(0)); + + $this->fs->ensureDirectoryExists($this->workingDir.'/.git'); + $downloader = $this->getDownloaderMock(null, new Config(), $processExecutor); + $downloader->update($packageMock, $packageMock, $this->workingDir); + } + public function testRemove() { $expectedGitResetCommand = $this->winCompat("cd 'composerPath' && git status --porcelain --untracked-files=no");