From 74de0e012cd93a5c1bd5961a8d3714678486f75e Mon Sep 17 00:00:00 2001 From: Ian MacLennan Date: Tue, 11 Oct 2011 16:51:12 -0400 Subject: [PATCH] Enhanced patch application and reverting ability --- .../com_patchtester/models/pull.php | 126 ++++++++++++++---- .../views/pulls/tmpl/default.php | 2 +- 2 files changed, 101 insertions(+), 27 deletions(-) diff --git a/administrator/components/com_patchtester/models/pull.php b/administrator/components/com_patchtester/models/pull.php index dacac87..d62b17d 100644 --- a/administrator/components/com_patchtester/models/pull.php +++ b/administrator/components/com_patchtester/models/pull.php @@ -37,6 +37,53 @@ class PatchtesterModelPull extends JModel } + protected function parsePatch($patch) + { + $state = 0; + $files = array(); + + foreach ($patch AS $line) { + switch ($state) + { + case 0: + if (strpos($line, 'diff --git') === 0) { + $state = 1; + } + $file = new stdClass; + $file->action = 'modified'; + break; + + case 1: + if (strpos($line, 'index') === 0) { + $file->index = substr($line, 6); + } + + if (strpos($line, '---') === 0) { + $file->old = substr($line, 6); + } + + if (strpos($line, '+++') === 0) { + $file->new = substr($line, 6); + } + + if (strpos($line, 'new file mode') === 0) { + $file->action = 'added'; + } + + if (strpos($line, 'deleted file mode') === 0) { + $file->action = 'deleted'; + } + + if (strpos($line, '@@') === 0) { + $state = 0; + $files[] = $file; + } + break; + } + } + return $files; + } + public function apply($id) { jimport('joomla.client.github'); @@ -45,50 +92,68 @@ class PatchtesterModelPull extends JModel $table = JTable::getInstance('tests', 'PatchTesterTable'); $github = new JGithub(); $pull = $github->pulls->get($this->getState('github_user'), $this->getState('github_repo'), $id); - $patchUrl = $pull->patch_url; + $patchUrl = $pull->diff_url; $http = new JHttp; $patch = $http->get($patchUrl)->body; $patch = explode("\n", $patch); - $files = array(); - foreach ($patch AS $line) - { - if (is_null($pull->head->repo)) { - $this->setError(JText::_('COM_PATCHTESTER_REPO_IS_GONE')); - return false; - } + if (is_null($pull->head->repo)) { + $this->setError(JText::_('COM_PATCHTESTER_REPO_IS_GONE')); + return false; + } + + $files = $this->parsePatch($patch); + + foreach($files AS $file) { + if ($file->action == 'added' || $file->action == 'modified') { + $http = new JHttp; + + $url = 'https://raw.github.com/' . $pull->head->user->login . '/' . $pull->head->repo->name . '/' . + $pull->head->ref . '/' . $file->new; - if (strpos($line, '--- a/') === 0) { - $file = substr($line, 6); // if the backup file already exists, we can't apply the patch - if (file_exists(JPATH_COMPONENT . '/backups/' . md5($file) . '.txt')) { + if ($file->action != 'deleted' && file_exists(JPATH_COMPONENT . '/backups/' . md5($file->new) . '.txt')) { $this->setError(JText::_('COM_PATCHTESTER_CONFLICT')); return false; } - $files[] = $file; + + if (($file->action == 'deleted' || $file->action == 'modified') && !file_exists(JPATH_ROOT . '/' . $file->old)) { + $this->setError(JText::_('COM_PATCHTESTER_FILE_DELETED_MODIFIED_DOES_NOT_EXIST')); + return false; + } + + try { + $file->body = $http->get($url)->body; + } catch (Exception $e) { + $this->setError(JText::_('COM_PATCHTESTER_APPLY_FAILED_ERROR_RETRIEVING_FILE')); + return false; + } } } - + + // at this point, we have ensured that we have all the new files and there are no conflicts + foreach ($files AS $file) { - $http = new JHttp; // we only create a backup if the file already exists - if (file_exists(JPATH_ROOT . '/' . $file)) { - JFile::copy(JPath::clean(JPATH_ROOT . '/' . $file), JPATH_COMPONENT . '/backups/' . md5($file) . '.txt'); + if (file_exists(JPATH_ROOT . '/' . $file->new) && ($file->action == 'modified' || $file->action == 'deleted')) { + JFile::copy(JPath::clean(JPATH_ROOT . '/' . $file->old), JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt'); } - $url = 'https://raw.github.com/' . $pull->head->user->login . '/' . $pull->head->repo->name . '/' . - $pull->head->ref . '/' . $file; - try { - $newFile = $http->get($url); - JFile::write(JPath::clean(JPATH_ROOT . '/' . $file), $newFile->body); - } catch (Exception $e) { - echo $e->getMessage(); - echo $url; + switch ($file->action) + { + case 'modified': + case 'added': + JFile::write(JPath::clean(JPATH_ROOT . '/' . $file->new), $file->body); + break; + + case 'deleted': + JFile::delete(JPATH::clean(JPATH_ROOT . '/' . $file->old)); + break; } } $table->pull_id = $pull->number; @@ -124,8 +189,17 @@ class PatchtesterModelPull extends JModel $files = json_decode($table->data); foreach ($files AS $file) { - JFile::copy(JPATH_COMPONENT . '/backups/' . md5($file) . '.txt', JPATH_ROOT . '/' . $file); - JFile::delete(JPATH_COMPONENT . '/backups/' . md5($file) . '.txt'); + switch ($file->action) { + case 'deleted': + case 'modified': + JFile::copy(JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt', JPATH_ROOT . '/' . $file->old); + JFile::delete(JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt'); + break; + + case 'added': + JFile::delete(JPath::clean(JPATH_ROOT . '/' . $file->new)); + break; + } } $table->applied_version = ''; diff --git a/administrator/components/com_patchtester/views/pulls/tmpl/default.php b/administrator/components/com_patchtester/views/pulls/tmpl/default.php index b2db4ac..d6bf945 100644 --- a/administrator/components/com_patchtester/views/pulls/tmpl/default.php +++ b/administrator/components/com_patchtester/views/pulls/tmpl/default.php @@ -56,7 +56,7 @@ JHtml::_('behavior.tooltip'); id); ?> - title; ?> + title; ?>