From 008322bec70a3a20bd00ed2219215a9f42fe0ca5 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Wed, 08 Jun 2011 20:48:07 -0400
Subject: [PATCH] Blame support finished, requires JGit 1.0.0. Checkstyle. Findbugs.

---
 src/com/gitblit/utils/StringUtils.java               |    4 
 src/com/gitblit/utils/TimeUtils.java                 |    4 
 tests/com/gitblit/tests/MetricUtilsTest.java         |    2 
 tests/com/gitblit/tests/StringUtilsTest.java         |    8 
 src/com/gitblit/wicket/pages/RepositoryPage.java     |    4 
 src/com/gitblit/wicket/panels/TagsPanel.java         |    6 
 src/com/gitblit/wicket/WicketUtils.java              |    4 
 src/com/gitblit/wicket/pages/CommitPage.java         |    4 
 tests/com/gitblit/tests/JGitUtilsTest.java           |   15 +-
 tests/com/gitblit/tests/TicgitUtilsTest.java         |    4 
 src/com/gitblit/wicket/pages/CommitDiffPage.java     |    3 
 src/com/gitblit/wicket/pages/BlobPage.java           |    3 
 src/com/gitblit/models/GitNote.java                  |    2 
 src/com/gitblit/utils/JGitUtils.java                 |    3 
 src/com/gitblit/wicket/pages/BlamePage.html          |    8 
 src/com/gitblit/wicket/pages/EditRepositoryPage.java |    6 
 src/com/gitblit/models/RefModel.java                 |    2 
 src/com/gitblit/wicket/panels/CommitLegendPanel.java |    2 
 src/com/gitblit/wicket/pages/BlamePage.java          |   86 +++++++++++-----
 src/com/gitblit/wicket/resources/gitblit.css         |   13 ++
 src/com/gitblit/wicket/pages/MetricsPage.java        |   10 +-
 src/com/gitblit/utils/DiffUtils.java                 |   26 +++++
 src/com/gitblit/wicket/panels/CommitHeaderPanel.java |    2 
 src/com/gitblit/wicket/panels/LogPanel.java          |    3 
 src/com/gitblit/utils/MetricUtils.java               |    3 
 src/com/gitblit/wicket/GitBlitWebApp.java            |    2 
 src/com/gitblit/models/AnnotatedLine.java            |   40 ++++++++
 src/com/gitblit/wicket/pages/SummaryPage.java        |   13 +-
 28 files changed, 203 insertions(+), 79 deletions(-)

diff --git a/src/com/gitblit/models/AnnotatedLine.java b/src/com/gitblit/models/AnnotatedLine.java
new file mode 100644
index 0000000..3d12476
--- /dev/null
+++ b/src/com/gitblit/models/AnnotatedLine.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.models;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import org.eclipse.jgit.revwalk.RevCommit;
+
+public class AnnotatedLine implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	public final String commitId;
+	public final String author;
+	public final Date when;
+	public final int lineNumber;
+	public final String data;
+
+	public AnnotatedLine(RevCommit commit, int lineNumber, String data) {
+		this.commitId = commit.getName();
+		this.author = commit.getAuthorIdent().getName();
+		this.when = commit.getAuthorIdent().getWhen();
+		this.lineNumber = lineNumber;
+		this.data = data;
+	}
+}
\ No newline at end of file
diff --git a/src/com/gitblit/models/GitNote.java b/src/com/gitblit/models/GitNote.java
index 0083637..3b66d89 100644
--- a/src/com/gitblit/models/GitNote.java
+++ b/src/com/gitblit/models/GitNote.java
@@ -22,7 +22,7 @@
 	private static final long serialVersionUID = 1L;
 
 	public String content;
-	public RefModel notesRef;	
+	public RefModel notesRef;
 
 	public GitNote(RefModel notesRef, String text) {
 		this.notesRef = notesRef;
diff --git a/src/com/gitblit/models/RefModel.java b/src/com/gitblit/models/RefModel.java
index 0b65c09..92c394c 100644
--- a/src/com/gitblit/models/RefModel.java
+++ b/src/com/gitblit/models/RefModel.java
@@ -127,7 +127,7 @@
 	public int compareTo(RefModel o) {
 		return getDate().compareTo(o.getDate());
 	}
-	
+
 	@Override
 	public String toString() {
 		return displayName;
diff --git a/src/com/gitblit/utils/DiffUtils.java b/src/com/gitblit/utils/DiffUtils.java
index fab9f43..0f56907 100644
--- a/src/com/gitblit/utils/DiffUtils.java
+++ b/src/com/gitblit/utils/DiffUtils.java
@@ -16,10 +16,14 @@
 package com.gitblit.utils;
 
 import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jgit.api.BlameCommand;
+import org.eclipse.jgit.blame.BlameResult;
 import org.eclipse.jgit.diff.DiffEntry;
 import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.diff.RawText;
 import org.eclipse.jgit.diff.RawTextComparator;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -29,6 +33,8 @@
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+import com.gitblit.models.AnnotatedLine;
 
 public class DiffUtils {
 
@@ -172,4 +178,24 @@
 		}
 		return null;
 	}
+
+	public static List<AnnotatedLine> blame(Repository r, String blobPath, String objectId) {
+		List<AnnotatedLine> lines = new ArrayList<AnnotatedLine>();
+		try {
+			BlameCommand blameCommand = new BlameCommand(r);
+			blameCommand.setFilePath(blobPath);
+			blameCommand.setStartCommit(r.resolve(objectId));
+			BlameResult blameResult = blameCommand.call();
+			RawText rawText = blameResult.getResultContents();
+			int length = rawText.size();
+			for (int i = 0; i < length; i++) {
+				RevCommit commit = blameResult.getSourceCommit(i);
+				AnnotatedLine line = new AnnotatedLine(commit, i + 1, rawText.getString(i));
+				lines.add(line);
+			}
+		} catch (Throwable t) {
+			LOGGER.error("failed to generate blame!", t);
+		}
+		return lines;
+	}
 }
diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java
index f6d7108..e8bb3bf 100644
--- a/src/com/gitblit/utils/JGitUtils.java
+++ b/src/com/gitblit/utils/JGitUtils.java
@@ -96,7 +96,8 @@
 		return r.toString().trim();
 	}
 
-	public static FetchResult cloneRepository(File repositoriesFolder, String name, String fromUrl) throws Exception {
+	public static FetchResult cloneRepository(File repositoriesFolder, String name, String fromUrl)
+			throws Exception {
 		FetchResult result = null;
 		if (!name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) {
 			name += Constants.DOT_GIT_EXT;
diff --git a/src/com/gitblit/utils/MetricUtils.java b/src/com/gitblit/utils/MetricUtils.java
index 492b024..85ef89f 100644
--- a/src/com/gitblit/utils/MetricUtils.java
+++ b/src/com/gitblit/utils/MetricUtils.java
@@ -39,7 +39,8 @@
 
 	private static final Logger LOGGER = LoggerFactory.getLogger(MetricUtils.class);
 
-	public static List<Metric> getDateMetrics(Repository r, String objectId, boolean includeTotal, String format) {
+	public static List<Metric> getDateMetrics(Repository r, String objectId, boolean includeTotal,
+			String format) {
 		Metric total = new Metric("TOTAL");
 		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
 		if (StringUtils.isEmpty(objectId)) {
diff --git a/src/com/gitblit/utils/StringUtils.java b/src/com/gitblit/utils/StringUtils.java
index 2a10a59..a881b58 100644
--- a/src/com/gitblit/utils/StringUtils.java
+++ b/src/com/gitblit/utils/StringUtils.java
@@ -134,8 +134,8 @@
 		}
 		return "";
 	}
-	
-	public static String getRelativePath(String basePath, String fullPath) {		
+
+	public static String getRelativePath(String basePath, String fullPath) {
 		String relativePath = fullPath.substring(basePath.length()).replace('\\', '/');
 		if (relativePath.charAt(0) == '/') {
 			relativePath = relativePath.substring(1);
diff --git a/src/com/gitblit/utils/TimeUtils.java b/src/com/gitblit/utils/TimeUtils.java
index 44f51a5..dcb60ab 100644
--- a/src/com/gitblit/utils/TimeUtils.java
+++ b/src/com/gitblit/utils/TimeUtils.java
@@ -30,14 +30,14 @@
 	public static final long ONEYEAR = ONEDAY * 365L;
 
 	public static boolean isToday(Date date) {
-		return (System.currentTimeMillis() - date.getTime()) < ONEDAY; 
+		return (System.currentTimeMillis() - date.getTime()) < ONEDAY;
 	}
 
 	public static boolean isYesterday(Date date) {
 		Calendar cal = Calendar.getInstance();
 		cal.setTime(date);
 		cal.add(Calendar.DATE, 1);
-		return (System.currentTimeMillis() - cal.getTimeInMillis()) < ONEDAY; 
+		return (System.currentTimeMillis() - cal.getTimeInMillis()) < ONEDAY;
 	}
 
 	public static String duration(int days) {
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.java b/src/com/gitblit/wicket/GitBlitWebApp.java
index daf1084..472a11d 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/com/gitblit/wicket/GitBlitWebApp.java
@@ -87,7 +87,7 @@
 		mount("/search", SearchPage.class);
 		mount("/metrics", MetricsPage.class, "r");
 		mount("/blame", BlamePage.class, "r", "h", "f");
-		
+
 		// setup ticket urls
 		mount("/tickets", TicketsPage.class, "r");
 		mount("/ticket", TicketPage.class, "r", "h", "f");
diff --git a/src/com/gitblit/wicket/WicketUtils.java b/src/com/gitblit/wicket/WicketUtils.java
index aef68ee..1d2a60f 100644
--- a/src/com/gitblit/wicket/WicketUtils.java
+++ b/src/com/gitblit/wicket/WicketUtils.java
@@ -299,7 +299,7 @@
 		WicketUtils.setHtmlTooltip(label, title);
 		return label;
 	}
-	
+
 	public static IChartData getChartData(Collection<Metric> metrics) {
 		final double[] commits = new double[metrics.size()];
 		final double[] tags = new double[metrics.size()];
@@ -334,7 +334,7 @@
 		}
 		return max;
 	}
-	
+
 	public static IChartData getScatterData(Collection<Metric> metrics) {
 		final double[] y = new double[metrics.size()];
 		final double[] x = new double[metrics.size()];
diff --git a/src/com/gitblit/wicket/pages/BlamePage.html b/src/com/gitblit/wicket/pages/BlamePage.html
index 27ed734..2e20c18 100644
--- a/src/com/gitblit/wicket/pages/BlamePage.html
+++ b/src/com/gitblit/wicket/pages/BlamePage.html
@@ -19,7 +19,7 @@
 	<div wicket:id="breadcrumbs">[breadcrumbs]</div>
 		
 	<!--  blame content -->
-	<table>
+	<table class="annotated" style="border-top: 0px; margin-bottom:5px;">
 		<tbody>
 			<tr>
 				<th>Commit</th>
@@ -27,9 +27,9 @@
 				<th>Data</th>
 			</tr>
 			<tr wicket:id="annotation">
-				<td><span wicket:id="commit"></span></td>
-				<td><span wicket:id="line"></span></td>
-				<td><span wicket:id="data"></span></td>
+				<td><span class="sha1" wicket:id="commit"></span></td>
+				<td><span class="sha1" wicket:id="line"></span></td>
+				<td><span class="sha1" wicket:id="data"></span></td>
 			</tr>
 		</tbody>
 	</table>
diff --git a/src/com/gitblit/wicket/pages/BlamePage.java b/src/com/gitblit/wicket/pages/BlamePage.java
index dc6aa1f..5462b88 100644
--- a/src/com/gitblit/wicket/pages/BlamePage.java
+++ b/src/com/gitblit/wicket/pages/BlamePage.java
@@ -15,8 +15,9 @@
  */
 package com.gitblit.wicket.pages;
 
-import java.io.Serializable;
-import java.util.Arrays;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
 import java.util.List;
 
 import org.apache.wicket.PageParameters;
@@ -28,6 +29,11 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.revwalk.RevCommit;
 
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.AnnotatedLine;
+import com.gitblit.utils.DiffUtils;
+import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.panels.CommitHeaderPanel;
 import com.gitblit.wicket.panels.LinkPanel;
@@ -43,8 +49,7 @@
 		RevCommit commit = getCommit();
 
 		add(new BookmarkablePageLink<Void>("blobLink", BlobPage.class,
-				WicketUtils.newPathParameter(repositoryName, objectId,
-							blobPath)));
+				WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
 		add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class,
 				WicketUtils.newObjectParameter(repositoryName, objectId)));
 		add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class,
@@ -60,38 +65,65 @@
 
 		add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
 
-		List<BlameLine> blame = Arrays.asList(new BlameLine("HEAD", "1", "Under Construction"));
-		ListDataProvider<BlameLine> blameDp = new ListDataProvider<BlameLine>(blame);
-		DataView<BlameLine> blameView = new DataView<BlameLine>("annotation", blameDp) {
+		String format = GitBlit.getString(Keys.web.datetimestampLongFormat,
+				"EEEE, MMMM d, yyyy h:mm a z");
+		final DateFormat df = new SimpleDateFormat(format);
+		df.setTimeZone(getTimeZone());
+		List<AnnotatedLine> lines = DiffUtils.blame(getRepository(), blobPath, objectId);
+		ListDataProvider<AnnotatedLine> blameDp = new ListDataProvider<AnnotatedLine>(lines);
+		DataView<AnnotatedLine> blameView = new DataView<AnnotatedLine>("annotation", blameDp) {
 			private static final long serialVersionUID = 1L;
+			private int count;
+			private String lastCommitId = "";
+			private boolean showInitials = true;
 
-			public void populateItem(final Item<BlameLine> item) {
-				BlameLine entry = item.getModelObject();
-				item.add(new LinkPanel("commit", "list", entry.objectId, CommitPage.class,
-						newCommitParameter(entry.objectId)));
-				item.add(new Label("line", entry.line));
-				item.add(new Label("data", entry.data));
+			public void populateItem(final Item<AnnotatedLine> item) {
+				AnnotatedLine entry = item.getModelObject();
+				item.add(new Label("line", "" + entry.lineNumber));
+				item.add(new Label("data", StringUtils.escapeForHtml(entry.data, true))
+						.setEscapeModelStrings(false));
+				if (!lastCommitId.equals(entry.commitId)) {
+					lastCommitId = entry.commitId;
+					count++;
+					// show the link for first line
+					LinkPanel commitLink = new LinkPanel("commit", null,
+							getShortObjectId(entry.commitId), CommitPage.class,
+							newCommitParameter(entry.commitId));
+					WicketUtils.setHtmlTooltip(commitLink,
+							MessageFormat.format("{0}, {1}", entry.author, df.format(entry.when)));
+					item.add(commitLink);
+					showInitials = true;
+				} else {
+					if (showInitials) {
+						showInitials = false;
+						// show author initials
+						item.add(new Label("commit", getInitials(entry.author)));
+					} else {
+						// hide the commit link until the next block
+						item.add(new Label("commit").setVisible(false));
+					}
+				}
+				if (count % 2 == 0) {
+					WicketUtils.setCssClass(item, "even");
+				} else {
+					WicketUtils.setCssClass(item, "odd");
+				}
 			}
 		};
 		add(blameView);
 	}
 
+	private String getInitials(String author) {
+		StringBuilder sb = new StringBuilder();
+		String[] chunks = author.split(" ");
+		for (String chunk : chunks) {
+			sb.append(chunk.charAt(0));
+		}
+		return sb.toString().toUpperCase();
+	}
+
 	@Override
 	protected String getPageName() {
 		return getString("gb.blame");
-	}
-	
-	private class BlameLine implements Serializable {
-		
-		private static final long serialVersionUID = 1L;
-		
-		final String objectId;
-		final String line;
-		final String data;
-		BlameLine(String objectId, String line, String data) {
-			this.objectId = objectId;
-			this.line = line;
-			this.data = data;
-		}
 	}
 }
diff --git a/src/com/gitblit/wicket/pages/BlobPage.java b/src/com/gitblit/wicket/pages/BlobPage.java
index 47c8ca8..1c43837 100644
--- a/src/com/gitblit/wicket/pages/BlobPage.java
+++ b/src/com/gitblit/wicket/pages/BlobPage.java
@@ -46,7 +46,8 @@
 			// blob by objectid
 
 			add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
-					WicketUtils.newPathParameter(repositoryName, objectId, blobPath)).setEnabled(false));
+					WicketUtils.newPathParameter(repositoryName, objectId, blobPath))
+					.setEnabled(false));
 			add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class).setEnabled(false));
 			add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
 					WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
diff --git a/src/com/gitblit/wicket/pages/CommitDiffPage.java b/src/com/gitblit/wicket/pages/CommitDiffPage.java
index 3abcf13..ecfcbac 100644
--- a/src/com/gitblit/wicket/pages/CommitDiffPage.java
+++ b/src/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -101,7 +101,8 @@
 				item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class,
 						newPathParameter(entry.path)));
 				item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
-						newPathParameter(entry.path)).setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+						newPathParameter(entry.path)).setEnabled(!entry.changeType
+						.equals(ChangeType.ADD)));
 
 				WicketUtils.setAlternatingBackground(item, counter);
 				counter++;
diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java
index d9ff1b7..0295600 100644
--- a/src/com/gitblit/wicket/pages/CommitPage.java
+++ b/src/com/gitblit/wicket/pages/CommitPage.java
@@ -65,8 +65,8 @@
 			add(new Label("parentLink", "none"));
 			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("parentLink", null, getShortObjectId(parents.get(0)),
+					CommitPage.class, newCommitParameter(parents.get(0))));
 			add(new LinkPanel("commitdiffLink", null, new StringResourceModel("gb.commitdiff",
 					this, null), CommitDiffPage.class, WicketUtils.newObjectParameter(
 					repositoryName, objectId)));
diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/com/gitblit/wicket/pages/EditRepositoryPage.java
index e5496a1..52ed548 100644
--- a/src/com/gitblit/wicket/pages/EditRepositoryPage.java
+++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -107,11 +107,11 @@
 					repositoryModel.name = repositoryModel.name.replace("//", "/");
 
 					// prohibit folder paths
-					if (repositoryModel.name.startsWith("/")) {						
+					if (repositoryModel.name.startsWith("/")) {
 						error("Leading root folder references (/) are prohibited.");
 						return;
 					}
-					if (repositoryModel.name.startsWith("../")) {						
+					if (repositoryModel.name.startsWith("../")) {
 						error("Relative folder references (../) are prohibited.");
 						return;
 					}
@@ -135,7 +135,7 @@
 							}
 						}
 					}
-					
+
 					// confirm access restriction selection
 					if (repositoryModel.accessRestriction == null) {
 						error("Please select access restriction!");
diff --git a/src/com/gitblit/wicket/pages/MetricsPage.java b/src/com/gitblit/wicket/pages/MetricsPage.java
index 9dd10d1..4d0cd4c 100644
--- a/src/com/gitblit/wicket/pages/MetricsPage.java
+++ b/src/com/gitblit/wicket/pages/MetricsPage.java
@@ -46,18 +46,18 @@
 public class MetricsPage extends RepositoryPage {
 
 	public MetricsPage(PageParameters params) {
-		super(params);				
+		super(params);
 		Repository r = getRepository();
 		add(new Label("branchTitle", objectId));
 		Metric metricsTotal = null;
 		List<Metric> metrics = MetricUtils.getDateMetrics(r, objectId, true, null);
 		metricsTotal = metrics.remove(0);
 		if (metricsTotal == null) {
-			add(new Label("branchStats", ""));			
+			add(new Label("branchStats", ""));
 		} else {
-			add(new Label("branchStats", MessageFormat.format(
-					"{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag,
-					TimeUtils.duration(metricsTotal.duration))));
+			add(new Label("branchStats",
+					MessageFormat.format("{0} commits and {1} tags in {2}", metricsTotal.count,
+							metricsTotal.tag, TimeUtils.duration(metricsTotal.duration))));
 		}
 		insertLinePlot("commitsChart", metrics);
 		insertBarPlot("dayOfWeekChart", getDayOfWeekMetrics(r, objectId));
diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java
index 3ceb9f5..e2120f7 100644
--- a/src/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/com/gitblit/wicket/pages/RepositoryPage.java
@@ -218,6 +218,10 @@
 		return commit;
 	}
 
+	protected String getShortObjectId(String objectId) {
+		return objectId.substring(0, 8);
+	}
+
 	protected void addRefs(Repository r, RevCommit c) {
 		add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r)));
 	}
diff --git a/src/com/gitblit/wicket/pages/SummaryPage.java b/src/com/gitblit/wicket/pages/SummaryPage.java
index 37b0bcf..e85901a 100644
--- a/src/com/gitblit/wicket/pages/SummaryPage.java
+++ b/src/com/gitblit/wicket/pages/SummaryPage.java
@@ -89,13 +89,14 @@
 		add(WicketUtils.createTimestampLabel("repositoryLastChange", JGitUtils.getLastChange(r),
 				getTimeZone()));
 		if (metricsTotal == null) {
-			add(new Label("branchStats", ""));			
+			add(new Label("branchStats", ""));
 		} else {
-			add(new Label("branchStats", MessageFormat.format(
-					"{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag,
-					TimeUtils.duration(metricsTotal.duration))));
+			add(new Label("branchStats",
+					MessageFormat.format("{0} commits and {1} tags in {2}", metricsTotal.count,
+							metricsTotal.tag, TimeUtils.duration(metricsTotal.duration))));
 		}
-		add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
+		add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class,
+				WicketUtils.newRepositoryParameter(repositoryName)));
 
 		List<String> repositoryUrls = new ArrayList<String>();
 
@@ -203,7 +204,7 @@
 					metrics.get(metrics.size() / 2).name, metrics.get(metrics.size() - 1).name });
 			provider.addAxis(dateAxis);
 
-			ChartAxis commitAxis = new ChartAxis(ChartAxisType.LEFT);			
+			ChartAxis commitAxis = new ChartAxis(ChartAxisType.LEFT);
 			commitAxis.setLabels(new String[] { "",
 					String.valueOf((int) WicketUtils.maxValue(metrics)) });
 			provider.addAxis(commitAxis);
diff --git a/src/com/gitblit/wicket/panels/CommitHeaderPanel.java b/src/com/gitblit/wicket/panels/CommitHeaderPanel.java
index 7d0ad0f..1f6ae56 100644
--- a/src/com/gitblit/wicket/panels/CommitHeaderPanel.java
+++ b/src/com/gitblit/wicket/panels/CommitHeaderPanel.java
@@ -32,7 +32,7 @@
 		add(new Label("author"));
 		add(new Label("date"));
 	}
-	
+
 	public CommitHeaderPanel(String id, String repositoryName, RevCommit c) {
 		super(id);
 		add(new LinkPanel("shortmessage", "title", c.getShortMessage(), CommitPage.class,
diff --git a/src/com/gitblit/wicket/panels/CommitLegendPanel.java b/src/com/gitblit/wicket/panels/CommitLegendPanel.java
index d875233..1e906ef 100644
--- a/src/com/gitblit/wicket/panels/CommitLegendPanel.java
+++ b/src/com/gitblit/wicket/panels/CommitLegendPanel.java
@@ -74,7 +74,7 @@
 		};
 		add(legendsView);
 	}
-	
+
 	protected Map<ChangeType, AtomicInteger> getChangedPathsStats(List<PathChangeModel> paths) {
 		Map<ChangeType, AtomicInteger> stats = new HashMap<ChangeType, AtomicInteger>();
 		for (PathChangeModel path : paths) {
diff --git a/src/com/gitblit/wicket/panels/LogPanel.java b/src/com/gitblit/wicket/panels/LogPanel.java
index 873d7d9..66fddd2 100644
--- a/src/com/gitblit/wicket/panels/LogPanel.java
+++ b/src/com/gitblit/wicket/panels/LogPanel.java
@@ -127,7 +127,8 @@
 				item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
 						.newObjectParameter(repositoryName, entry.getName())));
 				item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class, WicketUtils
-						.newObjectParameter(repositoryName, entry.getName())).setEnabled(entry.getParentCount() > 0));
+						.newObjectParameter(repositoryName, entry.getName())).setEnabled(entry
+						.getParentCount() > 0));
 				item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
 						.newObjectParameter(repositoryName, entry.getName())));
 
diff --git a/src/com/gitblit/wicket/panels/TagsPanel.java b/src/com/gitblit/wicket/panels/TagsPanel.java
index 4504c51..95bc857 100644
--- a/src/com/gitblit/wicket/panels/TagsPanel.java
+++ b/src/com/gitblit/wicket/panels/TagsPanel.java
@@ -44,7 +44,7 @@
 public class TagsPanel extends BasePanel {
 
 	private static final long serialVersionUID = 1L;
-	
+
 	private final boolean hasTags;
 
 	public TagsPanel(String wicketId, final String repositoryName, Repository r, final int maxCount) {
@@ -164,10 +164,10 @@
 			add(new LinkPanel("allTags", "link", new StringResourceModel("gb.allTags", this, null),
 					TagsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
 		}
-		
+
 		hasTags = tags.size() > 0;
 	}
-	
+
 	public TagsPanel hideIfEmpty() {
 		setVisible(hasTags);
 		return this;
diff --git a/src/com/gitblit/wicket/resources/gitblit.css b/src/com/gitblit/wicket/resources/gitblit.css
index 7c896df..4309458 100644
--- a/src/com/gitblit/wicket/resources/gitblit.css
+++ b/src/com/gitblit/wicket/resources/gitblit.css
@@ -633,6 +633,19 @@
 	border-left: 1px solid #ccc;
 }
 
+table.annotated {
+	width: 100%;
+	border: 1px solid #bbb;
+}
+
+table.annotated tr.even {
+	background-color: white;
+}
+
+table.annotated tr.odd {
+	background-color: #fdfbdf;
+}
+
 tr th a { padding-right: 15px; background-position: right; background-repeat:no-repeat; }
 tr th.wicket_orderDown a {background-image: url(arrow_down.png); }
 tr th.wicket_orderUp a { background-image: url(arrow_up.png); }
diff --git a/tests/com/gitblit/tests/JGitUtilsTest.java b/tests/com/gitblit/tests/JGitUtilsTest.java
index 277166c..1b57585 100644
--- a/tests/com/gitblit/tests/JGitUtilsTest.java
+++ b/tests/com/gitblit/tests/JGitUtilsTest.java
@@ -96,10 +96,11 @@
 
 	public void testCreateRepository() throws Exception {
 		String[] repositories = { "NewTestRepository.git", "NewTestRepository" };
-		for (String repositoryName : repositories) {			
+		for (String repositoryName : repositories) {
 			Repository repository = JGitUtils.createRepository(GitBlitSuite.REPOSITORIES,
 					repositoryName);
-			File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName), FS.DETECTED);
+			File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName),
+					FS.DETECTED);
 			assertTrue(repository != null);
 			assertFalse(JGitUtils.hasCommits(repository));
 			assertTrue(JGitUtils.getFirstCommit(repository, null) == null);
@@ -121,10 +122,11 @@
 			List<RefModel> list = entry.getValue();
 			for (RefModel ref : list) {
 				if (ref.displayName.equals("refs/tags/spearce-gpg-pub")) {
-					assertTrue(ref.getObjectId().getName().equals("8bbde7aacf771a9afb6992434f1ae413e010c6d8"));
+					assertTrue(ref.getObjectId().getName()
+							.equals("8bbde7aacf771a9afb6992434f1ae413e010c6d8"));
 					assertTrue(ref.getAuthorIdent().getEmailAddress().equals("spearce@spearce.org"));
 					assertTrue(ref.getShortMessage().startsWith("GPG key"));
-					assertTrue(ref.getFullMessage().startsWith("GPG key"));					
+					assertTrue(ref.getFullMessage().startsWith("GPG key"));
 					assertTrue(ref.getReferencedObjectType() == Constants.OBJ_BLOB);
 				} else if (ref.displayName.equals("refs/tags/v0.12.1")) {
 					assertTrue(ref.isAnnotatedTag());
@@ -168,13 +170,14 @@
 					+ model.getName().hashCode());
 		}
 		repository.close();
-		
+
 		repository = GitBlitSuite.getBluezGnomeRepository();
 		for (RefModel model : JGitUtils.getTags(repository, true, -1)) {
 			if (model.getObjectId().getName().equals("728643ec0c438c77e182898c2f2967dbfdc231c8")) {
 				assertFalse(model.isAnnotatedTag());
 				assertTrue(model.getAuthorIdent().getEmailAddress().equals("marcel@holtmann.org"));
-				assertTrue(model.getFullMessage().equals("Update changelog and bump version number\n"));
+				assertTrue(model.getFullMessage().equals(
+						"Update changelog and bump version number\n"));
 			}
 		}
 		repository.close();
diff --git a/tests/com/gitblit/tests/MetricUtilsTest.java b/tests/com/gitblit/tests/MetricUtilsTest.java
index db994b8..c9d007d 100644
--- a/tests/com/gitblit/tests/MetricUtilsTest.java
+++ b/tests/com/gitblit/tests/MetricUtilsTest.java
@@ -32,7 +32,7 @@
 		repository.close();
 		assertTrue("No date metrics found!", metrics.size() > 0);
 	}
-	
+
 	public void testAuthorMetrics() throws Exception {
 		Repository repository = GitBlitSuite.getHelloworldRepository();
 		List<Metric> byEmail = MetricUtils.getAuthorMetrics(repository, null, true);
diff --git a/tests/com/gitblit/tests/StringUtilsTest.java b/tests/com/gitblit/tests/StringUtilsTest.java
index 49938ae..df51a4b 100644
--- a/tests/com/gitblit/tests/StringUtilsTest.java
+++ b/tests/com/gitblit/tests/StringUtilsTest.java
@@ -38,10 +38,10 @@
 
 	public void testEscapeForHtml() throws Exception {
 		String input = "& < > \" \t";
-		String output_nochange = "&amp; &lt; &gt; &quot; \t";
-		String output_change = "&amp;&nbsp;&lt;&nbsp;&gt;&nbsp;&quot;&nbsp; &nbsp; &nbsp;";
-		assertTrue(StringUtils.escapeForHtml(input, false).equals(output_nochange));
-		assertTrue(StringUtils.escapeForHtml(input, true).equals(output_change));
+		String outputNoChange = "&amp; &lt; &gt; &quot; \t";
+		String outputChange = "&amp;&nbsp;&lt;&nbsp;&gt;&nbsp;&quot;&nbsp; &nbsp; &nbsp;";
+		assertTrue(StringUtils.escapeForHtml(input, false).equals(outputNoChange));
+		assertTrue(StringUtils.escapeForHtml(input, true).equals(outputChange));
 	}
 
 	public void testFlattenStrings() throws Exception {
diff --git a/tests/com/gitblit/tests/TicgitUtilsTest.java b/tests/com/gitblit/tests/TicgitUtilsTest.java
index 792c426..9f5a488 100644
--- a/tests/com/gitblit/tests/TicgitUtilsTest.java
+++ b/tests/com/gitblit/tests/TicgitUtilsTest.java
@@ -33,7 +33,7 @@
 		RefModel branch = TicgitUtils.getTicketsBranch(repository);
 		repository.close();
 		assertTrue("Ticgit branch does not exist!", branch != null);
-		
+
 		repository = GitBlitSuite.getHelloworldRepository();
 		branch = TicgitUtils.getTicketsBranch(repository);
 		repository.close();
@@ -60,7 +60,7 @@
 				assertTrue(commentA.hashCode() == commentA.text.hashCode());
 			}
 		}
-		
+
 		repository = GitBlitSuite.getHelloworldRepository();
 		List<TicketModel> ticketsC = TicgitUtils.getTickets(repository);
 		repository.close();

--
Gitblit v1.9.1