History cache not cleared on edit, and can follow renames

This commit is contained in:
Chase Sterling 2016-07-10 01:00:43 -04:00
parent 687b0f5ced
commit b754b9ac5a
1 changed files with 72 additions and 51 deletions

View File

@ -1,5 +1,3 @@
import collections
import itertools
import os import os
import posixpath import posixpath
import re import re
@ -115,53 +113,70 @@ class WikiPage(HookMixin):
"""Get page history. """Get page history.
History can take a long time to generate for repositories with many commits. History can take a long time to generate for repositories with many commits.
This returns an iterator to avoid having to load them all at once. This returns an iterator to avoid having to load them all at once, and caches
as it goes.
:return: iter -- Iterator over dicts :return: iter -- Iterator over dicts
""" """
cache_complete = False cache_head = []
cached_revs = cache.get(self._cache_key('history')) or [] cache_tail = cache.get(self._cache_key('history')) or [{'_cache_missing': True}]
start_sha = None while True:
if cached_revs: if not cache_tail:
if cached_revs[-1] == 'TAIL': return
del cached_revs[-1] for index, cached_rev in enumerate(cache_tail):
cache_complete = True if cached_rev.get("_cache_missing"):
else: break
start_sha = cached_revs[-1]['sha'] else:
for rev in cached_revs: yield cached_rev
yield rev cache_head.extend(cache_tail[:index])
if cache_complete: cache_tail = cache_tail[index+1:]
return start_sha = cached_rev.get('sha')
end_sha = cache_tail[0].get('sha') if cache_tail else None
for rev in self._iter_revs(start_sha=start_sha, end_sha=end_sha, filename=cached_rev.get('filename')):
cache_head.append(rev)
placeholder = {
'_cache_missing': True,
'sha': rev['sha'],
'filename': rev['new_filename']
}
cache.set(self._cache_key('history'), cache_head + [placeholder] + cache_tail)
yield rev
cache.set(self._cache_key('history'), cache_head + cache_tail)
def _iter_revs(self, start_sha=None, end_sha=None, filename=None):
if end_sha:
end_sha = [end_sha]
if not len(self.wiki.repo.open_index()): if not len(self.wiki.repo.open_index()):
# Index is empty, no commits # Index is empty, no commits
return return
walker = iter(self.wiki.repo.get_walker(paths=[self.filename], include=start_sha, follow=True)) filename = filename or self.filename
walker = iter(self.wiki.repo.get_walker(paths=[filename],
include=start_sha,
exclude=end_sha,
follow=True))
if start_sha: if start_sha:
# If we are not starting from HEAD, we already have the start commit # If we are not starting from HEAD, we already have the start commit
next(walker) next(walker)
filename = self.filename filename = self.filename
try: for entry in walker:
for entry in walker: change_type = None
change_type = None for change in entry.changes():
for change in entry.changes(): if change.new.path == filename:
if change.new.path == filename: filename = change.old.path
filename = change.old.path change_type = change.type
change_type = change.type break
break
author_name, author_email = entry.commit.author.rstrip('>').split('<') author_name, author_email = entry.commit.author.rstrip('>').split('<')
r = dict(author=author_name.strip(), r = dict(author=author_name.strip(),
author_email=author_email, author_email=author_email,
time=entry.commit.author_time, time=entry.commit.author_time,
message=entry.commit.message, message=entry.commit.message,
sha=entry.commit.id, sha=entry.commit.id,
type=change_type) type=change_type,
cached_revs.append(r) new_filename=change.new.path,
yield r old_filename=change.old.path)
cached_revs.append('TAIL') yield r
finally:
cache.set(self._cache_key('history'), cached_revs)
@property @property
def history_cache(self): def history_cache(self):
@ -169,13 +184,12 @@ class WikiPage(HookMixin):
:return: tuple -- (cached items, cache complete?) :return: tuple -- (cached items, cache complete?)
""" """
cache_complete = False cached_revs = cache.get(self._cache_key('history'))
cached_revs = cache.get(self._cache_key('history')) or [] if not cached_revs:
if cached_revs: return 0, False
if cached_revs[-1] == 'TAIL': elif any(rev.get('_cache_missing') for rev in cached_revs):
del cached_revs[-1] return len(cached_revs) - 1, False
cache_complete = True return len(cached_revs), True
return len(cached_revs), cache_complete
@property @property
def partials(self): def partials(self):
@ -222,9 +236,14 @@ class WikiPage(HookMixin):
return username, email return username, email
def _clear_cache(self): def _invalidate_cache(self, save_history=None):
for p in ['data', 'history']: cache.delete(self._cache_key('data'))
cache.delete(self._cache_key(p)) if save_history:
if not save_history[0].get('_cache_missing'):
save_history = [{'_cache_missing': True}] + save_history
cache.set(self._cache_key('history'), save_history)
else:
cache.delete(self._cache_key('history'))
def delete(self, username=None, email=None, message=None): def delete(self, username=None, email=None, message=None):
"""Delete page. """Delete page.
@ -245,7 +264,7 @@ class WikiPage(HookMixin):
email=email, email=email,
message=message, message=message,
files=[self.filename]) files=[self.filename])
self._clear_cache() self._invalidate_cache()
return commit return commit
def rename(self, new_name, username=None, email=None, message=None): def rename(self, new_name, username=None, email=None, message=None):
@ -283,11 +302,12 @@ class WikiPage(HookMixin):
message=message, message=message,
files=[old_filename, new_filename]) files=[old_filename, new_filename])
self._clear_cache() old_history = cache.get(self._cache_key('history'))
self._invalidate_cache()
self.name = new_name self.name = new_name
self.filename = new_filename self.filename = new_filename
# We need to clear the cache for the new name as well as the old # We need to clear the cache for the new name as well as the old
self._clear_cache() self._invalidate_cache(save_history=old_history)
return commit return commit
@ -323,7 +343,8 @@ class WikiPage(HookMixin):
message=message, message=message,
files=[self.filename]) files=[self.filename])
self._clear_cache() old_history = cache.get(self._cache_key('history'))
self._invalidate_cache(save_history=old_history)
return ret return ret
def revert(self, commit_sha, message, username, email): def revert(self, commit_sha, message, username, email):