Show More
@@ -823,3 +823,72 b' def remoteui(src, opts):' | |||
|
823 | 823 | dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied') |
|
824 | 824 | |
|
825 | 825 | return dst |
|
826 | ||
|
827 | # Files of interest | |
|
828 | # Used to check if the repository has changed looking at mtime and size of | |
|
829 | # theses files. | |
|
830 | foi = [('spath', '00changelog.i'), | |
|
831 | ('spath', 'phaseroots'), # ! phase can change content at the same size | |
|
832 | ('spath', 'obsstore'), | |
|
833 | ('path', 'bookmarks'), # ! bookmark can change content at the same size | |
|
834 | ] | |
|
835 | ||
|
836 | class cachedlocalrepo(object): | |
|
837 | """Holds a localrepository that can be cached and reused.""" | |
|
838 | ||
|
839 | def __init__(self, repo): | |
|
840 | """Create a new cached repo from an existing repo. | |
|
841 | ||
|
842 | We assume the passed in repo was recently created. If the | |
|
843 | repo has changed between when it was created and when it was | |
|
844 | turned into a cache, it may not refresh properly. | |
|
845 | """ | |
|
846 | assert isinstance(repo, localrepo.localrepository) | |
|
847 | self._repo = repo | |
|
848 | self._state, self.mtime = self._repostate() | |
|
849 | ||
|
850 | def fetch(self): | |
|
851 | """Refresh (if necessary) and return a repository. | |
|
852 | ||
|
853 | If the cached instance is out of date, it will be recreated | |
|
854 | automatically and returned. | |
|
855 | ||
|
856 | Returns a tuple of the repo and a boolean indicating whether a new | |
|
857 | repo instance was created. | |
|
858 | """ | |
|
859 | # We compare the mtimes and sizes of some well-known files to | |
|
860 | # determine if the repo changed. This is not precise, as mtimes | |
|
861 | # are susceptible to clock skew and imprecise filesystems and | |
|
862 | # file content can change while maintaining the same size. | |
|
863 | ||
|
864 | state, mtime = self._repostate() | |
|
865 | if state == self._state: | |
|
866 | return self._repo, False | |
|
867 | ||
|
868 | self._repo = repository(self._repo.baseui, self._repo.url()) | |
|
869 | self._state = state | |
|
870 | self.mtime = mtime | |
|
871 | ||
|
872 | return self._repo, True | |
|
873 | ||
|
874 | def _repostate(self): | |
|
875 | state = [] | |
|
876 | maxmtime = -1 | |
|
877 | for attr, fname in foi: | |
|
878 | prefix = getattr(self._repo, attr) | |
|
879 | p = os.path.join(prefix, fname) | |
|
880 | try: | |
|
881 | st = os.stat(p) | |
|
882 | except OSError: | |
|
883 | st = os.stat(prefix) | |
|
884 | state.append((st.st_mtime, st.st_size)) | |
|
885 | maxmtime = max(maxmtime, st.st_mtime) | |
|
886 | ||
|
887 | return tuple(state), maxmtime | |
|
888 | ||
|
889 | def copy(self): | |
|
890 | """Obtain a copy of this class instance.""" | |
|
891 | c = cachedlocalrepo(self._repo) | |
|
892 | c._state = self._state | |
|
893 | c.mtime = self.mtime | |
|
894 | return c |
@@ -25,15 +25,6 b' perms = {' | |||
|
25 | 25 | 'pushkey': 'push', |
|
26 | 26 | } |
|
27 | 27 | |
|
28 | ## Files of interest | |
|
29 | # Used to check if the repository has changed looking at mtime and size of | |
|
30 | # theses files. This should probably be relocated a bit higher in core. | |
|
31 | foi = [('spath', '00changelog.i'), | |
|
32 | ('spath', 'phaseroots'), # ! phase can change content at the same size | |
|
33 | ('spath', 'obsstore'), | |
|
34 | ('path', 'bookmarks'), # ! bookmark can change content at the same size | |
|
35 | ] | |
|
36 | ||
|
37 | 28 | def makebreadcrumb(url, prefix=''): |
|
38 | 29 | '''Return a 'URL breadcrumb' list |
|
39 | 30 | |
@@ -66,8 +57,8 b' class requestcontext(object):' | |||
|
66 | 57 | is prone to race conditions. Instances of this class exist to hold |
|
67 | 58 | mutable and race-free state for requests. |
|
68 | 59 | """ |
|
69 | def __init__(self, app): | |
|
70 |
self.repo = |
|
|
60 | def __init__(self, app, repo): | |
|
61 | self.repo = repo | |
|
71 | 62 | self.reponame = app.reponame |
|
72 | 63 | |
|
73 | 64 | self.archives = ('zip', 'gz', 'bz2') |
@@ -217,10 +208,8 b' class hgweb(object):' | |||
|
217 | 208 | # break some wsgi implementation. |
|
218 | 209 | r.ui.setconfig('progress', 'disable', 'true', 'hgweb') |
|
219 | 210 | r.baseui.setconfig('progress', 'disable', 'true', 'hgweb') |
|
220 | self.repo = self._webifyrepo(r) | |
|
211 | self._repo = hg.cachedlocalrepo(self._webifyrepo(r)) | |
|
221 | 212 | hook.redirect(True) |
|
222 | self.repostate = None | |
|
223 | self.mtime = -1 | |
|
224 | 213 | self.reponame = name |
|
225 | 214 | |
|
226 | 215 | def _webifyrepo(self, repo): |
@@ -228,25 +217,13 b' class hgweb(object):' | |||
|
228 | 217 | self.websubtable = webutil.getwebsubs(repo) |
|
229 | 218 | return repo |
|
230 | 219 | |
|
231 |
def |
|
|
232 | repostate = [] | |
|
233 | mtime = 0 | |
|
234 | # file of interrests mtime and size | |
|
235 | for meth, fname in foi: | |
|
236 | prefix = getattr(self.repo, meth) | |
|
237 | st = get_stat(prefix, fname) | |
|
238 | repostate.append((st.st_mtime, st.st_size)) | |
|
239 | mtime = max(mtime, st.st_mtime) | |
|
240 | repostate = tuple(repostate) | |
|
241 | # we need to compare file size in addition to mtime to catch | |
|
242 | # changes made less than a second ago | |
|
243 | if repostate != self.repostate: | |
|
244 | r = hg.repository(self.repo.baseui, self.repo.url()) | |
|
245 | self.repo = self._webifyrepo(r) | |
|
246 | # update these last to avoid threads seeing empty settings | |
|
247 | self.repostate = repostate | |
|
248 | # mtime is needed for ETag | |
|
249 | self.mtime = mtime | |
|
220 | def _getrepo(self): | |
|
221 | r, created = self._repo.fetch() | |
|
222 | if created: | |
|
223 | r = self._webifyrepo(r) | |
|
224 | ||
|
225 | self.mtime = self._repo.mtime | |
|
226 | return r | |
|
250 | 227 | |
|
251 | 228 | def run(self): |
|
252 | 229 | """Start a server from CGI environment. |
@@ -274,8 +251,8 b' class hgweb(object):' | |||
|
274 | 251 | This is typically only called by Mercurial. External consumers |
|
275 | 252 | should be using instances of this class as the WSGI application. |
|
276 | 253 | """ |
|
277 |
self. |
|
|
278 | rctx = requestcontext(self) | |
|
254 | repo = self._getrepo() | |
|
255 | rctx = requestcontext(self, repo) | |
|
279 | 256 | |
|
280 | 257 | # This state is global across all threads. |
|
281 | 258 | encoding.encoding = rctx.config('web', 'encoding', encoding.encoding) |
@@ -115,8 +115,6 b" Check hgweb's load order:" | |||
|
115 | 115 | 3) bar extsetup |
|
116 | 116 | 4) foo reposetup |
|
117 | 117 | 4) bar reposetup |
|
118 | 4) foo reposetup | |
|
119 | 4) bar reposetup | |
|
120 | 118 | |
|
121 | 119 | $ echo 'foo = !' >> $HGRCPATH |
|
122 | 120 | $ echo 'bar = !' >> $HGRCPATH |
@@ -64,7 +64,7 b' by the WSGI standard and strictly implem' | |||
|
64 | 64 | > print '---- OS.ENVIRON wsgi variables' |
|
65 | 65 | > print sorted([x for x in os.environ if x.startswith('wsgi')]) |
|
66 | 66 | > print '---- request.ENVIRON wsgi variables' |
|
67 | > print sorted([x for x in i.repo.ui.environ if x.startswith('wsgi')]) | |
|
67 | > print sorted([x for x in i._getrepo().ui.environ if x.startswith('wsgi')]) | |
|
68 | 68 | > EOF |
|
69 | 69 | $ python request.py |
|
70 | 70 | ---- STATUS |
General Comments 0
You need to be logged in to leave comments.
Login now