Browse Source

Merge commit 'b1a78b60fe552bc6e7df09cb1c0154797fea6f64'

Jordi Boggiano 7 years ago
parent
commit
a51911f295

+ 17 - 7
src/Composer/Command/StatusCommand.php

@@ -12,6 +12,7 @@
 
 namespace Composer\Command;
 
+use Composer\Downloader\DownloaderInterface;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
@@ -36,6 +37,9 @@ class StatusCommand extends BaseCommand
     const EXIT_CODE_UNPUSHED_CHANGES = 2;
     const EXIT_CODE_VERSION_CHANGES = 4;
 
+    /**
+     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
+     */
     protected function configure()
     {
         $this
@@ -44,7 +48,8 @@ class StatusCommand extends BaseCommand
             ->setDefinition(array(
                 new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Show modified files for each directory that contains changes.'),
             ))
-            ->setHelp(<<<EOT
+            ->setHelp(
+                <<<EOT
 The status command displays a list of dependencies that have
 been modified locally.
 
@@ -53,6 +58,11 @@ EOT
         ;
     }
 
+    /**
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     * @return int|null
+     */
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         // init repos
@@ -91,9 +101,7 @@ EOT
                 if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
                     $errors[$targetDir] = $changes;
                 }
-            }
-
-            if ($downloader instanceof VcsCapableDownloaderInterface) {
+            } elseif ($downloader instanceof VcsCapableDownloaderInterface) {
                 if ($currentRef = $downloader->getVcsReference($package, $targetDir)) {
                     switch ($package->getInstallationSource()) {
                         case 'source':
@@ -121,12 +129,14 @@ EOT
                         );
                     }
                 }
-            }
-
-            if ($downloader instanceof DvcsDownloaderInterface) {
+            } elseif ($downloader instanceof DvcsDownloaderInterface) {
                 if ($unpushed = $downloader->getUnpushedChanges($package, $targetDir)) {
                     $unpushedChanges[$targetDir] = $unpushed;
                 }
+            } elseif ($downloader instanceof DownloaderInterface) {
+                if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
+                    $errors[$targetDir] = $changes;
+                }
             }
         }
 

+ 5 - 1
src/Composer/Downloader/ArchiveDownloader.php

@@ -27,6 +27,8 @@ abstract class ArchiveDownloader extends FileDownloader
 {
     /**
      * {@inheritDoc}
+     * @throws \RuntimeException
+     * @throws \UnexpectedValueException
      */
     public function download(PackageInterface $package, $path, $output = true)
     {
@@ -35,7 +37,9 @@ abstract class ArchiveDownloader extends FileDownloader
         while ($retries--) {
             $fileName = parent::download($package, $path, $output);
 
-            $this->io->writeError(' Extracting archive', false, IOInterface::VERBOSE);
+            if ($output) {
+                $this->io->writeError(' Extracting archive', false, IOInterface::VERBOSE);
+            }
 
             try {
                 $this->filesystem->ensureDirectoryExists($temporaryDir);

+ 9 - 0
src/Composer/Downloader/DownloaderInterface.php

@@ -61,4 +61,13 @@ interface DownloaderInterface
      * @return DownloaderInterface
      */
     public function setOutputProgress($outputProgress);
+
+    /**
+     * Checks for changes to the local copy
+     *
+     * @param  PackageInterface $package package instance
+     * @param  string           $path    package directory
+     * @return string|null      changes or null
+     */
+    public function getLocalChanges(PackageInterface $package, $path);
 }

+ 33 - 1
src/Composer/Downloader/FileDownloader.php

@@ -16,6 +16,8 @@ use Composer\Config;
 use Composer\Cache;
 use Composer\Factory;
 use Composer\IO\IOInterface;
+use Composer\IO\NullIO;
+use Composer\Package\Comparer\Comparer;
 use Composer\Package\PackageInterface;
 use Composer\Plugin\PluginEvents;
 use Composer\Plugin\PreFileDownloadEvent;
@@ -166,7 +168,9 @@ class FileDownloader implements DownloaderInterface
                     $this->cache->copyFrom($cacheKey, $fileName);
                 }
             } else {
-                $this->io->writeError('Loading from cache', false);
+                if (!$this->outputProgress) {
+                    $this->io->writeError('Loading from cache', false);
+                }
             }
 
             if (!file_exists($fileName)) {
@@ -278,4 +282,32 @@ class FileDownloader implements DownloaderInterface
 
         return $package->getName().'/'.$cacheKey.'.'.$package->getDistType();
     }
+
+    /**
+     * {@inheritDoc}
+     * @throws \RuntimeException
+     */
+    public function getLocalChanges(PackageInterface $package, $targetDir)
+    {
+        $prevIO = $this->io;
+        $prevProgress = $this->outputProgress;
+
+        $this->io = new NullIO;
+        $this->io->loadConfiguration($this->config);
+        $this->outputProgress = false;
+
+        $this->download($package, $targetDir.'_compare', false);
+
+        $comparer = new Comparer();
+        $comparer->setSource($targetDir.'_compare');
+        $comparer->setUpdate($targetDir);
+        $comparer->doCompare();
+        $output = $comparer->getChanged(true, true);
+        $this->filesystem->removeDirectory($targetDir.'_compare');
+
+        $this->io = $prevIO;
+        $this->outputProgress = $prevProgress;
+
+        return trim($output);
+    }
 }

+ 128 - 0
src/Composer/Package/Comparer/Comparer.php

@@ -0,0 +1,128 @@
+<?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\Package\Comparer;
+
+/**
+ * class Comparer
+ *
+ * @author Hector Prats <hectorpratsortega@gmail.com>
+ */
+class Comparer
+{
+    private $source;
+    private $update;
+    private $changed;
+
+    public function setSource($source)
+    {
+        $this->source = $source;
+    }
+
+    public function setUpdate($update)
+    {
+        $this->update = $update;
+    }
+
+    public function getChanged($toString = false, $explicated = false)
+    {
+        $changed = $this->changed;
+        if (!count($changed)) {
+            return false;
+        }
+        if ($explicated) {
+            foreach ($changed as $sectionKey => $itemSection) {
+                foreach ($itemSection as $itemKey => $item) {
+                    $changed[$sectionKey][$itemKey] = $item.' ('.$sectionKey.')';
+                }
+            }
+        }
+
+        if ($toString) {
+            foreach ($changed as $sectionKey => $itemSection) {
+                foreach ($itemSection as $itemKey => $item) {
+                    $changed['string'][] = $item."\r\n";
+                }
+            }
+            $changed = implode("\r\n", $changed['string']);
+        }
+
+        return $changed;
+    }
+
+    public function doCompare()
+    {
+        $source = array();
+        $destination = array();
+        $this->changed = array();
+        $currentDirectory = getcwd();
+        chdir($this->source);
+        $source = $this->doTree('.', $source);
+        if (!is_array($source)) {
+            return;
+        }
+        chdir($this->update);
+        $destination = $this->doTree('.', $destination);
+        if (!is_array($destination)) {
+            exit;
+        }
+        chdir($currentDirectory);
+        foreach ($source as $dir => $value) {
+            foreach ($value as $file => $hash) {
+                if (isset($destination[$dir][$file])) {
+                    if ($hash !== $destination[$dir][$file]) {
+                        $this->changed['changed'][] = $dir.'/'.$file;
+                    }
+                } else {
+                    $this->changed['removed'][] = $dir.'/'.$file;
+                }
+            }
+        }
+        foreach ($destination as $dir => $value) {
+            foreach ($value as $file => $hash) {
+                if (!isset($source[$dir][$file])) {
+                    $this->changed['added'][] = $dir.'/'.$file;
+                }
+            }
+        }
+    }
+
+    private function doTree($dir, &$array)
+    {
+        if ($dh = opendir($dir)) {
+            while ($file = readdir($dh)) {
+                if ($file !== '.' && $file !== '..') {
+                    if (is_dir($dir.'/'.$file)) {
+                        if (!count($array)) {
+                            $array[0] = 'Temp';
+                        }
+                        if (!$this->doTree($dir.'/'.$file, $array)) {
+                            return false;
+                        }
+                    } else {
+                        if (filesize($dir.'/'.$file)) {
+                            set_time_limit(30);
+                            $array[$dir][$file] = md5_file($dir.'/'.$file);
+                        }
+                    }
+                }
+            }
+            if (count($array) > 1 && isset($array['0'])) {
+                unset($array['0']);
+            }
+
+            return $array;
+        }
+
+        return false;
+    }
+}