1
0
Fork 0

Merge remote-tracking branch 'greg0ire/improve-status-for-unpushed-branches'

pull/4964/head
Jordi Boggiano 2016-02-25 19:43:20 +00:00
commit 9f4f8a9578
4 changed files with 108 additions and 10 deletions

View File

@ -19,6 +19,8 @@ use Composer\Downloader\ChangeReportInterface;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Script\ScriptEvents;
use Composer\Downloader\VcsDownloader;
use Composer\Downloader\DvcsDownloaderInterface;
/**
* @author Tiago Ribeiro <tiago.ribeiro@seegno.com>
@ -61,6 +63,7 @@ EOT
$errors = array();
$io = $this->getIO();
$unpushedChanges = array();
// list packages
foreach ($installedRepo->getPackages() as $package) {
@ -76,13 +79,19 @@ EOT
if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
$errors[$targetDir] = $changes;
}
if ($downloader instanceof DvcsDownloaderInterface) {
if ($unpushed = $downloader->getUnpushedChanges($targetDir)) {
$unpushedChanges[$targetDir] = $unpushed;
}
}
}
}
// output errors/warnings
if (!$errors) {
if (!$errors && !$unpushed) {
$io->writeError('<info>No local changes</info>');
} else {
} elseif ($errors) {
$io->writeError('<error>You have changes in the following dependencies:</error>');
}
@ -98,6 +107,22 @@ EOT
}
}
if ($unpushedChanges) {
$io->writeError('<warning>You have unpushed changes on the current branch in the following dependencies:</warning>');
foreach ($unpushedChanges as $path => $changes) {
if ($input->getOption('verbose')) {
$indentedChanges = implode("\n", array_map(function ($line) {
return ' ' . ltrim($line);
}, explode("\n", $changes)));
$io->write('<info>'.$path.'</info>:');
$io->write($indentedChanges);
} else {
$io->write($path);
}
}
}
if ($errors && !$input->getOption('verbose')) {
$io->writeError('Use --verbose (-v) to see modified files');
}
@ -105,6 +130,6 @@ EOT
// Dispatch post-status-command
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_STATUS_CMD, true);
return $errors ? 1 : 0;
return ($errors ? 1 : 0) + ($unpushedChanges ? 2 : 0);
}
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Downloader;
/**
* DVCS Downloader interface.
*
* @author James Titcumb <james@asgrim.com>
*/
interface DvcsDownloaderInterface
{
/**
* Checks for unpushed changes to a current branch
*
* @param string $path package directory
* @return string|null changes or null
*/
public function getUnpushedChanges($path);
}

View File

@ -23,7 +23,7 @@ use Composer\Config;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class GitDownloader extends VcsDownloader
class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
{
private $hasStashedChanges = false;
private $hasDiscardedChanges = false;
@ -112,6 +112,29 @@ class GitDownloader extends VcsDownloader
return trim($output) ?: null;
}
public function getUnpushedChanges($path)
{
GitUtil::cleanEnv();
$path = $this->normalizePath($path);
if (!$this->hasMetadataRepository($path)) {
return;
}
$command = 'git rev-parse --abbrev-ref HEAD';
if (0 !== $this->process->execute($command, $output, $path)) {
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
}
$branch = trim($output);
$command = sprintf('git diff --name-status %s..composer/%s', $branch, $branch);
if (0 !== $this->process->execute($command, $output, $path)) {
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
}
return trim($output) ?: null;
}
/**
* {@inheritDoc}
*/
@ -119,6 +142,11 @@ class GitDownloader extends VcsDownloader
{
GitUtil::cleanEnv();
$path = $this->normalizePath($path);
if (null !== $this->getUnpushedChanges($path)) {
throw new \RuntimeException('Source directory ' . $path . ' has unpushed changes on the current branch.');
}
if (!$changes = $this->getLocalChanges($package, $path)) {
return;
}

View File

@ -267,21 +267,29 @@ class GitDownloaderTest extends TestCase
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->at(0))
->method('execute')
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
->with($this->equalTo($this->winCompat("git rev-parse --abbrev-ref HEAD")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(1))
->method('execute')
->with($this->equalTo($this->winCompat("git remote -v")))
->with($this->equalTo($this->winCompat("git diff --name-status ..composer/")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(2))
->method('execute')
->with($this->equalTo($expectedGitUpdateCommand))
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(3))
->method('execute')
->with($this->equalTo('git branch -r'))
->with($this->equalTo($this->winCompat("git remote -v")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(4))
->method('execute')
->with($this->equalTo($this->winCompat($expectedGitUpdateCommand)), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
->will($this->returnValue(0));
$processExecutor->expects($this->at(5))
->method('execute')
->with($this->equalTo('git branch -r'))
->will($this->returnValue(0));
$processExecutor->expects($this->at(6))
->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));
@ -309,13 +317,21 @@ class GitDownloaderTest extends TestCase
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->at(0))
->method('execute')
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
->with($this->equalTo($this->winCompat("git rev-parse --abbrev-ref HEAD")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(1))
->method('execute')
->with($this->equalTo($this->winCompat("git remote -v")))
->with($this->equalTo($this->winCompat("git diff --name-status ..composer/")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(2))
->method('execute')
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(3))
->method('execute')
->with($this->equalTo($this->winCompat("git remote -v")))
->will($this->returnValue(0));
$processExecutor->expects($this->at(4))
->method('execute')
->with($this->equalTo($expectedGitUpdateCommand))
->will($this->returnValue(1));