From 608ecec9dff2ea4ce8e2a1ea6fc8909a64492a68 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Wed, 13 Apr 2011 09:43:56 -0400
Subject: [PATCH] Patch formatter.  Ensure html escaping. css tweaks.

---
 src/com/gitblit/utils/HtmlDiffFormatter.java       |   23 +
 src/com/gitblit/utils/PatchFormatter.java          |  111 ++++++++
 src/com/gitblit/wicket/pages/TagPage.html          |    2 
 src/com/gitblit/wicket/pages/PatchPage.java        |   46 +++
 src/com/gitblit/wicket/pages/BlobDiffPage.java     |   44 +++
 src/com/gitblit/wicket/pages/RawPage.html          |   13 +
 src/com/gitblit/wicket/GitBlitWebApp.properties    |    4 
 src/com/gitblit/wicket/WicketUtils.java            |    3 
 src/com/gitblit/wicket/pages/CommitDiffPage.html   |   38 ++
 src/com/gitblit/wicket/pages/CommitPage.java       |    9 
 src/com/gitblit/wicket/pages/RawPage.java          |   91 +++++++
 src/com/gitblit/wicket/pages/BlobPage.html         |    2 
 src/com/gitblit/wicket/pages/PatchPage.html        |   13 +
 src/com/gitblit/wicket/pages/BlobDiffPage.html     |    2 
 src/com/gitblit/wicket/pages/CommitDiffPage.java   |   80 ++++++
 src/com/gitblit/wicket/pages/BlobPage.java         |    7 
 src/com/gitblit/utils/JGitUtils.java               |   78 +++++
 src/com/gitblit/wicket/pages/TreePage.java         |    6 
 src/com/gitblit/wicket/pages/TicGitPage.java       |    2 
 src/com/gitblit/wicket/pages/TreePage.html         |    2 
 src/com/gitblit/wicket/pages/CommitPage.html       |    4 
 src/com/gitblit/utils/Utils.java                   |   47 +++
 src/com/gitblit/wicket/models/TicGitTicket.java    |    5 
 src/com/gitblit/wicket/resources/gitblit.css       |   15 
 /dev/null                                          |   48 ---
 src/com/gitblit/wicket/pages/TicGitTicketPage.java |    4 
 src/com/gitblit/wicket/panels/LogPanel.java        |    4 
 src/com/gitblit/wicket/GitBlitWebApp.java          |   12 
 src/com/gitblit/tests/JGitUtilsTest.java           |    2 
 src/com/gitblit/wicket/pages/TagPage.java          |   15 +
 30 files changed, 624 insertions(+), 108 deletions(-)

diff --git a/src/com/gitblit/tests/JGitUtilsTest.java b/src/com/gitblit/tests/JGitUtilsTest.java
index 708f35a..abc733c 100644
--- a/src/com/gitblit/tests/JGitUtilsTest.java
+++ b/src/com/gitblit/tests/JGitUtilsTest.java
@@ -15,9 +15,9 @@
 import org.eclipse.jgit.storage.file.FileRepository;
 
 import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.TicGitTicket;
 import com.gitblit.wicket.models.PathModel;
 import com.gitblit.wicket.models.RefModel;
+import com.gitblit.wicket.models.TicGitTicket;
 
 public class JGitUtilsTest extends TestCase {
 
diff --git a/src/com/gitblit/utils/HtmlDiffFormatter.java b/src/com/gitblit/utils/HtmlDiffFormatter.java
index 7c37862..08c0c21 100644
--- a/src/com/gitblit/utils/HtmlDiffFormatter.java
+++ b/src/com/gitblit/utils/HtmlDiffFormatter.java
@@ -2,6 +2,7 @@
 
 import static org.eclipse.jgit.lib.Constants.encodeASCII;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 
@@ -9,6 +10,7 @@
 import org.eclipse.jgit.diff.RawText;
 
 public class HtmlDiffFormatter extends DiffFormatter {
+		
 	private final OutputStream os;
 
 	public HtmlDiffFormatter(OutputStream os) {
@@ -39,9 +41,6 @@
 		os.write(' ');
 		os.write('@');
 		os.write('@');
-		// TODO not sure if JGit can determine hunk section
-		//os.write("<span class=\"diff hunk_section\">".getBytes());
-		//os.write("</span>".getBytes());
 		os.write("</span></div>".getBytes());
 	}
 
@@ -82,18 +81,22 @@
 	protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException {
 		switch (prefix) {
 		case '+':
-			os.write("<div class=\"diff add\">".getBytes());
+			os.write("<span class=\"diff add\">".getBytes());
 			break;
 		case '-':
-			os.write("<div class=\"diff remove\">".getBytes());
+			os.write("<span class=\"diff remove\">".getBytes());
 			break;
 		}
 		os.write(prefix);
-		text.writeLine(os, cur);
+		ByteArrayOutputStream bos = new ByteArrayOutputStream();
+		text.writeLine(bos, cur);
+		String line = bos.toString();
+		line = Utils.escapeForHtml(line, false);
+		os.write(line.getBytes());
 		switch (prefix) {
 		case '+':
 		case '-':
-			os.write("</div>".getBytes());
+			os.write("</span>\n".getBytes());
 			break;
 		default:
 			os.write('\n');
@@ -115,14 +118,14 @@
 			if (line.startsWith("diff")) {
 				sb.append("<div class=\"diff header\">").append(line).append("</div>");
 			} else if (line.startsWith("---")) {
-				sb.append("<div class=\"diff remove\">").append(line).append("</div>");
+				sb.append("<span class=\"diff remove\">").append(line).append("</span><br/>");
 			} else if (line.startsWith("+++")) {
-				sb.append("<div class=\"diff add\">").append(line).append("</div>");
+				sb.append("<span class=\"diff add\">").append(line).append("</span><br/>");
 			} else {
 				sb.append(line).append('\n');
 			}
 		}
-		sb.append("</div>");
+		sb.append("</div>\n");
 		return sb.toString();
 	}
 }
diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java
index abecac4..69c46da 100644
--- a/src/com/gitblit/utils/JGitUtils.java
+++ b/src/com/gitblit/utils/JGitUtils.java
@@ -42,10 +42,11 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.gitblit.utils.TicGitTicket.Comment;
 import com.gitblit.wicket.models.Metric;
 import com.gitblit.wicket.models.PathModel;
 import com.gitblit.wicket.models.RefModel;
+import com.gitblit.wicket.models.TicGitTicket;
+import com.gitblit.wicket.models.TicGitTicket.Comment;
 
 public class JGitUtils {
 
@@ -98,12 +99,15 @@
 		return getCommitDate(commit);
 	}
 
-	public static RevCommit getCommit(Repository r, String commitId) {
+	public static RevCommit getCommit(Repository r, String objectId) {
 		RevCommit commit = null;
 		try {
-			ObjectId objectId = r.resolve(commitId);
+			if (objectId == null || objectId.trim().length() == 0) {
+				objectId = Constants.HEAD;
+			}
+			ObjectId object = r.resolve(objectId);
 			RevWalk walk = new RevWalk(r);
-			RevCommit rev = walk.parseCommit(objectId);
+			RevCommit rev = walk.parseCommit(object);
 			commit = rev;
 			walk.dispose();
 		} catch (Throwable t) {
@@ -205,8 +209,8 @@
 		return new String(getRawContent(r, (RevBlob) obj));
 	}
 
-	public static List<PathModel> getFilesInPath(Repository r, String basePath, String commitId) {
-		RevCommit commit = getCommit(r, commitId);
+	public static List<PathModel> getFilesInPath(Repository r, String basePath, String objectId) {
+		RevCommit commit = getCommit(r, objectId);
 		return getFilesInPath(r, basePath, commit);
 	}
 
@@ -298,11 +302,7 @@
 			walk.setRecursive(true);
 			walk.addTree(parentTree);
 			walk.addTree(commitTree);
-			if (path != null && path.trim().length() > 0) {
-				walk.setFilter(PathFilter.create(path));
-			} else {
-				walk.setFilter(TreeFilter.ANY_DIFF);
-			}
+			walk.setFilter(TreeFilter.ANY_DIFF);
 
 			final ByteArrayOutputStream os = new ByteArrayOutputStream();
 			RawTextComparator cmp = RawTextComparator.DEFAULT;
@@ -316,7 +316,16 @@
 			df.setDiffComparator(cmp);
 			df.setDetectRenames(true);
 			List<DiffEntry> diffs = df.scan(parentTree, commitTree);
-			df.format(diffs);
+			if (path != null && path.length() > 0) {
+				for (DiffEntry diff : diffs) {
+					if (diff.getNewPath().equalsIgnoreCase(path)) {
+						df.format(diff);
+						break;
+					}
+				}
+			} else {
+				df.format(diffs);
+			}
 			String diff;
 			if (outputHtml) {
 				// workaround for complex private methods in DiffFormatter
@@ -324,6 +333,50 @@
 			} else {
 				diff = os.toString();
 			}
+			df.flush();
+			return diff;
+		} catch (Throwable t) {
+			LOGGER.error("failed to generate commit diff!", t);
+		}
+		return null;
+	}
+	
+	public static String getCommitPatch(Repository r, RevCommit commit) {
+		return getCommitPatch(r, commit);
+	}
+	
+	public static String getCommitPatch(Repository r, RevCommit commit, String path) {
+		try {
+			final RevWalk rw = new RevWalk(r);
+			RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
+			RevTree parentTree = parent.getTree();
+			RevTree commitTree = commit.getTree();
+
+			final TreeWalk walk = new TreeWalk(r);
+			walk.reset();
+			walk.setRecursive(true);
+			walk.addTree(parentTree);
+			walk.addTree(commitTree);
+			walk.setFilter(TreeFilter.ANY_DIFF);
+
+			final ByteArrayOutputStream os = new ByteArrayOutputStream();
+			RawTextComparator cmp = RawTextComparator.DEFAULT;
+			PatchFormatter df = new PatchFormatter(os);
+			df.setRepository(r);
+			df.setDiffComparator(cmp);
+			df.setDetectRenames(true);
+			List<DiffEntry> diffs = df.scan(parentTree, commitTree);
+			if (path != null && path.length() > 0) {
+				for (DiffEntry diff : diffs) {
+					if (diff.getNewPath().equalsIgnoreCase(path)) {
+						df.format(diff);
+						break;
+					}
+				}
+			} else {
+				df.format(diffs);
+			}
+			String diff = df.getPatch(commit);
 			df.flush();
 			return diff;
 		} catch (Throwable t) {
@@ -523,6 +576,7 @@
 	}
 
 	public static List<Metric> getDateMetrics(Repository r) {
+		final List<RefModel> tags = getTags(r, -1);
 		final Map<String, Metric> map = new HashMap<String, Metric>();
 		try {
 			DateFormat df = new SimpleDateFormat("yyyy-MM");
diff --git a/src/com/gitblit/utils/PatchFormatter.java b/src/com/gitblit/utils/PatchFormatter.java
new file mode 100644
index 0000000..f019ce4
--- /dev/null
+++ b/src/com/gitblit/utils/PatchFormatter.java
@@ -0,0 +1,111 @@
+package com.gitblit.utils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.diff.RawText;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.Constants;
+
+public class PatchFormatter extends DiffFormatter {
+
+	private final OutputStream os;
+
+	private PatchTouple currentTouple = null;
+
+	Map<String, PatchTouple> changes = new HashMap<String, PatchTouple>();
+
+	public PatchFormatter(OutputStream os) {
+		super(os);
+		this.os = os;
+	}
+
+	public void format(DiffEntry entry) throws IOException {
+		currentTouple = new PatchTouple();
+		changes.put(entry.getNewPath(), currentTouple);
+		super.format(entry);
+	}
+
+	@Override
+	protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException {
+		switch (prefix) {
+		case '+':
+			currentTouple.insertions++;
+			break;
+		case '-':
+			currentTouple.deletions++;
+			break;
+		}
+		super.writeLine(prefix, text, cur);
+	}
+
+	public String getPatch(RevCommit commit) {
+		StringBuilder patch = new StringBuilder();
+		// hard-code the mon sep 17 2001 date string.
+		// I have no idea why that is there. it seems to be a constant.
+		patch.append("From " + commit.getName() + " Mon Sep 17 00:00:00 2001" + "\n");
+		patch.append("From: " + JGitUtils.getDisplayName(commit.getAuthorIdent()) + "\n");
+		patch.append("Date: " + (new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(new Date(commit.getCommitTime() * 1000l))) + "\n");
+		patch.append("Subject: [PATCH] " + commit.getShortMessage() + "\n");
+		patch.append("\n");
+		patch.append("---");
+		int maxPathLen = 0;
+		int files = 0;
+		int insertions = 0;
+		int deletions = 0;
+		for (String path : changes.keySet()) {
+			if (path.length() > maxPathLen) {
+				maxPathLen = path.length();
+			}
+			PatchTouple touple = changes.get(path);
+			files++;
+			insertions += touple.insertions;
+			deletions += touple.deletions;
+		}
+		int columns = 60;
+		int total = insertions + deletions;
+		int unit = total / columns + (total % columns > 0 ? 1 : 0);
+		if (unit == 0) {
+			unit = 1;
+		}
+		for (String path : changes.keySet()) {
+			PatchTouple touple = changes.get(path);
+			patch.append("\n " + Utils.rightPad(path, maxPathLen, ' ') + " | " + Utils.leftPad("" + touple.total(), 4, ' ') + " " + touple.relativeScale(unit));
+		}
+		patch.append(MessageFormat.format("\n {0} files changed, {1} insertions(+), {2} deletions(-)\n\n", files, insertions, deletions));
+		patch.append(os.toString());
+		patch.append("\n--\n");
+		patch.append(Constants.getRunningVersion());
+		return patch.toString();
+	}
+
+	private class PatchTouple {
+		int insertions = 0;
+		int deletions = 0;
+
+		int total() {
+			return insertions + deletions;
+		}
+
+		String relativeScale(int unit) {
+			int plus = (insertions / unit);
+			int minus = (deletions / unit);
+			StringBuilder sb = new StringBuilder();
+			for (int i = 0; i < plus; i++) {
+				sb.append('+');
+			}
+			for (int i = 0; i < minus; i++) {
+				sb.append('-');
+			}
+			return sb.toString();
+		}
+	}
+}
diff --git a/src/com/gitblit/utils/Utils.java b/src/com/gitblit/utils/Utils.java
index bef41b2..c742fb6 100644
--- a/src/com/gitblit/utils/Utils.java
+++ b/src/com/gitblit/utils/Utils.java
@@ -116,4 +116,51 @@
 		}
 		return ago;
 	}
+	
+	public static String leftPad(String input, int length, char pad) {
+		if (input.length() < length) {
+			StringBuilder sb = new StringBuilder();
+			for (int i = 0, len = length - input.length(); i < len; i++) {
+				sb.append(pad);
+			}
+			sb.append(input);
+			return sb.toString();
+		}
+		return input;
+	}
+	
+	public static String rightPad(String input, int length, char pad) {
+		if (input.length() < length) {
+			StringBuilder sb = new StringBuilder();
+			sb.append(input);
+			for (int i = 0, len = length - input.length(); i < len; i++) {
+				sb.append(pad);
+			}			
+			return sb.toString();
+		}
+		return input;
+	}
+	
+	public static String escapeForHtml(String inStr, boolean changeSpace) {
+		StringBuffer retStr = new StringBuffer();
+		int i = 0;
+		while (i < inStr.length()) {
+			if (inStr.charAt(i) == '&') {
+				retStr.append("&amp;");
+			} else if (inStr.charAt(i) == '<') {
+				retStr.append("&lt;");
+			} else if (inStr.charAt(i) == '>') {
+				retStr.append("&gt;");
+			} else if (inStr.charAt(i) == '\"') {
+				retStr.append("&quot;");
+			} else if (changeSpace && inStr.charAt(i) == ' ') {
+				retStr.append("&nbsp;");
+			} else if (changeSpace && inStr.charAt(i) == '\t') {
+				retStr.append(" &nbsp; &nbsp;");
+			} else
+				retStr.append(inStr.charAt(i));
+			i++;
+		}
+		return retStr.toString();
+	}
 }
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.java b/src/com/gitblit/wicket/GitBlitWebApp.java
index 90a0371..b869a05 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/com/gitblit/wicket/GitBlitWebApp.java
@@ -28,12 +28,15 @@
 import com.gitblit.StoredSettings;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.wicket.models.RepositoryModel;
+import com.gitblit.wicket.pages.BlobDiffPage;
 import com.gitblit.wicket.pages.BlobPage;
 import com.gitblit.wicket.pages.BranchesPage;
 import com.gitblit.wicket.pages.CommitPage;
-import com.gitblit.wicket.pages.DiffPage;
-import com.gitblit.wicket.pages.RepositoriesPage;
+import com.gitblit.wicket.pages.CommitDiffPage;
 import com.gitblit.wicket.pages.LogPage;
+import com.gitblit.wicket.pages.PatchPage;
+import com.gitblit.wicket.pages.RawPage;
+import com.gitblit.wicket.pages.RepositoriesPage;
 import com.gitblit.wicket.pages.SummaryPage;
 import com.gitblit.wicket.pages.TagPage;
 import com.gitblit.wicket.pages.TagsPage;
@@ -70,7 +73,10 @@
 		mount(new MixedParamUrlCodingStrategy("/tag", TagPage.class, new String[] { "r", "h" }));
 		mount(new MixedParamUrlCodingStrategy("/tree", TreePage.class, new String[] { "r", "h", "f" }));
 		mount(new MixedParamUrlCodingStrategy("/blob", BlobPage.class, new String[] { "r", "h", "f" }));
-		mount(new MixedParamUrlCodingStrategy("/diff", DiffPage.class, new String[] { "r", "h", "f" }));
+		mount(new MixedParamUrlCodingStrategy("/raw", RawPage.class, new String[] { "r", "h", "f" }));
+		mount(new MixedParamUrlCodingStrategy("/blobdiff", BlobDiffPage.class, new String[] { "r", "h", "f" }));
+		mount(new MixedParamUrlCodingStrategy("/commitdiff", CommitDiffPage.class, new String[] { "r", "h" }));
+		mount(new MixedParamUrlCodingStrategy("/patch", PatchPage.class, new String[] { "r", "h", "f" }));
 		
 		// setup extended urls
 		mount(new MixedParamUrlCodingStrategy("/ticgit", TicGitPage.class, new String[] { "p" }));
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties
index 1d7e7ba..273ff5a 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -38,4 +38,6 @@
 gb.pageFirst = first
 gb.pagePrevious prev
 gb.pageNext = next
-gb.parent = parent
\ No newline at end of file
+gb.parent = parent
+gb.head = HEAD
+gb.blame = blame
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/WicketUtils.java b/src/com/gitblit/wicket/WicketUtils.java
index 3a5ab92..65270a2 100644
--- a/src/com/gitblit/wicket/WicketUtils.java
+++ b/src/com/gitblit/wicket/WicketUtils.java
@@ -10,6 +10,7 @@
 import org.apache.wicket.PageParameters;
 import org.apache.wicket.behavior.SimpleAttributeModifier;
 import org.apache.wicket.markup.html.basic.Label;
+import org.eclipse.jgit.lib.Constants;
 
 import com.gitblit.StoredSettings;
 import com.gitblit.utils.Utils;
@@ -108,7 +109,7 @@
 	}
 
 	public static String getObject(PageParameters params) {
-		return params.getString("h", "");
+		return params.getString("h", Constants.HEAD);
 	}
 
 	public static String getPath(PageParameters params) {
diff --git a/src/com/gitblit/utils/TicGitTicket.java b/src/com/gitblit/wicket/models/TicGitTicket.java
similarity index 94%
rename from src/com/gitblit/utils/TicGitTicket.java
rename to src/com/gitblit/wicket/models/TicGitTicket.java
index 48491a6..50f1e08 100644
--- a/src/com/gitblit/utils/TicGitTicket.java
+++ b/src/com/gitblit/wicket/models/TicGitTicket.java
@@ -1,13 +1,10 @@
-package com.gitblit.utils;
+package com.gitblit.wicket.models;
 
 import java.io.Serializable;
 import java.text.ParseException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Date;
 import java.util.List;
-
-import com.gitblit.wicket.models.PathModel;
 
 public class TicGitTicket implements Serializable, Comparable<TicGitTicket> {
 
diff --git a/src/com/gitblit/wicket/pages/DiffPage.html b/src/com/gitblit/wicket/pages/BlobDiffPage.html
similarity index 66%
rename from src/com/gitblit/wicket/pages/DiffPage.html
rename to src/com/gitblit/wicket/pages/BlobDiffPage.html
index 6a5d21b..2267b37 100644
--- a/src/com/gitblit/wicket/pages/DiffPage.html
+++ b/src/com/gitblit/wicket/pages/BlobDiffPage.html
@@ -12,7 +12,7 @@
 	
 	<!-- blob nav links -->	
 	<div class="page_nav2">
-		<span wicket:id="historyLink">[history link]</span> | <span wicket:id="rawLink">[raw link]</span> | <span wicket:id="headLink">[head link]</span>
+		<span wicket:id="blameLink">[blame link]</span> | <span wicket:id="historyLink">[history link]</span> | <a wicket:id="patchLink"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="commitLink"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="commitDiffLink"><wicket:message key="gb.commitdiff"></wicket:message></a>
 	</div>	
 	
 	<!-- shortlog header -->
diff --git a/src/com/gitblit/wicket/pages/BlobDiffPage.java b/src/com/gitblit/wicket/pages/BlobDiffPage.java
new file mode 100644
index 0000000..17d7896
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/BlobDiffPage.java
@@ -0,0 +1,44 @@
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.LinkPanel;
+import com.gitblit.wicket.RepositoryPage;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
+
+public class BlobDiffPage extends RepositoryPage {
+
+	public BlobDiffPage(PageParameters params) {
+		super(params);
+
+		final String blobPath = WicketUtils.getPath(params);
+
+		Repository r = getRepository();
+		RevCommit commit = JGitUtils.getCommit(r, objectId);
+		String diff = JGitUtils.getCommitDiff(r, commit, blobPath, true);
+		add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+		add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
+		add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
+
+		// diff page links
+		add(new Label("blameLink", getString("gb.blame")));
+		add(new Label("historyLink", getString("gb.history")));
+
+		add(new LinkPanel("shortlog", "title", commit.getShortMessage(), CommitPage.class, newCommitParameter()));
+
+		add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
+		
+		add(new Label("diffText", diff).setEscapeModelStrings(false));
+	}
+	
+	@Override
+	protected String getPageName() {
+		return getString("gb.diff");
+	}
+}
diff --git a/src/com/gitblit/wicket/pages/BlobPage.html b/src/com/gitblit/wicket/pages/BlobPage.html
index 82ba320..393470a 100644
--- a/src/com/gitblit/wicket/pages/BlobPage.html
+++ b/src/com/gitblit/wicket/pages/BlobPage.html
@@ -21,7 +21,7 @@
 	
 		<!-- blob nav links -->	
 		<div class="page_nav2">
-			<span wicket:id="historyLink">[history link]</span> | <span wicket:id="rawLink">[raw link]</span> | <span wicket:id="headLink">[head link]</span>
+			<span wicket:id="blameLink">[blame link]</span> | <span wicket:id="historyLink">[history link]</span> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a>
 		</div>	
 	
 		<!-- shortlog header -->
diff --git a/src/com/gitblit/wicket/pages/BlobPage.java b/src/com/gitblit/wicket/pages/BlobPage.java
index 7d7259a..0c112d0 100644
--- a/src/com/gitblit/wicket/pages/BlobPage.java
+++ b/src/com/gitblit/wicket/pages/BlobPage.java
@@ -6,6 +6,8 @@
 import org.apache.wicket.Component;
 import org.apache.wicket.PageParameters;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 
@@ -28,9 +30,10 @@
 		RevCommit commit = JGitUtils.getCommit(r, objectId);
 
 		// blob page links
+		add(new Label("blameLink", getString("gb.blame")));
 		add(new Label("historyLink", getString("gb.history")));
-		add(new Label("rawLink", getString("gb.raw")));
-		add(new Label("headLink", "HEAD"));
+		add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+		add(new BookmarkablePageLink<Void>("headLink", BlobPage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath)));
 
 		add(new LinkPanel("shortlog", "title", commit.getShortMessage(), CommitPage.class, newCommitParameter()));
 
diff --git a/src/com/gitblit/wicket/pages/CommitDiffPage.html b/src/com/gitblit/wicket/pages/CommitDiffPage.html
new file mode 100644
index 0000000..2759de8
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/CommitDiffPage.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"  
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  
+      xml:lang="en"  
+      lang="en"> 
+
+<body>
+<wicket:extend>
+
+	<!-- page nav links -->	
+	<div wicket:id="pageLinks">[page links]</div>
+	
+	<!-- commitdiff nav links -->	
+	<div class="page_nav2">
+		<wicket:message key="gb.parent"></wicket:message>: <span wicket:id="parentLink">[parent link]</span> | <a wicket:id="patchLink"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="commitLink"><wicket:message key="gb.commit"></wicket:message></a>
+	</div>	
+	
+	<!-- shortlog header -->
+	<div class="header" wicket:id="shortlog">[shortlog header]</div>
+
+	<!-- changed paths -->
+	<table style="margin-top:10px;border-top:1px solid #bbb;" class="pretty">
+		<tr wicket:id="changedPath">
+			<td class="path"><span wicket:id="pathName">[commit path]</span></td>			
+			<td>
+				<div class="link">
+					<a wicket:id="patch"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
+				</div>
+			</td>
+		</tr>
+	</table>
+	
+	<!--  diff content -->
+	<pre wicket:id="diffText">[diff text]</pre>
+	
+</wicket:extend>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/CommitDiffPage.java b/src/com/gitblit/wicket/pages/CommitDiffPage.java
new file mode 100644
index 0000000..7f6d347
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -0,0 +1,80 @@
+package com.gitblit.wicket.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.LinkPanel;
+import com.gitblit.wicket.RepositoryPage;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.models.PathModel;
+
+public class CommitDiffPage extends RepositoryPage {
+
+	public CommitDiffPage(PageParameters params) {
+		super(params);
+
+		Repository r = getRepository();
+		RevCommit commit = JGitUtils.getCommit(r, objectId);
+		String diff = JGitUtils.getCommitDiff(r, commit, true);		
+		
+		List<String> parents = new ArrayList<String>();
+		if (commit.getParentCount() > 0) {
+			for (RevCommit parent : commit.getParents()) {
+				parents.add(parent.name());
+			}
+		}
+		
+		// commit page links
+		if (parents.size() == 0) {
+			add(new Label("parentLink", "none"));
+		} else {
+			add(new LinkPanel("parentLink", null, parents.get(0).substring(0, 8), CommitDiffPage.class, newCommitParameter(parents.get(0))));
+		}
+		add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
+		add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
+
+		add(new LinkPanel("shortlog", "title", commit.getShortMessage(), CommitPage.class, newCommitParameter()));
+
+		// changed paths list
+		List<PathModel> paths  = JGitUtils.getFilesInCommit(r, commit);
+		ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);
+		DataView<PathModel> pathsView = new DataView<PathModel>("changedPath", pathsDp) {
+			private static final long serialVersionUID = 1L;
+			int counter = 0;
+
+			public void populateItem(final Item<PathModel> item) {
+				final PathModel entry = item.getModelObject();
+				if (entry.isTree()) {
+					item.add(new LinkPanel("pathName", null, entry.path, TreePage.class, newPathParameter(entry.path)));
+				} else {
+					item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class, newPathParameter(entry.path)));
+				}
+				
+				item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, newPathParameter(entry.path)));
+				item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, newPathParameter(entry.path)));
+				item.add(new BookmarkablePageLink<Void>("blame", BlobPage.class).setEnabled(false));
+				item.add(new BookmarkablePageLink<Void>("history", BlobPage.class).setEnabled(false));
+
+				WicketUtils.setAlternatingBackground(item, counter);
+				counter++;
+			}
+		};
+		add(pathsView);
+		add(new Label("diffText", diff).setEscapeModelStrings(false));
+	}
+	
+	@Override
+	protected String getPageName() {
+		return getString("gb.commitdiff");
+	}
+}
diff --git a/src/com/gitblit/wicket/pages/CommitPage.html b/src/com/gitblit/wicket/pages/CommitPage.html
index 0614c06..a65c588 100644
--- a/src/com/gitblit/wicket/pages/CommitPage.html
+++ b/src/com/gitblit/wicket/pages/CommitPage.html
@@ -12,7 +12,7 @@
 	
 	<!-- commit nav links -->	
 	<div class="page_nav2">
-		<wicket:message key="gb.parent"></wicket:message>: <span wicket:id="parentLink">[parent link]</span> | <span wicket:id="patchLink">[patch link]</span> | <span wicket:id="commitdiffLink">[commitdiff link]</span>
+		<wicket:message key="gb.parent"></wicket:message>: <span wicket:id="parentLink">[parent link]</span> | <a wicket:id="patchLink"><wicket:message key="gb.patch"></wicket:message></a> | <span wicket:id="commitdiffLink">[commitdiff link]</span>
 	</div>	
 	
 	<!-- shortlog header -->
@@ -40,7 +40,7 @@
 			<td class="path"><span wicket:id="pathName">[commit path]</span></td>			
 			<td>
 				<div class="link">
-					<a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
+					<a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
 				</div>
 			</td>
 		</tr>
diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java
index a53e322..d906f07 100644
--- a/src/com/gitblit/wicket/pages/CommitPage.java
+++ b/src/com/gitblit/wicket/pages/CommitPage.java
@@ -41,11 +41,11 @@
 			add(new Label("commitdiffLink", getString("gb.commitdiff")));
 		} else {
 			add(new LinkPanel("parentLink", null, parents.get(0).substring(0, 8), CommitPage.class, newCommitParameter(parents.get(0))));
-			add(new LinkPanel("commitdiffLink", null, new StringResourceModel("gb.commitdiff", this, null), DiffPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
+			add(new LinkPanel("commitdiffLink", null, new StringResourceModel("gb.commitdiff", this, null), CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
 		}
-		add(new Label("patchLink", getString("gb.patch")));
+		add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
 		
-		add(new LinkPanel("shortlog", "title", c.getShortMessage(), LogPage.class, newRepositoryParameter()));
+		add(new LinkPanel("shortlog", "title", c.getShortMessage(), CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
 		
 		addRefs(r, c);
 
@@ -88,8 +88,9 @@
 					item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class, newPathParameter(entry.path)));
 				}
 				
-				item.add(new BookmarkablePageLink<Void>("diff", DiffPage.class, newPathParameter(entry.path)));
+				item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, newPathParameter(entry.path)));
 				item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, newPathParameter(entry.path)));
+				item.add(new BookmarkablePageLink<Void>("blame", BlobPage.class).setEnabled(false));
 				item.add(new BookmarkablePageLink<Void>("history", BlobPage.class).setEnabled(false));
 
 				WicketUtils.setAlternatingBackground(item, counter);
diff --git a/src/com/gitblit/wicket/pages/DiffPage.java b/src/com/gitblit/wicket/pages/DiffPage.java
deleted file mode 100644
index 1912c42..0000000
--- a/src/com/gitblit/wicket/pages/DiffPage.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.gitblit.wicket.pages;
-
-import org.apache.wicket.PageParameters;
-import org.apache.wicket.markup.html.basic.Label;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-
-import com.gitblit.utils.JGitUtils;
-import com.gitblit.wicket.LinkPanel;
-import com.gitblit.wicket.RepositoryPage;
-import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
-
-public class DiffPage extends RepositoryPage {
-
-	public DiffPage(PageParameters params) {
-		super(params);
-
-		final String blobPath = WicketUtils.getPath(params);
-
-		Repository r = getRepository();
-		RevCommit commit = JGitUtils.getCommit(r, objectId);
-		String diff;
-		if (blobPath != null && blobPath.length() > 0) {
-			// blob diff
-			diff = JGitUtils.getCommitDiff(r, commit, blobPath, true);
-		} else {
-			// commit diff
-			diff = JGitUtils.getCommitDiff(r, commit, true);
-		}
-
-		// diff page links
-		add(new Label("historyLink", getString("gb.history")));
-		add(new Label("rawLink", getString("gb.raw")));
-		add(new Label("headLink", "HEAD"));
-
-		add(new LinkPanel("shortlog", "title", commit.getShortMessage(), CommitPage.class, newCommitParameter()));
-
-		add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
-
-		add(new Label("diffText", diff).setEscapeModelStrings(false));
-	}
-	
-	@Override
-	protected String getPageName() {
-		return getString("gb.diff");
-	}
-}
diff --git a/src/com/gitblit/wicket/pages/PatchPage.html b/src/com/gitblit/wicket/pages/PatchPage.html
new file mode 100644
index 0000000..719a46d
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/PatchPage.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"  
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  
+      xml:lang="en"  
+      lang="en">
+      
+<body>
+
+		<!-- patch content -->
+		<pre style="border:0px;" wicket:id="patchText">[patch content]</pre>
+	
+</body>
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/PatchPage.java b/src/com/gitblit/wicket/pages/PatchPage.java
new file mode 100644
index 0000000..52790d6
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/PatchPage.java
@@ -0,0 +1,46 @@
+package com.gitblit.wicket.pages;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.GitBlitWebApp;
+import com.gitblit.wicket.WicketUtils;
+
+
+public class PatchPage extends WebPage {
+
+	public PatchPage(PageParameters params) {
+		super(params);
+
+		if (!params.containsKey("r")) {
+			error("Repository not specified!");
+			redirectToInterceptPage(new RepositoriesPage());
+		}
+		final String repositoryName = WicketUtils.getRepositoryName(params);
+		final String objectId = WicketUtils.getObject(params);		
+		final String blobPath = WicketUtils.getPath(params);
+
+		ServletWebRequest servletWebRequest = (ServletWebRequest) getRequest();
+		HttpServletRequest req = servletWebRequest.getHttpServletRequest();
+		req.getServerName();
+
+		Repository r = GitBlitWebApp.get().getRepository(req, repositoryName);
+		if (r == null) {
+			error("Can not load repository " + repositoryName);
+			redirectToInterceptPage(new RepositoriesPage());
+			return;
+		}
+
+		RevCommit commit = JGitUtils.getCommit(r, objectId);
+		String patch = JGitUtils.getCommitPatch(r, commit, blobPath);
+		add(new Label("patchText", patch));
+		r.close();
+	}	
+}
diff --git a/src/com/gitblit/wicket/pages/RawPage.html b/src/com/gitblit/wicket/pages/RawPage.html
new file mode 100644
index 0000000..30dd316
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/RawPage.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"  
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  
+      xml:lang="en"  
+      lang="en">
+      
+<body>
+
+		<!-- raw content -->
+		<pre style="border:0px;" wicket:id="rawText">[raw content]</pre>
+	
+</body>
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/RawPage.java b/src/com/gitblit/wicket/pages/RawPage.java
new file mode 100644
index 0000000..18b91bc
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/RawPage.java
@@ -0,0 +1,91 @@
+package com.gitblit.wicket.pages;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.StoredSettings;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.GitBlitWebApp;
+import com.gitblit.wicket.WicketUtils;
+
+
+public class RawPage extends WebPage {
+
+	public RawPage(PageParameters params) {
+		super(params);
+
+		if (!params.containsKey("r")) {
+			error("Repository not specified!");
+			redirectToInterceptPage(new RepositoriesPage());
+		}
+		final String repositoryName = WicketUtils.getRepositoryName(params);
+		final String objectId = WicketUtils.getObject(params);		
+		final String blobPath = WicketUtils.getPath(params);
+
+		ServletWebRequest servletWebRequest = (ServletWebRequest) getRequest();
+		HttpServletRequest req = servletWebRequest.getHttpServletRequest();
+		req.getServerName();
+
+		Repository r = GitBlitWebApp.get().getRepository(req, repositoryName);
+		if (r == null) {
+			error("Can not load repository " + repositoryName);
+			redirectToInterceptPage(new RepositoriesPage());
+			return;
+		}
+
+		RevCommit commit = JGitUtils.getCommit(r, objectId);
+
+		String extension = null;
+		if (blobPath.lastIndexOf('.') > -1) {
+			extension = blobPath.substring(blobPath.lastIndexOf('.') + 1);
+		}
+
+		// Map the extensions to types
+		Map<String, Integer> map = new HashMap<String, Integer>();
+		for (String ext : StoredSettings.getStrings("imageExtensions")) {
+			map.put(ext.toLowerCase(), 2);
+		}
+		for (String ext : StoredSettings.getStrings("binaryExtensions")) {
+			map.put(ext.toLowerCase(), 3);
+		}
+
+		if (extension != null) {
+			int type = 0;
+			if (map.containsKey(extension)) {
+				type = map.get(extension);
+			}
+			Component c = null;
+			switch (type) {
+			case 2:
+				// TODO image blobs
+				c = new Label("rawText", "Image File");
+				break;
+			case 3:
+				// TODO binary blobs
+				c = new Label("rawText", "Binary File");
+				break;
+			default:
+				// plain text
+				c = new Label("rawText", JGitUtils.getRawContentAsString(r, commit, blobPath));
+				WicketUtils.setCssClass(c, "plainprint");
+			}
+			add(c);
+		} else {
+			// plain text
+			Label blobLabel = new Label("rawText", JGitUtils.getRawContentAsString(r, commit, blobPath));
+			WicketUtils.setCssClass(blobLabel, "plainprint");
+			add(blobLabel);
+		}
+		r.close();
+	}	
+}
diff --git a/src/com/gitblit/wicket/pages/TagPage.html b/src/com/gitblit/wicket/pages/TagPage.html
index 34c240c..4eb3b4b 100644
--- a/src/com/gitblit/wicket/pages/TagPage.html
+++ b/src/com/gitblit/wicket/pages/TagPage.html
@@ -11,7 +11,7 @@
 	<div wicket:id="pageLinks">[page links]</div>
 
 	<!-- summary header -->
-	<div style="padding-top: 5px;" class="header" wicket:id="commit">[shortlog header]</div>
+	<div style="margin-top: 5px;" class="header" wicket:id="commit">[shortlog header]</div>
 	
 	<!-- commit info -->
 	<table class="plain">
diff --git a/src/com/gitblit/wicket/pages/TagPage.java b/src/com/gitblit/wicket/pages/TagPage.java
index 95be46c..622c2b1 100644
--- a/src/com/gitblit/wicket/pages/TagPage.java
+++ b/src/com/gitblit/wicket/pages/TagPage.java
@@ -1,5 +1,7 @@
 package com.gitblit.wicket.pages;
 
+import java.util.List;
+
 import org.apache.wicket.PageParameters;
 import org.apache.wicket.markup.html.basic.Label;
 import org.eclipse.jgit.lib.Repository;
@@ -9,6 +11,7 @@
 import com.gitblit.wicket.LinkPanel;
 import com.gitblit.wicket.RepositoryPage;
 import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.models.RefModel;
 
 
 public class TagPage extends RepositoryPage {
@@ -16,10 +19,18 @@
 	public TagPage(PageParameters params) {
 		super(params);
 
-		Repository r = getRepository();
+		Repository r = getRepository();		
+		List<RefModel> tags = JGitUtils.getTags(r, -1);
 		RevCommit c = JGitUtils.getCommit(r, objectId);
+		
+		String name = c.getName();
+		for (RefModel tag:tags) {
+			if (tag.getName().equals(objectId)) {
+				name = tag.getDisplayName();
+			}
+		}
 
-		add(new LinkPanel("commit", "title", c.getName(), CommitPage.class, newCommitParameter()));
+		add(new LinkPanel("commit", "title", name, CommitPage.class, newCommitParameter()));
 
 		add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class, newCommitParameter(c.getName())));
 		add(new Label("tagAuthor", JGitUtils.getDisplayName(c.getAuthorIdent())));
diff --git a/src/com/gitblit/wicket/pages/TicGitPage.java b/src/com/gitblit/wicket/pages/TicGitPage.java
index 2cfb772..2a042a6 100644
--- a/src/com/gitblit/wicket/pages/TicGitPage.java
+++ b/src/com/gitblit/wicket/pages/TicGitPage.java
@@ -9,11 +9,11 @@
 import org.apache.wicket.markup.repeater.data.ListDataProvider;
 
 import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.TicGitTicket;
 import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.LinkPanel;
 import com.gitblit.wicket.RepositoryPage;
 import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.models.TicGitTicket;
 
 public class TicGitPage extends RepositoryPage {
 
diff --git a/src/com/gitblit/wicket/pages/TicGitTicketPage.java b/src/com/gitblit/wicket/pages/TicGitTicketPage.java
index a27c8dd..fdd6ea0 100644
--- a/src/com/gitblit/wicket/pages/TicGitTicketPage.java
+++ b/src/com/gitblit/wicket/pages/TicGitTicketPage.java
@@ -8,11 +8,11 @@
 import org.eclipse.jgit.lib.Repository;
 
 import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.TicGitTicket;
-import com.gitblit.utils.TicGitTicket.Comment;
 import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.RepositoryPage;
 import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.models.TicGitTicket;
+import com.gitblit.wicket.models.TicGitTicket.Comment;
 
 public class TicGitTicketPage extends RepositoryPage {
 
diff --git a/src/com/gitblit/wicket/pages/TreePage.html b/src/com/gitblit/wicket/pages/TreePage.html
index 3eda96b..85fbdfc 100644
--- a/src/com/gitblit/wicket/pages/TreePage.html
+++ b/src/com/gitblit/wicket/pages/TreePage.html
@@ -12,7 +12,7 @@
 	
 	<!-- blob nav links -->	
 	<div class="page_nav2">
-		<span wicket:id="historyLink">[history link]</span> | <span wicket:id="headLink">[head link]</span>
+		<span wicket:id="historyLink">[history link]</span> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a>
 	</div>	
 	
 	<!-- shortlog header -->
diff --git a/src/com/gitblit/wicket/pages/TreePage.java b/src/com/gitblit/wicket/pages/TreePage.java
index 90e0a65..2bea987 100644
--- a/src/com/gitblit/wicket/pages/TreePage.java
+++ b/src/com/gitblit/wicket/pages/TreePage.java
@@ -9,6 +9,7 @@
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.markup.repeater.data.DataView;
 import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 
@@ -34,7 +35,8 @@
 
 		// tree page links
 		add(new Label("historyLink", getString("gb.history")));
-		add(new Label("headLink", "HEAD"));
+		add(new BookmarkablePageLink<Void>("headLink", TreePage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, basePath)));
+
 
 		add(new LinkPanel("shortlog", "title", commit.getShortMessage(), CommitPage.class, newCommitParameter()));
 
@@ -79,7 +81,7 @@
 						// links
 						Fragment links = new Fragment("pathLinks", "blobLinks", this);
 						links.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
-						links.add(new BookmarkablePageLink<Void>("raw", BlobPage.class).setEnabled(false));
+						links.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
 						links.add(new BookmarkablePageLink<Void>("history", BlobPage.class).setEnabled(false));
 						item.add(links);
 					}
diff --git a/src/com/gitblit/wicket/panels/LogPanel.java b/src/com/gitblit/wicket/panels/LogPanel.java
index ccc46fd..8ef1e3e 100644
--- a/src/com/gitblit/wicket/panels/LogPanel.java
+++ b/src/com/gitblit/wicket/panels/LogPanel.java
@@ -19,7 +19,7 @@
 import com.gitblit.wicket.LinkPanel;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.pages.CommitPage;
-import com.gitblit.wicket.pages.DiffPage;
+import com.gitblit.wicket.pages.CommitDiffPage;
 import com.gitblit.wicket.pages.LogPage;
 import com.gitblit.wicket.pages.SummaryPage;
 import com.gitblit.wicket.pages.TreePage;
@@ -82,7 +82,7 @@
 				item.add(new RefsPanel("commitRefs", repositoryName, entry, allRefs));
 
 				item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
-				item.add(new BookmarkablePageLink<Void>("diff", DiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
+				item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
 				item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
 
 				WicketUtils.setAlternatingBackground(item, counter);
diff --git a/src/com/gitblit/wicket/resources/gitblit.css b/src/com/gitblit/wicket/resources/gitblit.css
index c089560..da0c317 100644
--- a/src/com/gitblit/wicket/resources/gitblit.css
+++ b/src/com/gitblit/wicket/resources/gitblit.css
@@ -229,9 +229,9 @@
     background-color: #EDECE6;
     border-color: #D9D8D1;
     border-style: solid;
-    border-width: 1px 0;
+    border-width: 1px;
     font-weight: bold;
-    margin-top: 4px;
+    margin-top: 10px;
     padding: 4px 0 2px;
 }
 
@@ -241,17 +241,17 @@
     font-family: inherit;
 }
 
-div.diff.add {
+span.diff.add {
 	color: #008800;
 	font-family: inherit;
 }
 
-div.diff.remove {
+span.diff.remove {
 	color: #cc0000;
 	font-family: inherit;
 }
 
-div.diff.unchanged {
+span.diff.unchanged {
 	color: inherit;
 	font-family: inherit;
 }
@@ -425,11 +425,12 @@
 
 span .tagRef a, span .headRef a, span .remoteRef a, span .otherRef a {
 	text-decoration: none;
-	color: inherit;
+	color: black;
 }
 
 span .tagRef a:hover, span .headRef a:hover, span .remoteRef a:hover, span .otherRef a:hover {
-	color: inherit;
+	color: black;
+	text-decoration: underline;
 }
 
 span .otherRef {

--
Gitblit v1.9.1