/* * 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 parameters = new ArrayList(); 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 getDateMetrics(Repository repository, String objectId, boolean includeTotal, String dateFormat, TimeZone timezone) { Metric total = new Metric("TOTAL"); final Map metricMap = new HashMap(); if (JGitUtils.hasCommits(repository)) { final List tags = JGitUtils.getTags(repository, true, -1); final Map tagMap = new HashMap(); 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 revlog = revWalk; for (RevCommit rev : revlog) { Date d = JGitUtils.getAuthorDate(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 keys = new ArrayList(metricMap.keySet()); Collections.sort(keys); List metrics = new ArrayList(); 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 getAuthorMetrics(Repository repository, String objectId, boolean byEmailAddress) { final Map metricMap = new HashMap(); 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 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 keys = new ArrayList(metricMap.keySet()); Collections.sort(keys); List metrics = new ArrayList(); for (String key : keys) { metrics.add(metricMap.get(key)); } return metrics; } }