diff --git a/tracext/git/git_fs.py b/tracext/git/git_fs.py
index 0d3088b..2a78d7f 100644
--- a/tracext/git/git_fs.py
+++ b/tracext/git/git_fs.py
@@ -14,7 +14,7 @@
from trac.core import *
from trac.util import TracError, shorten_line
-from trac.util.datefmt import FixedOffset, to_timestamp, format_datetime
+from trac.util.datefmt import FixedOffset, to_timestamp, format_datetime, parse_date
from trac.util.text import to_unicode
from trac.versioncontrol.api import \
Changeset, Node, Repository, IRepositoryConnector, NoSuchChangeset, NoSuchNode
@@ -24,6 +24,9 @@
from trac.config import BoolOption, IntOption, PathOption, Option
from trac.web.chrome import Chrome
+from trac.versioncontrol.api import RepositoryManager
+from trac.timeline.api import ITimelineEventProvider
+
from genshi.builder import tag
from datetime import datetime
@@ -107,7 +110,7 @@ def _parse_user_time(s):
return user, time
class GitConnector(Component):
- implements(IRepositoryConnector, IWikiSyntaxProvider)
+ implements(IRepositoryConnector, IWikiSyntaxProvider, ITimelineEventProvider)
def __init__(self):
self._version = None
@@ -124,6 +127,68 @@ def __init__(self):
self.log.error("GIT version %s installed not compatible (need >= %s)" %
(self._version['v_str'], self._version['v_min_str']))
+ #########################
+ # ITimelineEventProvider
+
+ def get_timeline_filters(self, req):
+ # (internal name, human-readable name, on-by-default)
+ return [('git-tags', 'Git Tags', True)]
+
+ def get_timeline_events(self, req, start, stop, filters):
+ # if git-tags is enabled, get all the git repositories, then
+ # get the tags from each repository and compare their timestamp
+ # to the requested time range. If they are between start and
+ # stop, return them using Trac's event tuple
+ tags = []
+ if 'git-tags' in filters:
+ rm = RepositoryManager(self.env)
+ all_repositories = rm.get_all_repositories()
+ for reponame in all_repositories:
+ repoinfo = all_repositories[reponame]
+ if 'type' in repoinfo and repoinfo['type'] == 'git':
+ repo = rm.get_repository(reponame)
+ for_each_args = [ '--sort=-taggerdate',
+ '--format=%(refname:short)|+%(*objectname)|+' +
+ '%(taggername) %(taggeremail)|+%(taggerdate:iso8601)|+' +
+ '%(subject)',
+ 'refs/tags' ]
+ if self._cached_repository:
+ tag_info = repo.repos.git.repo.for_each_ref(*for_each_args)
+ else:
+ tag_info = repo.git.repo.for_each_ref(*for_each_args)
+ for line in tag_info.splitlines():
+ (tag_name,tag_deref,user,tag_time_str,tag_msg) = line.split('|+')
+ if not tag_time_str:
+ continue # probably a lightweight tag
+
+ # Parse time using Trac's parse_time (requires T and Z unfortunately)
+ date_str, time_str, tz_str = tag_time_str.rsplit(None, 2)
+ tag_time = parse_date(date_str+'T'+time_str+'Z'+tz_str)
+
+ if tag_time > start and tag_time < stop:
+ self.log.debug( 'found tag %s in repo %s'%(tag_name,repoinfo['name']) )
+ # (internal-name,date,author,opaque-data)
+ tags.append(('git-tags',tag_time,user,
+ { 'name':tag_name,
+ 'repo':repoinfo['name'],
+ 'deref':tag_deref,
+ 'msg':tag_msg }))
+ elif tag_time < start:
+ # git returns tags in reverse chronological order, so if the current
+ # tag is before the start, all the remaining will also be
+ break
+ return tags
+
+ def render_timeline_event(self, context, field, event):
+ # Timeline will create "title by user
description"
+ if field == 'title':
+ title = 'Tag %s created in %s'%(event[3]['name'], event[3]['repo'])
+ return title
+ elif field == 'description':
+ return event[3]['msg']
+ elif field == 'url':
+ return context.href.changeset(event[3]['deref'], event[3]['repo'] or None)
+
#######################
# IWikiSyntaxProvider