/* | |
* Copyright 2011 gitblit.com. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.gitblit.utils; | |
import java.text.DateFormat; | |
import java.text.MessageFormat; | |
import java.text.SimpleDateFormat; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.TimeZone; | |
import org.eclipse.jgit.lib.ObjectId; | |
import org.eclipse.jgit.lib.Repository; | |
import org.eclipse.jgit.revwalk.RevCommit; | |
import org.eclipse.jgit.revwalk.RevWalk; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import com.gitblit.models.Metric; | |
import com.gitblit.models.RefModel; | |
/** | |
* Utility class for collecting metrics on a branch, tag, or other ref within | |
* the repository. | |
* | |
* @author James Moger | |
* | |
*/ | |
public class MetricUtils { | |
private static final Logger LOGGER = LoggerFactory.getLogger(MetricUtils.class); | |
/** | |
* Log an error message and exception. | |
* | |
* @param t | |
* @param repository | |
* if repository is not null it MUST be the {0} parameter in the | |
* pattern. | |
* @param pattern | |
* @param objects | |
*/ | |
private static void error(Throwable t, Repository repository, String pattern, Object... objects) { | |
List<Object> parameters = new ArrayList<Object>(); | |
if (objects != null && objects.length > 0) { | |
for (Object o : objects) { | |
parameters.add(o); | |
} | |
} | |
if (repository != null) { | |
parameters.add(0, repository.getDirectory().getAbsolutePath()); | |
} | |
LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t); | |
} | |
/** | |
* Returns the list of metrics for the specified commit reference, branch, | |
* or tag within the repository. If includeTotal is true, the total of all | |
* the metrics will be included as the first element in the returned list. | |
* | |
* If the dateformat is unspecified an attempt is made to determine an | |
* appropriate date format by determining the time difference between the | |
* first commit on the branch and the most recent commit. This assumes that | |
* the commits are linear. | |
* | |
* @param repository | |
* @param objectId | |
* if null or empty, HEAD is assumed. | |
* @param includeTotal | |
* @param dateFormat | |
* @param timezone | |
* @return list of metrics | |
*/ | |
public static List<Metric> getDateMetrics(Repository repository, String objectId, | |
boolean includeTotal, String dateFormat, TimeZone timezone) { | |
Metric total = new Metric("TOTAL"); | |
final Map<String, Metric> metricMap = new HashMap<String, Metric>(); | |
if (JGitUtils.hasCommits(repository)) { | |
final List<RefModel> tags = JGitUtils.getTags(repository, true, -1); | |
final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>(); | |
for (RefModel tag : tags) { | |
tagMap.put(tag.getReferencedObjectId(), tag); | |
} | |
RevWalk revWalk = null; | |
try { | |
// resolve branch | |
ObjectId branchObject; | |
if (StringUtils.isEmpty(objectId)) { | |
branchObject = JGitUtils.getDefaultBranch(repository); | |
} else { | |
branchObject = repository.resolve(objectId); | |
} | |
revWalk = new RevWalk(repository); | |
RevCommit lastCommit = revWalk.parseCommit(branchObject); | |
revWalk.markStart(lastCommit); | |
DateFormat df; | |
if (StringUtils.isEmpty(dateFormat)) { | |
// dynamically determine date format | |
RevCommit firstCommit = JGitUtils.getFirstCommit(repository, | |
branchObject.getName()); | |
int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime()) | |
/ (60 * 60 * 24); | |
total.duration = diffDays; | |
if (diffDays <= 365) { | |
// Days | |
df = new SimpleDateFormat("yyyy-MM-dd"); | |
} else { | |
// Months | |
df = new SimpleDateFormat("yyyy-MM"); | |
} | |
} else { | |
// use specified date format | |
df = new SimpleDateFormat(dateFormat); | |
} | |
df.setTimeZone(timezone); | |
Iterable<RevCommit> revlog = revWalk; | |
for (RevCommit rev : revlog) { | |
Date d = JGitUtils.getCommitDate(rev); | |
String p = df.format(d); | |
if (!metricMap.containsKey(p)) { | |
metricMap.put(p, new Metric(p)); | |
} | |
Metric m = metricMap.get(p); | |
m.count++; | |
total.count++; | |
if (tagMap.containsKey(rev.getId())) { | |
m.tag++; | |
total.tag++; | |
} | |
} | |
} catch (Throwable t) { | |
error(t, repository, "{0} failed to mine log history for date metrics of {1}", | |
objectId); | |
} finally { | |
if (revWalk != null) { | |
revWalk.dispose(); | |
} | |
} | |
} | |
List<String> keys = new ArrayList<String>(metricMap.keySet()); | |
Collections.sort(keys); | |
List<Metric> metrics = new ArrayList<Metric>(); | |
for (String key : keys) { | |
metrics.add(metricMap.get(key)); | |
} | |
if (includeTotal) { | |
metrics.add(0, total); | |
} | |
return metrics; | |
} | |
/** | |
* Returns a list of author metrics for the specified repository. | |
* | |
* @param repository | |
* @param objectId | |
* if null or empty, HEAD is assumed. | |
* @param byEmailAddress | |
* group metrics by author email address otherwise by author name | |
* @return list of metrics | |
*/ | |
public static List<Metric> getAuthorMetrics(Repository repository, String objectId, | |
boolean byEmailAddress) { | |
final Map<String, Metric> metricMap = new HashMap<String, Metric>(); | |
if (JGitUtils.hasCommits(repository)) { | |
try { | |
RevWalk walk = new RevWalk(repository); | |
// resolve branch | |
ObjectId branchObject; | |
if (StringUtils.isEmpty(objectId)) { | |
branchObject = JGitUtils.getDefaultBranch(repository); | |
} else { | |
branchObject = repository.resolve(objectId); | |
} | |
RevCommit lastCommit = walk.parseCommit(branchObject); | |
walk.markStart(lastCommit); | |
Iterable<RevCommit> revlog = walk; | |
for (RevCommit rev : revlog) { | |
String p; | |
if (byEmailAddress) { | |
p = rev.getAuthorIdent().getEmailAddress().toLowerCase(); | |
if (StringUtils.isEmpty(p)) { | |
p = rev.getAuthorIdent().getName().toLowerCase(); | |
} | |
} else { | |
p = rev.getAuthorIdent().getName().toLowerCase(); | |
if (StringUtils.isEmpty(p)) { | |
p = rev.getAuthorIdent().getEmailAddress().toLowerCase(); | |
} | |
} | |
p = p.replace('\n',' ').replace('\r', ' ').trim(); | |
if (!metricMap.containsKey(p)) { | |
metricMap.put(p, new Metric(p)); | |
} | |
Metric m = metricMap.get(p); | |
m.count++; | |
} | |
} catch (Throwable t) { | |
error(t, repository, "{0} failed to mine log history for author metrics of {1}", | |
objectId); | |
} | |
} | |
List<String> keys = new ArrayList<String>(metricMap.keySet()); | |
Collections.sort(keys); | |
List<Metric> metrics = new ArrayList<Metric>(); | |
for (String key : keys) { | |
metrics.add(metricMap.get(key)); | |
} | |
return metrics; | |
} | |
} |