diff -urN drush_make.download.inc drush_make.download.inc --- drush_make/drush_make.download.inc 2011-08-14 16:25:31.000000000 +0200 +++ drush_make.download.inc 2012-05-01 15:27:32.000000000 +0200 @@ -1,9 +1,9 @@ $name, '%module' => $download['module'])), 'ok'); + if ($update_in_place) { + drush_log(dt('%project updated.', array('%project' => $name)), 'ok'); + } + else { + drush_log(dt('%project downloaded from %module.', array('%project' => $name, '%module' => $download['module'])), 'ok'); + } return $download_location; } } @@ -61,7 +74,48 @@ return FALSE; } -function drush_make_download_file($name, $download, $download_location) { +// Can't use `drush_get_projects`, since it requires a full drupal environment +function drush_make_parse_project_info($filename) { + $contents = file_get_contents($filename); + $struct = array( + 'project' => null, + 'core' => null, + 'version' => null); + if (preg_match_all('/^project\s*=\s*[\'"]?([^\'"\n]+)[\'"]?\s*$/m', $contents, $mm)) { + $struct['project'] = $mm[1][count($mm[1])-1]; + } + if (preg_match_all('/^version\s*=\s*[\'"]?([^\'"\n]+)[\'"]?\s*$/m', $contents, $mm)) { + $full_version = $mm[1][count($mm[1])-1]; + list($core, $version) = explode('-', $full_version, 2); + $struct['core'] = $core; + $struct['version'] = $version; + } + return $struct; +} + +// Finds and parses all info files in a directory +function drush_make_load_project_info_from_dir($path) { + $finder = new RegexIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.')), '/^.+\.info$/', RecursiveRegexIterator::GET_MATCH); + $files = array_map('array_pop', iterator_to_array($finder)); + $projects = array(); + foreach ($files as $filename) { + $info = drush_make_parse_project_info($filename); + $projects[$info['project']] = $info; + } + return $projects; +} + +function drush_make_download_file($name, $download, $download_location, $project = null) { + if (drush_get_option('remake') && $project) { + // Check if we're about to download something that already is there + $infos = drush_make_load_project_info_from_dir($download_location . '/' . $name); + if (isset($infos[$name])) { + if ($infos[$name]['core'] == $project->core && $infos[$name]['version'] == $project->version) { + drush_log(dt('%project version %version already installed.', array('%project' => $name, '%version' => $infos[$name]['version'])), 'ok'); + return TRUE; + } + } + } if ($filename = _drush_make_download_file($download)) { if (!drush_get_option('ignore-checksums') && !_drush_make_verify_checksums($download, $filename)) { return FALSE; @@ -298,7 +352,7 @@ list($main_directory) = array_reverse(explode('/', $download_location)); drush_make_mkdir($tmp_path . '/__unzip__'); - drush_shell_exec("unzip %s -d %s", $filename, $tmp_path . '/__unzip__'); + drush_shell_exec("unzip -o %s -d %s", $filename, $tmp_path . '/__unzip__'); _drush_make_download_file_move($tmp_path, $filename, $download_location); } @@ -332,8 +386,8 @@ // Backwards compatibility. -function drush_make_download_get($name, $download, $download_location) { - return drush_make_download_file($name, $download, $download_location); +function drush_make_download_get($name, $download, $download_location, $project = null) { + return drush_make_download_file($name, $download, $download_location, $project); } function drush_make_download_post($name, $download, $download_location) { @@ -345,6 +399,7 @@ function drush_make_download_git($name, $download, $download_location) { $tmp_path = drush_make_tmp(); $wc = drush_get_option('working-copy'); + $update_in_place = drush_get_option('remake') && $wc && is_dir($download_location . '/.git'); // check if branch option is set in info file, otherwise set default to master branch $download['branch'] = isset($download['branch']) ? $download['branch'] : 'master'; @@ -415,12 +470,26 @@ } } - $tmp_location = $tmp_path . '/__git__/' . basename($download_location); - - drush_make_mkdir($tmp_path . '/__git__/'); + // clone|pull the given repository + if ($update_in_place) { + $tmp_location = $download_location; + $cwd = getcwd(); + chdir($tmp_location); + drush_shell_exec("git stash"); + chdir($cwd); + $clone_ok = true; + drush_log(dt('%project already cloned.', array('%project' => $name)), 'ok'); + } else { + $tmp_location = $tmp_path . '/__git__/' . basename($download_location); + drush_make_mkdir($tmp_path . '/__git__/'); + $clone_ok = drush_shell_exec("git clone %s %s", $url, $tmp_location); + if ($clone_ok) { + drush_log(dt('%project cloned from %url.', array('%project' => $name, '%url' => $url)), 'ok'); + } + } // clone the given repository - if (drush_shell_exec("git clone %s %s", $url, $tmp_location)) { + if ($clone_ok) { drush_log(dt('%project cloned from %url.', array('%project' => $name, '%url' => $url)), 'ok'); // GIT Checkout only work on a ready cloned repo. So we switch to branch or to tag (only if we have no branch) after cloneing. @@ -488,8 +557,10 @@ if (!$wc && file_exists($tmp_location . '/.git')) { drush_shell_exec("rm -rf %s", $tmp_location . '/.git'); } - drush_shell_exec('cp -Rf %s %s', $tmp_location, dirname($download_location)); - drush_shell_exec("rm -rf %s", dirname($tmp_location)); + if (!$update_in_place) { + drush_shell_exec('cp -Rf %s %s', $tmp_location, dirname($download_location)); + drush_shell_exec("rm -rf %s", dirname($tmp_location)); + } return dirname($tmp_location); } else { @@ -554,7 +625,11 @@ if (!isset($download['force']) || $download['force']) { $options = ' --force'; } - if (drush_get_option('working-copy')) { + $update_in_place = drush_get_option('remake') && drush_get_option('working-copy') && is_dir($download_location . '/.svn'); + if ($update_in_place) { + $command = 'svn ' . $options . ' update'; + } + elseif (drush_get_option('working-copy')) { $command = 'svn' . $options . ' checkout'; } else { @@ -568,9 +643,15 @@ $args[] = $download['revision']; } - $command .= ' %s %s'; - $args[] = $download['url']; - $args[] = $download_location; + if ($update_in_place) { + $command .= ' %s'; + $args[] = $download_location; + } + else { + $command .= ' %s %s'; + $args[] = $download['url']; + $args[] = $download_location; + } if (!empty($download['username'])) { $command .= ' --username %s'; @@ -583,7 +664,12 @@ array_unshift($args, $command); $result = call_user_func_array($function, $args); if ($result) { - drush_log(dt('%project @command from %url.', array('%project' => $name, '@command' => $command, '%url' => $download['url'])), 'ok'); + if ($update_in_place) { + drush_log(dt('%project @command.', array('%project' => $name, '@command' => $command)), 'ok'); + } + else { + drush_log(dt('%project @command from %url.', array('%project' => $name, '@command' => $command, '%url' => $download['url'])), 'ok'); + } return $download_location; } else { diff -urN drush_make.drush.inc drush_make.drush.inc --- drush_make/drush_make.drush.inc 2011-08-14 16:25:31.000000000 +0200 +++ drush_make.drush.inc 2012-05-01 15:29:36.000000000 +0200 @@ -44,6 +44,7 @@ '--test' => 'Run a temporary test build and clean up.', '--translations=languages' => 'Retrieve translations for the specified comma-separated list of language(s) if available for all projects.', '--working-copy' => 'Where possible, retrieve a working copy of projects from their respective repositories.', + '--remake' => 'Will update modules in place, overriding downloaded code or updating working copies of modules.', '--download-mechanism' => 'How to download files. Should be autodetected, but this is an override if it doesn\'t work. Options are "curl" and "drush_make" (a native download method).', ), ); @@ -102,9 +103,20 @@ * Drush callback; make based on the makefile. */ function drush_drush_make_make($makefile = NULL, $build_path = NULL) { + if (drush_get_option('remake') && (drush_get_option('test') || drush_get_option('tar'))) { + drush_make_error('BUILD_ERROR', "The options --test, --tar are incompatible with --remake"); + return FALSE; + } + + if (!($build_path = drush_make_build_path($build_path))) { return; } + if (drush_get_option('remake')) { + $GLOBALS['drush_make_tmp_build_path'] = $build_path; + } else { + $GLOBALS['drush_make_tmp_build_path'] = null; + } $info = drush_make_parse_info_file($makefile); if ($info === FALSE || ($info = drush_make_validate_info_file($info)) === FALSE) { return FALSE; @@ -131,13 +143,16 @@ drush_make_md5(); } - // Only take final build steps if not in testing mode. - if (!drush_get_option('test')) { - if (drush_get_option('tar')) { - drush_make_tar($build_path); - } - else { - drush_make_move_build($build_path); + // Don't do the final cleanup job if we're remaking, since it's in-place + if (!drush_get_option('remake')) { + // Only take final build steps if not in testing mode. + if (!drush_get_option('test')) { + if (drush_get_option('tar')) { + drush_make_tar($build_path); + } + else { + drush_make_move_build($build_path); + } } } @@ -397,7 +412,7 @@ $build_path = rtrim($build_path, '/'); } // Allow tests to run without a specified base path. - elseif (drush_get_option('test') || drush_confirm(dt("Make new site in the current directory?"))) { + elseif (drush_get_option('test') || drush_get_option('remake') || drush_confirm(dt("Make new site in the current directory?"))) { $build_path = '.'; } else { @@ -416,15 +431,15 @@ $tmp_path = drush_make_tmp(); $ret = TRUE; if ($build_path == '.') { - drush_shell_exec('ls -A %s', $tmp_path . '/__build__'); + drush_shell_exec('ls -A %s', drush_make_tmp_build_path()); $info = drush_shell_exec_output(); foreach ($info as $file) { - $ret = $ret && drush_shell_exec("cp -Rf %s %s", $tmp_path . '/__build__/' . $file, $build_path); + $ret = $ret && drush_shell_exec("cp -Rf %s %s", drush_make_tmp_build_path() . '/' . $file, $build_path); } } else { drush_make_mkdir(dirname($build_path)); - drush_shell_exec("mv %s %s", $tmp_path . '/__build__', $tmp_path . '/' . basename($build_path)); + drush_shell_exec("mv %s %s", drush_make_tmp_build_path(), $tmp_path . '/' . basename($build_path)); drush_shell_exec("cp -Rf %s %s", $tmp_path . '/' . basename($build_path), dirname($build_path)); } if (!$ret) { diff -urN drush_make.project.inc drush_make.project.inc --- drush_make/drush_make.project.inc 2011-08-14 16:25:31.000000000 +0200 +++ drush_make.project.inc 2012-05-01 16:10:31.000000000 +0200 @@ -56,7 +56,7 @@ $this->made = TRUE; $download_location = $this->findDownloadLocation(); - if (drush_make_download_factory($this->name, $this->download, $download_location) === FALSE) { + if (drush_make_download_factory($this->name, $this->download, $download_location, $this) === FALSE) { return FALSE; } if (!$this->addLockfile($download_location)) { @@ -81,8 +81,11 @@ // This directory shouldn't exist yet -- if it does, stop, // unless overwrite has been set to TRUE. if (is_dir($this->download_location) && !$this->overwrite) { - drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location))); - return FALSE; + // .. except if we're remaking + if (!drush_get_option('remake')) { + drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location))); + return FALSE; + } } else { drush_make_mkdir($this->download_location); @@ -98,67 +101,86 @@ return TRUE; } + $patches_file_name = $project_directory . '/PATCHES.txt'; + $already_applied_patches = array(); + + if (drush_get_option('remake') && is_file($patches_file_name)) { + $contents = file_get_contents($patches_file_name); + if (!preg_match('/The following patches have been applied to this project:(.*)This file was automatically generated by Drush Make/s', $contents, $mm)) { + drush_make_error('BUILD_ERROR', "Unable to parse $patches_file_name"); + return FALSE; + } + preg_match_all('~^- (.*://.*)~m', $contents, $mm); + $already_applied_patches = $mm[1]; + } + $patches_txt = ''; $ignore_checksums = drush_get_option('ignore-checksums'); foreach ($this->patch as $info) { if (!is_array($info)) { $info = array('url' => $info); } - // Download the patch. - if ($filename = _drush_make_download_file($info)) { - $patched = FALSE; - $output = ''; - // Test each patch style; -p1 is the default with git. See - // http://drupal.org/node/1054616 - $patch_levels = array('-p1', '-p0'); - foreach ($patch_levels as $patch_level) { - $checked = drush_shell_exec('cd %s && GIT_WORK_TREE=. git apply --check %s %s --verbose', $project_directory, $patch_level, $filename); - if ($checked) { - // Apply the first successful style. - $patched = drush_shell_exec('cd %s && GIT_WORK_TREE=. git apply %s %s --verbose', $project_directory, $patch_level, $filename); - break; - } - } - - // In some rare cases, git will fail to apply a patch, fallback to using - // the 'patch -p0' command. - if (!$patched) { - $patched = drush_shell_exec("patch -p0 -d %s < %s", $project_directory, $filename); - } - - if ($output = drush_shell_exec_output()) { - // Log any command output, visible only in --verbose or --debug mode. - drush_log(implode("\n", $output)); - } - // Set up string placeholders to pass to dt(). - $dt_args = array( - '@name' => $this->name, - '@filename' => basename($filename), - ); - - if ($patched) { - if (!$ignore_checksums && !_drush_make_verify_checksums($info, $filename)) { - return FALSE; + if (!in_array($info['url'], $already_applied_patches)) { + // Download the patch. + if ($filename = _drush_make_download_file($info)) { + $patched = FALSE; + $output = ''; + // Test each patch style; -p1 is the default with git. See + // http://drupal.org/node/1054616 + $patch_levels = array('-p1', '-p0'); + foreach ($patch_levels as $patch_level) { + $checked = drush_shell_exec('cd %s && GIT_WORK_TREE=. git apply --check %s %s --verbose', $project_directory, $patch_level, $filename); + if ($checked) { + // Apply the first successful style. + $patched = drush_shell_exec('cd %s && GIT_WORK_TREE=. git apply %s %s --verbose', $project_directory, $patch_level, $filename); + break; + } + } + + // In some rare cases, git will fail to apply a patch, fallback to using + // the 'patch -p0' command. + if (!$patched) { + $patched = drush_shell_exec("patch -p0 -d %s < %s", $project_directory, $filename); + } + + if ($output = drush_shell_exec_output()) { + // Log any command output, visible only in --verbose or --debug mode. + drush_log(implode("\n", $output)); + } + + // Set up string placeholders to pass to dt(). + $dt_args = array( + '@name' => $this->name, + '@filename' => basename($filename), + ); + + if ($patched) { + if (!$ignore_checksums && !_drush_make_verify_checksums($info, $filename)) { + return FALSE; + } + $patches_txt .= '- ' . $info['url'] . "\n"; + drush_log(dt('@name patched with @filename.', $dt_args), 'ok'); + drush_op('unlink', $filename); + } + else { + drush_make_error('PATCH_ERROR', dt("Unable to patch @name with @filename.", $dt_args)); + return FALSE; } - $patches_txt .= '- ' . $info['url'] . "\n"; - drush_log(dt('@name patched with @filename.', $dt_args), 'ok'); - } - else { - drush_make_error('PATCH_ERROR', dt("Unable to patch @name with @filename.", $dt_args)); } - drush_op('unlink', $filename); - } - else { - drush_make_error('DOWNLOAD_ERROR', 'Unable to download ' . $info['url'] . '.'); - return FALSE; } } if (!empty($patches_txt) && !drush_get_option('no-patch-txt') && !file_exists($project_directory . '/PATCHES.txt')) { + // Make sure that already applied patches are kept in the registry + $tmp = ""; + foreach ($already_applied_patches as $patch_url) { + $tmp .= '- ' . $patch_url . "\n"; + } $patches_txt = "The following patches have been applied to this project:\n" . + $tmp . $patches_txt . "\nThis file was automatically generated by Drush Make (http://drupal.org/project/drush_make)."; - file_put_contents($project_directory . '/PATCHES.txt', $patches_txt); + file_put_contents($patches_file_name, $patches_txt); drush_log('Generated PATCHES.txt file for ' . $this->name, 'ok'); } return TRUE; @@ -269,8 +291,7 @@ protected function generatePath($base = TRUE) { $path = array(); if ($base) { - $path[] = drush_make_tmp(); - $path[] = '__build__'; + $path[] = drush_make_tmp_build_path(); } if (!empty($this->contrib_destination)) { $path[] = $this->contrib_destination; @@ -323,8 +344,11 @@ $this->path = $this->download_location = $this->generatePath(); $this->project_directory = ''; if (is_dir($this->download_location)) { - drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location))); - return FALSE; + // .. except if we're remaking + if (!drush_get_option('remake')) { + drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location))); + return FALSE; + } } else { drush_make_mkdir($this->download_location); diff -urN drush_make.utilities.inc drush_make.utilities.inc --- drush_make/drush_make.utilities.inc 2011-08-14 16:25:31.000000000 +0200 +++ drush_make.utilities.inc 2012-05-01 15:20:58.000000000 +0200 @@ -302,6 +302,14 @@ return $tmp_dir; } +function drush_make_tmp_build_path() { + global $drush_make_tmp_build_path; + if (!isset($drush_make_tmp_build_path)) { + $drush_make_tmp_build_path = drush_make_tmp() . '/__build__'; + } + return $drush_make_tmp_build_path; +} + function drush_make_clean_tmp() { if (!($tmp_dir = drush_make_tmp(FALSE))) { return; @@ -315,7 +323,7 @@ } function drush_make_prepare_install($build_path) { - $default = drush_make_tmp() . '/__build__/sites/default'; + $default = drush_make_tmp_build_path() . '/sites/default'; drush_shell_exec("cp %s %s", $default . '/default.settings.php', $default . '/settings.php'); drush_make_mkdir($default . '/files'); drush_shell_exec("chmod a+w %s %s", $default . '/settings.php', $default . '/files'); @@ -346,14 +354,14 @@ $dirname = basename($build_path, '.tar.gz'); // Move the build directory to a more human-friendly name, so that tar will // use it instead. - drush_shell_exec("mv %s %s", $tmp_path . '/__build__', $tmp_path . '/' . $dirname); + drush_shell_exec("mv %s %s", drush_make_tmp_build_path(), $tmp_path . '/' . $dirname); // Only move the tar file to it's final location if it's been built // successfully. if (drush_shell_exec("tar -C %s -Pczf %s %s", $tmp_path, $tmp_path . '/' . $filename, $dirname)) { drush_shell_exec("mv %s %s", $tmp_path . '/' . $filename, $build_path); }; // Move the build directory back to it's original location for consistency. - drush_shell_exec("mv %s %s", $tmp_path . '/' . $dirname, $tmp_path . '/__build__'); + drush_shell_exec("mv %s %s", $tmp_path . '/' . $dirname, drush_make_tmp_build_path()); } /**