#!/usr/bin/php $drupal_root, 'site_name' => $site_name, 'cvs_root' => $cvs_root, 'tmp_root' => $tmp_root, ); foreach ($vars as $name => $val) { if (empty($val)) { print "ERROR: \"\$$name\" variable not set, aborting\n"; $fatal_err = true; } } if ($fatal_err) { exit(1); } // Find what tag we're trying to convert if ($argv[1]) { $tag = $argv[1]; } else { print "ERROR: You must specify the tag to convert to a branch\n"; exit(1); } putenv("CVSROOT=$cvs_root"); $script_name = $argv[0]; // Setup variables for Drupal bootstrap $_SERVER['HTTP_HOST'] = $site_name; $_SERVER['REQUEST_URI'] = '/' . $script_name; $_SERVER['SCRIPT_NAME'] = '/' . $script_name; $_SERVER['PHP_SELF'] = '/' . $script_name; $_SERVER['SCRIPT_FILENAME'] = $_SERVER['PWD'] . '/' . $script_name; $_SERVER['PATH_TRANSLATED'] = $_SERVER['SCRIPT_FILENAME']; if (!chdir($drupal_root)) { print "ERROR: Can't chdir($drupal_root): aborting.\n"; exit(1); } require_once 'includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); initialize_tmp_dir(); initialize_repository_info(); $conversions = array(); convert_tags($tag); // ------------------------------------------------------------ // Functions: main work // ------------------------------------------------------------ function convert_tags($tag) { global $conversions; $query = db_query("SELECT pp.uri, prn.nid, prn.pid, prn.tag, cp.directory, cp.rid FROM {project_release_nodes} prn INNER JOIN {project_projects} pp ON prn.pid = pp.nid INNER JOIN {node} np ON prn.pid = np.nid INNER JOIN {project_release_projects} prp ON prp.nid = prn.pid INNER JOIN {cvs_projects} cp ON prn.pid = cp.nid INNER JOIN {cvs_tags} ct ON ct.nid = prn.pid AND ct.tag = prn.tag WHERE prn.tag='%s' AND ct.branch = 0 ORDER BY cp.directory", $tag); $num_converted = 0; $num_considered = 0; while ($release = db_fetch_object($query)) { $uri = $release->uri; $tag = $release->tag; $nid = $release->nid; wd_msg(t("Working on converting %tag for %uri", array('%uri' => $uri, '%tag' => $tag)), l(t('view'), 'node/' . $nid)); $uri = escapeshellcmd($uri); $tag = escapeshellcmd($tag); $dir = escapeshellcmd(trim($release->directory, '/')); if (convert_tag($tag, $nid, $uri, $dir, $release->rid)) { $num_converted++; } $num_considered++; } if ($num_converted) { wd_msg(t("Done converting %tag tags into branches: !num_converted converted, !num_considered considered.", array('%tag' => $tag, '!num_converted' => $num_converted, '!num_considered' => $num_considered)) . "
\n" . implode("\n", $conversions));
  }
}

function convert_tag($tag, $nid, $uri, $dir, $rid) {
  global $tmp_dir, $repositories, $conversions;
  global $cvs, $rm;
  
  if (!drupal_chdir($tmp_dir)) {
    return false;
  }

  $view_link = l(t('view'), 'node/' . $nid);
  $tmp_tag = $tag .'_';
  $cvs_dir = $repositories[$rid]['modules'] . '/' . $dir;

  // Don't use drupal_exec or return if this fails, we expect it to be empty.
  exec("$rm -rf $tmp_dir/$uri");
  @mkdir("$tmp_dir/$uri");
  if (!drupal_chdir("$tmp_dir/$uri")) {
    return false;
  }

  drupal_exec("$cvs -q checkout -r $tag $cvs_dir");
  if (!drupal_chdir($cvs_dir)) {
    return false;
  }

  db_query("UPDATE {project_release_nodes} SET tag='%s' WHERE nid = %d", $tmp_tag, $nid);
  drupal_exec("$cvs tag -d $tag");
  drupal_exec("$cvs tag -b $tag");
  db_query("UPDATE {project_release_nodes} SET tag='%s' WHERE nid = %d", $tag, $nid);

  $conversions[] = "$dir";
  return true;
}


// ------------------------------------------------------------
// Functions: utility methods
// ------------------------------------------------------------

/**
 * Wrapper for exec() that logs errors to the watchdog.
 * @param $cmd
 *   String of the command to execute (assumed to be safe, the caller is
 *   responsible for calling escapeshellcmd() if necessary).
 * @return true if the command was successful (0 exit status), else false.
 */
function drupal_exec($cmd) {
  // Made sure we grab stderr, too...
  exec("$cmd 2>&1", $output, $rval);
  if ($rval) {
    wd_err(t("ERROR: %cmd failed with status !rval", array('%cmd' => $cmd, '!rval' => $rval)) . '
' . implode("\n", array_map('htmlspecialchars', $output)));
    return false;
  }
  wd_msg(t("%cmd success", array('%cmd' => $cmd)) . '
' . implode("\n", array_map('htmlspecialchars', $output)));
  return true;
}

/**
 * Wrapper for chdir() that logs errors to the watchdog.
 * @param $dir Directory to change into.
 * @return true if the command was successful (0 exit status), else false.
 */
function drupal_chdir($dir) {
  if (!chdir($dir)) {
    wd_err(t("ERROR: Can't chdir(@dir)", array('@dir' => $dir)));
    return false;
  }
  return true;
}

/**
 * Wrapper function for watchdog() to log notice messages. Uses a
 * different watchdog message type depending on the task (branch vs. tag).
 */
function wd_msg($msg, $link = NULL) {
  watchdog('cvs_convert' . $task, $msg, WATCHDOG_NOTICE, $link);
}

/**
 * Wrapper function for watchdog() to log error messages.
 */
function wd_err($msg, $link = NULL) {
  watchdog('cvs_convert_error', $msg, WATCHDOG_ERROR, $link);
}

/**
 * Initialize the tmp directory.
 */
function initialize_tmp_dir() {
  global $tmp_dir, $tmp_root;
  global $rm;

  if (!is_dir($tmp_root)) {
    wd_err(t("ERROR: tmp_root: @dir is not a directory", array('@dir' => $tmp_root)));
    exit(1);
  }

  $tmp_dir = $tmp_root . '/tag-convert';
  if (is_dir($tmp_dir)) {
    // Make sure we start with a clean slate
    drupal_exec("$rm -rf $tmp_dir/*");
  }
  else if (!@mkdir($tmp_dir)) {
    wd_err(t("ERROR: mkdir(@dir) failed", array('@dir' => $tmp_dir)));
    exit(1);
  }
}

/**
 * Initialize info from the {cvs_repositories} table, since there are
 * usually only a tiny handful of records, and it'll be faster to do
 * whatever we need via php than another JOIN...
 */
function initialize_repository_info() {
  global $repositories;
  $query = db_query("SELECT rid, root, modules, name FROM {cvs_repositories}");
  while ($repo = db_fetch_object($query)) {
    $repositories[$repo->rid] = array('root' => $repo->root, 'modules' => $repo->modules, 'name' => $repo->name);
  }
}