From 98ce17280c49050213ad0ca628964eb174f89497 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Mon, 18 Apr 2011 16:53:23 -0400
Subject: [PATCH] Author and Committer search integration.

---
 src/com/gitblit/wicket/panels/SearchPanel.html  |   31 +++
 src/com/gitblit/wicket/RepositoryPage.java      |   37 ++++
 src/com/gitblit/wicket/panels/TagsPanel.html    |   18 +
 src/com/gitblit/wicket/pages/TagPage.html       |   12 +
 src/com/gitblit/wicket/pages/SearchPage.html    |   28 +++
 src/com/gitblit/wicket/pages/CommitPage.html    |   16 +
 src/com/gitblit/wicket/resources/gitblit.css    |   11 +
 src/com/gitblit/wicket/panels/TagsPanel.java    |   17 +
 src/com/gitblit/wicket/panels/BasePanel.java    |   11 +
 gitblit.properties                              |    2 
 src/com/gitblit/wicket/GitBlitWebApp.properties |    5 
 src/com/gitblit/wicket/WicketUtils.java         |   25 ++
 src/com/gitblit/wicket/pages/CommitPage.java    |    9 
 src/com/gitblit/wicket/panels/HistoryPanel.java |    9 
 src/com/gitblit/wicket/pages/SearchPage.java    |   42 +++++
 src/com/gitblit/wicket/panels/SearchPanel.java  |  102 ++++++++++++
 src/com/gitblit/wicket/panels/LogPanel.java     |    9 
 src/com/gitblit/wicket/GitBlitWebApp.java       |    4 
 src/com/gitblit/wicket/pages/TagPage.java       |    4 
 src/com/gitblit/Constants.java                  |    2 
 src/com/gitblit/utils/JGitUtils.java            |   85 +++++++++
 21 files changed, 442 insertions(+), 37 deletions(-)

diff --git a/gitblit.properties b/gitblit.properties
index 93183eb..ecefedf 100644
--- a/gitblit.properties
+++ b/gitblit.properties
@@ -48,7 +48,7 @@
 
 # This is the message display above the repositories table.
 # This can point to a file with HTML content.
-web.repositoriesMessage = Welcome to Git:Blit!<br>A quick and easy way to host your own Git repositories.<br>Built with <a href="http://eclipse.org/jgit">JGit</a>, <a href="http://wicket.apache.org">Wicket</a>, <a href="http://code.google.com/p/google-code-prettify/">google-code-prettify</a>, <a href="http://eclipse.org/jetty">Jetty</a>, <a href="http://www.slf4j.org">SLF4J</a>, <a href="http://logging.apache.org/log4j">Log4j</a>, and <a href="http://jcommander.org">JCommander</a>.
+web.repositoriesMessage = Welcome to Git:Blit!<br>A quick and easy way to host your own Git repositories.<br>Built with <a href="http://eclipse.org/jgit">JGit</a>, <a href="http://wicket.apache.org">Wicket</a>, <a href="http://eclipse.org/jetty">Jetty</a>, <a href="http://www.slf4j.org">SLF4J</a>, <a href="http://logging.apache.org/log4j">Log4j</a>, <a href="http://code.google.com/p/google-code-prettify/">google-code-prettify</a>, and <a href="http://jcommander.org">JCommander</a>.
 
 # Use the client timezone when formatting dates.
 # This uses AJAX to determine the browser's timezone and enables Wicket 
diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java
index 75f1bad..d5fc1a4 100644
--- a/src/com/gitblit/Constants.java
+++ b/src/com/gitblit/Constants.java
@@ -4,7 +4,7 @@
 
 	public final static String NAME = "Git:Blit";
 
-	public final static String VERSION = "0.0.1";
+	public final static String VERSION = "0.1.0-SNAPSHOT";
 
 	public final static String ADMIN_ROLE = "admin";
 
diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java
index 5590ffe..3b69c22 100644
--- a/src/com/gitblit/utils/JGitUtils.java
+++ b/src/com/gitblit/utils/JGitUtils.java
@@ -20,6 +20,9 @@
 import org.eclipse.jgit.diff.DiffFormatter;
 import org.eclipse.jgit.diff.RawTextComparator;
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StopWalkException;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
@@ -34,6 +37,7 @@
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
@@ -306,7 +310,7 @@
 				final RevWalk rw = new RevWalk(r);
 				RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
 				rw.dispose();
-				baseTree = parent.getTree();	
+				baseTree = parent.getTree();
 			} else {
 				baseTree = baseCommit.getTree();
 			}
@@ -364,7 +368,7 @@
 	public static String getCommitPatch(Repository r, RevCommit commit, String path) {
 		return getCommitPatch(r, null, commit, path);
 	}
-	
+
 	public static String getCommitPatch(Repository r, RevCommit baseCommit, RevCommit commit, String path) {
 		try {
 			RevTree baseTree;
@@ -463,7 +467,7 @@
 	public static List<RevCommit> getRevLog(Repository r, String objectId, int offset, int maxCount) {
 		return getRevLog(r, objectId, null, offset, maxCount);
 	}
-	
+
 	public static List<RevCommit> getRevLog(Repository r, String objectId, String path, int offset, int maxCount) {
 		List<RevCommit> list = new ArrayList<RevCommit>();
 		try {
@@ -474,9 +478,7 @@
 			ObjectId object = r.resolve(objectId);
 			walk.markStart(walk.parseCommit(object));
 			if (!StringUtils.isEmpty(path)) {
-				TreeFilter filter = AndTreeFilter.create(PathFilterGroup
-						.createFromStrings(Collections.singleton(path)),
-						TreeFilter.ANY_DIFF);
+				TreeFilter filter = AndTreeFilter.create(PathFilterGroup.createFromStrings(Collections.singleton(path)), TreeFilter.ANY_DIFF);
 				walk.setTreeFilter(filter);
 			}
 			Iterable<RevCommit> revlog = walk;
@@ -506,6 +508,77 @@
 		return list;
 	}
 
+	public static enum SearchType {
+		AUTHOR, COMMITTER, COMMIT;
+
+		public static SearchType forName(String name) {
+			for (SearchType type : values()) {
+				if (type.name().equalsIgnoreCase(name)) {
+					return type;
+				}
+			}
+			return null;
+		}
+	}
+
+	public static List<RevCommit> searchRevlogs(Repository r, String objectId, String value, final SearchType type, int offset, int maxCount) {
+		final String lcValue = value.toLowerCase();
+		List<RevCommit> list = new ArrayList<RevCommit>();
+		try {
+			if (objectId == null || objectId.trim().length() == 0) {
+				objectId = Constants.HEAD;
+			}
+			RevWalk walk = new RevWalk(r);
+			walk.setRevFilter(new RevFilter() {
+
+				@Override
+				public RevFilter clone() {
+					return this;
+				}
+
+				@Override
+				public boolean include(RevWalk walker, RevCommit commit) throws StopWalkException, MissingObjectException, IncorrectObjectTypeException, IOException {
+					switch (type) {
+					case AUTHOR:
+						return (commit.getAuthorIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getAuthorIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
+					case COMMITTER:
+						return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1)|| (commit.getCommitterIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
+					case COMMIT:
+						return commit.getFullMessage().toLowerCase().indexOf(lcValue) > -1;
+					}
+					return false;
+				}
+
+			});
+			ObjectId object = r.resolve(objectId);
+			walk.markStart(walk.parseCommit(object));
+			Iterable<RevCommit> revlog = walk;
+			if (offset > 0) {
+				int count = 0;
+				for (RevCommit rev : revlog) {
+					count++;
+					if (count > offset) {
+						list.add(rev);
+						if (maxCount > 0 && list.size() == maxCount) {
+							break;
+						}
+					}
+				}
+			} else {
+				for (RevCommit rev : revlog) {
+					list.add(rev);
+					if (maxCount > 0 && list.size() == maxCount) {
+						break;
+					}
+				}
+			}
+			walk.dispose();
+		} catch (Throwable t) {
+			LOGGER.error("Failed to determine last change", t);
+		}
+		return list;
+	}
+
 	public static List<RefModel> getTags(Repository r, int maxCount) {
 		return getRefs(r, Constants.R_TAGS, maxCount);
 	}
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.java b/src/com/gitblit/wicket/GitBlitWebApp.java
index 74057c9..dc1e2c9 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/com/gitblit/wicket/GitBlitWebApp.java
@@ -22,11 +22,12 @@
 import com.gitblit.wicket.pages.PatchPage;
 import com.gitblit.wicket.pages.RawPage;
 import com.gitblit.wicket.pages.RepositoriesPage;
+import com.gitblit.wicket.pages.SearchPage;
 import com.gitblit.wicket.pages.SummaryPage;
 import com.gitblit.wicket.pages.TagPage;
 import com.gitblit.wicket.pages.TagsPage;
-import com.gitblit.wicket.pages.TicketsPage;
 import com.gitblit.wicket.pages.TicketPage;
+import com.gitblit.wicket.pages.TicketsPage;
 import com.gitblit.wicket.pages.TreePage;
 
 public class GitBlitWebApp extends WebApplication {
@@ -62,6 +63,7 @@
 		mount(new MixedParamUrlCodingStrategy("/commitdiff", CommitDiffPage.class, new String[] { "r", "h" }));
 		mount(new MixedParamUrlCodingStrategy("/patch", PatchPage.class, new String[] { "r", "h", "f" }));
 		mount(new MixedParamUrlCodingStrategy("/history", HistoryPage.class, new String[] { "r", "h", "f" }));
+		mount(new MixedParamUrlCodingStrategy("/search", SearchPage.class, new String[] { "r", "h", "a", "c" }));
 
 		// setup ticket urls
 		mount(new MixedParamUrlCodingStrategy("/tickets", TicketsPage.class, new String[] { "r" }));
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties
index 331fc29..ca75776 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -47,4 +47,7 @@
 gb.password = Password
 gb.tagger = tagger
 gb.moreHistory = more history...
-gb.difftocurrent = diff to current
\ No newline at end of file
+gb.difftocurrent = diff to current
+gb.search = search
+gb.searchForAuthor = Search for commits authored by
+gb.searchForCommitter = Search for commits committed by
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/RepositoryPage.java b/src/com/gitblit/wicket/RepositoryPage.java
index 7378543..6442bee 100644
--- a/src/com/gitblit/wicket/RepositoryPage.java
+++ b/src/com/gitblit/wicket/RepositoryPage.java
@@ -6,9 +6,12 @@
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.wicket.Component;
 import org.apache.wicket.PageParameters;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.slf4j.Logger;
@@ -18,7 +21,9 @@
 import com.gitblit.Keys;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.JGitUtils.SearchType;
 import com.gitblit.wicket.pages.RepositoriesPage;
+import com.gitblit.wicket.pages.SearchPage;
 import com.gitblit.wicket.panels.PageLinksPanel;
 import com.gitblit.wicket.panels.RefsPanel;
 
@@ -109,6 +114,38 @@
 
 	protected abstract String getPageName();
 
+	
+	protected Component createPersonPanel(String wicketId, PersonIdent identity, SearchType searchType) {
+		if (StringUtils.isEmpty(identity.getName()) || StringUtils.isEmpty(identity.getEmailAddress())) {
+			String value = identity.getName();
+			if (StringUtils.isEmpty(value)) {
+				value = identity.getEmailAddress();
+			}
+			Fragment partial = new Fragment(wicketId, "partialPersonIdent", this);
+			LinkPanel link = new LinkPanel("personName", "list", value, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType));
+			setPersonSearchTooltip(link, value, searchType);
+			partial.add(link);
+			return partial;
+		} else {
+			Fragment fullPerson = new Fragment(wicketId, "fullPersonIdent", this);
+			LinkPanel nameLink = new LinkPanel("personName", "list", identity.getName(), SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, identity.getName(), searchType));
+			setPersonSearchTooltip(nameLink, identity.getName(), searchType);
+			fullPerson.add(nameLink);
+			
+			LinkPanel addressLink = new LinkPanel("personAddress", "list", "<" + identity.getEmailAddress() + ">", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, identity.getEmailAddress(), searchType));
+			setPersonSearchTooltip(addressLink, identity.getEmailAddress(), searchType);
+			fullPerson.add(addressLink);
+			return fullPerson;
+		}
+	}
+	
+	protected void setPersonSearchTooltip(Component component, String value, SearchType searchType) {
+		if (searchType.equals(SearchType.AUTHOR)) {
+			WicketUtils.setHtmlTitle(component, getString("gb.searchForAuthor") + " " + value);
+		} else if (searchType.equals(SearchType.COMMITTER)) {
+			WicketUtils.setHtmlTitle(component, getString("gb.searchForCommitter") + " " + value);
+		}
+	}
 	@Override
 	protected void onBeforeRender() {
 		// dispose of repository object
diff --git a/src/com/gitblit/wicket/WicketUtils.java b/src/com/gitblit/wicket/WicketUtils.java
index 0a1c3a0..e474325 100644
--- a/src/com/gitblit/wicket/WicketUtils.java
+++ b/src/com/gitblit/wicket/WicketUtils.java
@@ -13,6 +13,7 @@
 
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
+import com.gitblit.utils.JGitUtils.SearchType;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.utils.TimeUtils;
 
@@ -81,7 +82,7 @@
 		}
 		return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",page=" + pageNumber);
 	}
-	
+
 	public static PageParameters newHistoryPageParameter(String repositoryName, String objectId, String path, int pageNumber) {
 		if (pageNumber <= 1) {
 			return newObjectParameter(repositoryName, objectId);
@@ -93,7 +94,17 @@
 		return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",f=" + path + ",hb=" + baseCommitId);
 	}
 
-	
+	public static PageParameters newSearchParameter(String repositoryName, String commitId, String search, SearchType type) {
+		if (StringUtils.isEmpty(commitId)) {
+			return new PageParameters("r=" + repositoryName + ",s=" + search + ",st=" + type.name());	
+		}
+		return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",s=" + search + ",st=" + type.name());
+	}
+
+	public static PageParameters newSearchParameter(String repositoryName, String commitId, String search, SearchType type, int pageNumber) {
+		return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",s=" + search + ",st=" + type.name() + ",page=" + pageNumber);
+	}
+
 	public static String getRepositoryName(PageParameters params) {
 		return params.getString("r", "");
 	}
@@ -105,11 +116,19 @@
 	public static String getPath(PageParameters params) {
 		return params.getString("f", null);
 	}
-	
+
 	public static String getBaseObjectId(PageParameters params) {
 		return params.getString("hb", null);
 	}
 
+	public static String getSearchString(PageParameters params) {
+		return params.getString("s", null);
+	}
+
+	public static String getSearchType(PageParameters params) {
+		return params.getString("st", null);
+	}
+
 	public static int getPage(PageParameters params) {
 		return params.getInt("page", 1); // index from 1
 	}
diff --git a/src/com/gitblit/wicket/pages/CommitPage.html b/src/com/gitblit/wicket/pages/CommitPage.html
index 8d06fc1..ec1b55d 100644
--- a/src/com/gitblit/wicket/pages/CommitPage.html
+++ b/src/com/gitblit/wicket/pages/CommitPage.html
@@ -21,10 +21,10 @@
 	<!-- commit info -->
 	<table class="plain">
 		<tr><th><wicket:message key="gb.refs">refs</wicket:message></th><td><div wicket:id="refsPanel">[references]</div></td></tr>
-		<tr><th><wicket:message key="gb.author">author</wicket:message></th><td><span wicket:id="commitAuthor">[author</span></td></tr>
-		<tr><th></th><td><span wicket:id="commitAuthorDate">[author date]</span></td></tr>
-		<tr><th><wicket:message key="gb.committer">committer</wicket:message></th><td><span wicket:id="commitCommitter">[committer]</span></td></tr>
-		<tr><th></th><td><span wicket:id="commitCommitterDate">[commit date]</span></td></tr>
+		<tr><th><wicket:message key="gb.author">author</wicket:message></th><td><span class="sha1" wicket:id="commitAuthor">[author</span></td></tr>
+		<tr><th></th><td><span class="sha1" wicket:id="commitAuthorDate">[author date]</span></td></tr>
+		<tr><th><wicket:message key="gb.committer">committer</wicket:message></th><td><span class="sha1" wicket:id="commitCommitter">[committer]</span></td></tr>
+		<tr><th></th><td><span class="sha1" wicket:id="commitCommitterDate">[commit date]</span></td></tr>
 		<tr><th><wicket:message key="gb.commit">commit</wicket:message></th><td><span class="sha1" wicket:id="commitId">[commit id]</span></td></tr>
 		<tr><th><wicket:message key="gb.tree">tree</wicket:message></th><td><span class="sha1" wicket:id="commitTree">[commit tree]</span></td></tr>
 		<tr><th valign="top"><wicket:message key="gb.parent">parent</wicket:message></th>
@@ -54,6 +54,14 @@
 		</tr>
 	</table>
 	
+	<wicket:fragment wicket:id="fullPersonIdent">
+		<span wicket:id="personName"></span><span wicket:id="personAddress"></span>
+	</wicket:fragment>
+	
+	<wicket:fragment wicket:id="partialPersonIdent">
+		<span wicket:id="personName"></span>
+	</wicket:fragment>
+	
 </wicket:extend>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java
index 63817ca..064a7fe 100644
--- a/src/com/gitblit/wicket/pages/CommitPage.java
+++ b/src/com/gitblit/wicket/pages/CommitPage.java
@@ -14,6 +14,7 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 
 import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.JGitUtils.SearchType;
 import com.gitblit.wicket.LinkPanel;
 import com.gitblit.wicket.RepositoryPage;
 import com.gitblit.wicket.WicketUtils;
@@ -48,10 +49,12 @@
 
 		addRefs(r, c);
 
-		add(new Label("commitAuthor", JGitUtils.getDisplayName(c.getAuthorIdent())));
+		// author
+		add(createPersonPanel("commitAuthor", c.getAuthorIdent(), SearchType.AUTHOR));
 		add(WicketUtils.createTimestampLabel("commitAuthorDate", c.getAuthorIdent().getWhen(), getTimeZone()));
 
-		add(new Label("commitCommitter", JGitUtils.getDisplayName(c.getCommitterIdent())));
+		// committer
+		add(createPersonPanel("commitCommitter", c.getCommitterIdent(), SearchType.COMMITTER));		
 		add(WicketUtils.createTimestampLabel("commitCommitterDate", c.getCommitterIdent().getWhen(), getTimeZone()));
 
 		add(new Label("commitId", c.getName()));
@@ -100,7 +103,7 @@
 		};
 		add(pathsView);
 	}
-
+	
 	@Override
 	protected String getPageName() {
 		return getString("gb.commit");
diff --git a/src/com/gitblit/wicket/pages/SearchPage.html b/src/com/gitblit/wicket/pages/SearchPage.html
new file mode 100644
index 0000000..7d2a537
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/SearchPage.html
@@ -0,0 +1,28 @@
+<!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>
+
+	<!-- pager links -->
+	<div style="padding-top:5px;padding-bottom:5px;">
+		<a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message></a> 
+	</div>
+	
+	<!-- history -->
+	<div style="margin-top:5px;" wicket:id="searchPanel">[search panel]</div>
+
+	<!-- pager links -->
+	<div style="padding-bottom:5px;">
+		<a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message></a> 
+	</div>
+
+</wicket:extend>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/SearchPage.java b/src/com/gitblit/wicket/pages/SearchPage.java
new file mode 100644
index 0000000..d0f12c2
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/SearchPage.java
@@ -0,0 +1,42 @@
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+
+import com.gitblit.utils.JGitUtils.SearchType;
+import com.gitblit.wicket.RepositoryPage;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.SearchPanel;
+
+public class SearchPage extends RepositoryPage {
+	
+	public SearchPage(PageParameters params) {
+		super(params);
+
+		String value = WicketUtils.getSearchString(params);
+		String type = WicketUtils.getSearchType(params);
+		SearchType searchType = SearchType.forName(type);
+		
+		int pageNumber = WicketUtils.getPage(params);
+		int prevPage = Math.max(0, pageNumber - 1);
+		int nextPage = pageNumber + 1;
+
+		SearchPanel search = new SearchPanel("searchPanel", repositoryName, objectId, value, searchType, getRepository(), -1, pageNumber - 1);
+		boolean hasMore = search.hasMore();
+		add(search);
+
+		add(new BookmarkablePageLink<Void>("firstPageTop", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType)).setEnabled(pageNumber > 1));
+		add(new BookmarkablePageLink<Void>("prevPageTop", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, prevPage)).setEnabled(pageNumber > 1));
+		add(new BookmarkablePageLink<Void>("nextPageTop", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, nextPage)).setEnabled(hasMore));
+
+		add(new BookmarkablePageLink<Void>("firstPageBottom", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType)).setEnabled(pageNumber > 1));
+		add(new BookmarkablePageLink<Void>("prevPageBottom", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, prevPage)).setEnabled(pageNumber > 1));
+		add(new BookmarkablePageLink<Void>("nextPageBottom", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, nextPage)).setEnabled(hasMore));
+
+	}
+
+	@Override
+	protected String getPageName() {
+		return getString("gb.search");
+	}
+}
diff --git a/src/com/gitblit/wicket/pages/TagPage.html b/src/com/gitblit/wicket/pages/TagPage.html
index cd2e19b..e410462 100644
--- a/src/com/gitblit/wicket/pages/TagPage.html
+++ b/src/com/gitblit/wicket/pages/TagPage.html
@@ -16,13 +16,21 @@
 	<!-- commit info -->
 	<table class="plain">
 		<tr><th><wicket:message key="gb.object">[object]</wicket:message></th><td><span class="sha1" wicket:id="tagId">[tag id]</span></td></tr>
-		<tr><th><wicket:message key="gb.tagger">[tagger]</wicket:message></th><td><span wicket:id="tagAuthor">[tag author]</span></td></tr>
-		<tr><th></th><td><span wicket:id="tagDate">[tag date]</span></td></tr>
+		<tr><th><wicket:message key="gb.tagger">[tagger]</wicket:message></th><td><span class="sha1" wicket:id="tagAuthor">[tag author]</span></td></tr>
+		<tr><th></th><td><span class="sha1" wicket:id="tagDate">[tag date]</span></td></tr>
 	</table>
 	
 	<!--  full message -->
 	<div style="border-bottom:0px;" class="commit_message" wicket:id="fullMessage">[tag full message]</div>
 	
+	<wicket:fragment wicket:id="fullPersonIdent">
+		<span wicket:id="personName"></span><span wicket:id="personAddress"></span>
+	</wicket:fragment>
+	
+	<wicket:fragment wicket:id="partialPersonIdent">
+		<span wicket:id="personName"></span>
+	</wicket:fragment>
+	
 </wicket:extend>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/TagPage.java b/src/com/gitblit/wicket/pages/TagPage.java
index d36c887..7448ae5 100644
--- a/src/com/gitblit/wicket/pages/TagPage.java
+++ b/src/com/gitblit/wicket/pages/TagPage.java
@@ -3,11 +3,11 @@
 import java.util.List;
 
 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.utils.JGitUtils.SearchType;
 import com.gitblit.wicket.LinkPanel;
 import com.gitblit.wicket.RepositoryPage;
 import com.gitblit.wicket.WicketUtils;
@@ -41,7 +41,7 @@
 			add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class, newCommitParameter(c.getName())));
 		}
 
-		add(new Label("tagAuthor", JGitUtils.getDisplayName(c.getAuthorIdent())));
+		add(createPersonPanel("tagAuthor", c.getAuthorIdent(), SearchType.AUTHOR));
 		add(WicketUtils.createTimestampLabel("tagDate", c.getAuthorIdent().getWhen(), getTimeZone()));
 
 		addFullText("fullMessage", c.getFullMessage(), true);
diff --git a/src/com/gitblit/wicket/panels/BasePanel.java b/src/com/gitblit/wicket/panels/BasePanel.java
index 6ddc0a0..8a168ab 100644
--- a/src/com/gitblit/wicket/panels/BasePanel.java
+++ b/src/com/gitblit/wicket/panels/BasePanel.java
@@ -2,11 +2,14 @@
 
 import java.util.TimeZone;
 
+import org.apache.wicket.Component;
 import org.apache.wicket.markup.html.panel.Panel;
 
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
+import com.gitblit.utils.JGitUtils.SearchType;
 import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
 
 public abstract class BasePanel extends Panel {
 
@@ -19,4 +22,12 @@
 	protected TimeZone getTimeZone() {
 		return GitBlit.self().settings().getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get().getTimezone() : TimeZone.getDefault();
 	}
+
+	protected void setPersonSearchTooltip(Component component, String value, SearchType searchType) {
+		if (searchType.equals(SearchType.AUTHOR)) {
+			WicketUtils.setHtmlTitle(component, getString("gb.searchForAuthor") + " " + value);
+		} else if (searchType.equals(SearchType.COMMITTER)) {
+			WicketUtils.setHtmlTitle(component, getString("gb.searchForCommitter") + " " + value);
+		}
+	}
 }
diff --git a/src/com/gitblit/wicket/panels/HistoryPanel.java b/src/com/gitblit/wicket/panels/HistoryPanel.java
index 00f4d53..dd1395a 100644
--- a/src/com/gitblit/wicket/panels/HistoryPanel.java
+++ b/src/com/gitblit/wicket/panels/HistoryPanel.java
@@ -19,6 +19,7 @@
 import com.gitblit.Keys;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.JGitUtils.SearchType;
 import com.gitblit.wicket.LinkPanel;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.models.PathModel;
@@ -28,6 +29,7 @@
 import com.gitblit.wicket.pages.CommitPage;
 import com.gitblit.wicket.pages.HistoryPage;
 import com.gitblit.wicket.pages.LogPage;
+import com.gitblit.wicket.pages.SearchPage;
 import com.gitblit.wicket.pages.TreePage;
 
 public class HistoryPanel extends BasePanel {
@@ -95,9 +97,12 @@
 
 				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone()));
 
+				// author search link
 				String author = entry.getAuthorIdent().getName();
-				item.add(WicketUtils.createAuthorLabel("commitAuthor", author));
-
+				LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, author, SearchType.AUTHOR));
+				setPersonSearchTooltip(authorLink, author, SearchType.AUTHOR);
+				item.add(authorLink);
+				
 				String shortMessage = entry.getShortMessage();
 				String trimmedMessage = StringUtils.trimShortLog(shortMessage);
 				LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject", trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName()));
diff --git a/src/com/gitblit/wicket/panels/LogPanel.java b/src/com/gitblit/wicket/panels/LogPanel.java
index 954af75..0457511 100644
--- a/src/com/gitblit/wicket/panels/LogPanel.java
+++ b/src/com/gitblit/wicket/panels/LogPanel.java
@@ -17,12 +17,14 @@
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
 import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.JGitUtils.SearchType;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.LinkPanel;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.pages.CommitDiffPage;
 import com.gitblit.wicket.pages.CommitPage;
 import com.gitblit.wicket.pages.LogPage;
+import com.gitblit.wicket.pages.SearchPage;
 import com.gitblit.wicket.pages.SummaryPage;
 import com.gitblit.wicket.pages.TreePage;
 
@@ -32,7 +34,7 @@
 	
 	private boolean hasMore = false;
 
-	public LogPanel(String wicketId, final String repositoryName, String objectId, Repository r, int limit, int pageOffset) {
+	public LogPanel(String wicketId, final String repositoryName, final String objectId, Repository r, int limit, int pageOffset) {
 		super(wicketId);
 		boolean pageResults = limit <= 0;
 		int itemsPerPage = GitBlit.self().settings().getInteger(Keys.web.logPageCommitCount, 50);
@@ -76,8 +78,11 @@
 
 				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone()));
 
+				// author search link
 				String author = entry.getAuthorIdent().getName();
-				item.add(WicketUtils.createAuthorLabel("commitAuthor", author));
+				LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, author, SearchType.AUTHOR));
+				setPersonSearchTooltip(authorLink, author, SearchType.AUTHOR);
+				item.add(authorLink);
 
 				String shortMessage = entry.getShortMessage();
 				String trimmedMessage = StringUtils.trimShortLog(shortMessage);
diff --git a/src/com/gitblit/wicket/panels/SearchPanel.html b/src/com/gitblit/wicket/panels/SearchPanel.html
new file mode 100644
index 0000000..e5b28da
--- /dev/null
+++ b/src/com/gitblit/wicket/panels/SearchPanel.html
@@ -0,0 +1,31 @@
+<!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:panel>
+
+	<!-- header -->	
+	<div class="header" wicket:id="header">[search header]</div>
+
+	<table style="width:100%" class="pretty">
+		<tbody>
+       		<tr wicket:id="commit">
+         		<td class="date"><span wicket:id="commitDate">[commit date]</span></td>
+         		<td class="author"><span wicket:id="commitAuthor">[commit author]</span></td>
+         		<td><div wicket:id="commitShortMessage">[commit short message]</div></td>
+         		<td class="rightAlign"><div wicket:id="commitRefs">[commit refs]</div></td>         		
+         		<td class="rightAlign">
+         			<span class="link">
+						<a wicket:id="commit"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="commitdiff"><wicket:message key="gb.commitdiff"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>
+					</span>
+				</td>
+       		</tr>
+    	</tbody>
+	</table>	
+	
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/panels/SearchPanel.java b/src/com/gitblit/wicket/panels/SearchPanel.java
new file mode 100644
index 0000000..6f4858b
--- /dev/null
+++ b/src/com/gitblit/wicket/panels/SearchPanel.java
@@ -0,0 +1,102 @@
+package com.gitblit.wicket.panels;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+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.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.JGitUtils.SearchType;
+import com.gitblit.wicket.LinkPanel;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.pages.CommitDiffPage;
+import com.gitblit.wicket.pages.CommitPage;
+import com.gitblit.wicket.pages.SearchPage;
+import com.gitblit.wicket.pages.TreePage;
+
+public class SearchPanel extends BasePanel {
+
+	private static final long serialVersionUID = 1L;
+
+	private boolean hasMore = false;
+
+	public SearchPanel(String wicketId, final String repositoryName, final String objectId, final String value, SearchType searchType, Repository r, int limit, int pageOffset) {
+		super(wicketId);
+		boolean pageResults = limit <= 0;
+		int itemsPerPage = GitBlit.self().settings().getInteger(Keys.web.logPageCommitCount, 50);
+		if (itemsPerPage <= 1) {
+			itemsPerPage = 50;
+		}
+
+		RevCommit commit = JGitUtils.getCommit(r, objectId);
+
+		final Map<ObjectId, List<String>> allRefs = JGitUtils.getAllRefs(r);
+		List<RevCommit> commits;
+		if (pageResults) {
+			// Paging result set
+			commits = JGitUtils.searchRevlogs(r, objectId, value, searchType, pageOffset * itemsPerPage, itemsPerPage);
+		} else {
+			// Fixed size result set
+			commits = JGitUtils.searchRevlogs(r, objectId, value, searchType, 0, limit);
+		}
+
+		// inaccurate way to determine if there are more commits.
+		// works unless commits.size() represents the exact end.
+		hasMore = commits.size() >= itemsPerPage;
+
+		// header
+		add(new LinkPanel("header", "title", commit.getShortMessage(), CommitPage.class, WicketUtils.newObjectParameter(repositoryName, commit.getName())));
+
+		ListDataProvider<RevCommit> dp = new ListDataProvider<RevCommit>(commits);
+		DataView<RevCommit> searchView = new DataView<RevCommit>("commit", dp) {
+			private static final long serialVersionUID = 1L;
+			int counter = 0;
+
+			public void populateItem(final Item<RevCommit> item) {
+				final RevCommit entry = item.getModelObject();
+				final Date date = JGitUtils.getCommitDate(entry);
+
+				item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone()));
+
+				// author search link
+				String author = entry.getAuthorIdent().getName();
+				LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, author, SearchType.AUTHOR));
+				setPersonSearchTooltip(authorLink, author, SearchType.AUTHOR);
+				item.add(authorLink);
+
+				String shortMessage = entry.getShortMessage();
+				String trimmedMessage = StringUtils.trimShortLog(shortMessage);
+				// TODO highlight matches
+				LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject", trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName()));
+				if (!shortMessage.equals(trimmedMessage)) {
+					WicketUtils.setHtmlTitle(shortlog, shortMessage);
+				}
+				item.add(shortlog);
+
+				item.add(new RefsPanel("commitRefs", repositoryName, entry, allRefs));
+
+				item.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
+				item.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
+				item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
+
+				WicketUtils.setAlternatingBackground(item, counter);
+				counter++;
+			}
+		};
+		add(searchView);
+	}
+
+	public boolean hasMore() {
+		return hasMore;
+	}
+}
diff --git a/src/com/gitblit/wicket/panels/TagsPanel.html b/src/com/gitblit/wicket/panels/TagsPanel.html
index 0255a72..278b7e9 100644
--- a/src/com/gitblit/wicket/panels/TagsPanel.html
+++ b/src/com/gitblit/wicket/panels/TagsPanel.html
@@ -16,9 +16,7 @@
     			<td><b><span wicket:id="tagName">[tag name]</span></b></td>
     			<td><span wicket:id="tagDescription">[tag description]</span></td>
     			<td class="rightAlign">
-    				<span class="link">
-						<a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="commit"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>
-					</span>
+    				<span wicket:id="tagLinks"></span>
 				</td>
     		</tr>
     	</tbody>
@@ -26,6 +24,20 @@
 	
 	<div wicket:id="allTags">[all tags]</div>	
 
+	<!--  annotated tag links -->
+	<wicket:fragment wicket:id="annotatedLinks">
+		<span class="link">
+			<a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="commit"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>
+		</span>
+	</wicket:fragment>
+	
+	<!-- lightweight tag links -->
+	<wicket:fragment wicket:id="lightweightLinks">
+		<span class="link">
+			<a wicket:id="commit"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>
+		</span>
+	</wicket:fragment>
+	
 </wicket:panel>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/panels/TagsPanel.java b/src/com/gitblit/wicket/panels/TagsPanel.java
index 979a275..0161668 100644
--- a/src/com/gitblit/wicket/panels/TagsPanel.java
+++ b/src/com/gitblit/wicket/panels/TagsPanel.java
@@ -4,6 +4,7 @@
 
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.markup.repeater.data.DataView;
 import org.apache.wicket.markup.repeater.data.ListDataProvider;
@@ -58,13 +59,19 @@
 					message = entry.getShortLog();
 				}
 				if (entry.isAnnotatedTag()) {
-					item.add(new LinkPanel("tagDescription", "list subject", message, TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())));
+					item.add(new LinkPanel("tagDescription", "list", message, TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())));
+					Fragment fragment = new Fragment("tagLinks", "annotatedLinks", this);
+					fragment.add(new BookmarkablePageLink<Void>("view", TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())).setEnabled(entry.isAnnotatedTag()));
+					fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getCommitId().getName())));
+					fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
+					item.add(fragment);
 				} else {
-					item.add(new LinkPanel("tagDescription", "list subject", message, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())));
+					item.add(new LinkPanel("tagDescription", "list", message, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())));
+					Fragment fragment = new Fragment("tagLinks", "lightweightLinks", this);
+					fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getCommitId().getName())));
+					fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
+					item.add(fragment);
 				}
-				item.add(new BookmarkablePageLink<Void>("view", TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())).setEnabled(entry.isAnnotatedTag()));
-				item.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getCommitId().getName())));
-				item.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
 
 				WicketUtils.setAlternatingBackground(item, counter);
 				counter++;
diff --git a/src/com/gitblit/wicket/resources/gitblit.css b/src/com/gitblit/wicket/resources/gitblit.css
index c5a416b..736dbb3 100644
--- a/src/com/gitblit/wicket/resources/gitblit.css
+++ b/src/com/gitblit/wicket/resources/gitblit.css
@@ -104,7 +104,7 @@
 	font-family: sans-serif;
 	font-weight: bold;
 	font-size: 150%;
-	color: #bbb;
+	color: #888;
 	background-color: #ffffff;
 }
 
@@ -129,6 +129,7 @@
 }
 
 div.page_footer {
+	clear: both;
 	height: 17px;
 	color: black;
 	background-color: #ffffff;
@@ -274,6 +275,14 @@
 	color: #000000;
 }
 
+a.list.subject {
+	font-weight: bold;
+}
+
+a.list.name {
+	font-weight: bold;	
+}
+
 a.list:hover {
 	text-decoration: underline;
 	color: #880000;

--
Gitblit v1.9.1