#!/usr/bin/env php
<?php
$wd = getcwd();
$git_dir = shell_exec('git rev-parse --show-toplevel');
if(!$git_dir) {
fprintf(STDERR, "Not inside a git path");
exit(1);
}
$git_wd = substr($wd, strlen($git_dir));
[$arguments, $flags] = parse_arguments(array_slice($argv, 1));
$branches = [];
if(count($arguments) == 1)
{
$branches['HEAD'] = hash_branch('HEAD', $flags);
}
foreach($arguments as $branch_name)
{
if(!isset($branches[$branch_name]))
{
$branches[$branch_name] = hash_branch($branch_name, $flags);
}
}
$patch_index = [];
foreach($branches as $branch_name => $branch_commits)
{
foreach($branch_commits as $commit)
{
if(empty($commit['patch_hash'])) continue;
$patch_index[$commit['patch_hash']][$commit['commit_hash']] = $commit['branches'];
}
}
$branch_compare_count = count($branches);
foreach($patch_index as $patch_hash => $patch_commits)
{
$patch_branches = [];
foreach($patch_commits as $commit_branches)
{
foreach($commit_branches as $branch_name)
{
$patch_branches[$branch_name] = $branch_name;
}
}
$patch_branch_count = count($patch_branches);
$first_commit = array_keys($patch_commits)[0];
$first_branch = $patch_commits[$first_commit][0];
$short_commit = substr($first_commit, 0, 7);
if($patch_branch_count == $branch_compare_count)
{
if(count($patch_commits) > 1)
{
echo "{$short_commit} (ALL:cherry) {$branches[$first_branch][$first_commit]['title']}\n";
}
else
{
echo "{$short_commit} (ALL:same) {$branches[$first_branch][$first_commit]['title']}\n";
}
}
else
{
echo "{$short_commit} (" . implode(', ', $patch_branches) . ") {$branches[$first_branch][$first_commit]['title']}\n";
}
}
exit(0);
function hash_branch($branch_name, $flags)
{
static $hashes = [];
$cmd = 'git log --format="format:%H %s" ';
if(!empty($flags['-c']))
{
$cmd .= ' -' . ((int) $flags['-c'][0]) . ' ';
}
if($branch_name)
{
$cmd .= escapeshellarg($branch_name);
}
$error = 0;
$output = [];
exec($cmd, $output, $error);
if($error)
{
return FALSE;
}
$branch_hases = [];
foreach($output as $row)
{
[$commit_hash, $commit_title] = explode(' ', $row, 2);
if(!isset($hashes[$commit_hash]))
{
$hashes[$commit_hash] = load_commit($commit_hash, $commit_title, $flags);
}
$hashes[$commit_hash]['branches'][] = $branch_name;
$branch_hases[$commit_hash] = &$hashes[$commit_hash];
}
return $branch_hases;
}
function load_commit($commit_hash, $commit_title, $flags)
{
$commit = ['commit_hash' => $commit_hash, 'title' => $commit_title, 'patch_hash' => NULL, 'branches' => []];
$cmd = 'git show --format=format:%H ' . escapeshellarg($commit_hash);
$error = 0;
$output = [];
exec($cmd, $output, $error);
if($error)
{
return FALSE;
}
$compare_commit_hash = trim(array_shift($output));
if($compare_commit_hash != $commit_hash)
{
return FALSE;
}
$hash_data = array();
$include_file = TRUE;
foreach($output as $row)
{
if(substr($row, 0, 5) == 'diff ')
{
if($flags AND isset($flags['-x']))
{
$match = FALSE;
$row_parts = explode(' ', $row, 3);
foreach($flags['-x'] as $path)
{
$path = trim($path);
if(substr($path, 0, 1) == '/')
{
$path = substr($path, 1);
}
if(substr($path, -1) == '*')
{
$path = substr($path, 0, -1);
}
else if(substr($path, -1) != '/')
{
$path .= ' ';
}
if($row_parts[1] == '--git')
{
if(substr($row, 0, 13 + strlen($path)) == "diff --git a/{$path}")
{
$match = TRUE;
break;
}
}
else if($row_parts[1] == '--cc')
{
if(substr($row, 0, 10 + strlen($path)) == "diff --cc {$path}")
{
$match = TRUE;
break;
}
}
}
if($match)
{
$include_file = FALSE;
continue;
}
}
$include_file = TRUE;
$hash_data[] = $row;
continue;
}
if(!$include_file)
{
continue;
}
if(substr($row, 0, 6) == 'index ')
{
continue;
}
if(substr($row, 0, 3) == '@@ ')
{
$hash_data[] = '@@';
continue;
}
$hash_data[] = $row;
}
if($hash_data)
{
$commit['patch_hash'] = sha1(implode(PHP_EOL, $hash_data));
}
return $commit;
}
function parse_arguments($raw_arguments)
{
$arguments = array();
$flags = array();
while($raw_arguments)
{
$current_arguemnt = array_shift($raw_arguments);
if(substr($current_arguemnt, 0, 1) != '-')
{
$arguments[] = $current_arguemnt;
continue;
}
if($current_arguemnt == '--')
{
while($current_arguemnt = array_shift($raw_arguments))
{
$arguments[] = $current_arguemnt;
}
break;
}
$next_argument = array_shift($raw_arguments);
$flags[$current_arguemnt][] = $next_argument;
}
return array($arguments, $flags);
}
Comments