*/\r
package com.codecommit.wicket;\r
\r
+import java.io.Serializable;\r
+\r
/**\r
* @author Daniel Spiewak\r
*/\r
-public class LineStyle implements ILineStyle {\r
+public class LineStyle implements ILineStyle, Serializable {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+ \r
private int blankLength = -1;\r
private int segmentLength = -1;\r
private int thickness = -1;\r
*/\r
package com.codecommit.wicket;\r
\r
+import java.io.Serializable;\r
+\r
/**\r
* @author Daniel Spiewak\r
*/\r
-public enum MarkerType {\r
+public enum MarkerType implements Serializable {\r
ARROW("a"),\r
CROSS("c"),\r
DIAMOND("d"),\r
package com.codecommit.wicket;\r
\r
import java.awt.Color;\r
+import java.io.Serializable;\r
\r
/**\r
* @author Daniel Spiewak\r
*/\r
-public class ShapeMarker implements IShapeMarker {\r
+public class ShapeMarker implements IShapeMarker, Serializable {\r
+ \r
+ private static final long serialVersionUID = 1L;\r
+ \r
private Color color;\r
private int index = -1;\r
private double point = -1;\r
\r
# The number of commits to display on the summary page\r
# Value must exceed 0 else default of 20 is used\r
-web.summaryCommitCount = 20\r
+web.summaryCommitCount = 16\r
\r
# The number of tags/heads to display on the summary page\r
# Value must exceed 0 else default of 5 is used\r
\r
# Specify the interface for Jetty to bind the standard connector.\r
# You may specify an ip or an empty value to bind to all interfaces. \r
-server.httpBindInterface = \r
+server.httpBindInterface = localhost\r
\r
# Specify the interface for Jetty to bind the secure connector.\r
# You may specify an ip or an empty value to bind to all interfaces.\r
-server.httpsBindInterface = \r
+server.httpsBindInterface = localhost\r
\r
# Password for SSL keystore (keystore password and certificate password must match)\r
server.storePassword = dosomegit\r
r.close();\r
assertTrue("Could not get last repository change date!", date != null);\r
}\r
+ \r
+ public void testFirstCommit() throws Exception {\r
+ Repository r = getRepository();\r
+ RevCommit commit = JGitUtils.getFirstCommit(r, null);\r
+ r.close();\r
+ assertTrue("Could not get first commit!", commit != null);\r
+ System.out.println(commit.getName() + " " + commit.getShortMessage());\r
+ }\r
\r
public void testRetrieveRevObject() throws Exception {\r
Repository r = getRepository();\r
import org.eclipse.jgit.revwalk.RevBlob;\r
import org.eclipse.jgit.revwalk.RevCommit;\r
import org.eclipse.jgit.revwalk.RevObject;\r
+import org.eclipse.jgit.revwalk.RevSort;\r
import org.eclipse.jgit.revwalk.RevTree;\r
import org.eclipse.jgit.revwalk.RevWalk;\r
import org.eclipse.jgit.revwalk.filter.RevFilter;\r
return list;\r
}\r
\r
+ public static RevCommit getFirstCommit(Repository r, String branch) {\r
+ if (StringUtils.isEmpty(branch)) {\r
+ branch = Constants.HEAD;\r
+ }\r
+ try {\r
+ RevWalk walk = new RevWalk(r);\r
+ walk.sort(RevSort.REVERSE);\r
+ RevCommit head = walk.parseCommit(r.resolve(branch));\r
+ walk.markStart(head);\r
+ RevCommit commit = walk.next();\r
+ walk.dispose();\r
+ return commit;\r
+ } catch (Throwable t) {\r
+ LOGGER.error("Failed to determine first commit", t);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ public static Date getFirstChange(Repository r, String branch) {\r
+ try {\r
+ RevCommit commit = getFirstCommit(r, branch);\r
+ return getCommitDate(commit);\r
+ } catch (Throwable t) {\r
+ LOGGER.error("Failed to determine first change", t);\r
+ }\r
+ return null;\r
+ }\r
+\r
public static Date getLastChange(Repository r) {\r
RevCommit commit = getCommit(r, Constants.HEAD);\r
return getCommitDate(commit);\r
}\r
return null;\r
}\r
- \r
+\r
public String toString() {\r
return name().toLowerCase();\r
}\r
case AUTHOR:\r
return (commit.getAuthorIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getAuthorIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);\r
case COMMITTER:\r
- return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1)|| (commit.getCommitterIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);\r
+ return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getCommitterIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);\r
case COMMIT:\r
return commit.getFullMessage().toLowerCase().indexOf(lcValue) > -1;\r
}\r
\r
public static List<Metric> getDateMetrics(Repository r) {\r
final List<RefModel> tags = getTags(r, -1);\r
- final Map<String, Metric> map = new HashMap<String, Metric>();\r
+ final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();\r
+ for (RefModel tag : tags) {\r
+ tagMap.put(tag.getCommitId(), tag);\r
+ }\r
+ Metric total = new Metric("TOTAL");\r
+ final Map<String, Metric> metricMap = new HashMap<String, Metric>();\r
try {\r
- DateFormat df = new SimpleDateFormat("yyyy-MM");\r
RevWalk walk = new RevWalk(r);\r
ObjectId object = r.resolve(Constants.HEAD);\r
- walk.markStart(walk.parseCommit(object));\r
+\r
+ RevCommit firstCommit = getFirstCommit(r, Constants.HEAD);\r
+ RevCommit lastCommit = walk.parseCommit(object);\r
+ int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime()) / (60 * 60 * 24);\r
+ total.duration = diffDays;\r
+ DateFormat df;\r
+ if (diffDays <= 90) {\r
+ // Days\r
+ df = new SimpleDateFormat("yyyy-MM-dd");\r
+ } else if (diffDays > 90 && diffDays < 365) {\r
+ // Weeks\r
+ df = new SimpleDateFormat("yyyy-MM (w)");\r
+ } else {\r
+ // Months\r
+ df = new SimpleDateFormat("yyyy-MM");\r
+ }\r
+ walk.markStart(lastCommit);\r
+ \r
Iterable<RevCommit> revlog = walk;\r
for (RevCommit rev : revlog) {\r
Date d = getCommitDate(rev);\r
String p = df.format(d);\r
- if (!map.containsKey(p))\r
- map.put(p, new Metric(p));\r
- map.get(p).count++;\r
- }\r
+ if (!metricMap.containsKey(p))\r
+ metricMap.put(p, new Metric(p));\r
+ Metric m = metricMap.get(p); \r
+ m.count++;\r
+ total.count++;\r
+ if (tagMap.containsKey(rev.getId())) {\r
+ m.tag++;\r
+ total.tag++;\r
+ }\r
+ } \r
} catch (Throwable t) {\r
LOGGER.error("Failed to mine log history for metrics", t);\r
}\r
- List<String> keys = new ArrayList<String>(map.keySet());\r
+ List<String> keys = new ArrayList<String>(metricMap.keySet());\r
Collections.sort(keys);\r
List<Metric> metrics = new ArrayList<Metric>();\r
for (String key : keys) {\r
- metrics.add(map.get(key));\r
+ metrics.add(metricMap.get(key));\r
}\r
+ metrics.add(0, total);\r
return metrics;\r
}\r
\r
return now.getDate() == (date.getDate() + 1) && now.getMonth() == date.getMonth() && now.getYear() == date.getYear();\r
}\r
\r
+ public static String duration(int days) {\r
+ if (days <= 60) {\r
+ return days + (days > 1 ? " days" : " day");\r
+ } else if (days <= 365) {\r
+ int rem = days % 30;\r
+ return (days / 30) + " months, " + rem + (rem > 1 ? " days" : " day");\r
+ } else {\r
+ int years = days / 365;\r
+ int rem = days % 365;\r
+ String yearsString = years + (years > 1 ? " years" : " year");\r
+ if (rem < 30) {\r
+ if (rem == 0) {\r
+ return yearsString;\r
+ } else {\r
+ return yearsString + ", " + rem + (rem > 1 ? " days" : " day");\r
+ }\r
+ } else {\r
+ int months = rem / 30;\r
+ int remDays = (rem % 30);\r
+ String monthsString;\r
+ if (months == 0) {\r
+ monthsString = yearsString;\r
+ } else {\r
+ monthsString = yearsString + ", " + months + (months > 1 ? " months" : " month");\r
+ }\r
+ if (remDays == 0) {\r
+ return monthsString;\r
+ } else {\r
+ return monthsString + ", " + remDays + (remDays > 1 ? " days":" day");\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
public static int minutesAgo(Date date, long endTime, boolean roundup) {\r
long diff = endTime - date.getTime();\r
int mins = (int) (diff / min);\r
gb.addition = addition\r
gb.modification = modification\r
gb.deletion = deletion\r
-gb.rename = rename
\ No newline at end of file
+gb.rename = rename\r
+gb.stats = stats
\ No newline at end of file
\r
public String name;\r
public double count;\r
+ public double tag;\r
+ public int duration;\r
\r
public Metric(String name) {\r
this.name = name;\r
\r
<div style="clear:both;">\r
<!-- Repository Activity Chart --> \r
- <div style="width:400px;float:right;">\r
+ <div style="float:right;">\r
<img class="activityGraph" wicket:id="commitsChart" />\r
</div> \r
\r
<!-- Repository info -->\r
- <div style="margin-right:410px;"> \r
+ <div> \r
<table class="plain">\r
- <tr><th><wicket:message key="gb.description">description</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>\r
- <tr><th><wicket:message key="gb.owner">owner</wicket:message></th><td><span wicket:id="repositoryOwner">[repository owner]</span></td></tr>\r
- <tr><th><wicket:message key="gb.lastChange">last change</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>\r
- <tr><th><wicket:message key="gb.url">URL</wicket:message></th><td><span wicket:id="repositoryCloneUrl">[repository clone url]</span></td></tr>\r
+ <tr><th><wicket:message key="gb.description">[description]</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>\r
+ <tr><th><wicket:message key="gb.owner">[owner]</wicket:message></th><td><span wicket:id="repositoryOwner">[repository owner]</span></td></tr>\r
+ <tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>\r
+ <tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="repositoryStats">[repository stats]</span></td></tr>\r
+ <tr><th><wicket:message key="gb.url">[URL]</wicket:message></th><td><span wicket:id="repositoryCloneUrl">[repository clone url]</span></td></tr>\r
</table>\r
</div>\r
</div>\r
package com.gitblit.wicket.pages;\r
\r
+import java.awt.Color;\r
import java.awt.Dimension;\r
+import java.text.MessageFormat;\r
import java.util.List;\r
\r
import org.apache.wicket.PageParameters;\r
import com.codecommit.wicket.ChartProvider;\r
import com.codecommit.wicket.ChartType;\r
import com.codecommit.wicket.IChartData;\r
+import com.codecommit.wicket.LineStyle;\r
+import com.codecommit.wicket.MarkerType;\r
+import com.codecommit.wicket.ShapeMarker;\r
import com.gitblit.GitBlit;\r
import com.gitblit.Keys;\r
import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.TimeUtils;\r
import com.gitblit.wicket.RepositoryPage;\r
import com.gitblit.wicket.WicketUtils;\r
import com.gitblit.wicket.models.Metric;\r
}\r
\r
Repository r = getRepository();\r
- List<Metric> metrics = JGitUtils.getDateMetrics(r);\r
-\r
- long numberOfCommits = 0;\r
- for (Metric m : metrics) {\r
- numberOfCommits += m.count;\r
+ List<Metric> metrics = null;\r
+ Metric metricsTotal = null;\r
+ if (GitBlit.self().settings().getBoolean(Keys.web.generateActivityGraph, true)) {\r
+ metrics = JGitUtils.getDateMetrics(r);\r
+ metricsTotal = metrics.remove(0);\r
}\r
\r
// repository description\r
add(new Label("repositoryOwner", JGitUtils.getRepositoryOwner(r)));\r
\r
add(WicketUtils.createTimestampLabel("repositoryLastChange", JGitUtils.getLastChange(r), getTimeZone()));\r
+ if (metricsTotal == null) {\r
+ add(new Label("repositoryStats", ""));\r
+ } else {\r
+ add(new Label("repositoryStats", MessageFormat.format("{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag, TimeUtils.duration(metricsTotal.duration))));\r
+ }\r
add(new Label("repositoryCloneUrl", GitBlit.self().getCloneUrl(repositoryName)));\r
\r
add(new LogPanel("commitsPanel", repositoryName, null, r, numberCommits, 0));\r
if (GitBlit.self().settings().getBoolean(Keys.web.generateActivityGraph, true)) {\r
IChartData data = getChartData(metrics);\r
\r
- ChartProvider provider = new ChartProvider(new Dimension(400, 80), ChartType.LINE, data);\r
+ ChartProvider provider = new ChartProvider(new Dimension(400, 100), ChartType.LINE, data);\r
ChartAxis dateAxis = new ChartAxis(ChartAxisType.BOTTOM);\r
dateAxis.setLabels(new String[] { metrics.get(0).name, metrics.get(metrics.size() / 2).name, metrics.get(metrics.size() - 1).name });\r
provider.addAxis(dateAxis);\r
commitAxis.setLabels(new String[] { "", String.valueOf((int) maxValue(metrics)) });\r
provider.addAxis(commitAxis);\r
\r
+ provider.setLineStyles(new LineStyle[] {new LineStyle(2, 4, 0), new LineStyle(0, 4, 1)}); \r
+ provider.addShapeMarker(new ShapeMarker(MarkerType.DIAMOND, Color.BLUE, 1, -1, 5));\r
+ \r
add(new Chart("commitsChart", provider));\r
} else {\r
add(new ContextImage("commitsChart", "blank.png"));\r
}\r
\r
protected IChartData getChartData(List<Metric> metrics) {\r
- final double[] counts = new double[metrics.size()];\r
+ final double[] commits = new double[metrics.size()];\r
+ final double[] tags = new double[metrics.size()];\r
int i = 0;\r
double max = 0;\r
for (Metric m : metrics) {\r
- counts[i++] = m.count;\r
+ commits[i] = m.count;\r
+ if (m.tag > 0) {\r
+ tags[i] = m.count;\r
+ } else {\r
+ tags[i] = -1d;\r
+ }\r
max = Math.max(max, m.count);\r
+ i++;\r
}\r
- final double dmax = max;\r
- IChartData data = new AbstractChartData() {\r
+ IChartData data = new AbstractChartData(max) {\r
private static final long serialVersionUID = 1L;\r
\r
public double[][] getData() {\r
- return new double[][] { counts };\r
- }\r
-\r
- public double getMax() {\r
- return dmax;\r
+ return new double[][] { commits, tags };\r
}\r
};\r
return data;\r