From 424fe1b372d225d65eb42e6125b24cbb982c5969 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Wed, 01 Jun 2011 19:51:41 -0400
Subject: [PATCH] Stats -> Metrics. Docs in distribution zip. Created MetricUtils.

---
 src/com/gitblit/wicket/pages/SummaryPage.html    |    2 
 src/com/gitblit/Build.java                       |    3 
 src/com/gitblit/BuildSite.java                   |   64 +++--
 src/com/gitblit/wicket/pages/RepositoryPage.java |    4 
 src/com/gitblit/wicket/pages/MetricsPage.java    |   16 
 src/com/gitblit/wicket/GitBlitWebApp.properties  |    2 
 tests/com/gitblit/tests/JGitUtilsTest.java       |    3 
 src/com/gitblit/wicket/pages/MetricsPage.html    |    0 
 src/com/gitblit/wicket/pages/RepositoryPage.html |    2 
 build.xml                                        |  138 ++++++++++---
 src/com/gitblit/utils/MetricUtils.java           |  179 +++++++++++++++++
 src/com/gitblit/wicket/GitBlitWebApp.java        |    4 
 docs/00_index.mkd                                |    2 
 src/com/gitblit/wicket/pages/SummaryPage.java    |    7 
 src/com/gitblit/utils/JGitUtils.java             |  144 --------------
 15 files changed, 346 insertions(+), 224 deletions(-)

diff --git a/build.xml b/build.xml
index 4b27910..7b22a6b 100644
--- a/build.xml
+++ b/build.xml
@@ -89,36 +89,6 @@
 			</manifest>
 		</jar>
 
-		<!-- Delete the deploy folder -->
-		<delete dir="${basedir}/deploy" />
-
-		<!-- Create deployment folder structure -->
-		<mkdir dir="${basedir}/deploy" />
-		<copy todir="${basedir}/deploy" file="${project.jar}" />
-		<copy todir="${basedir}/deploy">
-			<fileset dir="${basedir}/distrib">
-				<include name="**/*" />
-			</fileset>
-		</copy>
-
-		<!-- Create Zip deployment -->
-		<property name="distribution.zipfile" value="gitblit-${gb.version}.zip" />
-		<zip destfile="${distribution.zipfile}">
-			<fileset dir="${basedir}/deploy">
-				<include name="**/*" />
-			</fileset>
-		</zip>
-
-		<!-- Delete the deploy folder -->
-		<delete dir="${basedir}/deploy" />
-
-		<!-- Cleanup builds -->
-		<delete>
-			<fileset dir="${basedir}">
-				<include name="${project.jar}" />
-			</fileset>
-		</delete>
-
 		<!-- Build Site -->
 		<delete dir="${basedir}/site" />
 		<mkdir dir="${basedir}/site" />
@@ -195,7 +165,7 @@
 
 			<arg value="--alias" />
 			<arg value="index=overview" />
-			
+
 			<arg value="--alias" />
 			<arg value="properties=gitblit.properties" />
 
@@ -216,6 +186,112 @@
 
 		</java>
 
+		<!-- Delete the deploy folder -->
+		<delete dir="${basedir}/deploy" />
+
+		<!-- Create deployment folder structure -->
+		<mkdir dir="${basedir}/deploy" />
+		<copy todir="${basedir}/deploy" file="${project.jar}" />
+		<copy todir="${basedir}/deploy">
+			<fileset dir="${basedir}/distrib">
+				<include name="**/*" />
+			</fileset>
+		</copy>
+
+		<!-- Build Deployment Docs -->
+		<mkdir dir="${basedir}/deploy/docs" />
+		<copy todir="${basedir}/deploy/docs">
+			<!-- Copy selected Gitblit resources -->
+			<fileset dir="${basedir}/src/com/gitblit/wicket/resources">
+				<include name="background.png" />
+				<include name="gitblit.css" />
+				<include name="markdown.css" />
+				<include name="gitblt_25.png" />
+				<include name="gitblt-favicon.png" />
+				<include name="lock_go_16x16.png" />
+				<include name="lock_pull_16x16.png" />
+				<include name="shield_16x16.png" />
+				<include name="cold_16x16.png" />
+				<include name="bug_16x16.png" />
+				<include name="book_16x16.png" />
+				<include name="blank.png" />
+			</fileset>
+
+			<!-- Copy Doc images -->
+			<fileset dir="${basedir}/docs">
+				<include name="*.png" />
+			</fileset>
+		</copy>
+
+		<!-- Copy google-code-prettify -->
+		<mkdir dir="${basedir}/src/com/gitblit/wicket/pages/prettify" />
+		<copy todir="${basedir}/deploy/docs/prettify">
+			<fileset dir="${basedir}/src/com/gitblit/wicket/pages/prettify">
+				<exclude name="thumbs.db" />
+			</fileset>
+		</copy>
+
+		<!-- Build deployment doc pages -->
+		<java classpath="${project.build.dir}" classname="com.gitblit.BuildSite">
+			<classpath refid="master-classpath" />
+			<arg value="--sourceFolder" />
+			<arg value="${basedir}/docs" />
+
+			<arg value="--outputFolder" />
+			<arg value="${basedir}/deploy/docs" />
+
+			<arg value="--pageHeader" />
+			<arg value="${basedir}/docs/page_header.html" />
+
+			<arg value="--pageFooter" />
+			<arg value="${basedir}/docs/page_footer.html" />
+
+			<arg value="--skip" />
+			<arg value="screenshots" />
+
+			<arg value="--skip" />
+			<arg value="releases" />
+
+			<arg value="--alias" />
+			<arg value="index=overview" />
+
+			<arg value="--alias" />
+			<arg value="properties=gitblit.properties" />
+
+			<arg value="--substitute" />
+			<arg value="%VERSION%=${gb.version}" />
+
+			<arg value="--substitute" />
+			<arg value="%DISTRIBUTION%=${distribution.zipfile}" />
+
+			<arg value="--substitute" />
+			<arg value="%BUILDDATE%=${gb.buildDate}" />
+
+			<arg value="--substitute" />
+			<arg value="%JGIT%=${jgit.version}" />
+
+			<arg value="--load" />
+			<arg value="%PROPERTIES%=${basedir}/distrib/gitblit.properties" />
+
+		</java>
+
+		<!-- Create Zip deployment -->
+		<property name="distribution.zipfile" value="gitblit-${gb.version}.zip" />
+		<zip destfile="${distribution.zipfile}">
+			<fileset dir="${basedir}/deploy">
+				<include name="**/*" />
+			</fileset>
+		</zip>
+
+		<!-- Delete the deploy folder -->
+		<delete dir="${basedir}/deploy" />
+
+		<!-- Cleanup builds -->
+		<delete>
+			<fileset dir="${basedir}">
+				<include name="${project.jar}" />
+			</fileset>
+		</delete>
 		<!-- Cleanup -->
 		<delete dir="${project.build.dir}" />
 	</target>
diff --git a/docs/00_index.mkd b/docs/00_index.mkd
index 9ad1c4e..0211be6 100644
--- a/docs/00_index.mkd
+++ b/docs/00_index.mkd
@@ -101,10 +101,10 @@
 - [Log4j](http://logging.apache.org/log4j) (Apache 2.0) 
 - [JCommander](http://jcommander.org) (Apache 2.0)
 - [BouncyCastle](http://www.bouncycastle.org) (MIT/X11)
+- [JSch - Java Secure Channel](http://www.jcraft.com/jsch) (BSD)
 
 ### Other Build Dependencies
 - [Fancybox image viewer](http://fancybox.net) (MIT and GPL dual-licensed)
-- [JSch - Java Secure Channel](http://www.jcraft.com/jsch) (BSD)
 - [JUnit](http://junit.org) (Common Public License)
 
 ## Building from Source
diff --git a/src/com/gitblit/Build.java b/src/com/gitblit/Build.java
index a6d5bb0..3948fd1 100644
--- a/src/com/gitblit/Build.java
+++ b/src/com/gitblit/Build.java
@@ -60,6 +60,7 @@
 		downloadFromApache(MavenObject.MARKDOWNPAPERS, BuildType.RUNTIME);
 		downloadFromApache(MavenObject.BOUNCYCASTLE, BuildType.RUNTIME);
 		downloadFromApache(MavenObject.BOUNCYCASTLE_MAIL, BuildType.RUNTIME);
+		downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME);
 
 		downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);
 		downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.RUNTIME);
@@ -80,8 +81,6 @@
 		downloadFromApache(MavenObject.MARKDOWNPAPERS, BuildType.COMPILETIME);
 		downloadFromApache(MavenObject.BOUNCYCASTLE, BuildType.COMPILETIME);
 		downloadFromApache(MavenObject.BOUNCYCASTLE_MAIL, BuildType.COMPILETIME);
-
-		downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME);
 		downloadFromApache(MavenObject.JSCH, BuildType.COMPILETIME);
 
 		downloadFromEclipse(MavenObject.JGIT, BuildType.COMPILETIME);
diff --git a/src/com/gitblit/BuildSite.java b/src/com/gitblit/BuildSite.java
index 890cec1..d96ca6e 100644
--- a/src/com/gitblit/BuildSite.java
+++ b/src/com/gitblit/BuildSite.java
@@ -73,13 +73,15 @@
 		StringBuilder sb = new StringBuilder();
 		for (File file : markdownFiles) {
 			String documentName = getDocumentName(file);
-			String displayName = documentName;
-			if (aliasMap.containsKey(documentName)) {
-				displayName = aliasMap.get(documentName);
+			if (!params.skips.contains(documentName)) {
+				String displayName = documentName;
+				if (aliasMap.containsKey(documentName)) {
+					displayName = aliasMap.get(documentName);
+				}
+				String fileName = documentName + ".html";
+				sb.append(MessageFormat.format(linkPattern, fileName, displayName));
+				sb.append(" | ");
 			}
-			String fileName = documentName + ".html";
-			sb.append(MessageFormat.format(linkPattern, fileName, displayName));
-			sb.append(" | ");
 		}
 		sb.setLength(sb.length() - 3);
 		sb.trimToSize();
@@ -93,29 +95,32 @@
 		for (File file : markdownFiles) {
 			try {
 				String documentName = getDocumentName(file);
-				String fileName = documentName + ".html";
-				System.out.println(MessageFormat.format("  {0} => {1}", file.getName(), fileName));
-				InputStreamReader reader = new InputStreamReader(new FileInputStream(file),
-						Charset.forName("UTF-8"));
-				String content = MarkdownUtils.transformMarkdown(reader);
-				for (String token : params.substitutions) {
-					String[] kv = token.split("=");
-					content = content.replace(kv[0], kv[1]);
+				if (!params.skips.contains(documentName)) {
+					String fileName = documentName + ".html";
+					System.out.println(MessageFormat.format("  {0} => {1}", file.getName(),
+							fileName));
+					InputStreamReader reader = new InputStreamReader(new FileInputStream(file),
+							Charset.forName("UTF-8"));
+					String content = MarkdownUtils.transformMarkdown(reader);
+					for (String token : params.substitutions) {
+						String[] kv = token.split("=");
+						content = content.replace(kv[0], kv[1]);
+					}
+					for (String alias : params.loads) {
+						String[] kv = alias.split("=");
+						String loadedContent = readContent(new File(kv[1]), "\n");
+						loadedContent = StringUtils.escapeForHtml(loadedContent, false);
+						loadedContent = StringUtils.breakLinesForHtml(loadedContent);
+						content = content.replace(kv[0], loadedContent);
+					}
+					OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(
+							new File(destinationFolder, fileName)), Charset.forName("UTF-8"));
+					writer.write(header);
+					writer.write(content);
+					writer.write(footer);
+					reader.close();
+					writer.close();
 				}
-				for (String alias : params.loads) {
-					String[] kv = alias.split("=");
-					String loadedContent = readContent(new File(kv[1]), "\n");
-					loadedContent = StringUtils.escapeForHtml(loadedContent, false);
-					loadedContent = StringUtils.breakLinesForHtml(loadedContent);
-					content = content.replace(kv[0], loadedContent);
-				}
-				OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(
-						destinationFolder, fileName)), Charset.forName("UTF-8"));
-				writer.write(header);
-				writer.write(content);
-				writer.write(footer);
-				reader.close();
-				writer.close();
 			} catch (Throwable t) {
 				System.err.println("Failed to transform " + file.getName());
 				t.printStackTrace();
@@ -179,6 +184,9 @@
 		@Parameter(names = { "--pageFooter" }, description = "Page Footer HTML Snippet", required = true)
 		public String pageFooter;
 
+		@Parameter(names = { "--skip" }, description = "Filename to skip", required = false)
+		public List<String> skips = new ArrayList<String>();
+
 		@Parameter(names = { "--alias" }, description = "Filename=Linkname aliases", required = false)
 		public List<String> aliases = new ArrayList<String>();
 
diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java
index 4c7f14f..5f72c9a 100644
--- a/src/com/gitblit/utils/JGitUtils.java
+++ b/src/com/gitblit/utils/JGitUtils.java
@@ -21,9 +21,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
-import java.text.DateFormat;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -73,7 +71,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.gitblit.models.Metric;
 import com.gitblit.models.PathModel;
 import com.gitblit.models.PathModel.PathChangeModel;
 import com.gitblit.models.RefModel;
@@ -82,7 +79,7 @@
 
 public class JGitUtils {
 
-	private static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
+	static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
 
 	public static Repository createRepository(File repositoriesFolder, String name, boolean bare) {
 		Git git = Git.init().setDirectory(new File(repositoriesFolder, name)).setBare(bare).call();
@@ -779,145 +776,6 @@
 		}
 		return false;
 	}
-
-	public static List<Metric> getDateMetrics(Repository r, boolean includeTotal, String format) {
-		Metric total = new Metric("TOTAL");
-		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
-
-		if (hasCommits(r)) {			
-			try {
-				RevWalk walk = new RevWalk(r);
-				ObjectId object = r.resolve(Constants.HEAD);
-				RevCommit lastCommit = walk.parseCommit(object);
-				walk.markStart(lastCommit);
-				SimpleDateFormat df = new SimpleDateFormat(format);
-				Iterable<RevCommit> revlog = walk;
-				for (RevCommit rev : revlog) {
-					Date d = getCommitDate(rev);
-					String p = df.format(d);
-					if (!metricMap.containsKey(p)) {
-						metricMap.put(p, new Metric(p));
-					}
-					Metric m = metricMap.get(p);
-					m.count++;
-					total.count++;					
-				}
-			} catch (Throwable t) {
-				LOGGER.error("Failed to mine log history for metrics", t);
-			}
-		}
-		List<String> keys = new ArrayList<String>(metricMap.keySet());
-		Collections.sort(keys);
-		List<Metric> metrics = new ArrayList<Metric>();
-		for (String key : keys) {
-			metrics.add(metricMap.get(key));
-		}
-		if (includeTotal) {
-			metrics.add(0, total);
-		}
-		return metrics;
-	}
-
-	public static List<Metric> getDateMetrics(Repository r, boolean includeTotal) {
-		Metric total = new Metric("TOTAL");
-		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
-
-		if (hasCommits(r)) {
-			final List<RefModel> tags = getTags(r, -1);
-			final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();
-			for (RefModel tag : tags) {
-				tagMap.put(tag.getCommitId(), tag);
-			}
-			try {
-				RevWalk walk = new RevWalk(r);
-				ObjectId object = r.resolve(Constants.HEAD);
-
-				RevCommit firstCommit = getFirstCommit(r, Constants.HEAD);
-				RevCommit lastCommit = walk.parseCommit(object);
-				int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime())
-						/ (60 * 60 * 24);
-				total.duration = diffDays;
-				DateFormat df;
-				if (diffDays <= 90) {
-					// Days
-					df = new SimpleDateFormat("yyyy-MM-dd");
-				} else if (diffDays > 90 && diffDays < 365) {
-					// Weeks
-					df = new SimpleDateFormat("yyyy-MM (w)");
-				} else {
-					// Months
-					df = new SimpleDateFormat("yyyy-MM");
-				}
-				walk.markStart(lastCommit);
-
-				Iterable<RevCommit> revlog = walk;
-				for (RevCommit rev : revlog) {
-					Date d = getCommitDate(rev);
-					String p = df.format(d);
-					if (!metricMap.containsKey(p)) {
-						metricMap.put(p, new Metric(p));
-					}
-					Metric m = metricMap.get(p);
-					m.count++;
-					total.count++;
-					if (tagMap.containsKey(rev.getId())) {
-						m.tag++;
-						total.tag++;
-					}
-				}
-			} catch (Throwable t) {
-				LOGGER.error("Failed to mine log history for metrics", t);
-			}
-		}
-		List<String> keys = new ArrayList<String>(metricMap.keySet());
-		Collections.sort(keys);
-		List<Metric> metrics = new ArrayList<Metric>();
-		for (String key : keys) {
-			metrics.add(metricMap.get(key));
-		}
-		if (includeTotal) {
-			metrics.add(0, total);
-		}
-		return metrics;
-	}
-	
-	public static List<Metric> getAuthorMetrics(Repository r) {
-		Metric total = new Metric("TOTAL");
-		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
-
-		if (hasCommits(r)) {
-			try {
-				RevWalk walk = new RevWalk(r);
-				ObjectId object = r.resolve(Constants.HEAD);
-				RevCommit lastCommit = walk.parseCommit(object);
-				walk.markStart(lastCommit);
-
-				Iterable<RevCommit> revlog = walk;
-				for (RevCommit rev : revlog) {
-					String p = rev.getAuthorIdent().getName();
-					if (StringUtils.isEmpty(p)) {
-						p = rev.getAuthorIdent().getEmailAddress();
-					}
-					if (!metricMap.containsKey(p)) {
-						metricMap.put(p, new Metric(p));
-					}
-					Metric m = metricMap.get(p);
-					m.count++;
-					total.count++;
-				}
-			} catch (Throwable t) {
-				LOGGER.error("Failed to mine log history for metrics", t);
-			}
-		}
-		List<String> keys = new ArrayList<String>(metricMap.keySet());
-		Collections.sort(keys);
-		List<Metric> metrics = new ArrayList<Metric>();
-		for (String key : keys) {
-			metrics.add(metricMap.get(key));
-		}
-		return metrics;
-	}
-
 
 	public static RefModel getTicketsBranch(Repository r) {
 		RefModel ticgitBranch = null;
diff --git a/src/com/gitblit/utils/MetricUtils.java b/src/com/gitblit/utils/MetricUtils.java
new file mode 100644
index 0000000..b1da273
--- /dev/null
+++ b/src/com/gitblit/utils/MetricUtils.java
@@ -0,0 +1,179 @@
+/*
+ * 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.utils;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.models.Metric;
+import com.gitblit.models.RefModel;
+
+public class MetricUtils {
+
+	private static final Logger LOGGER = LoggerFactory.getLogger(MetricUtils.class);
+
+	public static List<Metric> getDateMetrics(Repository r, boolean includeTotal, String format) {
+		Metric total = new Metric("TOTAL");
+		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
+	
+		if (JGitUtils.hasCommits(r)) {			
+			try {
+				RevWalk walk = new RevWalk(r);
+				ObjectId object = r.resolve(Constants.HEAD);
+				RevCommit lastCommit = walk.parseCommit(object);
+				walk.markStart(lastCommit);
+				SimpleDateFormat df = new SimpleDateFormat(format);
+				Iterable<RevCommit> revlog = walk;
+				for (RevCommit rev : revlog) {
+					Date d = JGitUtils.getCommitDate(rev);
+					String p = df.format(d);
+					if (!metricMap.containsKey(p)) {
+						metricMap.put(p, new Metric(p));
+					}
+					Metric m = metricMap.get(p);
+					m.count++;
+					total.count++;					
+				}
+			} catch (Throwable t) {
+				JGitUtils.LOGGER.error("Failed to mine log history for metrics", t);
+			}
+		}
+		List<String> keys = new ArrayList<String>(metricMap.keySet());
+		Collections.sort(keys);
+		List<Metric> metrics = new ArrayList<Metric>();
+		for (String key : keys) {
+			metrics.add(metricMap.get(key));
+		}
+		if (includeTotal) {
+			metrics.add(0, total);
+		}
+		return metrics;
+	}
+
+	public static List<Metric> getDateMetrics(Repository r, boolean includeTotal) {
+		Metric total = new Metric("TOTAL");
+		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
+	
+		if (JGitUtils.hasCommits(r)) {
+			final List<RefModel> tags = JGitUtils.getTags(r, -1);
+			final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();
+			for (RefModel tag : tags) {
+				tagMap.put(tag.getCommitId(), tag);
+			}
+			try {
+				RevWalk walk = new RevWalk(r);
+				ObjectId object = r.resolve(Constants.HEAD);
+	
+				RevCommit firstCommit = JGitUtils.getFirstCommit(r, Constants.HEAD);
+				RevCommit lastCommit = walk.parseCommit(object);
+				int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime())
+						/ (60 * 60 * 24);
+				total.duration = diffDays;
+				DateFormat df;
+				if (diffDays <= 90) {
+					// Days
+					df = new SimpleDateFormat("yyyy-MM-dd");
+				} else if (diffDays > 90 && diffDays < 365) {
+					// Weeks
+					df = new SimpleDateFormat("yyyy-MM (w)");
+				} else {
+					// Months
+					df = new SimpleDateFormat("yyyy-MM");
+				}
+				walk.markStart(lastCommit);
+	
+				Iterable<RevCommit> revlog = walk;
+				for (RevCommit rev : revlog) {
+					Date d = JGitUtils.getCommitDate(rev);
+					String p = df.format(d);
+					if (!metricMap.containsKey(p)) {
+						metricMap.put(p, new Metric(p));
+					}
+					Metric m = metricMap.get(p);
+					m.count++;
+					total.count++;
+					if (tagMap.containsKey(rev.getId())) {
+						m.tag++;
+						total.tag++;
+					}
+				}
+			} catch (Throwable t) {
+				JGitUtils.LOGGER.error("Failed to mine log history for metrics", t);
+			}
+		}
+		List<String> keys = new ArrayList<String>(metricMap.keySet());
+		Collections.sort(keys);
+		List<Metric> metrics = new ArrayList<Metric>();
+		for (String key : keys) {
+			metrics.add(metricMap.get(key));
+		}
+		if (includeTotal) {
+			metrics.add(0, total);
+		}
+		return metrics;
+	}
+
+	public static List<Metric> getAuthorMetrics(Repository r) {
+		Metric total = new Metric("TOTAL");
+		final Map<String, Metric> metricMap = new HashMap<String, Metric>();
+	
+		if (JGitUtils.hasCommits(r)) {
+			try {
+				RevWalk walk = new RevWalk(r);
+				ObjectId object = r.resolve(Constants.HEAD);
+				RevCommit lastCommit = walk.parseCommit(object);
+				walk.markStart(lastCommit);
+	
+				Iterable<RevCommit> revlog = walk;
+				for (RevCommit rev : revlog) {
+					String p = rev.getAuthorIdent().getName();
+					if (StringUtils.isEmpty(p)) {
+						p = rev.getAuthorIdent().getEmailAddress();
+					}
+					if (!metricMap.containsKey(p)) {
+						metricMap.put(p, new Metric(p));
+					}
+					Metric m = metricMap.get(p);
+					m.count++;
+					total.count++;
+				}
+			} catch (Throwable t) {
+				JGitUtils.LOGGER.error("Failed to mine log history for metrics", t);
+			}
+		}
+		List<String> keys = new ArrayList<String>(metricMap.keySet());
+		Collections.sort(keys);
+		List<Metric> metrics = new ArrayList<Metric>();
+		for (String key : keys) {
+			metrics.add(metricMap.get(key));
+		}
+		return metrics;
+	}
+}
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.java b/src/com/gitblit/wicket/GitBlitWebApp.java
index 71f5aad..61b51e8 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/com/gitblit/wicket/GitBlitWebApp.java
@@ -41,7 +41,7 @@
 import com.gitblit.wicket.pages.RawPage;
 import com.gitblit.wicket.pages.RepositoriesPage;
 import com.gitblit.wicket.pages.SearchPage;
-import com.gitblit.wicket.pages.StatsPage;
+import com.gitblit.wicket.pages.MetricsPage;
 import com.gitblit.wicket.pages.SummaryPage;
 import com.gitblit.wicket.pages.TagPage;
 import com.gitblit.wicket.pages.TagsPage;
@@ -84,7 +84,7 @@
 		mount("/patch", PatchPage.class, "r", "h", "f");
 		mount("/history", HistoryPage.class, "r", "h", "f");
 		mount("/search", SearchPage.class);
-		mount("/stats", StatsPage.class, "r");
+		mount("/metrics", MetricsPage.class, "r");
 		
 		// setup ticket urls
 		mount("/tickets", TicketsPage.class, "r");
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties
index 45e4503..ac45a25 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -54,7 +54,7 @@
 gb.modification = modification
 gb.deletion = deletion
 gb.rename = rename
-gb.stats = stats
+gb.metrics = metrics
 gb.markdown = markdown
 gb.changedFiles = changed files 
 gb.filesAdded = {0} files added
diff --git a/src/com/gitblit/wicket/pages/StatsPage.html b/src/com/gitblit/wicket/pages/MetricsPage.html
similarity index 100%
rename from src/com/gitblit/wicket/pages/StatsPage.html
rename to src/com/gitblit/wicket/pages/MetricsPage.html
diff --git a/src/com/gitblit/wicket/pages/StatsPage.java b/src/com/gitblit/wicket/pages/MetricsPage.java
similarity index 92%
rename from src/com/gitblit/wicket/pages/StatsPage.java
rename to src/com/gitblit/wicket/pages/MetricsPage.java
index 0b16211..2186ae3 100644
--- a/src/com/gitblit/wicket/pages/StatsPage.java
+++ b/src/com/gitblit/wicket/pages/MetricsPage.java
@@ -39,15 +39,15 @@
 import org.wicketstuff.googlecharts.ShapeMarker;
 
 import com.gitblit.models.Metric;
-import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MetricUtils;
 import com.gitblit.wicket.WicketUtils;
 
-public class StatsPage extends RepositoryPage {
+public class MetricsPage extends RepositoryPage {
 
-	public StatsPage(PageParameters params) {
+	public MetricsPage(PageParameters params) {
 		super(params);
 		Repository r = getRepository();
-		insertLinePlot("commitsChart", JGitUtils.getDateMetrics(r, false));
+		insertLinePlot("commitsChart", MetricUtils.getDateMetrics(r, false));
 		insertBarPlot("dayOfWeekChart", getDayOfWeekMetrics(r));
 		insertLinePlot("timeOfDayChart", getTimeOfDayMetrics(r));
 		insertPieChart("authorsChart", getAuthorMetrics(r));
@@ -119,7 +119,7 @@
 	}
 
 	private List<Metric> getDayOfWeekMetrics(Repository repository) {
-		List<Metric> list = JGitUtils.getDateMetrics(repository, false, "E");
+		List<Metric> list = MetricUtils.getDateMetrics(repository, false, "E");
 		SimpleDateFormat sdf = new SimpleDateFormat("E");
 		Calendar cal = Calendar.getInstance();
 
@@ -146,7 +146,7 @@
 	private List<Metric> getTimeOfDayMetrics(Repository repository) {
 		SimpleDateFormat ndf = new SimpleDateFormat("yyyy-MM-dd");
 		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-		List<Metric> list = JGitUtils.getDateMetrics(repository, false, "yyyy-MM-dd HH:mm");
+		List<Metric> list = MetricUtils.getDateMetrics(repository, false, "yyyy-MM-dd HH:mm");
 		Calendar cal = Calendar.getInstance();
 
 		for (Metric metric : list) {
@@ -164,7 +164,7 @@
 	}
 
 	private List<Metric> getAuthorMetrics(Repository repository) {
-		List<Metric> authors = JGitUtils.getAuthorMetrics(repository);
+		List<Metric> authors = MetricUtils.getAuthorMetrics(repository);
 		Collections.sort(authors, new Comparator<Metric>() {
 			@Override
 			public int compare(Metric o1, Metric o2) {
@@ -184,6 +184,6 @@
 
 	@Override
 	protected String getPageName() {
-		return getString("gb.stats");
+		return getString("gb.metrics");
 	}
 }
diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.html b/src/com/gitblit/wicket/pages/RepositoryPage.html
index 0f245ef..ffb484f 100644
--- a/src/com/gitblit/wicket/pages/RepositoryPage.html
+++ b/src/com/gitblit/wicket/pages/RepositoryPage.html
@@ -18,7 +18,7 @@
 		
 			<!-- page nav links -->
 			<div class="page_nav">		
-				<a wicket:id="summary"><wicket:message key="gb.summary"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="branches"><wicket:message key="gb.branches"></wicket:message></a> | <a wicket:id="tags"><wicket:message key="gb.tags"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>  | <a wicket:id="stats"><wicket:message key="gb.stats"></wicket:message></a> <span wicket:id="extra"><span wicket:id="extraSeparator"></span><span wicket:id="extraLink"></span></span>
+				<a wicket:id="summary"><wicket:message key="gb.summary"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="branches"><wicket:message key="gb.branches"></wicket:message></a> | <a wicket:id="tags"><wicket:message key="gb.tags"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>  | <a wicket:id="metrics"><wicket:message key="gb.metrics"></wicket:message></a> <span wicket:id="extra"><span wicket:id="extraSeparator"></span><span wicket:id="extraLink"></span></span>
 			</div>
 		</div>
 		
diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java
index 2610c4c..143d885 100644
--- a/src/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/com/gitblit/wicket/pages/RepositoryPage.java
@@ -75,7 +75,7 @@
 			put("branches", "gb.branches");
 			put("tags", "gb.tags");
 			put("tree", "gb.tree");
-			put("stats", "gb.stats");
+			put("metrics", "gb.metrics");
 			put("tickets", "gb.tickets");
 			put("edit", "gb.edit");
 		}
@@ -104,7 +104,7 @@
 				WicketUtils.newRepositoryParameter(repositoryName)));
 		add(new BookmarkablePageLink<Void>("tree", TreePage.class,
 				WicketUtils.newRepositoryParameter(repositoryName)));
-		add(new BookmarkablePageLink<Void>("stats", StatsPage.class,
+		add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class,
 				WicketUtils.newRepositoryParameter(repositoryName)));
 
 		// per-repository extra page links
diff --git a/src/com/gitblit/wicket/pages/SummaryPage.html b/src/com/gitblit/wicket/pages/SummaryPage.html
index 1a90c99..ed94192 100644
--- a/src/com/gitblit/wicket/pages/SummaryPage.html
+++ b/src/com/gitblit/wicket/pages/SummaryPage.html
@@ -19,7 +19,7 @@
 				<tr><th><wicket:message key="gb.description">[description]</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>
 				<tr><th><wicket:message key="gb.owner">[owner]</wicket:message></th><td><span wicket:id="repositoryOwner">[repository owner]</span></td></tr>
 				<tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>
-				<tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="repositoryStats">[repository stats]</span></td></tr>
+				<tr><th><wicket:message key="gb.metrics">[metrics]</wicket:message></th><td><span wicket:id="repositoryMetrics">[repository metrics]</span></td></tr>
 				<tr><th valign="top"><wicket:message key="gb.url">[URL]</wicket:message></th><td><img style="vertical-align: top; padding-right:5px;" wicket:id="accessRestrictionIcon" /><span wicket:id="repositoryCloneUrl">[repository clone url]</span></td></tr>
 			</table>
 		</div>
diff --git a/src/com/gitblit/wicket/pages/SummaryPage.java b/src/com/gitblit/wicket/pages/SummaryPage.java
index 1157d30..f820694 100644
--- a/src/com/gitblit/wicket/pages/SummaryPage.java
+++ b/src/com/gitblit/wicket/pages/SummaryPage.java
@@ -43,6 +43,7 @@
 import com.gitblit.Keys;
 import com.gitblit.models.Metric;
 import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MetricUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.utils.TimeUtils;
 import com.gitblit.wicket.WicketUtils;
@@ -72,7 +73,7 @@
 		List<Metric> metrics = null;
 		Metric metricsTotal = null;
 		if (GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {
-			metrics = JGitUtils.getDateMetrics(r, true);
+			metrics = MetricUtils.getDateMetrics(r, true);
 			metricsTotal = metrics.remove(0);
 		}
 
@@ -83,9 +84,9 @@
 		add(WicketUtils.createTimestampLabel("repositoryLastChange", JGitUtils.getLastChange(r),
 				getTimeZone()));
 		if (metricsTotal == null) {
-			add(new Label("repositoryStats", ""));
+			add(new Label("repositoryMetrics", ""));
 		} else {
-			add(new Label("repositoryStats", MessageFormat.format(
+			add(new Label("repositoryMetrics", MessageFormat.format(
 					"{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag,
 					TimeUtils.duration(metricsTotal.duration))));
 		}
diff --git a/tests/com/gitblit/tests/JGitUtilsTest.java b/tests/com/gitblit/tests/JGitUtilsTest.java
index 9007b42..14d318f 100644
--- a/tests/com/gitblit/tests/JGitUtilsTest.java
+++ b/tests/com/gitblit/tests/JGitUtilsTest.java
@@ -36,6 +36,7 @@
 import com.gitblit.models.TicketModel;
 import com.gitblit.models.TicketModel.Comment;
 import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MetricUtils;
 
 public class JGitUtilsTest extends TestCase {
 
@@ -184,7 +185,7 @@
 
 	public void testMetrics() throws Exception {
 		Repository repository = GitBlitSuite.getHelloworldRepository();
-		List<Metric> metrics = JGitUtils.getDateMetrics(repository, true);
+		List<Metric> metrics = MetricUtils.getDateMetrics(repository, true);
 		repository.close();
 		assertTrue("No metrics found!", metrics.size() > 0);
 	}

--
Gitblit v1.9.1