Increase TOC indent a bit
Increase TOC indent a bit

--- a/css/gitphpskin.css
+++ b/css/gitphpskin.css
@@ -400,6 +400,40 @@
 	border-right: 1px solid #d9d8d1;
 }
 
+/*
+ * side-by-side commitdiff
+ */
+div.commitDiffSBS
+{
+	width: 100%;
+	border-top: 2px solid #edece6;
+}
+
+div.commitDiffSBS div.SBSTOC
+{
+	float: left;
+	width: 19%;
+	word-wrap: break-word;
+}
+
+div.commitDiffSBS div.SBSTOC ul
+{
+	margin-left: 8px;
+	padding-left: 8px;
+}
+
+div.commitDiffSBS .SBSContent
+{
+	float: right;
+	width: 80%;
+	border-left: 1px solid #edece6;
+}
+
+div.commitDiffSBS .SBSFooter
+{
+	clear: both;
+}
+
 
 /*
  * Blob/blame display

--- a/include/controller/Controller_Blobdiff.class.php
+++ b/include/controller/Controller_Blobdiff.class.php
@@ -10,21 +10,7 @@
  * @subpackage Controller
  */
 
-/**
- * Constants for blobdiff modes
- */
-define('GITPHP_BLOBDIFF_UNIFIED', 1);
-define('GITPHP_BLOBDIFF_SIDEBYSIDE', 2);
-
-/**
- * Constant of the blobdiff mode cookie in the user's browser
- */
-define('GITPHP_BLOBDIFF_MODE_COOKIE', 'GitPHPBlobdiffMode');
-
-/**
- * Blobdiff mode cookie lifetime
- */
-define('GITPHP_BLOBDIFF_MODE_COOKIE_LIFETIME', 60*60*24*365);		// 1 year
+require_once(GITPHP_CONTROLLERDIR . 'Controller_DiffBase.class.php');
 
 /**
  * Blobdiff controller class
@@ -32,7 +18,7 @@
  * @package GitPHP
  * @subpackage Controller
  */
-class GitPHP_Controller_Blobdiff extends GitPHP_ControllerBase
+class GitPHP_Controller_Blobdiff extends GitPHP_Controller_DiffBase
 {
 
 	/**
@@ -106,6 +92,8 @@
 	 */
 	protected function ReadQuery()
 	{
+		parent::ReadQuery();
+
 		if (isset($_GET['f']))
 			$this->params['file'] = $_GET['f'];
 		if (isset($_GET['h']))
@@ -114,55 +102,6 @@
 			$this->params['hashbase'] = $_GET['hb'];
 		if (isset($_GET['hp']))
 			$this->params['hashparent'] = $_GET['hp'];
-
-		if (!isset($this->params['plain']) || $this->params['plain'] != true) {
-
-			$mode = GITPHP_BLOBDIFF_UNIFIED;	// default
-
-			/*
-			 * Check cookie
-			 */
-			if (!empty($_COOKIE[GITPHP_BLOBDIFF_MODE_COOKIE])) {
-				$mode = $_COOKIE[GITPHP_BLOBDIFF_MODE_COOKIE];
-			} else {
-				/*
-				 * Create cookie to prevent browser delay
-				 */
-				setcookie(GITPHP_BLOBDIFF_MODE_COOKIE, $mode, time()+GITPHP_BLOBDIFF_MODE_COOKIE_LIFETIME);
-			}
-
-			if (isset($_GET['o'])) {
-				/*
-				 * User is choosing a new mode
-				 */
-				if ($_GET['o'] == 'sidebyside') {
-					$mode = GITPHP_BLOBDIFF_SIDEBYSIDE;
-					setcookie(GITPHP_BLOBDIFF_MODE_COOKIE, GITPHP_BLOBDIFF_SIDEBYSIDE, time()+GITPHP_BLOBDIFF_MODE_COOKIE_LIFETIME);
-				} else if ($_GET['o'] == 'unified') {
-					$mode = GITPHP_BLOBDIFF_UNIFIED;
-					setcookie(GITPHP_BLOBDIFF_MODE_COOKIE, GITPHP_BLOBDIFF_UNIFIED, time()+GITPHP_BLOBDIFF_MODE_COOKIE_LIFETIME);
-				}
-			}
-
-			if ($mode == GITPHP_BLOBDIFF_SIDEBYSIDE) {
-				$this->params['sidebyside'] = true;
-			}
-
-		}
-	}
-
-	/**
-	 * LoadHeaders
-	 *
-	 * Loads headers for this template
-	 *
-	 * @access protected
-	 */
-	protected function LoadHeaders()
-	{
-		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
-			$this->headers[] = 'Content-type: text/plain; charset=UTF-8';
-		}
 	}
 
 	/**

--- a/include/controller/Controller_Commitdiff.class.php
+++ b/include/controller/Controller_Commitdiff.class.php
@@ -10,13 +10,15 @@
  * @subpackage Controller
  */
 
+require_once(GITPHP_CONTROLLERDIR . 'Controller_DiffBase.class.php');
+
 /**
  * Commitdiff controller class
  *
  * @package GitPHP
  * @subpackage Controller
  */
-class GitPHP_Controller_Commitdiff extends GitPHP_ControllerBase
+class GitPHP_Controller_Commitdiff extends GitPHP_Controller_DiffBase
 {
 
 	/**
@@ -61,7 +63,11 @@
 	 */
 	protected function GetCacheKey()
 	{
-		return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['hashparent']) ? $this->params['hashparent'] : '');
+		$key = (isset($this->params['hash']) ? $this->params['hash'] : '')
+		. '|' . (isset($this->params['hashparent']) ? $this->params['hashparent'] : '')
+		. '|' . (isset($this->params['sidebyside']) && ($this->params['sidebyside'] === true) ? '1' : '');
+
+		return $key;
 	}
 
 	/**
@@ -90,6 +96,8 @@
 	 */
 	protected function ReadQuery()
 	{
+		parent::ReadQuery();
+
 		if (isset($_GET['h']))
 			$this->params['hash'] = $_GET['h'];
 		if (isset($_GET['hp']))
@@ -105,10 +113,9 @@
 	 */
 	protected function LoadHeaders()
 	{
+		parent::LoadHeaders();
+
 		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
-			GitPHP_Log::GetInstance()->SetEnabled(false);
-
-			$this->headers[] = 'Content-type: text/plain; charset=UTF-8';
 			$this->headers[] = 'Content-disposition: inline; filename="git-' . $this->params['hash'] . '.patch"';
 		}
 	}
@@ -129,6 +136,10 @@
 			$this->tpl->assign("hashparent", $this->params['hashparent']);
 		}
 
+		if (isset($this->params['sidebyside']) && ($this->params['sidebyside'] === true)) {
+			$this->tpl->assign('sidebyside', true);
+		}
+
 		$treediff = new GitPHP_TreeDiff($this->project, $this->params['hash'], (isset($this->params['hashparent']) ? $this->params['hashparent'] : ''));
 		$this->tpl->assign('treediff', $treediff);
 	}

--- /dev/null
+++ b/include/controller/Controller_DiffBase.class.php
@@ -1,1 +1,113 @@
+<?php
+/**
+ * GitPHP Controller DiffBase
+ *
+ * Base controller for diff-type views
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2011 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+
+/**
+ * Constants for diff modes
+ */
+define('GITPHP_DIFF_UNIFIED', 1);
+define('GITPHP_DIFF_SIDEBYSIDE', 2);
+
+/**
+ * Constant of the diff mode cookie in the user's browser
+ */
+define('GITPHP_DIFF_MODE_COOKIE', 'GitPHPDiffMode');
+
+/**
+ * Diff mode cookie lifetime
+ */
+define('GITPHP_DIFF_MODE_COOKIE_LIFETIME', 60*60*24*365);           // 1 year
+
+/**
+ * DiffBase controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+abstract class GitPHP_Controller_DiffBase extends GitPHP_ControllerBase
+{
+	
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (!isset($this->params['plain']) || $this->params['plain'] != true) {
+
+			if ($this->DiffMode(isset($_GET['o']) ? $_GET['o'] : '') == GITPHP_DIFF_SIDEBYSIDE) {
+				$this->params['sidebyside'] = true;
+			}
+
+		}
+	}
+
+	/**
+	 * DiffMode
+	 *
+	 * Determines the diff mode to use
+	 *
+	 * @param string $overrideMode mode overridden by the user
+	 * @access protected
+	 */
+	protected function DiffMode($overrideMode = '')
+	{
+		$mode = GITPHP_DIFF_UNIFIED;	// default
+
+		/*
+		 * Check cookie
+		 */
+		if (!empty($_COOKIE[GITPHP_DIFF_MODE_COOKIE])) {
+			$mode = $_COOKIE[GITPHP_DIFF_MODE_COOKIE];
+		} else {
+			/*
+			 * Create cookie to prevent browser delay
+			 */
+			setcookie(GITPHP_DIFF_MODE_COOKIE, $mode, time()+GITPHP_DIFF_MODE_COOKIE_LIFETIME);
+		}
+
+		if (!empty($overrideMode)) {
+			/*
+			 * User is choosing a new mode
+			 */
+			if ($overrideMode == 'sidebyside') {
+				$mode = GITPHP_DIFF_SIDEBYSIDE;
+				setcookie(GITPHP_DIFF_MODE_COOKIE, GITPHP_DIFF_SIDEBYSIDE, time()+GITPHP_DIFF_MODE_COOKIE_LIFETIME);
+			} else if ($overrideMode == 'unified') {
+				$mode = GITPHP_DIFF_UNIFIED;
+				setcookie(GITPHP_DIFF_MODE_COOKIE, GITPHP_DIFF_UNIFIED, time()+GITPHP_DIFF_MODE_COOKIE_LIFETIME);
+			}
+		}
+
+		return $mode;
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
+			GitPHP_Log::GetInstance()->SetEnabled(false);
+			$this->headers[] = 'Content-type: text/plain; charset=UTF-8';
+		}
+	}
+
+}
+

--- a/include/git/FileDiff.class.php
+++ b/include/git/FileDiff.class.php
@@ -351,6 +351,38 @@
 	}
 
 	/**
+	 * GetFromBlob
+	 *
+	 * Gets the from file blob
+	 *
+	 * @access public
+	 * @return mixed blob object
+	 */
+	public function GetFromBlob()
+	{
+		if (empty($this->fromHash))
+			return null;
+
+		return $this->project->GetBlob($this->fromHash);
+	}
+
+	/**
+	 * GetToBlob
+	 *
+	 * Gets the to file blob
+	 *
+	 * @access public
+	 * @return mixed blob object
+	 */
+	public function GetToBlob()
+	{
+		if (empty($this->toHash))
+			return null;
+
+		return $this->project->GetBlob($this->toHash);
+	}
+
+	/**
 	 * GetStatus
 	 *
 	 * Gets the status of the change
@@ -556,7 +588,7 @@
 		$toName = null;
 
 		if ((empty($this->status)) || ($this->status == 'D') || ($this->status == 'M')) {
-			$fromBlob = $this->project->GetBlob($this->fromHash);
+			$fromBlob = $this->GetFromBlob();
 			$fromTmpFile = 'gitphp_' . $pid . '_from';
 			$tmpdir->AddFile($fromTmpFile, $fromBlob->GetData());
 
@@ -571,7 +603,7 @@
 		}
 
 		if ((empty($this->status)) || ($this->status == 'A') || ($this->status == 'M')) {
-			$toBlob = $this->project->GetBlob($this->toHash);
+			$toBlob = $this->GetToBlob();
 			$toTmpFile = 'gitphp_' . $pid . '_to';
 			$tmpdir->AddFile($toTmpFile, $toBlob->GetData());
 

--- a/include/git/Project.class.php
+++ b/include/git/Project.class.php
@@ -590,26 +590,43 @@
 			$head = $this->GetHead(substr($hash, 11));
 			if ($head != null)
 				return $head->GetCommit();
+			return null;
 		} else if (substr_compare($hash, 'refs/tags/', 0, 10) === 0) {
 			$tag = $this->GetTag(substr($hash, 10));
 			if ($tag != null) {
-				$obj = $tag->GetObject();
+				$obj = $tag->GetCommit();
 				if ($obj != null) {
 					return $obj;
 				}
 			}
-		}
-
-		if (!isset($this->commitCache[$hash])) {
-			$cacheKey = 'project|' . $this->project . '|commit|' . $hash;
-			$cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
-			if ($cached)
-				$this->commitCache[$hash] = $cached;
-			else
-				$this->commitCache[$hash] = new GitPHP_Commit($this, $hash);
-		}
-
-		return $this->commitCache[$hash];
+			return null;
+		}
+
+		if (preg_match('/[0-9a-f]{40}/i', $hash)) {
+
+			if (!isset($this->commitCache[$hash])) {
+				$cacheKey = 'project|' . $this->project . '|commit|' . $hash;
+				$cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
+				if ($cached)
+					$this->commitCache[$hash] = $cached;
+				else
+					$this->commitCache[$hash] = new GitPHP_Commit($this, $hash);
+			}
+
+			return $this->commitCache[$hash];
+
+		}
+
+		if (!$this->readRefs)
+			$this->ReadRefList();
+
+		if (isset($this->heads['refs/heads/' . $hash]))
+			return $this->heads['refs/heads/' . $hash]->GetCommit();
+
+		if (isset($this->tags['refs/tags/' . $hash]))
+			return $this->tags['refs/tags/' . $hash]->GetCommit();
+
+		return null;
 	}
 
 	/**

--- a/templates/commitdiff.tpl
+++ b/templates/commitdiff.tpl
@@ -14,7 +14,12 @@
    {/if}
    {include file='nav.tpl' current='commitdiff' logcommit=$commit treecommit=$commit}
    <br />
-   <a href="{$SCRIPT_NAME}?p={$project->GetProject()|urlencode}&amp;a=commitdiff_plain&amp;h={$commit->GetHash()}{if $hashparent}&amp;hp={$hashparent}{/if}">{t}plain{/t}</a>
+   {if $sidebyside}
+   <a href="{$SCRIPT_NAME}?p={$project->GetProject()|urlencode}&amp;a=commitdiff&amp;h={$commit->GetHash()}{if $hashparent}&amp;hp={$hashparent}{/if}&amp;o=unified">{t}unified{/t}</a>
+   {else}
+   <a href="{$SCRIPT_NAME}?p={$project->GetProject()|urlencode}&amp;a=commitdiff&amp;h={$commit->GetHash()}{if $hashparent}&amp;hp={$hashparent}{/if}&amp;o=sidebyside">{t}side by side{/t}</a>
+   {/if}
+   | <a href="{$SCRIPT_NAME}?p={$project->GetProject()|urlencode}&amp;a=commitdiff_plain&amp;h={$commit->GetHash()}{if $hashparent}&amp;hp={$hashparent}{/if}">{t}plain{/t}</a>
  </div>
 
  {include file='title.tpl' titlecommit=$commit}
@@ -26,8 +31,44 @@
      {$line|htmlspecialchars|buglink:$bugpattern:$bugurl}<br />
    {/foreach}
    <br />
+
+   {if $sidebyside && ($treediff->Count() > 1)}
+    <div class="commitDiffSBS">
+
+     <div class="SBSTOC">
+       <ul>
+       {foreach from=$treediff item=filediff}
+       <li>
+       <a href="#{$filediff->GetFromHash()}_{$filediff->GetToHash()}">
+       {if $filediff->GetStatus() == 'A'}
+         {if $filediff->GetToFile()}{$filediff->GetToFile()}{else}{$filediff->GetToHash()}{/if} {t}(new){/t}
+       {elseif $filediff->GetStatus() == 'D'}
+         {if $filediff->GetFromFile()}{$filediff->GetFromFile()}{else}{$filediff->GetToFile()}{/if} {t}(deleted){/t}
+       {elseif $filediff->GetStatus() == 'M'}
+         {if $filediff->GetFromFile()}
+	   {assign var=fromfilename value=$filediff->GetFromFile()}
+	 {else}
+	   {assign var=fromfilename value=$filediff->GetFromHash()}
+	 {/if}
+	 {if $filediff->GetToFile()}
+	   {assign var=tofilename value=$filediff->GetToFile()}
+	 {else}
+	   {assign var=tofilename value=$filediff->GetToHash()}
+	 {/if}
+	 {$fromfilename}{if $fromfilename != $tofilename} -&gt; {$tofilename}{/if}
+       {/if}
+       </a>
+       </li>
+       {/foreach}
+       </ul>
+     </div>
+
+     <div class="SBSContent">
+   {/if}
+
    {* Diff each file changed *}
    {foreach from=$treediff item=filediff}
+     <div class="diffBlob" id="{$filediff->GetFromHash()}_{$filediff->GetToHash()}">
      <div class="diff_info">
      {if ($filediff->GetStatus() == 'D') || ($filediff->GetStatus() == 'M')}
        {assign var=localfromtype value=$filediff->GetFromFileType(1)}
@@ -50,8 +91,22 @@
        {/if}
      {/if}
      </div>
-     {include file='filediff.tpl' diff=$filediff->GetDiff('', true, true)}
+     {if $sidebyside}
+        {include file='filediffsidebyside.tpl' diffsplit=$filediff->GetDiffSplit()}
+     {else}
+        {include file='filediff.tpl' diff=$filediff->GetDiff('', true, true)}
+     {/if}
+     </div>
    {/foreach}
+
+   {if $sidebyside && ($treediff->Count() > 1)}
+     </div>
+     <div class="SBSFooter"></div>
+
+    </div>
+   {/if}
+
+
  </div>
 
  {include file='footer.tpl'}

--- a/templates/filediffsidebyside.tpl
+++ b/templates/filediffsidebyside.tpl
@@ -10,19 +10,37 @@
  * @subpackage Template
  *}
 <table class="diffTable">
-  {foreach from=$diffsplit item=lineinfo}
-    {if $lineinfo[0]=='added'}
-    <tr class="diff-added">
-    {elseif $lineinfo[0]=='deleted'}
-    <tr class="diff-deleted">
-    {elseif $lineinfo[0]=='modified'}
-    <tr class="diff-modified">
-    {else}
-    <tr>
-    {/if}
-      <td class="diff-left">{if $lineinfo[1]}{$lineinfo[1]|escape}{else}&nbsp;{/if}</td>
-      <td>{if $lineinfo[2]}{$lineinfo[2]|escape}{else}&nbsp;{/if}</td>
-    </tr>
-  {/foreach}
+  {if $filediff->GetStatus() == 'D'}
+    {assign var=delblob value=$filediff->GetFromBlob()}
+    {foreach from=$delblob->GetData(true) item=blobline}
+      <tr class="diff-deleted">
+        <td class="diff-left">{$blobline|escape}</td>
+	<td>&nbsp;</td>
+      </tr>
+    {/foreach}
+  {elseif $filediff->GetStatus() == 'A'}
+    {assign var=newblob value=$filediff->GetToBlob()}
+    {foreach from=$newblob->GetData(true) item=blobline}
+      <tr class="diff-added">
+        <td class="diff-left">&nbsp;</td>
+	<td>{$blobline|escape}</td>
+      </tr>
+    {/foreach}
+  {else}
+    {foreach from=$diffsplit item=lineinfo}
+      {if $lineinfo[0]=='added'}
+      <tr class="diff-added">
+      {elseif $lineinfo[0]=='deleted'}
+      <tr class="diff-deleted">
+      {elseif $lineinfo[0]=='modified'}
+      <tr class="diff-modified">
+      {else}
+      <tr>
+      {/if}
+        <td class="diff-left">{if $lineinfo[1]}{$lineinfo[1]|escape}{else}&nbsp;{/if}</td>
+        <td>{if $lineinfo[2]}{$lineinfo[2]|escape}{else}&nbsp;{/if}</td>
+      </tr>
+    {/foreach}
+  {/if}
 </table>
 

comments