grumPHP = $grumPHP; $this->repository = $repository; $this->io = $io; } public static function getSubscribedEvents(): array { $events = [ RunnerEvents::RUNNER_RUN => ['saveStash', 10000], RunnerEvents::RUNNER_COMPLETE => ['popStash', -10000], RunnerEvents::RUNNER_FAILED => ['popStash', -10000], ]; // Backward compatibility layer for Symfony Console < 4.0. // @todo Replace with ConsoleEvents::EXCEPTION when bumping symfony/console to ^3.3 // (and maybe revert back to returning a simple array, as it was before?) $consoleErrorEvent = defined(ConsoleEvents::class.'::ERROR') ? ConsoleEvents::ERROR : ConsoleEvents::EXCEPTION; $events[$consoleErrorEvent] = ['handleErrors', -10000]; return $events; } public function saveStash(RunnerEvent $e): void { if (!$this->isStashEnabled($e->getContext())) { return; } $this->doSaveStash(); } /** * @throws ProcessException */ public function popStash(RunnerEvent $e): void { if (!$this->isStashEnabled($e->getContext())) { return; } $this->doPopStash(); } public function handleErrors(): void { if (!$this->grumPHP->ignoreUnstagedChanges()) { return; } $this->doPopStash(); } /** * Check if there is a pending diff and stash the changes. * * @reurn void */ private function doSaveStash(): void { $pending = $this->repository->getWorkingCopy()->getDiffPending(); if (!\count($pending->getFiles())) { return; } try { $this->io->write(['Detected unstaged changes... Stashing them!']); $this->repository->run('stash', ['save', '--quiet', '--keep-index', uniqid('grumphp')]); } catch (Exception $e) { // No worries ... $this->io->write([sprintf('Failed stashing changes: %s', $e->getMessage())]); return; } $this->stashIsApplied = true; $this->registerShutdownHandler(); } private function doPopStash(): void { if (!$this->stashIsApplied) { return; } try { $this->io->write(['Reapplying unstaged changes from stash.']); $this->repository->run('stash', ['pop', '--quiet']); } catch (Exception $e) { throw new RuntimeException( 'The stashed changes could not be applied. Please run `git stash pop` manually!' .'More info: '.$e->__toString(), 0, $e ); } $this->stashIsApplied = false; } private function isStashEnabled(ContextInterface $context): bool { return $this->grumPHP->ignoreUnstagedChanges() && $context instanceof GitPreCommitContext; } /** * Make sure to fetch errors and pop the stash before crashing. */ private function registerShutdownHandler(): void { if ($this->shutdownFunctionRegistered) { return; } $subscriber = $this; register_shutdown_function(function () use ($subscriber) : void { if (!$error = error_get_last()) { return; } // Don't fail on non-blcoking errors! if (\in_array($error['type'], [E_DEPRECATED, E_USER_DEPRECATED, E_CORE_WARNING, E_CORE_ERROR], true)) { return; } $subscriber->handleErrors(); }); $this->shutdownFunctionRegistered = true; } }