hgext3rd/topic/__init__.py
changeset 2731 d39942773163
parent 2718 b6fa7b3e13d4
child 2733 adfbb984ebbb
--- a/hgext3rd/topic/__init__.py	Tue Jul 11 11:24:43 2017 +0200
+++ b/hgext3rd/topic/__init__.py	Tue Jul 11 21:39:39 2017 +0530
@@ -53,6 +53,7 @@
 from __future__ import absolute_import
 
 import re
+import time
 
 from mercurial.i18n import _
 from mercurial import (
@@ -68,9 +69,11 @@
     namespaces,
     node,
     obsolete,
+    obsutil,
     patch,
     phases,
     registrar,
+    templatefilters,
     util,
 )
 
@@ -297,6 +300,7 @@
         ('', 'clear', False, 'clear active topic if any'),
         ('r', 'rev', '', 'revset of existing revisions', _('REV')),
         ('l', 'list', False, 'show the stack of changeset in the topic'),
+        ('', 'age', False, 'show when you last touched the topics')
     ] + commands.formatteropts)
 def topics(ui, repo, topic='', clear=False, rev=None, list=False, **opts):
     """View current topic, set current topic, change topic for a set of revisions, or see all topics.
@@ -316,6 +320,9 @@
     List of topics:
         `hg topics`
 
+    List of topics with their last touched time sorted according to it:
+        `hg topic --age`
+
     The active topic (if any) will be prepended with a "*".
 
     The --verbose version of this command display various information on the state of each topic."""
@@ -442,6 +449,11 @@
 
 def _listtopics(ui, repo, opts):
     fm = ui.formatter('topics', opts)
+    showlast = opts.get('age')
+    if showlast:
+        # we have a new function as plugging logic into existing function is
+        # pretty much difficult
+        return _showlasttouched(repo, fm, opts)
     activetopic = repo.currenttopic
     namemask = '%s'
     if repo.topics and ui.verbose:
@@ -494,6 +506,76 @@
         fm.plain('\n')
     fm.end()
 
+def _showlasttouched(repo, fm, opts):
+    topics = repo.topics
+    timedict = _getlasttouched(repo, topics)
+    times = timedict.keys()
+    times.sort()
+    if topics:
+        maxwidth = max(len(t) for t in topics)
+        namemask = '%%-%is' % maxwidth
+    activetopic = repo.currenttopic
+    for timevalue in times:
+        curtopics = timedict[timevalue][1]
+        for topic in curtopics:
+            fm.startitem()
+            marker = ' '
+            label = 'topic'
+            active = (topic == activetopic)
+            if active:
+                marker = '*'
+                label = 'topic.active'
+            fm.plain(' %s ' % marker, label=label)
+            fm.write('topic', namemask, topic, label=label)
+            fm.data(active=active)
+            fm.plain(' (')
+            if timevalue == -1:
+                timestr = 'not yet touched'
+            else:
+                timestr = templatefilters.age(timedict[timevalue][0])
+            fm.write('lasttouched', '%s', timestr, label='topic.list.time')
+            fm.plain(')')
+            fm.plain('\n')
+    fm.end()
+
+def _getlasttouched(repo, topics):
+    """
+    Calculates the last time a topic was used. Returns a dictionary of seconds
+    passed from current time for a topic as keys and topic name as values.
+    """
+    topicstime = {}
+    curtime = time.time()
+    for t in topics:
+        maxtime = (0, 0)
+        trevs = repo.revs("topic(%s)", t)
+        # Need to check for the time of all changesets in the topic, whether
+        # they are obsolete of non-heads
+        # XXX: can we just rely on the max rev number for this
+        for revs in trevs:
+            rt = repo[revs].date()
+            if rt[0] > maxtime[0]:
+                # Can store the rev to gather more info
+                # latesthead = revs
+                maxtime = rt
+            # looking on the markers also to get more information and accurate
+            # last touch time.
+            obsmarkers = obsutil.getmarkers(repo, [repo[revs].node()])
+            for marker in obsmarkers:
+                rt = marker.date()
+                if rt[0] > maxtime[0]:
+                    maxtime = rt
+        # is the topic still yet untouched
+        if not trevs:
+            secspassed = -1
+        else:
+            secspassed = (curtime - maxtime[0])
+        try:
+            topicstime[secspassed][1].append(t)
+        except KeyError:
+            topicstime[secspassed] = (maxtime, [t])
+
+    return topicstime
+
 def panicforuntopicedcommit():
     msg = _("no active topic")
     hint = _("set a current topic or use '--config " +