. */ class diff { private $commit, $tree, $diff, $data = array(); public function __construct($commit, $tree, $diff) { $this->commit = $commit; $this->tree = $tree; $this->diff = $diff; } public static function generate_html($commit, $diff_data) { $diff_id = 0; $patch_id = 1; $html = array(); foreach($diff_data['tree'] as $tree) { do { if(array_key_exists($diff_id, $diff_data['diffs'])) $diff = $diff_data['diffs'][$diff_id]; else $diff = null; if($diff && preg_match('/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/i', $diff[0], $matches)) $to_file = $matches[2]; else if($diff && preg_match('/^diff --(cc|combined) ("?.*"?)$/i', $diff[0], $matches)) $to_file = $matches[2]; else $to_file = null; if(!$to_file || strtolower($to_file) != $tree['to_file']) { if($tree['parents'] > 1) { //jump over missing diffs $diffobj = new diff($commit, $tree, null); $html[] = $diffobj->gen_contentprovider($patch_id); $patch_id++; break; } if(!$to_file) break; } $diffobj = new diff($commit, $tree, $diff); $html[] = $diffobj->gen_contentprovider($patch_id); $diff_id++; do { if(!array_key_exists($diff_id, $diff_data['diffs'])) break; $diff = $diff_data['diffs'][$diff_id]; if(preg_match('/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/i', $diff[0], $matches)) $to_file = $matches[2]; else if(preg_match('/^diff --(cc|combined) ("?.*"?)$/i', $diff[0], $matches)) $to_file = $matches[2]; else break; if(!array_key_exists('to_file', $tree)) $tree['to_file'] = $tree['file']; if(strtolower($to_file) != $tree['to_file']) break; $diffobj = new diff($commit, $tree, $diff); $html[] = $diffobj->gen_contentprovider(null); $diff_id++; } while(true); $patch_id++; break; } while(true); } if($patch_id == 1) { if(count($commit['parent']) > 1) $html[] = new ContentProvider('commitdiff', 'patch_nopatch_merge'); else $html[] = new ContentProvider('commitdiff', 'patch_nopatch_simple'); } return $html; } private function gen_diffcmd_header() { $header = array(); if(!$this->diff) { $header[] = "diff --cc "; $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => '')); } else if($this->tree['parents'] > 1) { preg_match('!^(diff (.*?) )"?.*$!', $this->diff[0], $match); $header[] = $match[1]; if(preg_match('/^[0]{40}$/', $this->tree['to_id'])) $header[] = $this->tree['to_file']; else $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => '')); } else { preg_match('!^(diff (.*?) )"?a/.*$!', $this->diff[0], $match); $header[] = $match[1]; if(preg_match('/^[0]{40}$/', $this->tree['from_id'])) $header[] = 'a/'.$this->tree['file']; else $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][0], 'file' => $this->tree['from_file'], 'path' => 'a/')); $header[] = ' '; if(preg_match('/^[0]{40}$/', $this->tree['to_id'])) $header[] = 'b/'.$this->tree['file']; else $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => 'b/')); } return $header; } private function gen_index_header($line) { $header = array(); if(preg_match('/^((copy|rename) from ).*$/', $line, $matches)) { $header[] = $matches[1]; if(array_key_exists('copy', $this->data)) $this->data['copy']++; else $this->data['copy'] = 0; $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][$this->data['copy']], 'file' => $this->tree['from_file'], 'path' => '')); } else if(preg_match('/^((copy|rename) to ).*$/', $line, $matches)) { $header[] = $matches[1]; $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => '')); } else if(preg_match('/^(index )[0-9a-fA-F]{40},[0-9a-fA-F]{40}/', $line, $matches)) { //combined diff $header[] = $matches[1]; $parent_id = 0; foreach($this->tree['from_id'] as $from_id) { if($parent_id) $header[] = ','; if(preg_match('/^[0]{40}$/', $from_id)) $header[] = '0000000'; else $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['parent'][$parent_id], 'file' => $this->tree['from_file'], 'id' => substr($from_id, 0, 7))); $parent_id++; } } else if(preg_match('/^(index )([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/', $line, $matches)) { $header[] = $matches[1]; if(preg_match('/^[0]{40}$/', $matches[2])) $header[] = '0000000'; else $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['parent'][0], 'file' => $this->tree['from_file'], 'id' => substr($matches[2], 0, 7))); $header[] = '..'; if(preg_match('/^[0]{40}$/', $matches[3])) $header[] = '0000000'; else $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'id' => substr($matches[3], 0, 7))); } else $header[] = $line; if(preg_match('/([0-7]{6})$/', $line, $matches)) { $ftype = Tools::get_filetype($matches[1], true); $header[] = new ContentProvider('commitdiff', 'patch_fileinfo', array('info' => $ftype)); } return $header; } private function format_line($line) { $lineobj = new ContentProvider('commitdiff', 'patch_diffline'); $class = ""; if($this->tree['parents'] > 1) { $prefix = substr($line, 0, $this->tree['parents']); if(preg_match('/@/', $prefix)) $class = " chunk_header"; else if(preg_match('/\\/', $prefix)) $class = " incomplete"; else if(preg_match('/+/', $prefix)) $class = " add"; else if(preg_match('/-/', $prefix)) $class = " rem"; } else { $prefix = substr($line, 0, 1); if($prefix == '@') $class = " chunk_header"; else if($prefix == '\\') $class = " incomplete"; else if($prefix == '+') $class = " add"; else if($prefix == '-') $class = " rem"; } $lineobj->set('class', $class); $line = Tools::replaceTabs($line); $lineobj->set('line', htmlentities($line)); return $lineobj; } private function gen_contentprovider($patch_id) { if(!$this->diff) $pageclass = 'patch_nodiff'; else if(!$patch_id) $pageclass = 'patch_continued'; else $pageclass = 'patch_normal'; $page = new ContentProvider('commitdiff', $pageclass); $page->set('diffcmd', $this->gen_diffcmd_header()); $page->set('patch_id', $patch_id); if(!$this->diff) return $page; $is_header = true; $first_header = true; for($i = 1; $i < count($this->diff); $i++) { $line = $this->diff[$i]; if($is_header) { if(preg_match('/^--- |^diff /', $line)) $is_header = false; else { if(!$first_header) $page->append('header', '
'); $page->append('header', $this->gen_index_header($line)); $first_header = false; continue; } } $page->append('patch', $this->format_line($line)); } if($is_header) $page->set('patch', ''); return $page; } } ?>