protected function migratePackage(AbstractMigration $migration, $force = false)
    {
        $packagePath = $this->currentPackageData['path'];
        if ($this->hasMigrationApplied($migration)) {
            $this->triggerEvent(self::EVENT_MIGRATION_ALREADY_APPLIED, array($migration, 'Migration already applied'));
            return;
        }
        $isWorkingCopy = Git::isWorkingCopy($packagePath);
        $hasLocalChanges = Git::isWorkingCopyDirty($packagePath);
        if (!$force) {
            if (!$isWorkingCopy) {
                $this->triggerEvent(self::EVENT_MIGRATION_SKIPPED, array($migration, 'Not a Git working copy, use --force to apply changes anyways'));
                return;
            }
            if ($hasLocalChanges) {
                $this->triggerEvent(self::EVENT_MIGRATION_SKIPPED, array($migration, 'Working copy contains local changes, use --force to apply changes anyways'));
                return;
            }
        }

        if ($isWorkingCopy) {
            $importResult = $this->importMigrationLogFromGitHistory(!$hasLocalChanges);
            if ($importResult !== null) {
                $this->triggerEvent(self::EVENT_MIGRATION_LOG_IMPORTED, array($migration, $importResult));
            }
        }

        $this->triggerEvent(self::EVENT_MIGRATION_EXECUTE, array($migration));
        try {
            $migration->prepare($this->currentPackageData);
            $migration->up();
            $migration->execute();
            $commitMessageNotice = null;
            if ($isWorkingCopy && !Git::isWorkingCopyDirty($packagePath)) {
                $commitMessageNotice = 'Note: This migration did not produce any changes, so the commit simply marks the migration as applied. This makes sure it will not be applied again.';
            }
            $this->markMigrationApplied($migration);
            $this->triggerEvent(self::EVENT_MIGRATION_EXECUTED, array($migration));
            if ($hasLocalChanges || !$isWorkingCopy) {
                $this->triggerEvent(self::EVENT_MIGRATION_COMMIT_SKIPPED, array($migration, $hasLocalChanges ? 'Working copy contains local changes' : 'No Git working copy'));
            } else {
                $migrationResult = $this->commitMigration($migration, $commitMessageNotice);
                $this->triggerEvent(self::EVENT_MIGRATION_COMMITTED, array($migration, $migrationResult));
            }
        } catch (\Exception $exception) {
            throw new \RuntimeException(sprintf('Applying migration "%s" to "%s" failed: "%s"', $migration->getIdentifier(), $this->currentPackageData['packageKey'], $exception->getMessage()), 1421692982, $exception);
        }
    }