import time
import datetime
from mercurial import repo, localrepo, util, cmdutil, hg
from mercurial import ui
hgui = ui.ui(interactive=False, quiet=True, report_untrusted=False)
def hgdate(date):
t, tz = date
return datetime.datetime(*time.gmtime(float(t) - tz)[:6])
class ChangeLog(object):
_revrange = None
def __init__(self, repo):
self.repo = repo
self._result_cache = None
self._iter = None
def __repr__(self):
return repr(list(self))
def __len__(self):
return self.repo.changelog.count()
def __iter__(self):
if self._result_cache is None:
self._iter = self.iterator()
self._result_cache = []
if self._iter:
return self._result_iter()
return iter(self._result_cache)
def _result_iter(self):
pos = 0
while 1:
upper = len(self._result_cache)
while pos < upper:
yield self._result_cache[pos]
pos += 1
if not self._iter:
raise StopIteration
if len(self._result_cache) <= pos:
self._fill_cache()
def __nonzero__(self):
if self._result_cache is not None:
return bool(self._result_cache)
try:
iter(self).next()
except StopIteration:
return False
return True
def __getitem__(self, k):
"Retrieve an item or slice from the set of results."
if not isinstance(k, (slice, int, long)):
raise TypeError
if self._result_cache is not None:
if self._iter is not None:
# The result cache has only been partially populated, so we may
# need to fill it out a bit more.
if isinstance(k, slice):
if k.stop is not None:
# Some people insist on passing in strings here.
bound = int(k.stop)
else:
bound = None
else:
bound = k + 1
if len(self._result_cache) < bound:
self._fill_cache(bound - len(self._result_cache))
return self._result_cache[k]
def revfix(repo, val, defval):
if not val and val != 0 and defval is not None:
return defval
return self.repo.changelog.rev(self.repo.lookup(val))
if isinstance(k, slice):
cl = self._clone()
start = revfix(self.repo, k.start, 0)
stop = revfix(self.repo, k.stop, self.repo.changelog.count() - 1)
cl._revrange = ['%s:%s' % (start or '', stop or '')]
return cl
cl = self._clone()
cl._revrange = ['%s' % k]
return list(cl)[0]
#############
# REAL WORK #
#############
def iterator(self):
"""
An iterator over the changes from repo.
"""
get = util.cachefunc(lambda r: self.repo.changectx(r).changeset())
log = self.repo.changelog
if self._revrange:
endrev = max(cmdutil.revrange(self.repo, self._revrange)) + 1
else:
endrev = self.repo.changelog.count()
opts = { 'rev': self._revrange, }
changeiter, matchfn = cmdutil.walkchangerevs(hgui, self.repo, [], get, opts)
cache = []
for st, rev, fns in changeiter:
if st == 'add':
node = log.node(rev)
changes = get(node)
cur_rev = {
'rev': rev,
'node': short(node),
'author': changes[1],
'date': hgdate(changes[2]),
'summary': changes[4].splitlines()[0],
}
cache.append(cur_rev)
elif st == 'iter':
# Don't worry, len(iter) == len(add),
# so cache will not raise IndexError
yield cache.pop()
###################
# PRIVATE METHODS #
###################
def _clone(self, **kwargs):
klass = self.__class__
c = klass(repo=self.repo)
c.__dict__.update(kwargs)
return c
def _fill_cache(self, num=100):
if self._iter:
try:
for i in xrange(num):
self._result_cache.append(self._iter.next())
except StopIteration:
self._iter = None