Skip to content

Instantly share code, notes, and snippets.

@etng
Last active July 26, 2021 13:42
Show Gist options
  • Save etng/5054579 to your computer and use it in GitHub Desktop.
Save etng/5054579 to your computer and use it in GitHub Desktop.
svn version difference package tool
#!/usr/bin/php
<?php
/**
* svn version difference package tool
*
* copy this script to /usr/local/bin/ and change work directory to your svn project root
* then you can use the following command to produce a release package for your project:
* ` svnpackdiff -b2013-02-03 -e2013-02-28 -apaul -atom -alily `
*
* @author etng<etng2004@gmail.com>
* @since 2013-02-28
*/
$options = getopt(implode("", array_keys($options_config = array(
"f::"=>"result file name",
"b::"=>"begin revision, default to the day of the last year",
"e::"=>"end revision, default to HEAD",
"a::"=>"authors, if specified then only these authors commit will be log",
"x::"=>"ignores, if specified then exclude these type of file",
"h"=>"print help message and die",
))));
$options['h'] = isset($options['h']);
if($options['h'])
{
echo "usage: svnpackdiff -b2013-02-22 -apaul -atom -alily", PHP_EOL;
print_r($options_config);
die();
}
if(!isset($options['b']))
{
$options['b'] = date('Y-m-d', strtotime('-1 year'));
}
if(!isset($options['e']))
{
$options['e'] = "HEAD";
}
if(!isset($options['f']))
{
$options['f'] = "changeset-{$options['b']}to{$options['e']}.zip";
if($options['e'] == "HEAD")
{
$ymd = date('Y-m-d');
$options['f'] = "changeset-{$options['b']}to{$ymd}.zip";
}
}
settype($options['a'], 'array');
settype($options['x'], 'array');
$command = "svn info --xml";
$xml = getCommandXmlResult($command, '.svninfo');
$entry = current($xml->xpath("/info/entry"));
$svn_url = strval($entry->url);
$repo_url = strval($entry->repository->root);
$options['prefix'] = substr($svn_url, strlen($repo_url));
echo "SVN URL:", $svn_url,PHP_EOL;
echo "REPO URL:", $repo_url,PHP_EOL;
echo 'using options:',PHP_EOL;
echo pretty_json(json_encode($options)),PHP_EOL;
echo 'fetching logs...',PHP_EOL;
$command = "svn log -v -r {{$options['b']}}:{$options['e']} --xml";
$xml = getCommandXmlResult($command, '.svnlog');
$filenames = getChangedFiles($xml, $options['prefix'], $options['x'], $options['a']);
if($filenames)
{
echo 'theses files are changed:',PHP_EOL;
echo implode($filenames,PHP_EOL), PHP_EOL;
if($zip_filename = compressFiles('./', $filenames, $options['f']))
{
echo 'zip file saved to:', $zip_filename, PHP_EOL;
}
else
{
echo 'can not create zip file',PHP_EOL;
}
}
else
{
echo 'nothing changed',PHP_EOL;
}
echo 'done',PHP_EOL;
function compressFiles($base_path, $filenames, $zip_filename)
{
$zip = new ZipArchive;
$res = $zip->open($zip_filename, ZipArchive::CREATE);
if ($res === TRUE)
{
foreach($filenames as $filename)
{
$full_path = $base_path.'/'.$filename;
if(!is_dir($full_path) && is_file($filename))
{
$zip->addFile($full_path = $base_path.'/'.$filename, $filename);
}
}
$zip->close();
return realpath($zip_filename);
}
else
{
$zip_err_dict = array(
ZIPARCHIVE::ER_EXISTS =>"File already exists. ",
ZIPARCHIVE::ER_INCONS =>"Zip archive inconsistent. ",
ZIPARCHIVE::ER_INVAL =>"Invalid argument. ",
ZIPARCHIVE::ER_MEMORY =>"Malloc failure. ",
ZIPARCHIVE::ER_NOENT =>"No such file. ",
ZIPARCHIVE::ER_NOZIP =>"Not a zip archive. ",
ZIPARCHIVE::ER_OPEN =>"Can't open file. ",
ZIPARCHIVE::ER_READ =>"Read error. ",
ZIPARCHIVE::ER_SEEK =>"Seek error. ",
);
trigger_error("can not open zip file:" . $zip_err_dict[$res]);
}
}
function getChangedFiles($xml, $prefix, $stop_keywords, $authors)
{
$stop_pattern = '';
if($stop_keywords)
{
$quoted_stop_keywords = array();
foreach($stop_keywords as $stop_keyword)
{
$quoted_stop_keywords []= preg_quote($stop_keyword);
}
$stop_pattern = '!('.implode('|', $quoted_stop_keywords).')!sim';
}
$log_entries = $xml->xpath("/log/logentry");
$filenames = array();
$prefix = rtrim($prefix, '/'). '/';
foreach($log_entries as $log_entry)
{
$author = (string)$log_entry->author;
if(!$authors || in_array($author, $authors))
{
$changes = array();
foreach($log_entry->paths->path as $path)
{
$action = (string)$path['action'];
$filename = substr((string)$path, strlen($prefix));
if(!$stop_pattern || !preg_match($stop_pattern, $filename))
{
if($action=='D')
{
while(($idx=array_search($filename, $filenames))!==false)
{
unset($filenames[$idx]);
}
}
else
{
$filenames []= $filename;
}
}
}
}
}
$filenames = array_unique($filenames);
sort($filenames);
return $filenames;
}
function getCommandXmlResult($command, $temp_file='.tmp')
{
exec($command . ' > ' . $temp_file);
if(!file_exists($temp_file))
{
echo "`{$command}` dit not run right" . PHP_EOL;
die();
}
libxml_use_internal_errors(true);
$xml = simplexml_load_file($temp_file);
@unlink($temp_file);
if (!$xml)
{
echo "try to run `{$command}` and find why the following errors:" . PHP_EOL;
$errors = libxml_get_errors();
foreach ($errors as $error)
{
echo "Error $error->code: " . trim($error->message) . "\n Line: {$error->line}" . "\n Column: {$error->column}";
}
libxml_clear_errors();
die();
}
return $xml;
}
/**
* Indents a flat JSON string to make it more human-readable.
*
* @param string $json The original JSON string to process.
*
* @return string Indented version of the original JSON string.
*/
function pretty_json($json)
{
$result = '';
$pos = 0;
$strLen = strlen($json);
$indentStr = ' ';
$newLine = "\n";
$prevChar = '';
$outOfQuotes = true;
for ($i=0; $i<=$strLen; $i++)
{
// Grab the next character in the string.
$char = substr($json, $i, 1);
// Are we inside a quoted string?
if ($char == '"' && $prevChar != '\\')
{
$outOfQuotes = !$outOfQuotes;
// If this character is the end of an element,
// output a new line and indent the next line.
}
else if(($char == '}' || $char == ']') && $outOfQuotes)
{
$result .= $newLine;
$pos --;
for ($j=0; $j<$pos; $j++)
{
$result .= $indentStr;
}
}
// Add the character to the result string.
$result .= $char;
// If the last character was the beginning of an element,
// output a new line and indent the next line.
if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes)
{
$result .= $newLine;
if ($char == '{' || $char == '[')
{
$pos ++;
}
for ($j = 0; $j < $pos; $j++)
{
$result .= $indentStr;
}
}
$prevChar = $char;
}
return $result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment