From ff7d3cffc7af1f24a1db8d42758943cc05bcbaa0 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Tue, 18 Jun 2013 22:22:31 -0400
Subject: [PATCH] Reflogs, Digests, and Dashboards

---
 src/main/java/com/gitblit/wicket/pages/ReflogPage.java              |   22 
 src/main/java/com/gitblit/wicket/pages/OverviewPage.html            |    2 
 src/main/java/com/gitblit/git/ReceiveHook.java                      |    4 
 src/main/java/com/gitblit/wicket/GitBlitWebApp.java                 |    8 
 src/main/java/com/gitblit/wicket/panels/ReflogPanel.java            |  305 +++++++
 src/main/distrib/data/gitblit.properties                            |   10 
 src/main/java/com/gitblit/wicket/pages/RootPage.java                |    2 
 src/main/java/com/gitblit/wicket/pages/OverviewPage.java            |    8 
 src/main/java/com/gitblit/wicket/pages/ProjectPage.html             |  114 +-
 src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java         |  311 +++++++
 src/main/java/com/gitblit/models/RefLogEntry.java                   |    6 
 src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html |    2 
 src/main/resources/gitblit.css                                      |   22 
 src/main/java/com/gitblit/wicket/pages/ProjectPage.java             |  189 ---
 src/main/java/com/gitblit/wicket/panels/ReflogPanel.html            |   10 
 src/main/java/com/gitblit/wicket/pages/RepositoryPage.java          |    6 
 src/main/java/com/gitblit/wicket/panels/DigestsPanel.java           |  273 ++++++
 src/main/java/com/gitblit/wicket/pages/MyDashboardPage.html         |  158 +++
 src/main/java/com/gitblit/utils/RefLogUtils.java                    |  163 ++-
 src/main/java/com/gitblit/wicket/pages/ProjectsPage.java            |  109 --
 src/test/java/com/gitblit/tests/GitServletTest.java                 |    6 
 src/main/java/com/gitblit/wicket/GitBlitWebApp.properties           |   11 
 src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java |    2 
 src/test/java/com/gitblit/tests/PushLogTest.java                    |    6 
 src/main/java/com/gitblit/models/DailyLogEntry.java                 |    2 
 /dev/null                                                           |  381 ---------
 src/main/java/com/gitblit/wicket/pages/DashboardPage.java           |  249 -----
 src/main/java/com/gitblit/wicket/pages/ReflogPage.html              |    4 
 src/main/java/com/gitblit/wicket/panels/DigestsPanel.html           |    9 
 src/main/java/com/gitblit/wicket/pages/ProjectsPage.html            |    1 
 30 files changed, 1,351 insertions(+), 1,044 deletions(-)

diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties
index 9f7eb77..7936bc2 100644
--- a/src/main/distrib/data/gitblit.properties
+++ b/src/main/distrib/data/gitblit.properties
@@ -853,17 +853,17 @@
 # SINCE 0.5.0
 web.itemsPerPage = 50
 
-# The number of pushes to display on the overview page
+# The number of reflog changes to display on the overview page
 # Value must exceed 0 else default of 5 is used
 #
 # SINCE 1.3.0
-web.overviewPushCount = 5
+web.overviewReflogCount = 5
 
-# The number of pushes to show on a push page before show the first, prev, next
-# pagination links.  A default of 10 is used for any invalid value.
+# The number of reflog changes to show on a reflog page before show the first,
+#  prev, next pagination links.  A default of 10 is used for any invalid value.
 #
 # SINCE 1.3.0
-web.pushesPerPage = 10
+web.reflogChangesPerPage = 10
 
 # Registered file extensions to ignore during Lucene indexing
 #
diff --git a/src/main/java/com/gitblit/git/ReceiveHook.java b/src/main/java/com/gitblit/git/ReceiveHook.java
index 3e00b38..e3435ff 100644
--- a/src/main/java/com/gitblit/git/ReceiveHook.java
+++ b/src/main/java/com/gitblit/git/ReceiveHook.java
@@ -45,7 +45,7 @@
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.ClientLogger;
 import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.PushLogUtils;
+import com.gitblit.utils.RefLogUtils;
 import com.gitblit.utils.StringUtils;
 
 /**
@@ -256,7 +256,7 @@
 
 		// update push log
 		try {
-			PushLogUtils.updatePushLog(user, rp.getRepository(), commands);
+			RefLogUtils.updateRefLog(user, rp.getRepository(), commands);
 			logger.debug(MessageFormat.format("{0} push log updated", repository.name));
 		} catch (Exception e) {
 			logger.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e);
diff --git a/src/main/java/com/gitblit/models/DailyLogEntry.java b/src/main/java/com/gitblit/models/DailyLogEntry.java
index a459d76..41f1381 100644
--- a/src/main/java/com/gitblit/models/DailyLogEntry.java
+++ b/src/main/java/com/gitblit/models/DailyLogEntry.java
@@ -28,7 +28,7 @@
  * 
  * @author James Moger
  */
-public class DailyLogEntry extends PushLogEntry implements Serializable {
+public class DailyLogEntry extends RefLogEntry implements Serializable {
 
 	private static final long serialVersionUID = 1L;
 
diff --git a/src/main/java/com/gitblit/models/PushLogEntry.java b/src/main/java/com/gitblit/models/RefLogEntry.java
similarity index 97%
rename from src/main/java/com/gitblit/models/PushLogEntry.java
rename to src/main/java/com/gitblit/models/RefLogEntry.java
index 77bed38..54d1777 100644
--- a/src/main/java/com/gitblit/models/PushLogEntry.java
+++ b/src/main/java/com/gitblit/models/RefLogEntry.java
@@ -39,7 +39,7 @@
  * 
  * @author James Moger
  */
-public class PushLogEntry implements Serializable, Comparable<PushLogEntry> {
+public class RefLogEntry implements Serializable, Comparable<RefLogEntry> {
 
 	private static final long serialVersionUID = 1L;
 
@@ -67,7 +67,7 @@
 	 * @param user
 	 *            the user who pushed
 	 */
-	public PushLogEntry(String repository, Date date, UserModel user) {
+	public RefLogEntry(String repository, Date date, UserModel user) {
 		this.repository = repository;
 		this.date = date;
 		this.user = user;
@@ -317,7 +317,7 @@
 	}
 
 	@Override
-	public int compareTo(PushLogEntry o) {
+	public int compareTo(RefLogEntry o) {
 		// reverse chronological order
 		return o.date.compareTo(date);
 	}
diff --git a/src/main/java/com/gitblit/utils/PushLogUtils.java b/src/main/java/com/gitblit/utils/RefLogUtils.java
similarity index 77%
rename from src/main/java/com/gitblit/utils/PushLogUtils.java
rename to src/main/java/com/gitblit/utils/RefLogUtils.java
index fed5b19..ce03a16 100644
--- a/src/main/java/com/gitblit/utils/PushLogUtils.java
+++ b/src/main/java/com/gitblit/utils/RefLogUtils.java
@@ -41,6 +41,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefRename;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.lib.Repository;
@@ -55,23 +56,23 @@
 import com.gitblit.Constants;
 import com.gitblit.models.DailyLogEntry;
 import com.gitblit.models.PathModel.PathChangeModel;
-import com.gitblit.models.PushLogEntry;
+import com.gitblit.models.RefLogEntry;
 import com.gitblit.models.RefModel;
 import com.gitblit.models.RepositoryCommit;
 import com.gitblit.models.UserModel;
 
 /**
- * Utility class for maintaining a pushlog within a git repository on an
+ * Utility class for maintaining a reflog within a git repository on an
  * orphan branch.
  * 
  * @author James Moger
  *
  */
-public class PushLogUtils {
+public class RefLogUtils {
 	
-	public static final String GB_PUSHES = "refs/gitblit/pushes";
+	private static final String GB_REFLOG = "refs/gitblit/reflog";
 
-	static final Logger LOGGER = LoggerFactory.getLogger(PushLogUtils.class);
+	private static final Logger LOGGER = LoggerFactory.getLogger(RefLogUtils.class);
 
 	/**
 	 * Log an error message and exception.
@@ -97,17 +98,39 @@
 	}
 
 	/**
-	 * Returns a RefModel for the gb-pushes branch in the repository. If the
+	 * Returns a RefModel for the reflog branch in the repository. If the
 	 * branch can not be found, null is returned.
 	 * 
 	 * @param repository
-	 * @return a refmodel for the gb-pushes branch or null
+	 * @return a refmodel for the reflog branch or null
 	 */
-	public static RefModel getPushLogBranch(Repository repository) {
+	public static RefModel getRefLogBranch(Repository repository) {
 		List<RefModel> refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT);
+		RefModel pushLog = null;
+		final String GB_PUSHES = "refs/gitblit/pushes";
 		for (RefModel ref : refs) {
-			if (ref.reference.getName().equals(GB_PUSHES)) {
+			if (ref.reference.getName().equals(GB_REFLOG)) {
 				return ref;
+			} else if (ref.reference.getName().equals(GB_PUSHES)) {
+				pushLog = ref;
+			}
+		}
+		if (pushLog != null) {
+			// rename refs/gitblit/pushes to refs/gitblit/reflog
+			RefRename cmd;
+			try {
+				cmd = repository.renameRef(GB_PUSHES, GB_REFLOG);
+				cmd.setRefLogIdent(new PersonIdent("Gitblit", "gitblit@localhost"));
+				cmd.setRefLogMessage("renamed " + GB_PUSHES + " => " + GB_REFLOG);
+				Result res = cmd.rename();
+				switch (res) {
+				case RENAMED:
+					return getRefLogBranch(repository);
+				default:
+					LOGGER.error("failed to rename " + GB_PUSHES + " => " + GB_REFLOG + " (" + res.name() + ")");
+				}
+			} catch (IOException e) {
+				LOGGER.error("failed to rename pushlog", e);
 			}
 		}
 		return null;
@@ -133,25 +156,25 @@
 	}
 	
 	/**
-	 * Updates a push log.
+	 * Updates the reflog with the received commands.
 	 * 
 	 * @param user
 	 * @param repository
 	 * @param commands
 	 * @return true, if the update was successful
 	 */
-	public static boolean updatePushLog(UserModel user, Repository repository,
+	public static boolean updateRefLog(UserModel user, Repository repository,
 			Collection<ReceiveCommand> commands) {
-		RefModel pushlogBranch = getPushLogBranch(repository);
-		if (pushlogBranch == null) {
-			JGitUtils.createOrphanBranch(repository, GB_PUSHES, null);
+		RefModel reflogBranch = getRefLogBranch(repository);
+		if (reflogBranch == null) {
+			JGitUtils.createOrphanBranch(repository, GB_REFLOG, null);
 		}
 		
 		boolean success = false;
 		String message = "push";
 		
 		try {
-			ObjectId headId = repository.resolve(GB_PUSHES + "^{commit}");
+			ObjectId headId = repository.resolve(GB_REFLOG + "^{commit}");
 			ObjectInserter odi = repository.newObjectInserter();
 			try {
 				// Create the in-memory index of the push log entry
@@ -184,7 +207,7 @@
 				RevWalk revWalk = new RevWalk(repository);
 				try {
 					RevCommit revCommit = revWalk.parseCommit(commitId);
-					RefUpdate ru = repository.updateRef(GB_PUSHES);
+					RefUpdate ru = repository.updateRef(GB_REFLOG);
 					ru.setNewObjectId(commitId);
 					ru.setExpectedOldObjectId(headId);
 					ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
@@ -201,7 +224,7 @@
 								ru.getRef(), rc);
 					default:
 						throw new JGitInternalException(MessageFormat.format(
-								JGitText.get().updatingRefFailed, GB_PUSHES, commitId.toString(),
+								JGitText.get().updatingRefFailed, GB_REFLOG, commitId.toString(),
 								rc));
 					}
 				} finally {
@@ -211,7 +234,7 @@
 				odi.release();
 			}
 		} catch (Throwable t) {
-			error(t, repository, "Failed to commit pushlog entry to {0}");
+			error(t, repository, "Failed to commit reflog entry to {0}");
 		}
 		return success;
 	}
@@ -316,25 +339,25 @@
 		return inCoreIndex;
 	}
 	
-	public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository) {
-		return getPushLog(repositoryName, repository, null, 0, -1);
+	public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository) {
+		return getRefLog(repositoryName, repository, null, 0, -1);
 	}
 
-	public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, int maxCount) {
-		return getPushLog(repositoryName, repository, null, 0, maxCount);
+	public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, int maxCount) {
+		return getRefLog(repositoryName, repository, null, 0, maxCount);
 	}
 
-	public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, int offset, int maxCount) {
-		return getPushLog(repositoryName, repository, null, offset, maxCount);
+	public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, int offset, int maxCount) {
+		return getRefLog(repositoryName, repository, null, offset, maxCount);
 	}
 
-	public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, Date minimumDate) {
-		return getPushLog(repositoryName, repository, minimumDate, 0, -1);
+	public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, Date minimumDate) {
+		return getRefLog(repositoryName, repository, minimumDate, 0, -1);
 	}
 	
 	/**
-	 * Returns the list of push log entries as they were recorded by Gitblit.
-	 * Each PushLogEntry may represent multiple ref updates.
+	 * Returns the list of reflog entries as they were recorded by Gitblit.
+	 * Each RefLogEntry may represent multiple ref updates.
 	 * 
 	 * @param repositoryName
 	 * @param repository
@@ -344,10 +367,10 @@
 	 * 			if < 0, all pushes are returned.
 	 * @return a list of push log entries
 	 */
-	public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository,
+	public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository,
 			Date minimumDate, int offset, int maxCount) {
-		List<PushLogEntry> list = new ArrayList<PushLogEntry>();
-		RefModel ref = getPushLogBranch(repository);
+		List<RefLogEntry> list = new ArrayList<RefLogEntry>();
+		RefModel ref = getRefLogBranch(repository);
 		if (ref == null) {
 			return list;
 		}
@@ -358,9 +381,9 @@
 		Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
 		List<RevCommit> pushes;
 		if (minimumDate == null) {
-			pushes = JGitUtils.getRevLog(repository, GB_PUSHES, offset, maxCount);
+			pushes = JGitUtils.getRevLog(repository, GB_REFLOG, offset, maxCount);
 		} else {
-			pushes = JGitUtils.getRevLog(repository, GB_PUSHES, minimumDate);
+			pushes = JGitUtils.getRevLog(repository, GB_REFLOG, minimumDate);
 		}
 		for (RevCommit push : pushes) {
 			if (push.getAuthorIdent().getName().equalsIgnoreCase("gitblit")) {
@@ -371,7 +394,7 @@
 			UserModel user = newUserModelFrom(push.getAuthorIdent());
 			Date date = push.getAuthorIdent().getWhen();
 			
-			PushLogEntry log = new PushLogEntry(repositoryName, date, user);
+			RefLogEntry log = new RefLogEntry(repositoryName, date, user);
 			list.add(log);
 			List<PathChangeModel> changedRefs = JGitUtils.getFilesInCommit(repository, push);
 			for (PathChangeModel change : changedRefs) {
@@ -410,8 +433,8 @@
 	 * @param maxCount
 	 * @return a list of push log entries separated by ref
 	 */
-	public static List<PushLogEntry> getPushLogByRef(String repositoryName, Repository repository, int maxCount) {
-		return getPushLogByRef(repositoryName, repository, 0, maxCount);
+	public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository, int maxCount) {
+		return getLogByRef(repositoryName, repository, 0, maxCount);
 	}
 	
 	/**
@@ -424,65 +447,65 @@
 	 * @param maxCount
 	 * @return a list of push log entries separated by ref
 	 */
-	public static List<PushLogEntry> getPushLogByRef(String repositoryName, Repository repository,  int offset,
+	public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository,  int offset,
 			int maxCount) {
 		// break the push log into ref push logs and then merge them back into a list
-		Map<String, List<PushLogEntry>> refMap = new HashMap<String, List<PushLogEntry>>();
-        List<PushLogEntry> pushes = getPushLog(repositoryName, repository, offset, maxCount);
-		for (PushLogEntry push : pushes) {
-			for (String ref : push.getChangedRefs()) {
+		Map<String, List<RefLogEntry>> refMap = new HashMap<String, List<RefLogEntry>>();
+        List<RefLogEntry> refLog = getRefLog(repositoryName, repository, offset, maxCount);
+		for (RefLogEntry entry : refLog) {
+			for (String ref : entry.getChangedRefs()) {
 				if (!refMap.containsKey(ref)) {
-					refMap.put(ref, new ArrayList<PushLogEntry>());
+					refMap.put(ref, new ArrayList<RefLogEntry>());
 				}
 				
-				// construct new ref-specific push log entry
-				PushLogEntry refPush;
-				if (push instanceof DailyLogEntry) {
+				// construct new ref-specific ref change entry
+				RefLogEntry refChange;
+				if (entry instanceof DailyLogEntry) {
 					// simulated push log from commits grouped by date
-					refPush = new DailyLogEntry(push.repository, push.date);
+					refChange = new DailyLogEntry(entry.repository, entry.date);
 				} else {
 					// real push log entry
-					refPush = new PushLogEntry(push.repository, push.date, push.user);
+					refChange = new RefLogEntry(entry.repository, entry.date, entry.user);
 				}
-				refPush.updateRef(ref, push.getChangeType(ref), push.getOldId(ref), push.getNewId(ref));
-				refPush.addCommits(push.getCommits(ref));
-				refMap.get(ref).add(refPush);
+				refChange.updateRef(ref, entry.getChangeType(ref), entry.getOldId(ref), entry.getNewId(ref));
+				refChange.addCommits(entry.getCommits(ref));
+				refMap.get(ref).add(refChange);
 			}
 		}
 		
-		// merge individual ref pushes into master list
-		List<PushLogEntry> refPushLog = new ArrayList<PushLogEntry>();
-		for (List<PushLogEntry> refPush : refMap.values()) {
-			refPushLog.addAll(refPush);
+		// merge individual ref changes into master list
+		List<RefLogEntry> mergedRefLog = new ArrayList<RefLogEntry>();
+		for (List<RefLogEntry> refPush : refMap.values()) {
+			mergedRefLog.addAll(refPush);
 		}
 		
-		// sort ref push log
-		Collections.sort(refPushLog);
+		// sort ref log
+		Collections.sort(mergedRefLog);
 		
-		return refPushLog;
+		return mergedRefLog;
 	}
 	
 	/**
-	 * Returns the list of pushes separated by ref (e.g. each ref has it's own
-	 * PushLogEntry object).
+	 * Returns the list of ref changes separated by ref (e.g. each ref has it's own
+	 * RefLogEntry object).
 	 *  
 	 * @param repositoryName
 	 * @param repository
 	 * @param minimumDate
-	 * @return a list of push log entries separated by ref
+	 * @return a list of ref log entries separated by ref
 	 */
-	public static List<PushLogEntry> getPushLogByRef(String repositoryName, Repository repository,  Date minimumDate) {
+	public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository,  Date minimumDate) {
 		// break the push log into ref push logs and then merge them back into a list
-		Map<String, List<PushLogEntry>> refMap = new HashMap<String, List<PushLogEntry>>();
-		List<PushLogEntry> pushes = getPushLog(repositoryName, repository, minimumDate);
-		for (PushLogEntry push : pushes) {
+		Map<String, List<RefLogEntry>> refMap = new HashMap<String, List<RefLogEntry>>();
+		List<RefLogEntry> pushes = getRefLog(repositoryName, repository, minimumDate);
+		for (RefLogEntry push : pushes) {
 			for (String ref : push.getChangedRefs()) {
 				if (!refMap.containsKey(ref)) {
-					refMap.put(ref, new ArrayList<PushLogEntry>());
+					refMap.put(ref, new ArrayList<RefLogEntry>());
 				}
 
                 // construct new ref-specific push log entry
-                PushLogEntry refPush = new PushLogEntry(push.repository, push.date, push.user);
+                RefLogEntry refPush = new RefLogEntry(push.repository, push.date, push.user);
                 refPush.updateRef(ref, push.getChangeType(ref), push.getOldId(ref), push.getNewId(ref));
 				refPush.addCommits(push.getCommits(ref));
 				refMap.get(ref).add(refPush);
@@ -490,8 +513,8 @@
 		}
 		
 		// merge individual ref pushes into master list
-		List<PushLogEntry> refPushLog = new ArrayList<PushLogEntry>();
-		for (List<PushLogEntry> refPush : refMap.values()) {
+		List<RefLogEntry> refPushLog = new ArrayList<RefLogEntry>();
+		for (List<RefLogEntry> refPush : refMap.values()) {
 			refPushLog.addAll(refPush);
 		}
 		
@@ -562,7 +585,7 @@
                         			Date tagDate = commit.getAuthorIdent().getWhen();
                         			tags.put(dateStr, new DailyLogEntry(repositoryName, tagDate, tagUser));
                                 }
-                                PushLogEntry tagEntry = tags.get(dateStr);
+                                RefLogEntry tagEntry = tags.get(dateStr);
                                 tagEntry.updateRef(ref.getName(), ReceiveCommand.Type.CREATE);
                                 tagEntry.addCommit(ref.getName(), commit);
                             } else if (ref.getName().startsWith(Constants.R_PULL)) {
@@ -572,7 +595,7 @@
                         			Date commitDate = commit.getAuthorIdent().getWhen();
                         			pulls.put(dateStr, new DailyLogEntry(repositoryName, commitDate, commitUser));
                                 }
-                                PushLogEntry pullEntry = pulls.get(dateStr);
+                                RefLogEntry pullEntry = pulls.get(dateStr);
                                 pullEntry.updateRef(ref.getName(), ReceiveCommand.Type.CREATE);
                                 pullEntry.addCommit(ref.getName(), commit);
                             }
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
index f4180fa..cdae093 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
@@ -44,18 +44,18 @@
 import com.gitblit.wicket.pages.GitSearchPage;
 import com.gitblit.wicket.pages.GravatarProfilePage;
 import com.gitblit.wicket.pages.HistoryPage;
-import com.gitblit.wicket.pages.DashboardPage;
 import com.gitblit.wicket.pages.LogPage;
 import com.gitblit.wicket.pages.LogoutPage;
 import com.gitblit.wicket.pages.LuceneSearchPage;
 import com.gitblit.wicket.pages.MarkdownPage;
 import com.gitblit.wicket.pages.MetricsPage;
+import com.gitblit.wicket.pages.MyDashboardPage;
 import com.gitblit.wicket.pages.OverviewPage;
 import com.gitblit.wicket.pages.PatchPage;
 import com.gitblit.wicket.pages.ProjectPage;
 import com.gitblit.wicket.pages.ProjectsPage;
-import com.gitblit.wicket.pages.PushesPage;
 import com.gitblit.wicket.pages.RawPage;
+import com.gitblit.wicket.pages.ReflogPage;
 import com.gitblit.wicket.pages.RepositoriesPage;
 import com.gitblit.wicket.pages.ReviewProposalPage;
 import com.gitblit.wicket.pages.SummaryPage;
@@ -69,7 +69,7 @@
 
 public class GitBlitWebApp extends WebApplication {
 
-	public final static Class<? extends BasePage> HOME_PAGE_CLASS = DashboardPage.class;
+	public final static Class<? extends BasePage> HOME_PAGE_CLASS = MyDashboardPage.class;
 	
 	@Override
 	public void init() {
@@ -98,7 +98,7 @@
 		mount("/repositories", RepositoriesPage.class);
 		mount("/overview", OverviewPage.class, "r", "h");
 		mount("/summary", SummaryPage.class, "r");
-		mount("/pushes", PushesPage.class, "r", "h");
+		mount("/reflog", ReflogPage.class, "r", "h");
 		mount("/commits", LogPage.class, "r", "h");
 		mount("/log", LogPage.class, "r", "h");
 		mount("/tags", TagsPage.class, "r");
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
index 2e17e60..f7e72ca 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -462,8 +462,7 @@
 gb.at = at
 gb.of = of
 gb.in = in
-gb.morePushes = all pushes...
-gb.pushes = pushes
+gb.moreChanges = all changes...
 gb.pushedNCommitsTo = pushed {0} commits to
 gb.pushedOneCommitTo = pushed 1 commit to
 gb.commitsTo = {0} commits to
@@ -488,8 +487,12 @@
 gb.starredRepositories = starred repositories
 gb.failedToUpdateUser = Failed to update user account!
 gb.myRepositories = my repositories
-gb.noActivity = there has been no recent commit activity
+gb.noActivity = there has been no activity in the last {0} days
 gb.findSomeRepositories = find some repositories
 gb.metricAuthorExclusions = author metric exclusions
 gb.myDashboard = my dashboard
-gb.failedToFindAccount = failed to find user account ''{0}''
\ No newline at end of file
+gb.failedToFindAccount = failed to find user account ''{0}''
+gb.reflog = reflog
+gb.active = active
+gb.starred = starred
+gb.owned = owned
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/DashboardPage.html b/src/main/java/com/gitblit/wicket/pages/DashboardPage.html
deleted file mode 100644
index 1da7ef4..0000000
--- a/src/main/java/com/gitblit/wicket/pages/DashboardPage.html
+++ /dev/null
@@ -1,100 +0,0 @@
-<!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>
-<div class="container">
-	<div class="hidden-phone markdown" style="padding-bottom:5px;" wicket:id="repositoriesMessage">[repositories message]</div>
-	
-	<div class="row">
-		<div class="span7">
-			<div style="color:#888;font-size:1.75em;padding-bottom:5px;" wicket:id="feedheader"></div>
-			<div class="hidden-phone hidden-tablet"  style="text-align:center;">
-				<table>
-					<tr>
-						<td><div id="chartRepositories" style="display:inline-block;width: 175px; height:175px"></div></td>
-						<td><div id="chartAuthors" style="display:inline-block;width: 175px; height: 175px;"></div></td>
-					</tr>
-				</table>
-			</div>
-			<div wicket:id="digests"></div>
-		</div>
-		<div class="span5">
-			<div wicket:id="active">[active]</div>
-			<div wicket:id="starred">[starred]</div>
-			<div wicket:id="owned">[owned]</div>
-		</div>
-		
-	</div>
-</div>
-
-<wicket:fragment wicket:id="starredListFragment">
-	<div ng-controller="starredCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">
-		<div class="header" style="padding: 5px;border: none;"><i class="icon-star"></i> <wicket:message key="gb.starredRepositories"></wicket:message> ({{starred.length}})
-			<div class="pull-right">
-				<a class="btn btn-mini">more</a>
-			</div>
-			<div style="padding: 5px 0px 0px;">
-				<input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
-			</div>
-		</div>
-		
-		<div ng-repeat="item in starred | limitTo: 20 | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
-			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>
-			<a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>
-			<span class="link hidden-tablet hidden-phone" style="color: #aaa;" title="{{item.d}}">{{item.t}}</span>
-			<span ng-show="item.s" class="pull-right">
-				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
-			</span>
-		</div>
-		
-	</div>
-</wicket:fragment>
-
-<wicket:fragment wicket:id="ownedListFragment">
-	<div ng-controller="ownedCtrl" style="border: 1px solid #ddd;border-radius: 4px;">
-		<div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.myRepositories"></wicket:message> ({{owned.length}})
-			<div class="hidden-phone pull-right">
-				<span wicket:id="create"></span>
-			</div>
-			<div style="padding: 5px 0px 0px;">
-				<input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
-			</div>
-		</div>
-		
-		<div ng-repeat="item in owned | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
-			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>
-			<a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>
-			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
-			<span ng-show="item.s" class="pull-right">
-				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
-			</span>
-		</div>		
-	</div>
-</wicket:fragment>
-
-<wicket:fragment wicket:id="activeListFragment">
-	<div ng-controller="activeCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">
-		<div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.activeRepositories"></wicket:message> ({{active.length}})
-			<div style="padding: 5px 0px 0px;">
-				<input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
-			</div>
-		</div>
-		
-		<div ng-repeat="item in active | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
-			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>
-			<a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>
-			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
-			<span ng-show="item.s" class="pull-right">
-				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
-			</span>
-		</div>		
-	</div>
-</wicket:fragment>
-
-</wicket:extend>
-</body>
-</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java
index 6c328b1..1c45b64 100644
--- a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java
@@ -15,49 +15,40 @@
  */
 package com.gitblit.wicket.pages;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.Serializable;
 import java.text.DateFormat;
 import java.text.MessageFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.TreeSet;
 
-import org.apache.wicket.Component;
 import org.apache.wicket.PageParameters;
 import org.apache.wicket.behavior.HeaderContributor;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.Fragment;
-import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
 import com.gitblit.models.DailyLogEntry;
 import com.gitblit.models.Metric;
-import com.gitblit.models.PushLogEntry;
+import com.gitblit.models.RefLogEntry;
 import com.gitblit.models.RepositoryCommit;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.MarkdownUtils;
-import com.gitblit.utils.PushLogUtils;
+import com.gitblit.utils.RefLogUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.GitBlitWebApp;
-import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.PageRegistration;
 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
 import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
@@ -66,19 +57,17 @@
 import com.gitblit.wicket.charting.GoogleCharts;
 import com.gitblit.wicket.charting.GooglePieChart;
 import com.gitblit.wicket.ng.NgController;
+import com.gitblit.wicket.panels.DigestsPanel;
 import com.gitblit.wicket.panels.LinkPanel;
-import com.gitblit.wicket.panels.PushesPanel;
 
-public class DashboardPage extends RootPage {
+public abstract class DashboardPage extends RootPage {
 
 	public DashboardPage() {
 		super();
-		setup(null);
 	}
 
 	public DashboardPage(PageParameters params) {
 		super(params);
-		setup(params);
 	}
 
 	@Override
@@ -86,115 +75,50 @@
 		return true;
 	}
 
-	private void setup(PageParameters params) {
-		setupPage("", "");
-		// check to see if we should display a login message
-		boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
-		if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
-			String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");
-			String message = readMarkdown(messageSource, "login.mkd");
-			Component repositoriesMessage = new Label("repositoriesMessage", message);
-			add(repositoriesMessage.setEscapeModelStrings(false));
-			add(new Label("digests"));
-			add(new Label("active").setVisible(false));
-			add(new Label("starred").setVisible(false));
-			add(new Label("owned").setVisible(false));
-			add(new Label("feedheader").setVisible(false));
-			return;
-		}
-
-		// Load the markdown welcome message
-		String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");
-		String message = readMarkdown(messageSource, "welcome.mkd");
-		Component repositoriesMessage = new Label("repositoriesMessage", message)
-				.setEscapeModelStrings(false).setVisible(message.length() > 0);
-		add(repositoriesMessage);
-
-		UserModel user = GitBlitWebSession.get().getUser();
-		if (user == null) {
-			user = UserModel.ANONYMOUS;
-		}
-
-		Comparator<RepositoryModel> lastUpdateSort = new Comparator<RepositoryModel>() {
-			@Override
-			public int compare(RepositoryModel o1, RepositoryModel o2) {
-				return o2.lastChange.compareTo(o1.lastChange);
-			}
-		};
-		
-		// parameters
-		int daysBack = params == null ? 0 : WicketUtils.getDaysBack(params);
-		if (daysBack < 1) {
-			daysBack = 7;
-		}
+	protected void addActivity(UserModel user, Collection<RepositoryModel> repositories, int daysBack) {
 		Calendar c = Calendar.getInstance();
 		c.add(Calendar.DATE, -1*daysBack);
 		Date minimumDate = c.getTime();
 		TimeZone timezone = getTimeZone();
 		
-		// build repo lists 
-		List<RepositoryModel> starred = new ArrayList<RepositoryModel>();
-		List<RepositoryModel> owned = new ArrayList<RepositoryModel>();
-		List<RepositoryModel> active = new ArrayList<RepositoryModel>();
-
-		for (RepositoryModel model : getRepositoryModels()) {
-			if (model.isUsersPersonalRepository(user.username) || model.isOwner(user.username)) {
-				owned.add(model);
-			}
-			
-			if (user.getPreferences().isStarredRepository(model.name)) {
-				starred.add(model);
-			}
-			
-			if (model.isShowActivity() && model.lastChange.after(minimumDate)) {
-				active.add(model);
-			}
-		}
-		
-		Collections.sort(owned, lastUpdateSort);
-		Collections.sort(starred, lastUpdateSort);
-		Collections.sort(active, lastUpdateSort);
-		
-		Set<RepositoryModel> feedSources = new HashSet<RepositoryModel>();
-		feedSources.addAll(starred);
-		if (feedSources.isEmpty()) {
-			feedSources.addAll(active);
-		}
-		
 		// create daily commit digest feed
-		List<PushLogEntry> pushes = new ArrayList<PushLogEntry>();
-		for (RepositoryModel model : feedSources) {
+		List<DailyLogEntry> digests = new ArrayList<DailyLogEntry>();
+		for (RepositoryModel model : repositories) {
 			Repository repository = GitBlit.self().getRepository(model.name);
-			List<DailyLogEntry> entries = PushLogUtils.getDailyLogByRef(model.name, repository, minimumDate, timezone);
-			pushes.addAll(entries);
+			List<DailyLogEntry> entries = RefLogUtils.getDailyLogByRef(model.name, repository, minimumDate, timezone);
+			digests.addAll(entries);
 			repository.close();
 		}
 		
-		if (pushes.size() == 0) {
+		Fragment activityFragment = new Fragment("activity", "activityFragment", this);
+		add(activityFragment);
+		if (digests.size() == 0) {
 			// quiet or no starred repositories
-			if (feedSources.size() == 0) {
+			if (repositories.size() == 0) {
 				if (UserModel.ANONYMOUS.equals(user)) {
-					add(new Label("digests", getString("gb.noActivity")));	
+					activityFragment.add(new Label("digests", MessageFormat.format(getString("gb.noActivity"), daysBack)));	
 				} else {
-					add(new LinkPanel("digests", null, getString("gb.findSomeRepositories"), RepositoriesPage.class));
+					activityFragment.add(new LinkPanel("digests", null, getString("gb.findSomeRepositories"), RepositoriesPage.class));
 				}
 			} else {
-				add(new Label("digests", getString("gb.noActivity")));
+				activityFragment.add(new Label("digests", MessageFormat.format(getString("gb.noActivity"), daysBack)));
 			}
 		} else {
 			// show daily commit digest feed
-			Collections.sort(pushes);
-			add(new PushesPanel("digests", pushes));
+			Collections.sort(digests);
+			DigestsPanel digestsPanel = new DigestsPanel("digests", digests);
+			WicketUtils.setCssStyle(digestsPanel,  "margin-top:-20px");
+			activityFragment.add(digestsPanel);
 		}
 		
 		// add the nifty charts
-		if (!ArrayUtils.isEmpty(pushes)) {
+		if (!ArrayUtils.isEmpty(digests)) {
 			// aggregate author exclusions
 			Set<String> authorExclusions = new TreeSet<String>();
 			for (String author : GitBlit.getStrings(Keys.web.metricAuthorExclusions)) {
 				authorExclusions.add(author.toLowerCase());
 			}
-			for (RepositoryModel model : feedSources) {
+			for (RepositoryModel model : repositories) {
 				if (!ArrayUtils.isEmpty(model.metricAuthorExclusions)) {
 					for (String author : model.metricAuthorExclusions) {
 						authorExclusions.add(author.toLowerCase());
@@ -202,40 +126,10 @@
 				}
 			}
 
-			addCharts(pushes, authorExclusions, daysBack);
+			addCharts(activityFragment, digests, authorExclusions, daysBack);
 		} else {
-			add(new Label("feedheader").setVisible(false));
-		}
-		
-		// active repository list
-		if (starred.isEmpty()) {
-			Fragment activeView = createNgList("active", "activeListFragment", "activeCtrl", active);
-			add(activeView);
-		} else {
-			add(new Label("active").setVisible(false));
-		}
-		
-		// starred repository list
-		if (ArrayUtils.isEmpty(starred)) {
-			add(new Label("starred").setVisible(false));
-		} else {
-			Fragment starredView = createNgList("starred", "starredListFragment", "starredCtrl", starred);
-			add(starredView);
-		}
-		
-		// owned repository list
-		if (ArrayUtils.isEmpty(owned)) {
-			add(new Label("owned").setVisible(false));
-		} else {
-			Fragment ownedView = createNgList("owned", "ownedListFragment", "ownedCtrl", owned);
-			if (user.canCreate) {
-				// create button
-				ownedView.add(new LinkPanel("create", "btn btn-mini", getString("gb.newRepository"), EditRepositoryPage.class));
-			} else {
-				// no button
-				ownedView.add(new Label("create").setVisible(false));
-			}
-			add(ownedView);
+			activityFragment.add(new Label("charts").setVisible(false));
+			activityFragment.add(new Label("feedheader").setVisible(false));
 		}
 	}
 	
@@ -259,6 +153,7 @@
 			item.n = name;
 			item.p = path;
 			item.r = repo.name;
+			item.i = repo.description;
 			item.s = GitBlit.self().getStarCount(repo);
 			item.t = getTimeUtils().timeAgo(repo.lastChange);
 			item.d = df.format(repo.lastChange);
@@ -295,104 +190,32 @@
 		pages.add(menu);
 	}
 
-	private String readMarkdown(String messageSource, String resource) {
-		String message = "";
-		if (messageSource.equalsIgnoreCase("gitblit")) {
-			// Read default message
-			message = readDefaultMarkdown(resource);
-		} else {
-			// Read user-supplied message
-			if (!StringUtils.isEmpty(messageSource)) {
-				File file = GitBlit.getFileOrFolder(messageSource);
-				if (file.exists()) {
-					try {
-						FileInputStream fis = new FileInputStream(file);
-						InputStreamReader reader = new InputStreamReader(fis,
-								Constants.CHARACTER_ENCODING);
-						message = MarkdownUtils.transformMarkdown(reader);
-						reader.close();
-					} catch (Throwable t) {
-						message = getString("gb.failedToRead") + " " + file;
-						warn(message, t);
-					}
-				} else {
-					message = messageSource + " " + getString("gb.isNotValidFile");
-				}
-			}
-		}
-		return message;
-	}
 
-	private String readDefaultMarkdown(String file) {
-		String base = file.substring(0, file.lastIndexOf('.'));
-		String ext = file.substring(file.lastIndexOf('.'));
-		String lc = getLanguageCode();
-		String cc = getCountryCode();
-
-		// try to read file_en-us.ext, file_en.ext, file.ext
-		List<String> files = new ArrayList<String>();
-		if (!StringUtils.isEmpty(lc)) {
-			if (!StringUtils.isEmpty(cc)) {
-				files.add(base + "_" + lc + "-" + cc + ext);
-				files.add(base + "_" + lc + "_" + cc + ext);
-			}
-			files.add(base + "_" + lc + ext);
-		}
-		files.add(file);
-
-		for (String name : files) {
-			String message;
-			InputStreamReader reader = null;
-			try {
-				InputStream is = getClass().getResourceAsStream("/" + name);
-				if (is == null) {
-					continue;
-				}
-				reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);
-				message = MarkdownUtils.transformMarkdown(reader);
-				reader.close();
-				return message;
-			} catch (Throwable t) {
-				message = MessageFormat.format(getString("gb.failedToReadMessage"), file);
-				error(message, t, false);
-				return message;
-			} finally {
-				if (reader != null) {
-					try {
-						reader.close();
-					} catch (Exception e) {
-					}
-				}
-			}			
-		}
-		return MessageFormat.format(getString("gb.failedToReadMessage"), file);
-	}
-	
 	/**
 	 * Creates the daily activity line chart, the active repositories pie chart,
 	 * and the active authors pie chart
 	 * 
-	 * @param recentPushes
+	 * @param recentChanges
 	 * @param authorExclusions
 	 * @param daysBack
 	 */
-	private void addCharts(List<PushLogEntry> recentPushes, Set<String> authorExclusions, int daysBack) {
+	protected void addCharts(Fragment frag, List<DailyLogEntry> recentChanges, Set<String> authorExclusions, int daysBack) {
 		// activity metrics
 		Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();
 		Map<String, Metric> authorMetrics = new HashMap<String, Metric>();
 
 		// aggregate repository and author metrics
 		int totalCommits = 0;
-		for (PushLogEntry push : recentPushes) {
+		for (RefLogEntry change : recentChanges) {
 
 			// aggregate repository metrics
-			String repository = StringUtils.stripDotGit(push.repository);
+			String repository = StringUtils.stripDotGit(change.repository);
 			if (!repositoryMetrics.containsKey(repository)) {
 				repositoryMetrics.put(repository, new Metric(repository));
 			}
 			repositoryMetrics.get(repository).count += 1;
 			
-			for (RepositoryCommit commit : push.getCommits()) {
+			for (RepositoryCommit commit : change.getCommits()) {
 				totalCommits++;
 				String author = StringUtils.removeNewlines(commit.getAuthorIdent().getName());
 				String authorName = author.toLowerCase();
@@ -406,7 +229,7 @@
 			}
 		}
 		
-		add(new Label("feedheader", MessageFormat.format(getString("gb.recentActivityStats"),
+		frag.add(new Label("feedheader", MessageFormat.format(getString("gb.recentActivityStats"),
 				daysBack, totalCommits, authorMetrics.size())));
 
 		// build google charts
@@ -430,10 +253,11 @@
 		chart.setShowLegend(false);
 		charts.addChart(chart);
 
-		add(new HeaderContributor(charts));
+		add(new HeaderContributor(charts));		
+		frag.add(new Fragment("charts", "chartsFragment", this));
 	}
 	
-	class RepoListItem implements Serializable {
+	protected class RepoListItem implements Serializable {
 
 		private static final long serialVersionUID = 1L;
 		
@@ -442,6 +266,7 @@
 		String p; // project/path
 		String t; // time ago
 		String d; // last updated
+		String i; // information/description
 		long s; // stars
 		String c; // html color
 		int wc; // working copy, 1 = true
diff --git a/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.html b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.html
new file mode 100644
index 0000000..6b78b14
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.html
@@ -0,0 +1,158 @@
+<!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>
+<div class="container">
+
+	<div class="row" style="padding-top:5px;">
+		<div class="span7">
+			<div class="hidden-phone markdown" style="padding-bottom: 30px;" wicket:id="repositoriesMessage">[repositories message]</div>
+			<div wicket:id="activity"></div>
+		</div>
+		<div class="span5">
+			<div wicket:id="repositoryTabs"></div>
+		</div>		
+	</div>
+</div>
+
+<wicket:fragment wicket:id="anonymousTabsFragment">
+	<ul class="nav nav-pills">
+		<li class="active"><a href="#recent" data-toggle="tab"><wicket:message key="gb.active">[active]</wicket:message></a></li>
+		<li><a href="#projects" data-toggle="tab"><wicket:message key="gb.projects">[projects]</wicket:message></a></li>
+	</ul>
+	<div class="tab-content">
+		<div class="tab-pane active" id="recent">
+			<div wicket:id="active">[recently active]</div>
+		</div>
+		<div class="tab-pane" id="projects">
+			<div wicket:id="projectList">[all projects]</div>
+		</div>
+	</div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="authenticatedTabsFragment">
+	<ul class="nav nav-pills">
+		<li class="active"><a href="#starred" data-toggle="tab"><wicket:message key="gb.starred">[starred]</wicket:message></a></li>
+		<li><a href="#owned" data-toggle="tab"><wicket:message key="gb.owned">[owned]</wicket:message></a></li>
+		<li><a href="#recent" data-toggle="tab"><wicket:message key="gb.active">[active]</wicket:message></a></li>
+		<li><a href="#projects" data-toggle="tab"><wicket:message key="gb.projects">[projects]</wicket:message></a></li>
+	</ul>
+	<div class="tab-content">
+		<div class="tab-pane active" id="starred">
+			<div wicket:id="starred">[starred repositories]</div>
+		</div>
+		<div class="tab-pane" id="owned">
+			<div wicket:id="owned">[my repositories]</div>
+		</div>
+		<div class="tab-pane" id="recent">
+			<div wicket:id="active">[recently active]</div>
+		</div>
+		<div class="tab-pane" id="projects">
+			<div wicket:id="projectList">[all projects]</div>
+		</div>
+	</div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="activityFragment">
+	<div class="dashboardTitle"><wicket:message key="gb.recentActivity"></wicket:message> <small><span wicket:id="feedheader"></span></small></div>
+	<div class="hidden-phone hidden-tablet"  style="text-align:center;">
+		<div wicket:id="charts"></div>
+	</div>
+	<div wicket:id="digests"></div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="chartsFragment">
+	<table>
+		<tr>
+			<td><div id="chartRepositories" style="display:inline-block;width: 175px; height:175px"></div></td>
+			<td><div id="chartAuthors" style="display:inline-block;width: 175px; height: 175px;"></div></td>
+		</tr>
+	</table>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="starredListFragment">
+	<div ng-controller="starredCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">
+		<div class="header" style="padding: 5px;border: none;"><i class="icon-star"></i> <wicket:message key="gb.starredRepositories"></wicket:message> ({{starred.length}})
+			<div style="padding: 5px 0px 0px;">
+				<input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
+			</div>
+		</div>
+		
+		<div ng-repeat="item in starred | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
+			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>
+			<a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>
+			<span class="link hidden-tablet hidden-phone" style="color: #aaa;" title="{{item.d}}">{{item.t}}</span>
+			<span ng-show="item.s" class="pull-right">
+				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
+			</span>
+		</div>
+		
+	</div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="ownedListFragment">
+	<div ng-controller="ownedCtrl" style="border: 1px solid #ddd;border-radius: 4px;">
+		<div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.myRepositories"></wicket:message> ({{owned.length}})
+			<div class="hidden-phone pull-right">
+				<span wicket:id="create"></span>
+			</div>
+			<div style="padding: 5px 0px 0px;">
+				<input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
+			</div>
+		</div>
+		
+		<div ng-repeat="item in owned | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
+			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>
+			<a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>
+			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
+			<span ng-show="item.s" class="pull-right">
+				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
+			</span>
+		</div>		
+	</div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="activeListFragment">
+	<div ng-controller="activeCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">
+		<div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.activeRepositories"></wicket:message> ({{active.length}})
+			<div style="padding: 5px 0px 0px;">
+				<input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
+			</div>
+		</div>
+		
+		<div ng-repeat="item in active | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
+			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>
+			<a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>
+			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
+			<span ng-show="item.s" class="pull-right">
+				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
+			</span>
+		</div>		
+	</div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="projectListFragment">
+	<div ng-controller="projectListCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">
+		<div class="header" style="padding: 5px;border: none;"><i class="icon-folder-close"></i> <wicket:message key="gb.projects"></wicket:message> ({{projectList.length}})
+			<div style="padding: 5px 0px 0px;">
+				<input type="text" ng-model="query.n" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
+			</div>
+		</div>
+		
+		<div ng-repeat="item in projectList | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
+			<a href="project/{{item.p}}" title="{{item.i}}"><b>{{item.n}}</b></a>
+			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
+			<span class="pull-right">
+				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;" wicket:message="title:gb.repositories">{{item.c | number}}</span>
+			</span>
+		</div>
+	</div>
+</wicket:fragment>
+
+</wicket:extend>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java
new file mode 100644
index 0000000..b0c89b7
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2013 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.wicket.pages;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.behavior.HeaderContributor;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.eclipse.jgit.lib.Constants;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.ng.NgController;
+import com.gitblit.wicket.panels.LinkPanel;
+
+public class MyDashboardPage extends DashboardPage {
+
+	public MyDashboardPage() {
+		super();
+		setup(null);
+	}
+
+	public MyDashboardPage(PageParameters params) {
+		super(params);
+		setup(params);
+	}
+
+	@Override
+	protected boolean reusePageParameters() {
+		return true;
+	}
+
+	private void setup(PageParameters params) {
+		setupPage("", "");
+		// check to see if we should display a login message
+		boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
+		if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
+			String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");
+			String message = readMarkdown(messageSource, "login.mkd");
+			Component repositoriesMessage = new Label("repositoriesMessage", message);
+			add(repositoriesMessage.setEscapeModelStrings(false));
+			add(new Label("activity").setVisible(false));
+			add(new Label("repositoryTabs").setVisible(false));
+			return;
+		}
+
+		// Load the markdown welcome message
+		String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");
+		String message = readMarkdown(messageSource, "welcome.mkd");
+		Component repositoriesMessage = new Label("repositoriesMessage", message)
+				.setEscapeModelStrings(false).setVisible(message.length() > 0);
+		add(repositoriesMessage);
+
+		UserModel user = GitBlitWebSession.get().getUser();
+		if (user == null) {
+			user = UserModel.ANONYMOUS;
+		}
+
+		// parameters
+		int daysBack = params == null ? 0 : WicketUtils.getDaysBack(params);
+		if (daysBack < 1) {
+			daysBack = 7;
+		}
+		Calendar c = Calendar.getInstance();
+		c.add(Calendar.DATE, -1*daysBack);
+		Date minimumDate = c.getTime();
+		
+		// build repo lists 
+		List<RepositoryModel> starred = new ArrayList<RepositoryModel>();
+		List<RepositoryModel> owned = new ArrayList<RepositoryModel>();
+		List<RepositoryModel> active = new ArrayList<RepositoryModel>();
+
+		for (RepositoryModel model : getRepositoryModels()) {
+			if (model.isUsersPersonalRepository(user.username) || model.isOwner(user.username)) {
+				owned.add(model);
+			}
+			
+			if (user.getPreferences().isStarredRepository(model.name)) {
+				starred.add(model);
+			}
+			
+			if (model.isShowActivity() && model.lastChange.after(minimumDate)) {
+				active.add(model);
+			}
+		}
+		
+		Comparator<RepositoryModel> lastUpdateSort = new Comparator<RepositoryModel>() {
+			@Override
+			public int compare(RepositoryModel o1, RepositoryModel o2) {
+				return o2.lastChange.compareTo(o1.lastChange);
+			}
+		};
+		
+		Collections.sort(owned, lastUpdateSort);
+		Collections.sort(starred, lastUpdateSort);
+		Collections.sort(active, lastUpdateSort);
+		
+		Set<RepositoryModel> feed = new HashSet<RepositoryModel>();
+		feed.addAll(starred);
+		feed.addAll(owned);
+		if (feed.isEmpty()) {
+			feed.addAll(active);
+		}
+		
+		addActivity(user, feed, daysBack);
+		
+		Fragment repositoryTabs;
+		if (UserModel.ANONYMOUS.equals(user)) {
+			repositoryTabs = new Fragment("repositoryTabs", "anonymousTabsFragment", this);
+		} else {
+			repositoryTabs = new Fragment("repositoryTabs", "authenticatedTabsFragment", this);
+		}
+		
+		add(repositoryTabs);
+		
+		Fragment projectList = createProjectList();
+		repositoryTabs.add(projectList);
+		
+		// active repository list
+		if (active.isEmpty()) {
+			repositoryTabs.add(new Label("active").setVisible(false));
+		} else {
+			Fragment activeView = createNgList("active", "activeListFragment", "activeCtrl", active);
+			repositoryTabs.add(activeView);
+		}
+		
+		// starred repository list
+		if (ArrayUtils.isEmpty(starred)) {
+			repositoryTabs.add(new Label("starred").setVisible(false));
+		} else {
+			Fragment starredView = createNgList("starred", "starredListFragment", "starredCtrl", starred);
+			repositoryTabs.add(starredView);
+		}
+		
+		// owned repository list
+		if (ArrayUtils.isEmpty(owned)) {
+			repositoryTabs.add(new Label("owned").setVisible(false));
+		} else {
+			Fragment ownedView = createNgList("owned", "ownedListFragment", "ownedCtrl", owned);
+			if (user.canCreate) {
+				// create button
+				ownedView.add(new LinkPanel("create", "btn btn-mini", getString("gb.newRepository"), EditRepositoryPage.class));
+			} else {
+				// no button
+				ownedView.add(new Label("create").setVisible(false));
+			}
+			repositoryTabs.add(ownedView);
+		}
+	}
+	
+	private String readMarkdown(String messageSource, String resource) {
+		String message = "";
+		if (messageSource.equalsIgnoreCase("gitblit")) {
+			// Read default message
+			message = readDefaultMarkdown(resource);
+		} else {
+			// Read user-supplied message
+			if (!StringUtils.isEmpty(messageSource)) {
+				File file = GitBlit.getFileOrFolder(messageSource);
+				if (file.exists()) {
+					try {
+						FileInputStream fis = new FileInputStream(file);
+						InputStreamReader reader = new InputStreamReader(fis,
+								Constants.CHARACTER_ENCODING);
+						message = MarkdownUtils.transformMarkdown(reader);
+						reader.close();
+					} catch (Throwable t) {
+						message = getString("gb.failedToRead") + " " + file;
+						warn(message, t);
+					}
+				} else {
+					message = messageSource + " " + getString("gb.isNotValidFile");
+				}
+			}
+		}
+		return message;
+	}
+
+	private String readDefaultMarkdown(String file) {
+		String base = file.substring(0, file.lastIndexOf('.'));
+		String ext = file.substring(file.lastIndexOf('.'));
+		String lc = getLanguageCode();
+		String cc = getCountryCode();
+
+		// try to read file_en-us.ext, file_en.ext, file.ext
+		List<String> files = new ArrayList<String>();
+		if (!StringUtils.isEmpty(lc)) {
+			if (!StringUtils.isEmpty(cc)) {
+				files.add(base + "_" + lc + "-" + cc + ext);
+				files.add(base + "_" + lc + "_" + cc + ext);
+			}
+			files.add(base + "_" + lc + ext);
+		}
+		files.add(file);
+
+		for (String name : files) {
+			String message;
+			InputStreamReader reader = null;
+			try {
+				InputStream is = getClass().getResourceAsStream("/" + name);
+				if (is == null) {
+					continue;
+				}
+				reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);
+				message = MarkdownUtils.transformMarkdown(reader);
+				reader.close();
+				return message;
+			} catch (Throwable t) {
+				message = MessageFormat.format(getString("gb.failedToReadMessage"), file);
+				error(message, t, false);
+				return message;
+			} finally {
+				if (reader != null) {
+					try {
+						reader.close();
+					} catch (Exception e) {
+					}
+				}
+			}			
+		}
+		return MessageFormat.format(getString("gb.failedToReadMessage"), file);
+	}
+	
+	protected Fragment createProjectList() {
+		String format = GitBlit.getString(Keys.web.datestampShortFormat, "MM/dd/yy");
+		final DateFormat df = new SimpleDateFormat(format);
+		df.setTimeZone(getTimeZone());
+		List<ProjectModel> projects = GitBlit.self().getProjectModels(getRepositoryModels(), false);
+		Collections.sort(projects, new Comparator<ProjectModel>() {
+			@Override
+			public int compare(ProjectModel o1, ProjectModel o2) {
+				return o2.lastChange.compareTo(o1.lastChange);
+			}
+		});
+
+		List<ProjectListItem> list = new ArrayList<ProjectListItem>();
+		for (ProjectModel proj : projects) {
+			if (proj.isUserProject() || proj.repositories.isEmpty()) {
+				// exclude user projects from list
+				continue;
+			}
+			ProjectListItem item = new ProjectListItem();
+			item.p = proj.name;
+			item.n = StringUtils.isEmpty(proj.title) ? proj.name : proj.title;
+			item.i = proj.description;
+			item.t = getTimeUtils().timeAgo(proj.lastChange);
+			item.d = df.format(proj.lastChange);
+			item.c = proj.repositories.size();
+			list.add(item);
+		}
+		
+		// inject an AngularJS controller with static data
+		NgController ctrl = new NgController("projectListCtrl");
+		ctrl.addVariable("projectList", list);
+		add(new HeaderContributor(ctrl));
+		
+		Fragment fragment = new Fragment("projectList", "projectListFragment", this);
+		return fragment;
+	}
+	
+	protected class ProjectListItem implements Serializable {
+
+		private static final long serialVersionUID = 1L;
+		
+		String p; // path
+		String n; // name
+		String t; // time ago
+		String d; // last updated
+		String i; // information/description
+		long c;   // repository count
+	}
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/OverviewPage.html b/src/main/java/com/gitblit/wicket/pages/OverviewPage.html
index 3340e31..995f8df 100644
--- a/src/main/java/com/gitblit/wicket/pages/OverviewPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/OverviewPage.html
@@ -48,7 +48,7 @@
 		<div class="span6">
 			<div class="hidden-tablet" style="padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px solid #ddd;" wicket:id="repositoryUrlPanel">[repository url panel]</div>
 		
-			<div wicket:id="pushesPanel">[pushes panel]</div>	
+			<div wicket:id="reflogPanel">[reflog panel]</div>	
 		</div>
 	</div>
 
diff --git a/src/main/java/com/gitblit/wicket/pages/OverviewPage.java b/src/main/java/com/gitblit/wicket/pages/OverviewPage.java
index 42d20c5..8848767 100644
--- a/src/main/java/com/gitblit/wicket/pages/OverviewPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/OverviewPage.java
@@ -41,7 +41,7 @@
 import com.gitblit.wicket.charting.GoogleLineChart;
 import com.gitblit.wicket.panels.BranchesPanel;
 import com.gitblit.wicket.panels.LinkPanel;
-import com.gitblit.wicket.panels.PushesPanel;
+import com.gitblit.wicket.panels.ReflogPanel;
 import com.gitblit.wicket.panels.RepositoryUrlPanel;
 import com.gitblit.wicket.panels.TagsPanel;
 
@@ -113,9 +113,9 @@
 
 		add(new RepositoryUrlPanel("repositoryUrlPanel", false, user, model));
 
-		int pushCount = GitBlit.getInteger(Keys.web.overviewPushCount, 5);
-		PushesPanel pushes = new PushesPanel("pushesPanel", getRepositoryModel(), r, pushCount, 0, false);
-		add(pushes);
+		int reflogCount = GitBlit.getInteger(Keys.web.overviewReflogCount, 5);
+		ReflogPanel reflog = new ReflogPanel("reflogPanel", getRepositoryModel(), r, reflogCount, 0);
+		add(reflog);
 		add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs).hideIfEmpty());
 		add(new BranchesPanel("branchesPanel", getRepositoryModel(), r, numberRefs, false).hideIfEmpty());
 
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectPage.html b/src/main/java/com/gitblit/wicket/pages/ProjectPage.html
index 3b5d4e6..9fbe1b2 100644
--- a/src/main/java/com/gitblit/wicket/pages/ProjectPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectPage.html
@@ -6,66 +6,70 @@
 
 <body>
 <wicket:extend>
-
-<div class="container">
-	<div class="row">
-		<div class="span12">
-			<h2><span wicket:id="projectTitle"></span> <small><span wicket:id="projectDescription"></span></small>
-				<a class="hidden-phone hidden-tablet brand" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
-					<img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>
-				</a>
-			</h2>
-			<div class="markdown" wicket:id="projectMessage">[project message]</div>
+		<div class="container">
+			<div class="row" style="padding-top:5px;">
+				<div class="span12">
+					<div class="dashboardTitle">
+						<span wicket:id="projectTitle"></span>
+						<small><span wicket:id="projectDescription"></span></small>
+						
+						<a
+							class="hidden-phone hidden-tablet brand"
+							style="text-decoration: none;" wicket:id="syndication"
+							wicket:message="title:gb.feed"> <img
+							style="border: 0px; vertical-align: middle;" src="feed_16x16.png"></img>
+						</a>
+					</div>
+				</div>
+			</div>
+			
+			<div class="row">
+				<div class="span7">					
+					<div class="markdown" style="padding-bottom: 30px;" wicket:id="projectMessage">[project message]</div>
+					<div wicket:id="activity">[activity panel]</div>
+				</div>
+				<div class="span5">
+					<div class="markdown" wicket:id="repositoriesMessage">[repositories message]</div>
+					<div wicket:id="repositoryList">[repository list]</div>
+				</div>
+			</div>
 		</div>
+
+<wicket:fragment wicket:id="activityFragment">
+	<div class="dashboardTitle"><wicket:message key="gb.recentActivity"></wicket:message> <small><span wicket:id="feedheader"></span></small></div>
+	<div class="hidden-phone hidden-tablet"  style="text-align:center;">
+		<div wicket:id="charts"></div>
 	</div>
+	<div wicket:id="digests"></div>
+</wicket:fragment>
 
-	<div class="tabbable">
-		<!-- tab titles -->
-		<ul class="nav nav-tabs">
-			<li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li>
-			<li ><a href="#activity" data-toggle="tab"><wicket:message key="gb.activity"></wicket:message></a></li>
-		</ul>
-	
-		<!-- tab content -->
-		<div class="tab-content">
-
-			<!-- repositories tab -->
-			<div class="tab-pane active" id="repositories">
-				<!-- markdown -->
-				<div class="row">
-					<div class="span12">
-						<div class="markdown" wicket:id="repositoriesMessage">[repositories message]</div>
-					</div>
-				</div>
-				<div class="row">
-					<div class="span6" wicket:id="repositoryList">
-						<span wicket:id="repository"></span>
-					</div>
-				</div>				
-			</div>
-			
-			<!-- activity tab -->
-			<div class="tab-pane" id="activity">
-				<div class="pageTitle">
-					<h2><wicket:message key="gb.recentActivity"></wicket:message><small> <span class="hidden-phone">/ <span wicket:id="subheader">[days back]</span></span></small></h2>
-				</div>
-			
-				<div class="hidden-phone" style="height: 155px;text-align: center;">
-					<table>
-					<tr>
-						<td><span class="hidden-tablet" id="chartDaily"></span></td>
-						<td><span id="chartRepositories"></span></td>
-						<td><span id="chartAuthors"></span></td>
-					</tr>
-					</table>
-				</div>
-			
-				<div wicket:id="activityPanel">[activity panel]</div>
-			</div>
+<wicket:fragment wicket:id="chartsFragment">
+	<table>
+		<tr>
+			<td><div id="chartRepositories" style="display:inline-block;width: 175px; height:175px"></div></td>
+			<td><div id="chartAuthors" style="display:inline-block;width: 175px; height: 175px;"></div></td>
+		</tr>
+	</table>
+</wicket:fragment>
 		
+<wicket:fragment wicket:id="repositoryListFragment">
+	<div ng-controller="repositoryListCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">
+		<div class="header" style="padding: 5px;border: none;"><img style="vertical-align: middle;" src="git-black-16x16.png"/> <wicket:message key="gb.repositories"></wicket:message> ({{repositoryList.length}})
+			<div style="padding: 5px 0px 0px;">
+				<input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
+			</div>
 		</div>
+		
+		<div ng-repeat="item in repositoryList | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
+			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>
+			<a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>
+			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
+			<span ng-show="item.s" class="pull-right">
+				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
+			</span>
+		</div>		
 	</div>
-	</div>
-</wicket:extend>
+</wicket:fragment>		
+	</wicket:extend>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
index 7eba033..c64e900 100644
--- a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
@@ -15,34 +15,23 @@
  */
 package com.gitblit.wicket.pages;
 
-import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.PageParameters;
-import org.apache.wicket.behavior.HeaderContributor;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.link.ExternalLink;
-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.apache.wicket.markup.html.panel.Fragment;
 
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
 import com.gitblit.SyndicationServlet;
-import com.gitblit.models.Activity;
-import com.gitblit.models.Metric;
 import com.gitblit.models.ProjectModel;
 import com.gitblit.models.RepositoryModel;
-import com.gitblit.utils.ActivityUtils;
+import com.gitblit.models.UserModel;
 import com.gitblit.utils.MarkdownUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.GitBlitWebApp;
@@ -52,14 +41,8 @@
 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
 import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
 import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.charting.GoogleChart;
-import com.gitblit.wicket.charting.GoogleCharts;
-import com.gitblit.wicket.charting.GoogleLineChart;
-import com.gitblit.wicket.charting.GooglePieChart;
-import com.gitblit.wicket.panels.ActivityPanel;
-import com.gitblit.wicket.panels.ProjectRepositoryPanel;
 
-public class ProjectPage extends RootPage {
+public class ProjectPage extends DashboardPage {
 	
 	List<ProjectModel> projectModels = new ArrayList<ProjectModel>();
 
@@ -72,10 +55,9 @@
 		super(params);
 		setup(params);
 	}
-
-	@Override
-	protected boolean reusePageParameters() {
-		return true;
+	
+	protected Class<? extends BasePage> getRootNavPageClass() {
+		return RepositoriesPage.class;
 	}
 
 	private void setup(PageParameters params) {
@@ -118,8 +100,20 @@
 				.setEscapeModelStrings(false).setVisible(rmessage.length() > 0);
 		add(repositoriesMessage);
 
-		List<RepositoryModel> repositories = getRepositories(params);
+		UserModel user = GitBlitWebSession.get().getUser();
+		if (user == null) {
+			user = UserModel.ANONYMOUS;
+		}
+		int daysBack = params == null ? 0 : WicketUtils.getDaysBack(params);
+		if (daysBack < 1) {
+			daysBack = 7;
+		}
+		// reset the daysback parameter so that we have a complete project
+		// repository list.  the recent activity will be built up by the
+		// reflog utils.
+		params.put("db", 0);
 		
+		List<RepositoryModel> repositories = getRepositories(params);
 		Collections.sort(repositories, new Comparator<RepositoryModel>() {
 			@Override
 			public int compare(RepositoryModel o1, RepositoryModel o2) {
@@ -128,144 +122,20 @@
 			}
 		});
 
-		final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);
-		DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {
-			private static final long serialVersionUID = 1L;
-
-			public void populateItem(final Item<RepositoryModel> item) {
-				final RepositoryModel entry = item.getModelObject();
-				
-				ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository", 
-						getLocalizer(), this, showAdmin, entry, getAccessRestrictions());
-				item.add(row);
-			}
-		};
-		add(dataView);
-
-		// project activity
-		// parameters
-		int daysBack = WicketUtils.getDaysBack(params);
-		if (daysBack < 1) {
-			daysBack = 14;
-		}
-		String objectId = WicketUtils.getObject(params);
-
-		List<Activity> recentActivity = ActivityUtils.getRecentActivity(repositories, 
-				daysBack, objectId, getTimeZone());
-		if (recentActivity.size() == 0) {
-			// no activity, skip graphs and activity panel
-			add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityNone"),
-					daysBack)));
-			add(new Label("activityPanel"));
+		
+		addActivity(user, repositories, daysBack);
+		
+		if (repositories.isEmpty()) {
+			add(new Label("repositoryList").setVisible(false));
 		} else {
-			// calculate total commits and total authors
-			int totalCommits = 0;
-			Set<String> uniqueAuthors = new HashSet<String>();
-			for (Activity activity : recentActivity) {
-				totalCommits += activity.getCommitCount();
-				uniqueAuthors.addAll(activity.getAuthorMetrics().keySet());
-			}
-			int totalAuthors = uniqueAuthors.size();
-
-			// add the subheader with stat numbers
-			add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityStats"),
-					daysBack, totalCommits, totalAuthors)));
-
-			// create the activity charts
-			GoogleCharts charts = createCharts(recentActivity);
-			add(new HeaderContributor(charts));
-
-			// add activity panel
-			add(new ActivityPanel("activityPanel", recentActivity));
+			Fragment activeView = createNgList("repositoryList", "repositoryListFragment", "repositoryListCtrl", repositories);
+			add(activeView);
 		}
 	}
 	
-	/**
-	 * Creates the daily activity line chart, the active repositories pie chart,
-	 * and the active authors pie chart
-	 * 
-	 * @param recentActivity
-	 * @return
-	 */
-	private GoogleCharts createCharts(List<Activity> recentActivity) {
-		// activity metrics
-		Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();
-		Map<String, Metric> authorMetrics = new HashMap<String, Metric>();
-
-		// aggregate repository and author metrics
-		for (Activity activity : recentActivity) {
-
-			// aggregate author metrics
-			for (Map.Entry<String, Metric> entry : activity.getAuthorMetrics().entrySet()) {
-				String author = entry.getKey();
-				if (!authorMetrics.containsKey(author)) {
-					authorMetrics.put(author, new Metric(author));
-				}
-				authorMetrics.get(author).count += entry.getValue().count;
-			}
-
-			// aggregate repository metrics
-			for (Map.Entry<String, Metric> entry : activity.getRepositoryMetrics().entrySet()) {
-				String repository = StringUtils.stripDotGit(entry.getKey());
-				if (!repositoryMetrics.containsKey(repository)) {
-					repositoryMetrics.put(repository, new Metric(repository));
-				}
-				repositoryMetrics.get(repository).count += entry.getValue().count;
-			}
-		}
-
-		// build google charts
-		int w = 310;
-		int h = 150;
-		GoogleCharts charts = new GoogleCharts();
-
-		// sort in reverse-chronological order and then reverse that
-		Collections.sort(recentActivity);
-		Collections.reverse(recentActivity);
-
-		// daily line chart
-		GoogleChart chart = new GoogleLineChart("chartDaily", getString("gb.dailyActivity"), "day",
-				getString("gb.commits"));
-		SimpleDateFormat df = new SimpleDateFormat("MMM dd");
-		df.setTimeZone(getTimeZone());
-		for (Activity metric : recentActivity) {
-			chart.addValue(df.format(metric.startDate), metric.getCommitCount());
-		}
-		chart.setWidth(w);
-		chart.setHeight(h);
-		charts.addChart(chart);
-
-		// active repositories pie chart
-		chart = new GooglePieChart("chartRepositories", getString("gb.activeRepositories"),
-				getString("gb.repository"), getString("gb.commits"));
-		for (Metric metric : repositoryMetrics.values()) {
-			chart.addValue(metric.name, metric.count);
-		}
-		chart.setWidth(w);
-		chart.setHeight(h);
-		charts.addChart(chart);
-
-		// active authors pie chart
-		chart = new GooglePieChart("chartAuthors", getString("gb.activeAuthors"),
-				getString("gb.author"), getString("gb.commits"));
-		for (Metric metric : authorMetrics.values()) {
-			chart.addValue(metric.name, metric.count);
-		}
-		chart.setWidth(w);
-		chart.setHeight(h);
-		charts.addChart(chart);
-
-		return charts;
-	}
-
 	@Override
 	protected void addDropDownMenus(List<PageRegistration> pages) {
 		PageParameters params = getPageParameters();
-
-		DropDownMenuRegistration projects = new DropDownMenuRegistration("gb.projects",
-				ProjectPage.class);
-		projects.menuItems.addAll(getProjectsMenu());
-		pages.add(0, projects);
 
 		DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
 				ProjectPage.class);
@@ -277,10 +147,15 @@
 
 		if (menu.menuItems.size() > 0) {
 			// Reset Filter
-			menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+			menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), "p", WicketUtils.getProjectName(params)));
 		}
 
 		pages.add(menu);
+		
+		DropDownMenuRegistration projects = new DropDownMenuRegistration("gb.projects",
+				ProjectPage.class);
+		projects.menuItems.addAll(getProjectsMenu());
+		pages.add(projects);
 	}
 	
 	@Override
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.html b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.html
index caa0f81..2d446ec 100644
--- a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.html
@@ -7,7 +7,6 @@
 <body>
 <wicket:extend>
 <div class="container">
-	<div class="markdown" style="padding-bottom:5px;" wicket:id="projectsMessage">[projects message]</div>
 	
 	<table class="repositories">
 		<thead>
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
index 7f0b002..d0001ec 100644
--- a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
@@ -15,29 +15,17 @@
  */
 package com.gitblit.wicket.pages;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.text.MessageFormat;
-import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.wicket.Component;
 import org.apache.wicket.PageParameters;
 import org.apache.wicket.markup.html.basic.Label;
 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.apache.wicket.resource.ContextRelativeResource;
-import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
-import org.eclipse.jgit.lib.Constants;
 
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
 import com.gitblit.models.ProjectModel;
-import com.gitblit.utils.MarkdownUtils;
-import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.GitBlitWebSession;
 import com.gitblit.wicket.PageRegistration;
 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
@@ -63,6 +51,11 @@
 	}
 	
 	@Override
+	protected Class<? extends BasePage> getRootNavPageClass() {
+		return RepositoriesPage.class;
+	}
+
+	@Override
 	protected List<ProjectModel> getProjectModels() {
 		return GitBlit.self().getProjectModels(getRepositoryModels(), false);
 	}
@@ -72,20 +65,9 @@
 		// check to see if we should display a login message
 		boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
 		if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
-			String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");
-			String message = readMarkdown(messageSource, "login.mkd");
-			Component repositoriesMessage = new Label("projectsMessage", message);
-			add(repositoriesMessage.setEscapeModelStrings(false));
 			add(new Label("projectsPanel"));
 			return;
 		}
-
-		// Load the markdown welcome message
-		String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");
-		String message = readMarkdown(messageSource, "welcome.mkd");
-		Component projectsMessage = new Label("projectsMessage", message).setEscapeModelStrings(
-				false).setVisible(message.length() > 0);
-		add(projectsMessage);
 
 		List<ProjectModel> projects = getProjects(params);
 
@@ -130,20 +112,12 @@
 			}
 		};
 		add(dataView);
-
-		// push the panel down if we are hiding the admin controls and the
-		// welcome message
-		if (!showAdmin && !projectsMessage.isVisible()) {
-			WicketUtils.setCssStyle(dataView, "padding-top:5px;");
-		}
 	}
 
 	@Override
 	protected void addDropDownMenus(List<PageRegistration> pages) {
 		PageParameters params = getPageParameters();
 		
-		pages.add(0, new PageRegistration("gb.projects", ProjectsPage.class, params));
-
 		DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
 				ProjectsPage.class);
 		// preserve time filter option on repository choices
@@ -158,78 +132,5 @@
 		}
 
 		pages.add(menu);
-	}
-
-	private String readMarkdown(String messageSource, String resource) {
-		String message = "";
-		if (messageSource.equalsIgnoreCase("gitblit")) {
-			// Read default message
-			message = readDefaultMarkdown(resource);
-		} else {
-			// Read user-supplied message
-			if (!StringUtils.isEmpty(messageSource)) {
-				File file = new File(messageSource);
-				if (file.exists()) {
-					try {
-						FileInputStream fis = new FileInputStream(file);
-						InputStreamReader reader = new InputStreamReader(fis,
-								Constants.CHARACTER_ENCODING);
-						message = MarkdownUtils.transformMarkdown(reader);
-						reader.close();
-					} catch (Throwable t) {
-						message = getString("gb.failedToRead") + " " + file;
-						warn(message, t);
-					}
-				} else {
-					message = messageSource + " " + getString("gb.isNotValidFile");
-				}
-			}
-		}
-		return message;
-	}
-
-	private String readDefaultMarkdown(String file) {
-		String base = file.substring(0, file.lastIndexOf('.'));
-		String ext = file.substring(file.lastIndexOf('.'));
-		String lc = getLanguageCode();
-		String cc = getCountryCode();
-
-		// try to read file_en-us.ext, file_en.ext, file.ext
-		List<String> files = new ArrayList<String>();
-		if (!StringUtils.isEmpty(lc)) {
-			if (!StringUtils.isEmpty(cc)) {
-				files.add(base + "_" + lc + "-" + cc + ext);
-				files.add(base + "_" + lc + "_" + cc + ext);
-			}
-			files.add(base + "_" + lc + ext);
-		}
-		files.add(file);
-		
-		for (String name : files) {
-			String message;
-			InputStreamReader reader = null;
-			try {
-				ContextRelativeResource res = WicketUtils.getResource(name);
-				InputStream is = res.getResourceStream().getInputStream();
-				reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);
-				message = MarkdownUtils.transformMarkdown(reader);
-				reader.close();
-				return message;
-			} catch (ResourceStreamNotFoundException t) {
-				continue;
-			} catch (Throwable t) {
-				message = MessageFormat.format(getString("gb.failedToReadMessage"), file);
-				error(message, t, false);
-				return message;
-			} finally {
-				if (reader != null) {
-					try {
-						reader.close();
-					} catch (Exception e) {
-					}
-				}
-			}			
-		}
-		return MessageFormat.format(getString("gb.failedToReadMessage"), file);
 	}
 }
diff --git a/src/main/java/com/gitblit/wicket/pages/PushesPage.html b/src/main/java/com/gitblit/wicket/pages/ReflogPage.html
similarity index 91%
rename from src/main/java/com/gitblit/wicket/pages/PushesPage.html
rename to src/main/java/com/gitblit/wicket/pages/ReflogPage.html
index 145db6f..c0ac7eb 100644
--- a/src/main/java/com/gitblit/wicket/pages/PushesPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/ReflogPage.html
@@ -12,8 +12,8 @@
 		<a wicket:id="firstPage"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPage">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a> 
 	</div>
 	
-	<!-- push log -->
-	<div style="margin-top:5px;" wicket:id="pushesPanel">[push log panel]</div>
+	<!-- ref log -->
+	<div style="margin-top:5px;" wicket:id="reflogPanel">[reflog panel]</div>
 
 	<!-- pager links -->
 	<div style="padding-bottom:5px;">
diff --git a/src/main/java/com/gitblit/wicket/pages/PushesPage.java b/src/main/java/com/gitblit/wicket/pages/ReflogPage.java
similarity index 72%
rename from src/main/java/com/gitblit/wicket/pages/PushesPage.java
rename to src/main/java/com/gitblit/wicket/pages/ReflogPage.java
index 866964a..884f616 100644
--- a/src/main/java/com/gitblit/wicket/pages/PushesPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ReflogPage.java
@@ -19,11 +19,11 @@
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
 
 import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.panels.PushesPanel;
+import com.gitblit.wicket.panels.ReflogPanel;
 
-public class PushesPage extends RepositoryPage {
+public class ReflogPage extends RepositoryPage {
 
-	public PushesPage(PageParameters params) {
+	public ReflogPage(PageParameters params) {
 		super(params);
 
 		addSyndicationDiscoveryLink();
@@ -32,24 +32,24 @@
 		int prevPage = Math.max(0, pageNumber - 1);
 		int nextPage = pageNumber + 1;
 
-		PushesPanel pushesPanel = new PushesPanel("pushesPanel", getRepositoryModel(), getRepository(), -1,
-				pageNumber - 1, false);
-		boolean hasMore = pushesPanel.hasMore();
-		add(pushesPanel);
+		ReflogPanel reflogPanel = new ReflogPanel("reflogPanel", getRepositoryModel(), getRepository(), -1,
+				pageNumber - 1);
+		boolean hasMore = reflogPanel.hasMore();
+		add(reflogPanel);
 
-		add(new BookmarkablePageLink<Void>("firstPage", PushesPage.class,
+		add(new BookmarkablePageLink<Void>("firstPage", ReflogPage.class,
 				WicketUtils.newObjectParameter(repositoryName, objectId))
 				.setEnabled(pageNumber > 1));
-		add(new BookmarkablePageLink<Void>("prevPage", PushesPage.class,
+		add(new BookmarkablePageLink<Void>("prevPage", ReflogPage.class,
 				WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage))
 				.setEnabled(pageNumber > 1));
-		add(new BookmarkablePageLink<Void>("nextPage", PushesPage.class,
+		add(new BookmarkablePageLink<Void>("nextPage", ReflogPage.class,
 				WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage))
 				.setEnabled(hasMore));
 	}
 
 	@Override
 	protected String getPageName() {
-		return getString("gb.pushes");
+		return getString("gb.reflog");
 	}
 }
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
index a15dd91..9657301 100644
--- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -60,7 +60,7 @@
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.DeepCopier;
 import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.PushLogUtils;
+import com.gitblit.utils.RefLogUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.utils.TicgitUtils;
 import com.gitblit.wicket.GitBlitWebSession;
@@ -187,12 +187,12 @@
 		RepositoryModel model = getRepositoryModel();
 
 		// standard links
-		if (PushLogUtils.getPushLogBranch(r) == null) {
+		if (RefLogUtils.getRefLogBranch(r) == null) {
 			pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
 		} else {
 			pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
 //			pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));
-			pages.put("pushes", new PageRegistration("gb.pushes", PushesPage.class, params));
+			pages.put("reflog", new PageRegistration("gb.reflog", ReflogPage.class, params));
 		}		
 		pages.put("commits", new PageRegistration("gb.commits", LogPage.class, params));
 		pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java
index d21be36..8c1a568 100644
--- a/src/main/java/com/gitblit/wicket/pages/RootPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -119,7 +119,7 @@
 		// navigation links
 		List<PageRegistration> pages = new ArrayList<PageRegistration>();
 		if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
-			pages.add(new PageRegistration(GitBlitWebSession.get().isLoggedIn() ? "gb.myDashboard" : "gb.dashboard", DashboardPage.class,
+			pages.add(new PageRegistration(GitBlitWebSession.get().isLoggedIn() ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,
 					getRootPageParameters()));
 			pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
 					getRootPageParameters()));
diff --git a/src/main/java/com/gitblit/wicket/panels/PushesPanel.html b/src/main/java/com/gitblit/wicket/panels/DigestsPanel.html
similarity index 65%
rename from src/main/java/com/gitblit/wicket/panels/PushesPanel.html
rename to src/main/java/com/gitblit/wicket/panels/DigestsPanel.html
index fb67cfc..53a8104 100644
--- a/src/main/java/com/gitblit/wicket/panels/PushesPanel.html
+++ b/src/main/java/com/gitblit/wicket/panels/DigestsPanel.html
@@ -6,15 +6,15 @@
 
 <body>
 <wicket:panel>
-<div wicket:id="push" class="push">
+<div wicket:id="change" class="reflog">
 	<table style="padding: 3px 0px;">
 	<tr>
-		<td class="icon hidden-phone"><i wicket:id="pushIcon"></i></td>
+		<td class="icon hidden-phone"><i wicket:id="changeIcon"></i></td>
 		<td style="padding-left: 7px;vertical-align:middle;">
 			<div>
-				<span style="color:#aaa;" wicket:id="whenPushed"></span> <span wicket:id="refRewind" class="alert alert-error" style="padding: 1px 5px;font-size: 10px;font-weight: bold;margin-left: 10px;">[rewind]</span>
+				<span class="when" wicket:id="whenChanged"></span>
 			</div>
-			<div style="font-weight:bold;"><span wicket:id="whoPushed">[pusher]</span> <span wicket:id="whatPushed"></span><span wicket:id="refPushed"></span> <span wicket:id="repoPreposition"></span> <span wicket:id="repoPushed"></span> <span wicket:id="byAuthors"></span></div>
+			<div style="font-weight:bold;"><span wicket:id="whoChanged">[who changed]</span> <span wicket:id="whatChanged"></span><span wicket:id="refChanged"></span> <span wicket:id="repoPreposition"></span> <span wicket:id="repoChanged"></span> <span wicket:id="byAuthors"></span></div>
 		</td>
 	</tr>
 	<tr>
@@ -37,7 +37,6 @@
 	</tr>	
 	</table>
 </div>
-<div wicket:id="morePushes">[more...]</div>
 </wicket:panel>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/panels/DigestsPanel.java b/src/main/java/com/gitblit/wicket/panels/DigestsPanel.java
new file mode 100644
index 0000000..0f380a4
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/panels/DigestsPanel.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2013 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.wicket.panels;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+
+import com.gitblit.Constants;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.DailyLogEntry;
+import com.gitblit.models.RepositoryCommit;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TimeUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.pages.CommitPage;
+import com.gitblit.wicket.pages.ComparePage;
+import com.gitblit.wicket.pages.SummaryPage;
+import com.gitblit.wicket.pages.TagPage;
+import com.gitblit.wicket.pages.TreePage;
+import com.gitblit.wicket.pages.UserPage;
+
+public class DigestsPanel extends BasePanel {
+
+	private static final long serialVersionUID = 1L;
+
+	private final boolean hasChanges;
+	
+	private boolean hasMore;
+
+	public DigestsPanel(String wicketId, List<DailyLogEntry> digests) {
+		super(wicketId);
+		hasChanges = digests.size() > 0;
+
+		final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);
+
+		String dateFormat = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");
+		final TimeZone timezone = getTimeZone();
+		final DateFormat df = new SimpleDateFormat(dateFormat);
+		df.setTimeZone(timezone);
+		final Calendar cal = Calendar.getInstance(timezone);
+		
+		ListDataProvider<DailyLogEntry> dp = new ListDataProvider<DailyLogEntry>(digests);
+		DataView<DailyLogEntry> pushView = new DataView<DailyLogEntry>("change", dp) {
+			private static final long serialVersionUID = 1L;
+
+			public void populateItem(final Item<DailyLogEntry> logItem) {
+				final DailyLogEntry change = logItem.getModelObject();
+				String fullRefName = change.getChangedRefs().get(0);
+				String shortRefName = fullRefName;
+				boolean isTag = false;
+				if (shortRefName.startsWith(Constants.R_HEADS)) {
+					shortRefName = shortRefName.substring(Constants.R_HEADS.length());
+				} else if (shortRefName.startsWith(Constants.R_TAGS)) {
+					shortRefName = shortRefName.substring(Constants.R_TAGS.length());
+					isTag = true;
+				}
+				
+				String fuzzydate;
+				TimeUtils tu = getTimeUtils();
+				Date pushDate = change.date;
+				if (TimeUtils.isToday(pushDate, timezone)) {
+					fuzzydate = tu.today();
+				} else if (TimeUtils.isYesterday(pushDate, timezone)) {
+					fuzzydate = tu.yesterday();
+				} else {
+					// calculate a fuzzy time ago date
+                	cal.setTime(pushDate);
+                	cal.set(Calendar.HOUR_OF_DAY, 0);
+                	cal.set(Calendar.MINUTE, 0);
+                	cal.set(Calendar.SECOND, 0);
+                	cal.set(Calendar.MILLISECOND, 0);
+                	pushDate = cal.getTime();
+					fuzzydate = getTimeUtils().timeAgo(pushDate);
+				}
+				logItem.add(new Label("whenChanged", fuzzydate + ", " + df.format(pushDate)));
+
+				Label changeIcon = new Label("changeIcon");
+				// use the repository hash color to differentiate the icon.
+                String color = StringUtils.getColor(StringUtils.stripDotGit(change.repository));
+                WicketUtils.setCssStyle(changeIcon, "color: " + color);
+
+				if (isTag) {
+					WicketUtils.setCssClass(changeIcon, "iconic-tag");
+				} else {
+					WicketUtils.setCssClass(changeIcon, "iconic-loop");
+				}
+				logItem.add(changeIcon);
+
+                if (!isTag) {
+                	logItem.add(new Label("whoChanged").setVisible(false));
+                } else {
+                	if (change.user.username.equals(change.user.emailAddress) && change.user.emailAddress.indexOf('@') > -1) {
+                		// username is an email address can not link - 1.2.1 push log bug
+                		logItem.add(new Label("whoChanged", change.user.getDisplayName()));
+                	} else {
+                		// link to user account page
+                		logItem.add(new LinkPanel("whoChanged", null, change.user.getDisplayName(),
+                				UserPage.class, WicketUtils.newUsernameParameter(change.user.username)));
+                	}
+                }
+				
+				String preposition = "gb.of";
+				boolean isDelete = false;
+				String what;
+				String by = null;
+				switch(change.getChangeType(fullRefName)) {
+				case CREATE:
+					if (isTag) {
+						// new tag
+						what = getString("gb.createdNewTag");
+						preposition = "gb.in";
+					} else {
+						// new branch
+						what = getString("gb.createdNewBranch");
+						preposition = "gb.in";
+					}
+					break;
+				case DELETE:
+					isDelete = true;
+					if (isTag) {
+						what = getString("gb.deletedTag");
+					} else {
+						what = getString("gb.deletedBranch");
+					}
+					preposition = "gb.from";
+					break;
+				default:
+					what = MessageFormat.format(change.getCommitCount() > 1 ? getString("gb.commitsTo") : getString("gb.oneCommitTo"), change.getCommitCount());
+					
+					if (change.getAuthorCount() == 1) {
+						by = MessageFormat.format(getString("gb.byOneAuthor"), change.getAuthorIdent().getName());
+					} else {
+						by = MessageFormat.format(getString("gb.byNAuthors"), change.getAuthorCount());	
+					}
+					break;
+				}
+				logItem.add(new Label("whatChanged", what));
+				logItem.add(new Label("byAuthors", by).setVisible(!StringUtils.isEmpty(by)));
+				
+				if (isDelete) {
+					// can't link to deleted ref
+					logItem.add(new Label("refChanged", shortRefName));
+				} else if (isTag) {
+					// link to tag
+					logItem.add(new LinkPanel("refChanged", null, shortRefName,
+							TagPage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));
+				} else {
+					// link to tree
+					logItem.add(new LinkPanel("refChanged", null, shortRefName,
+						TreePage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));
+				}
+				
+				// to/from/etc
+				logItem.add(new Label("repoPreposition", getString(preposition)));
+				String repoName = StringUtils.stripDotGit(change.repository);
+				logItem.add(new LinkPanel("repoChanged", null, repoName,
+						SummaryPage.class, WicketUtils.newRepositoryParameter(change.repository)));
+				
+				int maxCommitCount = 5;
+				List<RepositoryCommit> commits = change.getCommits();
+				if (commits.size() > maxCommitCount) {
+					commits = new ArrayList<RepositoryCommit>(commits.subList(0,  maxCommitCount));					
+				}
+				
+				// compare link
+				String compareLinkText = null;
+				if ((change.getCommitCount() <= maxCommitCount) && (change.getCommitCount() > 1)) {
+					compareLinkText = MessageFormat.format(getString("gb.viewComparison"), commits.size());
+				} else if (change.getCommitCount() > maxCommitCount) {
+					int diff = change.getCommitCount() - maxCommitCount;
+					compareLinkText = MessageFormat.format(diff > 1 ? getString("gb.nMoreCommits") : getString("gb.oneMoreCommit"), diff);
+				}
+				if (StringUtils.isEmpty(compareLinkText)) {
+					logItem.add(new Label("compareLink").setVisible(false));
+				} else {
+					String endRangeId = change.getNewId(fullRefName);
+					String startRangeId = change.getOldId(fullRefName);
+					logItem.add(new LinkPanel("compareLink", null, compareLinkText, ComparePage.class, WicketUtils.newRangeParameter(change.repository, startRangeId, endRangeId)));
+				}
+				
+				final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);
+				
+				ListDataProvider<RepositoryCommit> cdp = new ListDataProvider<RepositoryCommit>(commits);
+				DataView<RepositoryCommit> commitsView = new DataView<RepositoryCommit>("commit", cdp) {
+					private static final long serialVersionUID = 1L;
+
+					public void populateItem(final Item<RepositoryCommit> commitItem) {
+						final RepositoryCommit commit = commitItem.getModelObject();
+
+						// author gravatar
+						commitItem.add(new GravatarImage("commitAuthor", commit.getAuthorIdent().getName(),
+								commit.getAuthorIdent().getEmailAddress(), null, 16, false, false));
+						
+						// merge icon
+						if (commit.getParentCount() > 1) {
+							commitItem.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));
+						} else {
+							commitItem.add(WicketUtils.newBlankImage("commitIcon"));
+						}
+
+						// short message
+						String shortMessage = commit.getShortMessage();
+						String trimmedMessage = shortMessage;
+						if (commit.getRefs() != null && commit.getRefs().size() > 0) {
+							trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);
+						} else {
+							trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);
+						}
+						LinkPanel shortlog = new LinkPanel("commitShortMessage", "list",
+								trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
+										change.repository, commit.getName()));
+						if (!shortMessage.equals(trimmedMessage)) {
+							WicketUtils.setHtmlTooltip(shortlog, shortMessage);
+						}
+						commitItem.add(shortlog);
+
+						// commit hash link
+						LinkPanel commitHash = new LinkPanel("hashLink", null, commit.getName().substring(0, hashLen),
+								CommitPage.class, WicketUtils.newObjectParameter(
+										change.repository, commit.getName()));
+						WicketUtils.setCssClass(commitHash, "shortsha1");
+						WicketUtils.setHtmlTooltip(commitHash, commit.getName());
+						commitItem.add(commitHash);
+						
+						if (showSwatch) {
+							// set repository color
+							String color = StringUtils.getColor(StringUtils.stripDotGit(change.repository));
+							WicketUtils.setCssStyle(commitItem, MessageFormat.format("border-left: 2px solid {0};", color));
+						}
+					}
+				};
+
+				logItem.add(commitsView);
+			}
+		};
+		
+		add(pushView);
+	}
+
+	public boolean hasMore() {
+		return hasMore;
+	}
+	
+	public boolean hideIfEmpty() {
+		setVisible(hasChanges);
+		return hasChanges;
+	}
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html
index e9196cd..02d67e3 100644
--- a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html
+++ b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html
@@ -70,8 +70,6 @@
 					<wicket:message key="gb.lastChange">[last change]</wicket:message> <span wicket:id="repositoryLastChange">[last change]</span>,
 					<span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span>
 				</div>
-        
-				<div class="hidden-phone hidden-tablet" style="padding-top: 5px;" wicket:id="repositoryPrimaryUrl">[repository primary url]</div>
 			</div>
 		</div>
 	</div>
diff --git a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
index e7fe017..37641d3 100644
--- a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
+++ b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
@@ -192,7 +192,5 @@
 		}
 
 		add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0)));
-
-		add(new RepositoryUrlPanel("repositoryPrimaryUrl", true, user, entry));
 	}
 }
diff --git a/src/main/java/com/gitblit/wicket/panels/PushesPanel.java b/src/main/java/com/gitblit/wicket/panels/PushesPanel.java
deleted file mode 100644
index 5c473f7..0000000
--- a/src/main/java/com/gitblit/wicket/panels/PushesPanel.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright 2013 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.wicket.panels;
-
-import java.text.DateFormat;
-import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.TimeZone;
-
-import org.apache.wicket.markup.html.basic.Label;
-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.apache.wicket.model.StringResourceModel;
-import org.eclipse.jgit.lib.Repository;
-
-import com.gitblit.Constants;
-import com.gitblit.GitBlit;
-import com.gitblit.Keys;
-import com.gitblit.models.DailyLogEntry;
-import com.gitblit.models.PushLogEntry;
-import com.gitblit.models.RepositoryCommit;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.utils.PushLogUtils;
-import com.gitblit.utils.StringUtils;
-import com.gitblit.utils.TimeUtils;
-import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.pages.CommitPage;
-import com.gitblit.wicket.pages.ComparePage;
-import com.gitblit.wicket.pages.PushesPage;
-import com.gitblit.wicket.pages.SummaryPage;
-import com.gitblit.wicket.pages.TagPage;
-import com.gitblit.wicket.pages.TreePage;
-import com.gitblit.wicket.pages.UserPage;
-
-public class PushesPanel extends BasePanel {
-
-	private static final long serialVersionUID = 1L;
-
-	private final boolean hasPushes;
-	
-	private boolean hasMore;
-
-	public PushesPanel(String wicketId, final RepositoryModel model, Repository r, int limit, int pageOffset, boolean showRepo) {
-		super(wicketId);
-		boolean pageResults = limit <= 0;
-		int pushesPerPage = GitBlit.getInteger(Keys.web.pushesPerPage, 10);
-		if (pushesPerPage <= 1) {
-			pushesPerPage = 10;
-		}
-
-		List<PushLogEntry> pushes;
-		if (pageResults) {
-			pushes = PushLogUtils.getPushLogByRef(model.name, r, pageOffset * pushesPerPage, pushesPerPage);
-		} else {
-			pushes = PushLogUtils.getPushLogByRef(model.name, r, limit);
-		}
-
-		// inaccurate way to determine if there are more commits.
-		// works unless commits.size() represents the exact end.
-		hasMore = pushes.size() >= pushesPerPage;
-		hasPushes = pushes.size() > 0;
-		
-		setup(pushes, showRepo);
-		
-		// determine to show pager, more, or neither
-		if (limit <= 0) {
-			// no display limit
-			add(new Label("morePushes").setVisible(false));
-		} else {
-			if (pageResults) {
-				// paging
-				add(new Label("morePushes").setVisible(false));
-			} else {
-				// more
-				if (pushes.size() == limit) {
-					// show more
-					add(new LinkPanel("morePushes", "link", new StringResourceModel("gb.morePushes",
-							this, null), PushesPage.class,
-							WicketUtils.newRepositoryParameter(model.name)));
-				} else {
-					// no more
-					add(new Label("morePushes").setVisible(false));
-				}
-			}
-		}
-	}
-	
-	public PushesPanel(String wicketId, List<PushLogEntry> pushes) {
-		super(wicketId);
-		hasPushes = pushes.size() > 0;
-		setup(pushes, true);
-		add(new Label("morePushes").setVisible(false));
-	}
-	
-	protected void setup(List<PushLogEntry> pushes, final boolean showRepo) {
-		final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);
-
-		String dateFormat = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");
-		final TimeZone timezone = getTimeZone();
-		final DateFormat df = new SimpleDateFormat(dateFormat);
-		df.setTimeZone(timezone);
-		final Calendar cal = Calendar.getInstance(timezone);
-		
-		ListDataProvider<PushLogEntry> dp = new ListDataProvider<PushLogEntry>(pushes);
-		DataView<PushLogEntry> pushView = new DataView<PushLogEntry>("push", dp) {
-			private static final long serialVersionUID = 1L;
-
-			public void populateItem(final Item<PushLogEntry> pushItem) {
-				final PushLogEntry push = pushItem.getModelObject();
-				String fullRefName = push.getChangedRefs().get(0);
-				String shortRefName = fullRefName;
-				boolean isTag = false;
-				boolean isPull = false;
-				if (shortRefName.startsWith(Constants.R_HEADS)) {
-					shortRefName = shortRefName.substring(Constants.R_HEADS.length());
-				} else if (shortRefName.startsWith(Constants.R_TAGS)) {
-					shortRefName = shortRefName.substring(Constants.R_TAGS.length());
-					isTag = true;
-				} else if (shortRefName.startsWith(Constants.R_PULL)) {
-					shortRefName = "#" + shortRefName.substring(Constants.R_PULL.length());
-					if (shortRefName.endsWith("/head")) {
-						// strip pull request head from name 
-						shortRefName = shortRefName.substring(0, shortRefName.length() - "/head".length());
-					}					
-					isPull = true;
-				}
-				boolean isDigest = push instanceof DailyLogEntry;
-				
-				String fuzzydate;
-				TimeUtils tu = getTimeUtils();
-				Date pushDate = push.date;
-				if (TimeUtils.isToday(pushDate, timezone)) {
-					fuzzydate = tu.today();
-				} else if (TimeUtils.isYesterday(pushDate, timezone)) {
-					fuzzydate = tu.yesterday();
-				} else {
-					// calculate a fuzzy time ago date
-                	cal.setTime(pushDate);
-                	cal.set(Calendar.HOUR_OF_DAY, 0);
-                	cal.set(Calendar.MINUTE, 0);
-                	cal.set(Calendar.SECOND, 0);
-                	cal.set(Calendar.MILLISECOND, 0);
-                	pushDate = cal.getTime();
-					fuzzydate = getTimeUtils().timeAgo(pushDate);
-				}
-				pushItem.add(new Label("whenPushed", fuzzydate + ", " + df.format(pushDate)));
-
-				Label pushIcon = new Label("pushIcon");
-				if (showRepo) {
-					// if we are showing the repo, we are showing multiple
-					// repos.  use the repository hash color to differentiate
-					// the icon.
-	                String color = StringUtils.getColor(StringUtils.stripDotGit(push.repository));
-	                WicketUtils.setCssStyle(pushIcon, "color: " + color);
-				}
-				if (isTag) {
-					WicketUtils.setCssClass(pushIcon, "iconic-tag");
-				} else if (isPull) {
-					WicketUtils.setCssClass(pushIcon, "iconic-share");
-				} else if (isDigest) {
-					WicketUtils.setCssClass(pushIcon, "iconic-loop");
-				} else {
-					WicketUtils.setCssClass(pushIcon, "iconic-upload");
-				}
-				pushItem.add(pushIcon);
-
-                if (isDigest && !isTag) {
-                	pushItem.add(new Label("whoPushed").setVisible(false));
-                } else {
-                	if (push.user.username.equals(push.user.emailAddress) && push.user.emailAddress.indexOf('@') > -1) {
-                		// username is an email address - 1.2.1 push log bug
-                		pushItem.add(new Label("whoPushed", push.user.getDisplayName()));
-                	} else {
-                		// link to user account page
-                		pushItem.add(new LinkPanel("whoPushed", null, push.user.getDisplayName(),
-                				UserPage.class, WicketUtils.newUsernameParameter(push.user.username)));
-                	}
-                }
-				
-				String preposition = "gb.of";
-				boolean isDelete = false;
-				boolean isRewind = false;
-				String what;
-				String by = null;
-				switch(push.getChangeType(fullRefName)) {
-				case CREATE:
-					if (isTag) {
-						// new tag
-						if (isDigest) {
-							what = getString("gb.createdNewTag");
-							preposition = "gb.in";
-						} else {
-							what = getString("gb.pushedNewTag");
-							preposition = "gb.to";
-						}
-					} else if (isPull) {
-						// merged pull request
-						what = getString("gb.mergedPullRequest");
-						preposition = "gb.in";
-					} else {
-						// new branch
-						if (isDigest) {
-							what = getString("gb.createdNewBranch");
-							preposition = "gb.in";
-						} else {
-							what = getString("gb.pushedNewBranch");
-							preposition = "gb.to";
-						}
-					}
-					break;
-				case DELETE:
-					isDelete = true;
-					if (isTag) {
-						what = getString("gb.deletedTag");
-					} if (isPull) {
-						what = getString("gb.deletedTag");
-					} else {
-						what = getString("gb.deletedBranch");
-					}
-					preposition = "gb.from";
-					break;
-				case UPDATE_NONFASTFORWARD:
-					isRewind = true;
-				default:
-					if (isDigest) {
-						what = MessageFormat.format(push.getCommitCount() > 1 ? getString("gb.commitsTo") : getString("gb.oneCommitTo"), push.getCommitCount());
-					} else {
-						what = MessageFormat.format(push.getCommitCount() > 1 ? getString("gb.pushedNCommitsTo") : getString("gb.pushedOneCommitTo") , push.getCommitCount());
-					}
-					
-					if (push.getAuthorCount() == 1) {
-						by = MessageFormat.format(getString("gb.byOneAuthor"), push.getAuthorIdent().getName());
-					} else {
-						by = MessageFormat.format(getString("gb.byNAuthors"), push.getAuthorCount());	
-					}
-					break;
-				}
-				pushItem.add(new Label("whatPushed", what));
-				pushItem.add(new Label("byAuthors", by).setVisible(!StringUtils.isEmpty(by)));
-				
-				pushItem.add(new Label("refRewind", getString("gb.rewind")).setVisible(isRewind));
-				
-				if (isDelete) {
-					// can't link to deleted ref
-					pushItem.add(new Label("refPushed", shortRefName));
-				} else if (isTag) {
-					// link to tag
-					pushItem.add(new LinkPanel("refPushed", null, shortRefName,
-							TagPage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));
-				} else if (isPull) {
-					// link to pull request
-					pushItem.add(new LinkPanel("refPushed", null, shortRefName,
-							TagPage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));
-				} else {
-					// link to tree
-					pushItem.add(new LinkPanel("refPushed", null, shortRefName,
-						TreePage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));
-				}
-				
-				if (showRepo) {
-					// to/from/etc
-					pushItem.add(new Label("repoPreposition", getString(preposition)));
-
-					String repoName = StringUtils.stripDotGit(push.repository);
-					pushItem.add(new LinkPanel("repoPushed", null, repoName,
-							SummaryPage.class, WicketUtils.newRepositoryParameter(push.repository)));
-				} else {
-					// do not display repository name if we are viewing the push
-					// log of a repository.
-					pushItem.add(new Label("repoPreposition").setVisible(false));
-					pushItem.add(new Label("repoPushed").setVisible(false));
-				}
-				
-				int maxCommitCount = 5;
-				List<RepositoryCommit> commits = push.getCommits();
-				if (commits.size() > maxCommitCount) {
-					commits = new ArrayList<RepositoryCommit>(commits.subList(0,  maxCommitCount));					
-				}
-				
-				// compare link
-				String compareLinkText = null;
-				if ((push.getCommitCount() <= maxCommitCount) && (push.getCommitCount() > 1)) {
-					compareLinkText = MessageFormat.format(getString("gb.viewComparison"), commits.size());
-				} else if (push.getCommitCount() > maxCommitCount) {
-					int diff = push.getCommitCount() - maxCommitCount;
-					compareLinkText = MessageFormat.format(diff > 1 ? getString("gb.nMoreCommits") : getString("gb.oneMoreCommit"), diff);
-				}
-				if (StringUtils.isEmpty(compareLinkText)) {
-					pushItem.add(new Label("compareLink").setVisible(false));
-				} else {
-					String endRangeId = push.getNewId(fullRefName);
-					String startRangeId = push.getOldId(fullRefName);
-					pushItem.add(new LinkPanel("compareLink", null, compareLinkText, ComparePage.class, WicketUtils.newRangeParameter(push.repository, startRangeId, endRangeId)));
-				}
-				
-				final boolean showSwatch = showRepo && GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);
-				
-				ListDataProvider<RepositoryCommit> cdp = new ListDataProvider<RepositoryCommit>(commits);
-				DataView<RepositoryCommit> commitsView = new DataView<RepositoryCommit>("commit", cdp) {
-					private static final long serialVersionUID = 1L;
-
-					public void populateItem(final Item<RepositoryCommit> commitItem) {
-						final RepositoryCommit commit = commitItem.getModelObject();
-
-						// author gravatar
-						commitItem.add(new GravatarImage("commitAuthor", commit.getAuthorIdent().getName(),
-								commit.getAuthorIdent().getEmailAddress(), null, 16, false, false));
-						
-						// merge icon
-						if (commit.getParentCount() > 1) {
-							commitItem.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));
-						} else {
-							commitItem.add(WicketUtils.newBlankImage("commitIcon"));
-						}
-
-						// short message
-						String shortMessage = commit.getShortMessage();
-						String trimmedMessage = shortMessage;
-						if (commit.getRefs() != null && commit.getRefs().size() > 0) {
-							trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);
-						} else {
-							trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);
-						}
-						LinkPanel shortlog = new LinkPanel("commitShortMessage", "list",
-								trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
-										push.repository, commit.getName()));
-						if (!shortMessage.equals(trimmedMessage)) {
-							WicketUtils.setHtmlTooltip(shortlog, shortMessage);
-						}
-						commitItem.add(shortlog);
-
-						// commit hash link
-						LinkPanel commitHash = new LinkPanel("hashLink", null, commit.getName().substring(0, hashLen),
-								CommitPage.class, WicketUtils.newObjectParameter(
-										push.repository, commit.getName()));
-						WicketUtils.setCssClass(commitHash, "shortsha1");
-						WicketUtils.setHtmlTooltip(commitHash, commit.getName());
-						commitItem.add(commitHash);
-						
-						if (showSwatch) {
-							// set repository color
-							String color = StringUtils.getColor(StringUtils.stripDotGit(push.repository));
-							WicketUtils.setCssStyle(commitItem, MessageFormat.format("border-left: 2px solid {0};", color));
-						}
-					}
-				};
-
-				pushItem.add(commitsView);
-			}
-		};
-		
-		add(pushView);
-	}
-
-	public boolean hasMore() {
-		return hasMore;
-	}
-	
-	public boolean hideIfEmpty() {
-		setVisible(hasPushes);
-		return hasPushes;
-	}
-}
diff --git a/src/main/java/com/gitblit/wicket/panels/PushesPanel.html b/src/main/java/com/gitblit/wicket/panels/ReflogPanel.html
similarity index 65%
copy from src/main/java/com/gitblit/wicket/panels/PushesPanel.html
copy to src/main/java/com/gitblit/wicket/panels/ReflogPanel.html
index fb67cfc..0148d89 100644
--- a/src/main/java/com/gitblit/wicket/panels/PushesPanel.html
+++ b/src/main/java/com/gitblit/wicket/panels/ReflogPanel.html
@@ -6,15 +6,15 @@
 
 <body>
 <wicket:panel>
-<div wicket:id="push" class="push">
+<div wicket:id="change" class="reflog">
 	<table style="padding: 3px 0px;">
 	<tr>
-		<td class="icon hidden-phone"><i wicket:id="pushIcon"></i></td>
+		<td class="icon hidden-phone"><i wicket:id="changeIcon"></i></td>
 		<td style="padding-left: 7px;vertical-align:middle;">
 			<div>
-				<span style="color:#aaa;" wicket:id="whenPushed"></span> <span wicket:id="refRewind" class="alert alert-error" style="padding: 1px 5px;font-size: 10px;font-weight: bold;margin-left: 10px;">[rewind]</span>
+				<span class="when" wicket:id="whenChanged"></span> <span wicket:id="refRewind" class="alert alert-error" style="padding: 1px 5px;font-size: 10px;font-weight: bold;margin-left: 10px;">[rewind]</span>
 			</div>
-			<div style="font-weight:bold;"><span wicket:id="whoPushed">[pusher]</span> <span wicket:id="whatPushed"></span><span wicket:id="refPushed"></span> <span wicket:id="repoPreposition"></span> <span wicket:id="repoPushed"></span> <span wicket:id="byAuthors"></span></div>
+			<div style="font-weight:bold;"><span wicket:id="whoChanged">[change author]</span> <span wicket:id="whatChanged"></span><span wicket:id="refChanged"></span> <span wicket:id="byAuthors"></span></div>
 		</td>
 	</tr>
 	<tr>
@@ -37,7 +37,7 @@
 	</tr>	
 	</table>
 </div>
-<div wicket:id="morePushes">[more...]</div>
+<div wicket:id="moreChanges">[more...]</div>
 </wicket:panel>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/panels/ReflogPanel.java b/src/main/java/com/gitblit/wicket/panels/ReflogPanel.java
new file mode 100644
index 0000000..048ce1b
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/panels/ReflogPanel.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2013 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.wicket.panels;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.apache.wicket.markup.html.basic.Label;
+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.apache.wicket.model.StringResourceModel;
+import org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.Constants;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.RefLogEntry;
+import com.gitblit.models.RepositoryCommit;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.utils.RefLogUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TimeUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.pages.CommitPage;
+import com.gitblit.wicket.pages.ComparePage;
+import com.gitblit.wicket.pages.ReflogPage;
+import com.gitblit.wicket.pages.TagPage;
+import com.gitblit.wicket.pages.TreePage;
+import com.gitblit.wicket.pages.UserPage;
+
+public class ReflogPanel extends BasePanel {
+
+	private static final long serialVersionUID = 1L;
+
+	private final boolean hasChanges;
+	
+	private boolean hasMore;
+
+	public ReflogPanel(String wicketId, final RepositoryModel model, Repository r, int limit, int pageOffset) {
+		super(wicketId);
+		boolean pageResults = limit <= 0;
+		int changesPerPage = GitBlit.getInteger(Keys.web.reflogChangesPerPage, 10);
+		if (changesPerPage <= 1) {
+			changesPerPage = 10;
+		}
+
+		List<RefLogEntry> changes;
+		if (pageResults) {
+			changes = RefLogUtils.getLogByRef(model.name, r, pageOffset * changesPerPage, changesPerPage);
+		} else {
+			changes = RefLogUtils.getLogByRef(model.name, r, limit);
+		}
+
+		// inaccurate way to determine if there are more commits.
+		// works unless commits.size() represents the exact end.
+		hasMore = changes.size() >= changesPerPage;
+		hasChanges = changes.size() > 0;
+		
+		setup(changes);
+		
+		// determine to show pager, more, or neither
+		if (limit <= 0) {
+			// no display limit
+			add(new Label("moreChanges").setVisible(false));
+		} else {
+			if (pageResults) {
+				// paging
+				add(new Label("moreChanges").setVisible(false));
+			} else {
+				// more
+				if (changes.size() == limit) {
+					// show more
+					add(new LinkPanel("moreChanges", "link", new StringResourceModel("gb.moreChanges",
+							this, null), ReflogPage.class,
+							WicketUtils.newRepositoryParameter(model.name)));
+				} else {
+					// no more
+					add(new Label("moreChanges").setVisible(false));
+				}
+			}
+		}
+	}
+	
+	public ReflogPanel(String wicketId, List<RefLogEntry> changes) {
+		super(wicketId);
+		hasChanges = changes.size() > 0;
+		setup(changes);
+		add(new Label("moreChanges").setVisible(false));
+	}
+	
+	protected void setup(List<RefLogEntry> changes) {
+		final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);
+
+		String dateFormat = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");
+		final TimeZone timezone = getTimeZone();
+		final DateFormat df = new SimpleDateFormat(dateFormat);
+		df.setTimeZone(timezone);
+		final Calendar cal = Calendar.getInstance(timezone);
+		
+		ListDataProvider<RefLogEntry> dp = new ListDataProvider<RefLogEntry>(changes);
+		DataView<RefLogEntry> changeView = new DataView<RefLogEntry>("change", dp) {
+			private static final long serialVersionUID = 1L;
+
+			public void populateItem(final Item<RefLogEntry> changeItem) {
+				final RefLogEntry change = changeItem.getModelObject();
+				String fullRefName = change.getChangedRefs().get(0);
+				String shortRefName = fullRefName;
+				boolean isTag = false;
+				if (shortRefName.startsWith(Constants.R_HEADS)) {
+					shortRefName = shortRefName.substring(Constants.R_HEADS.length());
+				} else if (shortRefName.startsWith(Constants.R_TAGS)) {
+					shortRefName = shortRefName.substring(Constants.R_TAGS.length());
+					isTag = true;
+				}
+				
+				String fuzzydate;
+				TimeUtils tu = getTimeUtils();
+				Date changeDate = change.date;
+				if (TimeUtils.isToday(changeDate, timezone)) {
+					fuzzydate = tu.today();
+				} else if (TimeUtils.isYesterday(changeDate, timezone)) {
+					fuzzydate = tu.yesterday();
+				} else {
+					// calculate a fuzzy time ago date
+                	cal.setTime(changeDate);
+                	cal.set(Calendar.HOUR_OF_DAY, 0);
+                	cal.set(Calendar.MINUTE, 0);
+                	cal.set(Calendar.SECOND, 0);
+                	cal.set(Calendar.MILLISECOND, 0);
+                	changeDate = cal.getTime();
+					fuzzydate = getTimeUtils().timeAgo(changeDate);
+				}
+				changeItem.add(new Label("whenChanged", fuzzydate + ", " + df.format(changeDate)));
+
+				Label changeIcon = new Label("changeIcon");
+				if (isTag) {
+					WicketUtils.setCssClass(changeIcon, "iconic-tag");
+				} else {
+					WicketUtils.setCssClass(changeIcon, "iconic-upload");
+				}
+				changeItem.add(changeIcon);
+
+				if (change.user.username.equals(change.user.emailAddress) && change.user.emailAddress.indexOf('@') > -1) {
+					// username is an email address - 1.2.1 push log bug
+					changeItem.add(new Label("whoChanged", change.user.getDisplayName()));
+				} else {
+					// link to user account page
+					changeItem.add(new LinkPanel("whoChanged", null, change.user.getDisplayName(),
+							UserPage.class, WicketUtils.newUsernameParameter(change.user.username)));
+				}
+				
+				boolean isDelete = false;
+				boolean isRewind = false;
+				String what;
+				String by = null;
+				switch(change.getChangeType(fullRefName)) {
+				case CREATE:
+					if (isTag) {
+						// new tag
+						what = getString("gb.pushedNewTag");
+					} else {
+						// new branch
+						what = getString("gb.pushedNewBranch");
+					}
+					break;
+				case DELETE:
+					isDelete = true;
+					if (isTag) {
+						what = getString("gb.deletedTag");
+					} else {
+						what = getString("gb.deletedBranch");
+					}
+					break;
+				case UPDATE_NONFASTFORWARD:
+					isRewind = true;
+				default:
+					what = MessageFormat.format(change.getCommitCount() > 1 ? getString("gb.pushedNCommitsTo") : getString("gb.pushedOneCommitTo") , change.getCommitCount());
+					
+					if (change.getAuthorCount() == 1) {
+						by = MessageFormat.format(getString("gb.byOneAuthor"), change.getAuthorIdent().getName());
+					} else {
+						by = MessageFormat.format(getString("gb.byNAuthors"), change.getAuthorCount());	
+					}
+					break;
+				}
+				changeItem.add(new Label("whatChanged", what));
+				changeItem.add(new Label("byAuthors", by).setVisible(!StringUtils.isEmpty(by)));
+				
+				changeItem.add(new Label("refRewind", getString("gb.rewind")).setVisible(isRewind));
+				
+				if (isDelete) {
+					// can't link to deleted ref
+					changeItem.add(new Label("refChanged", shortRefName));
+				} else if (isTag) {
+					// link to tag
+					changeItem.add(new LinkPanel("refChanged", null, shortRefName,
+							TagPage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));
+				} else {
+					// link to tree
+					changeItem.add(new LinkPanel("refChanged", null, shortRefName,
+						TreePage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));
+				}
+				
+				int maxCommitCount = 5;
+				List<RepositoryCommit> commits = change.getCommits();
+				if (commits.size() > maxCommitCount) {
+					commits = new ArrayList<RepositoryCommit>(commits.subList(0,  maxCommitCount));					
+				}
+				
+				// compare link
+				String compareLinkText = null;
+				if ((change.getCommitCount() <= maxCommitCount) && (change.getCommitCount() > 1)) {
+					compareLinkText = MessageFormat.format(getString("gb.viewComparison"), commits.size());
+				} else if (change.getCommitCount() > maxCommitCount) {
+					int diff = change.getCommitCount() - maxCommitCount;
+					compareLinkText = MessageFormat.format(diff > 1 ? getString("gb.nMoreCommits") : getString("gb.oneMoreCommit"), diff);
+				}
+				if (StringUtils.isEmpty(compareLinkText)) {
+					changeItem.add(new Label("compareLink").setVisible(false));
+				} else {
+					String endRangeId = change.getNewId(fullRefName);
+					String startRangeId = change.getOldId(fullRefName);
+					changeItem.add(new LinkPanel("compareLink", null, compareLinkText, ComparePage.class, WicketUtils.newRangeParameter(change.repository, startRangeId, endRangeId)));
+				}
+				
+				ListDataProvider<RepositoryCommit> cdp = new ListDataProvider<RepositoryCommit>(commits);
+				DataView<RepositoryCommit> commitsView = new DataView<RepositoryCommit>("commit", cdp) {
+					private static final long serialVersionUID = 1L;
+
+					public void populateItem(final Item<RepositoryCommit> commitItem) {
+						final RepositoryCommit commit = commitItem.getModelObject();
+
+						// author gravatar
+						commitItem.add(new GravatarImage("commitAuthor", commit.getAuthorIdent().getName(),
+								commit.getAuthorIdent().getEmailAddress(), null, 16, false, false));
+						
+						// merge icon
+						if (commit.getParentCount() > 1) {
+							commitItem.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));
+						} else {
+							commitItem.add(WicketUtils.newBlankImage("commitIcon"));
+						}
+
+						// short message
+						String shortMessage = commit.getShortMessage();
+						String trimmedMessage = shortMessage;
+						if (commit.getRefs() != null && commit.getRefs().size() > 0) {
+							trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);
+						} else {
+							trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);
+						}
+						LinkPanel shortlog = new LinkPanel("commitShortMessage", "list",
+								trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
+										change.repository, commit.getName()));
+						if (!shortMessage.equals(trimmedMessage)) {
+							WicketUtils.setHtmlTooltip(shortlog, shortMessage);
+						}
+						commitItem.add(shortlog);
+
+						// commit hash link
+						LinkPanel commitHash = new LinkPanel("hashLink", null, commit.getName().substring(0, hashLen),
+								CommitPage.class, WicketUtils.newObjectParameter(
+										change.repository, commit.getName()));
+						WicketUtils.setCssClass(commitHash, "shortsha1");
+						WicketUtils.setHtmlTooltip(commitHash, commit.getName());
+						commitItem.add(commitHash);
+					}
+				};
+
+				changeItem.add(commitsView);
+			}
+		};
+		
+		add(changeView);
+	}
+
+	public boolean hasMore() {
+		return hasMore;
+	}
+	
+	public boolean hideIfEmpty() {
+		setVisible(hasChanges);
+		return hasChanges;
+	}
+}
diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css
index 351d43f..6074b36 100644
--- a/src/main/resources/gitblit.css
+++ b/src/main/resources/gitblit.css
@@ -129,23 +129,39 @@
     background-color: #002060;
 }
 
-div.push {
+div.reflog {
     border-bottom: 1px solid #ddd;
 	margin-bottom: 5px;
 	padding-bottom: 5px;
 }
 
-div.push .icon {
+div.reflog .icon {
     font-size: 42px;
     line-height: 42px;
 }
 
-div.push i {
+div.reflog .when {
+	color: #aaa;
+}
+
+div.reflog i {
     font-size: 42px;
     color: #bbb;
     vertical-align: middle;
 }
 
+div.dashboardTitle {
+	font-size: 1.75em;
+	padding-bottom: 5px;
+	margin-bottom: 10px;
+	border-bottom: 1px solid #ccc;
+}
+
+div.dashboardTitle small {
+	color: #888;
+	font-size: 0.7em;
+}
+
 .repositorynavbar {
 	background-color: #fbfbfb;
 	border-bottom: 1px solid #ccc;
diff --git a/src/test/java/com/gitblit/tests/GitServletTest.java b/src/test/java/com/gitblit/tests/GitServletTest.java
index c1aaf1a..7607fbf 100644
--- a/src/test/java/com/gitblit/tests/GitServletTest.java
+++ b/src/test/java/com/gitblit/tests/GitServletTest.java
@@ -38,12 +38,12 @@
 import com.gitblit.Constants.AuthorizationControl;
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
-import com.gitblit.models.PushLogEntry;
+import com.gitblit.models.RefLogEntry;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.PushLogUtils;
+import com.gitblit.utils.RefLogUtils;
 
 public class GitServletTest {
 
@@ -788,7 +788,7 @@
 		String name = "refchecks/ticgit.git";
 		File refChecks = new File(GitBlitSuite.REPOSITORIES, name);
 		Repository repository = new FileRepositoryBuilder().setGitDir(refChecks).build();
-		List<PushLogEntry> pushes = PushLogUtils.getPushLog(name, repository);
+		List<RefLogEntry> pushes = RefLogUtils.getRefLog(name, repository);
 		GitBlitSuite.close(repository);
 		assertTrue("Repository has an empty push log!", pushes.size() > 0);
 	}
diff --git a/src/test/java/com/gitblit/tests/PushLogTest.java b/src/test/java/com/gitblit/tests/PushLogTest.java
index 0f46b53..f5d5965 100644
--- a/src/test/java/com/gitblit/tests/PushLogTest.java
+++ b/src/test/java/com/gitblit/tests/PushLogTest.java
@@ -25,8 +25,8 @@
 import org.eclipse.jgit.util.FS;
 import org.junit.Test;
 
-import com.gitblit.models.PushLogEntry;
-import com.gitblit.utils.PushLogUtils;
+import com.gitblit.models.RefLogEntry;
+import com.gitblit.utils.RefLogUtils;
 
 public class PushLogTest {
 
@@ -35,7 +35,7 @@
 		String name = "~james/helloworld.git";
 		File gitDir = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, name), FS.DETECTED);
 		Repository repository = new FileRepositoryBuilder().setGitDir(gitDir).build();
-		List<PushLogEntry> pushes = PushLogUtils.getPushLog(name, repository);
+		List<RefLogEntry> pushes = RefLogUtils.getRefLog(name, repository);
 		GitBlitSuite.close(repository);
 	}
 }
\ No newline at end of file

--
Gitblit v1.9.1