added shortlog graphs
authorpk910 <philipp@zoelle1.de>
Tue, 28 Aug 2012 23:33:19 +0000 (01:33 +0200)
committerpk910 <philipp@zoelle1.de>
Tue, 28 Aug 2012 23:33:19 +0000 (01:33 +0200)
13 files changed:
git_graph.php [new file with mode: 0644]
images/branch_left.png [new file with mode: 0644]
images/branch_right.png [new file with mode: 0644]
images/dot.png [new file with mode: 0644]
images/dot_init.png [new file with mode: 0644]
images/dot_merge.png [new file with mode: 0644]
images/dot_merge_left.png [new file with mode: 0644]
images/dot_merge_right.png [new file with mode: 0644]
images/line.png [new file with mode: 0644]
images/line_h.png [new file with mode: 0644]
images/merge_left.png [new file with mode: 0644]
images/merge_right.png [new file with mode: 0644]
patch/gitweb.cgi.diff [new file with mode: 0644]

diff --git a/git_graph.php b/git_graph.php
new file mode 100644 (file)
index 0000000..a749c24
--- /dev/null
@@ -0,0 +1,331 @@
+<?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
diff --git a/images/branch_left.png b/images/branch_left.png
new file mode 100644 (file)
index 0000000..06016a1
Binary files /dev/null and b/images/branch_left.png differ
diff --git a/images/branch_right.png b/images/branch_right.png
new file mode 100644 (file)
index 0000000..1db9a66
Binary files /dev/null and b/images/branch_right.png differ
diff --git a/images/dot.png b/images/dot.png
new file mode 100644 (file)
index 0000000..433e75a
Binary files /dev/null and b/images/dot.png differ
diff --git a/images/dot_init.png b/images/dot_init.png
new file mode 100644 (file)
index 0000000..5fbde45
Binary files /dev/null and b/images/dot_init.png differ
diff --git a/images/dot_merge.png b/images/dot_merge.png
new file mode 100644 (file)
index 0000000..70691d0
Binary files /dev/null and b/images/dot_merge.png differ
diff --git a/images/dot_merge_left.png b/images/dot_merge_left.png
new file mode 100644 (file)
index 0000000..0fa7c87
Binary files /dev/null and b/images/dot_merge_left.png differ
diff --git a/images/dot_merge_right.png b/images/dot_merge_right.png
new file mode 100644 (file)
index 0000000..8ccef25
Binary files /dev/null and b/images/dot_merge_right.png differ
diff --git a/images/line.png b/images/line.png
new file mode 100644 (file)
index 0000000..1358887
Binary files /dev/null and b/images/line.png differ
diff --git a/images/line_h.png b/images/line_h.png
new file mode 100644 (file)
index 0000000..0585711
Binary files /dev/null and b/images/line_h.png differ
diff --git a/images/merge_left.png b/images/merge_left.png
new file mode 100644 (file)
index 0000000..cfb950d
Binary files /dev/null and b/images/merge_left.png differ
diff --git a/images/merge_right.png b/images/merge_right.png
new file mode 100644 (file)
index 0000000..67e9d32
Binary files /dev/null and b/images/merge_right.png differ
diff --git a/patch/gitweb.cgi.diff b/patch/gitweb.cgi.diff
new file mode 100644 (file)
index 0000000..f2a9976
--- /dev/null
@@ -0,0 +1,27 @@
+--- index.cgi  Tue Aug 28 23:20:03 2012
++++ index-graph.cgi    Tue Aug 28 23:21:08 2012
+@@ -4769,8 +4772,15 @@
+       $from = 0 unless defined $from;
+       $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
+-      print "<table class=\"shortlog\">\n";
++      print "<table class=\"shortlog\" cellspacing=\"0\" cellpadding=\"0\">\n";
+       my $alternate = 1;
++      my $graph_rand = int(rand(99999));
++
++      my $head = git_get_head_hash($project);
++      if (!defined $page) {
++              $page = 0;
++      }
++
+       for (my $i = $from; $i <= $to; $i++) {
+               my %co = %{$commitlist->[$i]};
+               my $commit = $co{'id'};
+@@ -4781,6 +4791,7 @@
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
++              print "<td><img class=\"graph\" src=\"git_graph.php?r=".$graph_rand.";p=".$project.";h=".$head.";from=".($from + (100 * $page)).";to=".($to + (100 * $page)).";c=".$commit."\" /></td>";
+               # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
+               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+                     format_author_html('td', \%co, 10) . "<td>";