Move blame code inside blob object
Move blame code inside blob object

--- a/include/controller/Controller_Blame.class.php
+++ b/include/controller/Controller_Blame.class.php
@@ -10,8 +10,6 @@
  * @subpackage Controller
  */
 
-require_once(GITPHP_INCLUDEDIR . 'gitutil.git_parse_blame.php');
-require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
 require_once(GITPHP_INCLUDEDIR . 'gitutil.git_path_trees.php');
 
 /**
@@ -94,34 +92,27 @@
 	 */
 	protected function LoadData()
 	{
+		$head = $this->project->GetHeadCommit();
+		$this->tpl->assign('head', $head);
+
+		$hashbase = $this->project->GetCommit($this->params['hashbase']);
+		$this->tpl->assign('hashbase', $hashbase);
+
 		if ((!isset($this->params['hash'])) && (isset($this->params['file']))) {
-			$this->params['hash'] = git_get_hash_by_path($this->params['hashbase'], $this->params['file'], 'blob');
+			$this->params['hash'] = $hashbase->PathToHash($this->params['file']);
 		}
-		$head = $this->project->GetHeadCommit()->GetHash();
-		$this->tpl->assign("hash", $this->params['hash']);
-		$this->tpl->assign("hashbase", $this->params['hashbase']);
-		$this->tpl->assign("head", $head);
-		$co = $this->project->GetCommit($this->params['hashbase']);
-		if ($co) {
-			$this->tpl->assign("fullnav",TRUE);
-			$refs = read_info_ref();
-			$this->tpl->assign("tree",$co->GetTree()->GetHash());
-			$this->tpl->assign("title",$co->GetTitle());
-			if (isset($this->params['file']))
-				$this->tpl->assign("file",$this->params['file']);
-			if ($this->params['hashbase'] == "HEAD") {
-				if (isset($refs[$head]))
-					$this->tpl->assign("hashbaseref",$refs[$head]);
-			} else {
-				if (isset($refs[$this->params['hashbase']]))
-					$this->tpl->assign("hashbaseref",$refs[$this->params['hashbase']]);
-			}
-		}
+		
+		$hash = $this->project->GetBlob($this->params['hash']);
+		$hash->SetCommit($hashbase);
+		$this->tpl->assign('hash', $hash);
+
+		$this->tpl->assign('tree', $hashbase->GetTree());
+
 		$paths = git_path_trees($this->params['hashbase'], $this->params['file']);
 		$this->tpl->assign("paths",$paths);
 
-		$blamedata = git_parse_blame($this->params['file'], $this->params['hashbase']);
-		$this->tpl->assign("blamedata",$blamedata);
+		$blame = $hash->GetBlame();
+		$this->tpl->assign('blame', $hash->GetBlame());
 	}
 
 }

--- a/include/git/Blob.class.php
+++ b/include/git/Blob.class.php
@@ -68,6 +68,24 @@
 	protected $historyRead = false;
 
 	/**
+	 * blame
+	 *
+	 * Stores blame info
+	 *
+	 * @access protected
+	 */
+	protected $blame = array();
+
+	/**
+	 * blameRead
+	 *
+	 * Stores whether blame was read
+	 *
+	 * @access protected
+	 */
+	protected $blameRead = false;
+
+	/**
 	 * __construct
 	 *
 	 * Instantiates object
@@ -89,14 +107,18 @@
 	 * Gets the blob data
 	 *
 	 * @access public
+	 * @param boolean $explode true to explode data into an array of lines
 	 * @return string blob data
 	 */
-	public function GetData()
+	public function GetData($explode = false)
 	{
 		if (!$this->dataRead)
 			$this->ReadData();
 
-		return $this->data;
+		if ($explode)
+			return explode("\n", $this->data);
+		else
+			return $this->data;
 	}
 
 	/**
@@ -390,5 +412,57 @@
 		}
 	}
 
+	/**
+	 * GetBlame
+	 *
+	 * Gets blame info
+	 *
+	 * @access public
+	 * @return array blame array (line to commit mapping)
+	 */
+	public function GetBlame()
+	{
+		if (!$this->blameRead)
+			$this->ReadBlame();
+
+		return $this->blame;
+	}
+
+	/**
+	 * ReadBlame
+	 *
+	 * Read blame info
+	 *
+	 * @access private
+	 */
+	private function ReadBlame()
+	{
+		$this->blameRead = true;
+
+		$exe = new GitPHP_GitExe($this->project);
+
+		$args = array();
+		$args[] = '-s';
+		$args[] = '-l';
+		if ($this->commit)
+			$args[] = $this->commit->GetHash();
+		else
+			$args[] = 'HEAD';
+		$args[] = '--';
+		$args[] = $this->GetPath();
+
+		$blamelines = explode("\n", $exe->Execute(GIT_BLAME, $args));
+
+		$lastcommit = '';
+		foreach ($blamelines as $line) {
+			if (preg_match('/^([0-9a-fA-F]{40})\s+([0-9]+)\)/', $line, $regs)) {
+				if ($regs[1] != $lastcommit) {
+					$this->blame[(int)($regs[2])] = $this->project->GetCommit($regs[1]);
+					$lastcommit = $regs[1];
+				}
+			}
+		}
+	}
+
 }
 

--- a/include/gitutil.git_parse_blame.php
+++ /dev/null
@@ -1,68 +1,1 @@
-<?php
-/*
- * gitutil.git_parse_blame.php
- * gitphp: A PHP git repository browser
- * Component: Git utility - parse blame info
- *
- * Copyright (C) 2010 Christopher Han <xiphux@gmail.com>
- */
 
-require_once('gitutil.git_read_blame.php');
-
-function git_parse_blame($file, $rev = null)
-{
-	$lines = explode("\n", git_read_blame($file, $rev));
-
-	if (count($lines) < 1)
-		return null;
-	
-	$blamedata = array();
-	$commitcache = array();
-	
-	$commitgroup = null;
-	foreach ($lines as $i => $line) {
-		/*
-		 * Only parsing a handful of the blame info, see
-		 * the git blame man page for all the data
-		 */
-		if (preg_match("/^([0-9a-fA-F]{40}) ([0-9]+) ([0-9]+) ([0-9]+)$/",$line,$regs)) {
-			/* starting a new commit group */
-			if ($commitgroup)
-				$blamedata[] = $commitgroup;
-			$commitgroup = array();
-			$commitgroup['lines'] = array();
-			$commitgroup['commit'] = $regs[1];
-			if (isset($commitcache[$regs[1]])) {
-				$commitgroup['commitdata'] = $commitcache[$regs[1]];
-			} else {
-				$commitgroup['commitdata'] = array();
-				$commitcache[$regs[1]] = array();
-			}
-		} else if (preg_match("/^author (.*)$/",$line,$regs)) {
-			$commitgroup['commitdata']['author'] = $regs[1];
-			$commitcache[$commitgroup['commit']]['author'] = $regs[1];
-		} else if (preg_match("/^author-mail (.*)$/",$line,$regs)) {
-			$commitgroup['commitdata']['authormail'] = $regs[1];
-			$commitcache[$commitgroup['commit']]['authormail'] = $regs[1];
-		} else if (preg_match("/^author-time (.*)$/",$line,$regs)) {
-			$commitgroup['commitdata']['authortime'] = $regs[1];
-			$commitcache[$commitgroup['commit']]['authortime'] = $regs[1];
-		} else if (preg_match("/^author-tz (.*)$/",$line,$regs)) {
-			$commitgroup['commitdata']['authortz'] = $regs[1];
-			$commitcache[$commitgroup['commit']]['authortz'] = $regs[1];
-		} else if (preg_match("/^summary (.*)$/",$line,$regs)) {
-			$commitgroup['commitdata']['summary'] = $regs[1];
-			$commitcache[$commitgroup['commit']]['summary'] = $regs[1];
-		} else if (preg_match("/^\t(.*)$/",$line,$regs)) {
-			/* tab starts a file content line */
-			$commitgroup['lines'][] = $regs[1];
-		}
-	}
-	if ($commitgroup)
-		$blamedata[] = $commitgroup;
-
-	return $blamedata;
-}
-
-?>
-

--- a/include/gitutil.git_read_blame.php
+++ /dev/null
@@ -1,32 +1,1 @@
-<?php
-/*
- * gitutil.git_read_blame.php
- * gitphp: A PHP git repository browser
- * Component: Git utility - get blame info
- *
- * Copyright (C) 2010 Christopher Han <xiphux@gmail.com>
- */
 
-require_once('defs.commands.php');
-require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
-
-function git_read_blame($file, $rev = null)
-{
-	global $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return '';
-
-	$exe = new GitPHP_GitExe($gitphp_current_project);
-
-	$args = array();
-
-	$args[] = '-p';	
-	if ($rev)
-		$args[] = $rev;
-	$args[] = $file;
-	return $exe->Execute(GIT_BLAME, $args);
-}
-
-?>
-

--- a/templates/blame.tpl
+++ b/templates/blame.tpl
@@ -10,70 +10,65 @@
 
  {* If we managed to look up commit info, we have enough info to display the full header - othewise just use a simple header *}
  <div class="page_nav">
-   {if $fullnav}
-     <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commit&h={$hashbase}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commitdiff&h={$hashbase}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=tree&h={$tree}&hb={$hashbase}">tree</a><br />
-     {if $file}
-       <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=blob_plain&h={$hash}&f={$file}">plain</a> | 
-       {if ($hashbase != "HEAD") && ($hashbase != $head)}
-         <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=blame&hb=HEAD&f={$file}">HEAD</a>
-       {else}
-         HEAD
-       {/if}
-        | blame
-       <br />
-     {else}
-       <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=blob_plain&h={$hash}">plain</a><br />
-     {/if}
+   <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commit&h={$hashbase->GetHash()}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commitdiff&h={$hashbase->GetHash()}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=tree&h={$tree->GetHash()}&hb={$hashbase->GetHash()}">tree</a><br />
+   <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=blob_plain&h={$hash->GetHash()}&f={$hash->GetPath()}">plain</a> | 
+   {if $hashbase->GetHash() != $head->GetHash()}
+     <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=blame&hb=HEAD&f={$hash->GetPath()}">HEAD</a>
    {else}
-     <br /><br />
+     HEAD
    {/if}
+    | blame
+   <br />
  </div>
  <div class="title">
-   {if $fullnav}
-     <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commit&h={$hashbase}" class="title">{$title}
-     {if $hashbaseref}
-       <span class="tag">{$hashbaseref}</span>
-     {/if}
-     </a>
-   {else}
-     <div class="title">{$hash}</div>
-   {/if}
+   <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commit&h={$hashbase->GetHash()}" class="title">{$hashbase->GetTitle()}</a>
+   <span class="refs">
+   {foreach from=$hashbase->GetHeads() item=head}
+     <span class="head">
+       <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=shortlog&h=refs/heads/{$head->GetName()}">{$head->GetName()}</a>
+     </span>
+   {/foreach}
+   {foreach from=$hashbase->GetTags() item=tag}
+     <span class="tag">
+       <a href="{$SCIRPT_NAME}?p={$project->GetProject()}&a=tag&h={$tag->GetName()}">{$tag->GetName()}</a>
+     </span>
+   {/foreach}
+   </span>
  </div>
  <div class="page_path">
    {* The path to the file, with directories broken into tree links *}
    <b>
-     <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=tree&hb={$hashbase}&h={$hashbase}">[{$project->GetProject()}]</a> / 
+     <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=tree&hb={$hashbase->GetHash()}&h={$hashbase->GetHash()}">[{$project->GetProject()}]</a> / 
      {foreach from=$paths item=path name=paths}
        {if $smarty.foreach.paths.last}
          <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=blob_plain&h={$path.tree}&f={$path.full}">{$path.short}</a>
        {else}
-         <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=tree&hb={$hashbase}&h={$path.tree}&f={$path.full}">{$path.short}</a> / 
+         <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=tree&hb={$hashbase->GetHash()}&h={$path.tree}&f={$path.full}">{$path.short}</a> / 
        {/if}
      {/foreach}
    </b>
  </div>
  <div class="page_body">
  	<table class="code">
-	{counter name=linecount start=0 print=false}
-	{foreach from=$blamedata item=blameitem}
-		{cycle values="light,dark" assign=rowclass}
-		{foreach from=$blameitem.lines name=linegroup item=blameline}
-		{counter name=linecount assign=linenum}
-		<tr class="{$rowclass}">
-			<td class="num"><a id="l{$linenum}" href="#l{$linenum}" class="linenr">{$linenum}</a></td>
-			<td class="date">
-			{if $smarty.foreach.linegroup.first}
-			{if $blameitem.commit}
-			<a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commit&h={$blameitem.commit}" title="{$blameitem.commitdata.summary}">{$blameitem.commitdata.authortime|date_format:"%F %X"}</a>
-			{else}
-			{$blameitem.commitdata.authortime|date_format:"%F %X"}
-			{/if}
-			{/if}
-			</td>
-			<td class="author">{if $smarty.foreach.linegroup.first}{$blameitem.commitdata.author}{/if}</td>
-			<td class="codeline">{$blameline|escape}</td>
-		</tr>
-		{/foreach}
+	{foreach from=$hash->GetData(true) item=blobline name=blob}
+	  {assign var=blamecommit value=$blame[$smarty.foreach.blob.iteration]}
+	  {if $blamecommit}
+	    {cycle values="light,dark" assign=rowclass}
+	  {/if}
+	  <tr class="{$rowclass}">
+	    <td class="date">
+	      {if $blamecommit}
+	        <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=commit&h={$blamecommit->GetHash()}" title="{$blamecommit->GetTitle()}">{$blamecommit->GetAuthorEpoch()|date_format:"%F %X"}</a>
+	      {/if}
+	    </td>
+	    <td class="author">
+	      {if $blamecommit}
+	        {$blamecommit->GetAuthor()}
+	      {/if}
+	    </td>
+	    <td class="num"><a id="l{$smarty.foreach.blob.iteration}" href="#l{$smarty.foreach.blob.iteration}" class="linenr">{$smarty.foreach.blob.iteration}</a></td>
+	    <td class="codeline">{$blobline|escape}</td>
+	  </tr>
 	{/foreach}
 	</table>
  </div>

comments