--- /dev/null
+<?php
+
+/* CONFIG */
+$repo_path = "/srv/gitosis/repositories/";
+$project_list = "/srv/gitosis/gitosis/projects.list";
+
+$size = 20;
+$tile_size = 20; /* do not edit */
+
+$max_branches = 30;
+
+$colors = array(
+ NULL,
+ array(255, 0, 0),
+ array(0, 255, 0),
+ array(0, 0, 255),
+ array(128, 128, 128),
+ array(128, 128, 0),
+ array(0, 128, 128),
+ array(128, 0, 128)
+);
+/* END CONFIG */
+
+session_start();
+
+//parse ; seprated query args
+$args = explode(";", $_SERVER["QUERY_STRING"]);
+foreach($args as $arg) {
+ $arg = explode("=", $arg, 2);
+ $_GET[$arg[0]] = $arg[1];
+}
+
+if($_SESSION['gitgraph_maxbranch'])
+ $max_branches = $_SESSION['gitgraph_maxbranch'];
+if($_GET['maxbranch']) {
+ $_SESSION['gitgraph_maxbranch'] = $_GET['maxbranch'];
+ die("maxbranch set.");
+}
+
+//check if project is in $project_list
+$project = NULL;
+if($project_list) {
+ $projects = file_get_contents($project_list);
+ foreach(explode("\n", $projects) as $cproject) {
+ $cproject = explode(" ", $cproject);
+ if(strtolower($_GET['p']) == strtolower($cproject[0])) {
+ if(file_exists($repo_path.$cproject[0]))
+ $project = $cproject[0];
+ break;
+ }
+ }
+} else {
+ if(!preg_match('#^[a-z0-9.-_]*$#i', $_GET['p']))
+ die("ERROR 0x01");
+ if(file_exists($repo_path.$_GET['p']))
+ $project = $_GET['p'];
+}
+if(!$project)
+ die("ERROR 0x02");
+
+//check other args
+if(!preg_match('#^[a-z0-9]*$#i', $_GET['h']))
+ die("ERROR 0x03");
+if(!preg_match('#^[a-z0-9]*$#i', $_GET['c']))
+ die("ERROR 0x04");
+if(!preg_match('#^[0-9]*$#i', $_GET['from']))
+ die("ERROR 0x05");
+if(!preg_match('#^[0-9]*$#i', $_GET['to']))
+ die("ERROR 0x06");
+
+//load old cache
+$data = array();
+if($_SESSION['git_graph']) {
+ $old_data = unserialize($_SESSION['git_graph']);
+ if($old_data['r'] == $_GET['r'])
+ $data = $old_data;
+}
+
+//load rev-list
+if(!$data['commits']) {
+ $cmd = "git --git-dir=".$repo_path.$project." rev-list --header --max-count=".($_GET['to'] + 1)." ".$_GET['h'];
+ $rev_list = shell_exec($cmd);
+ foreach(explode("\000", $rev_list) as $rev) {
+ $commit = array();
+ $rev_lines = explode("\n", str_replace("\r", "", $rev));
+ $commit['id'] = $rev_lines[0];
+ foreach($rev_lines as $rev_line) {
+ if(substr($rev_line, 0, 4) == " ") {
+ if($commit['text'])
+ $commit['text'] .= "\n";
+ $commit['text'] .= substr($rev_line, 4);
+ } else {
+ $opt = explode(" ", $rev_line);
+ if($opt[0] == "tree")
+ $commit['tree'] = $opt[1];
+ else if($opt[0] == "parent")
+ $commit['parent'][] = $opt[1];
+ else if($opt[0] == "author") {
+ $commit['author'] = $opt[1];
+ $commit['author_mail'] = $opt[2];
+ $commit['author_time'] = $opt[3];
+ } else if($opt[0] == "committer") {
+ $commit['committer'] = $opt[1];
+ $commit['committer_mail'] = $opt[2];
+ $commit['committer_time'] = $opt[3];
+ }
+ }
+ }
+ $data['commits'][] = $commit;
+ }
+}
+
+//save cache
+$_SESSION['git_graph'] = serialize($data);
+
+//generate graph data
+$data['branches'] = array();
+$data['ubranches'] = array();
+$brach_id = 1;
+$brach_uid = 1;
+$graph_commit = NULL;
+$first_commit = true;
+
+for($i = 0; $i <= intval($_GET['to']); $i++) {
+//for($i = 0; $i < count($data['commits']); $i++) {
+ $commit = &$data['commits'][$i];
+ //get current branch
+ if($first_commit) {
+ $first_commit = false;
+ $data['branches'][0] = array();
+ $branch = &$data['branches'][0];
+ $branch['id'] = $brach_id++;
+ $branch['uid'] = $branch_uid++;
+ $branch['active'] = true;
+ } else {
+ $first = true;
+ foreach($data['branches'] as $id => &$cbranch) {
+ if($cbranch['next'] == $commit['id']) {
+ if($first && !$cbranch['pre_merge']) {
+ $branch = &$data['branches'][$id];
+ $first = false;
+ }
+ }
+ }
+ foreach($data['branches'] as $id => &$cbranch) {
+ if($cbranch['next'] == $commit['id']) {
+ if($first) {
+ $branch = &$data['branches'][$id];
+ $first = false;
+ } else if($cbranch['id'] == $branch['id']) {
+ } else {
+ $commit['merge'][] = array("point" => $cbranch['id'], "start" => true);
+ $cbranch['active'] = false;
+ if($cbranch['pre_merge']) {
+ $cbranch['pre_merge_start'] = true;
+ $cbranch['pre_merge_id'] = $branch['id'];
+ $data['ubranches'][$cbranch['uid']] = $data['branches'][$cbranch['id']-1];
+ }
+ }
+ }
+ }
+ unset($cbranch);
+ if($first)
+ continue;
+ }
+
+ if(count($commit['parent']) > 1) {
+ //merge(s)
+ for($j = 1; $j < count($commit['parent']); $j++) {
+ $add = true;
+ foreach($data['branches'] as $cbranch) {
+ if($cbranch['next'] == $commit['parent'][$j]) {
+ $add = false;
+ break;
+ }
+ }
+ if($add) {
+ $cadd = true;
+ foreach($data['branches'] as $bid => &$cbranch) {
+ if(!$cbranch['active']) {
+ $cadd = false;
+ break;
+ }
+ }
+ if($cadd) {
+ $data['branches'][count($data['branches'])] = array();
+ $cbranch = &$data['branches'][count($data['branches'])-1];
+ $cbranch['id'] = $brach_id++;
+ }
+ $cbranch['uid'] = $branch_uid++;
+ $cbranch['active'] = true;
+ $cbranch['pre_merge'] = true;
+ $cbranch['next'] = $commit['parent'][$j];
+ }
+ $commit['merge'][] = array("point" => $cbranch['id'], "end" => $add);
+ $commit['dot_merge'] = true;
+ $data['ubranches'][$cbranch['uid']] = $data['branches'][$cbranch['id']-1];
+ unset($cbranch);
+ }
+ } else if(count($commit['parent']) == 0) {
+ $branch['active'] = false;
+ $commit['dot_init'] = true;
+ }
+ $branch['next'] = $commit['parent'][0];
+ $branch['pre_merge'] = false;
+ $data['ubranches'][$branch['uid']] = $data['branches'][$branch['id']-1];
+
+ $commit['dot'] = $branch['id'];
+
+ foreach($data['branches'] as $id => $cbranch) {
+ $commit['branches'][$id] = $cbranch;
+ }
+
+ if($commit['id'] == $_GET['c']) {
+ //yeaaa i've found a PHP Bug :D
+ //$graph_commit['branches'] still gets updated.. (damn references)
+ //we need a fully independent copy of the array....
+ $graph_commit = $commit;
+ }
+}
+
+if($graph_commit == NULL)
+ die("ERROR 0x07");
+
+//DEV Breakpoint
+//print_r($data);
+//die();
+
+//generate image
+$count = count($data['branches']);
+if($count > $max_branches)
+ $count = $max_branches;
+$image = imagecreatetruecolor($count * $size, $size);
+$transparentIndex = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
+imagefill($image, 0, 0, $transparentIndex);
+
+function image_set_color($src, $color) {
+ global $size;
+ imagesavealpha($src, true);
+ imagealphablending($src, false);
+ // scan image pixels
+ for ($x = 0; $x < $size; $x++) {
+ for ($y = 0; $y < $size; $y++) {
+ $src_pix = imagecolorat($src,$x,$y);
+ $src_pix_array = imagecolorsforindex($src, $src_pix);
+
+ imagesetpixel($src, $x, $y, imagecolorallocatealpha($src, $color[0], $color[1], $color[2], $src_pix_array['alpha']));
+ }
+ }
+}
+
+function overlay_image($name, $left, $color = false) {
+ global $image, $size, $tile_size;
+ $image2 = imagecreatefrompng($name);
+
+ if($color) {
+ image_set_color($image2, $color);
+ }
+ imagecopyresampled($image, $image2, $left, 0, 0, 0, $size, $size, $tile_size, $tile_size);
+}
+
+function get_color($id) {
+ global $colors, $graph_commit, $data;
+ if($graph_commit['branches'][($id-1)]['pre_merge']) {
+ $branch = $data['ubranches'][$graph_commit['branches'][($id-1)]['uid']];
+ if($branch['pre_merge'] && $branch['pre_merge_start'])
+ $id = $branch['pre_merge_id'];
+ }
+ return $colors[($id - 1) % count($colors)];
+}
+
+foreach($graph_commit['branches'] as $branch) {
+ $left = ($branch['id']-1) * $size;
+ if($branch['active']) {
+ if($graph_commit['dot'] == $branch['id']) continue;
+ $show = true;
+ if($graph_commit['merge']) {
+ foreach($graph_commit['merge'] as $merge) {
+ if($merge['point'] == $branch['id']) {
+ $show = false;
+ break;
+ }
+ }
+ }
+ if(!$show) continue;
+ if($branch['id'] > $max_branches) continue;
+ overlay_image("images/line.png", $left, get_color($branch['id']));
+ }
+}
+
+if($graph_commit['merge']) {
+ foreach($graph_commit['merge'] as $merge) {
+ $dot_dir = ($graph_commit['dot'] < $merge['point'] ? "right" : "left");
+ $merge_dir = ($graph_commit['dot'] < $merge['point'] ? "left" : "right");
+
+ if($graph_commit['dot'] <= $max_branches)
+ overlay_image("images/dot_merge_".$dot_dir.".png", ($graph_commit['dot'] - 1) * $size, get_color($merge['point']));
+
+ if($merge['point'] <= $max_branches) {
+ overlay_image("images/".($merge['start'] ? "branch" : "merge")."_".$merge_dir.".png", ($merge['point'] - 1) * $size, get_color($merge['point']));
+ if(!$merge['start'] && !$merge['end'])
+ overlay_image("images/line.png", ($merge['point'] - 1) * $size, get_color($merge['point']));
+ }
+ $min = ($graph_commit['dot'] < $merge['point'] ? $graph_commit['dot'] : $merge['point']) + 1;
+ $max = ($graph_commit['dot'] < $merge['point'] ? $merge['point'] : $graph_commit['dot']);
+ for($i = $min; $i < $max; $i++) {
+ if($i > $max_branches) continue;
+ overlay_image("images/line_h.png", ($i - 1) * $size, get_color($merge['point']));
+ }
+ }
+ if($graph_commit['dot'] <= $max_branches) {
+ if($graph_commit['dot_merge'])
+ overlay_image("images/dot_merge.png", ($graph_commit['dot'] - 1) * $size, get_color($graph_commit['dot']));
+ else
+ overlay_image("images/dot.png", ($graph_commit['dot'] - 1) * $size, get_color($graph_commit['dot']));
+ }
+} else if($graph_commit['dot_init']) {
+ if($graph_commit['dot'] <= $max_branches)
+ overlay_image("images/dot_init.png", ($graph_commit['dot'] - 1) * $size, get_color($graph_commit['dot']));
+} else if($graph_commit['dot']) {
+ if($graph_commit['dot'] <= $max_branches)
+ overlay_image("images/dot.png", ($graph_commit['dot'] - 1) * $size, get_color($graph_commit['dot']));
+}
+
+imagecolortransparent($image, $transparentIndex);
+
+header('Content-Type: image/png');
+imagepng($image);
+imagedestroy($image);
+
+?>
\ No newline at end of file