Show More
@@ -823,3 +823,72 b' def remoteui(src, opts):' | |||||
823 | dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied') |
|
823 | dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied') | |
824 |
|
824 | |||
825 | return dst |
|
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 | 'pushkey': 'push', |
|
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 | def makebreadcrumb(url, prefix=''): |
|
28 | def makebreadcrumb(url, prefix=''): | |
38 | '''Return a 'URL breadcrumb' list |
|
29 | '''Return a 'URL breadcrumb' list | |
39 |
|
30 | |||
@@ -66,8 +57,8 b' class requestcontext(object):' | |||||
66 | is prone to race conditions. Instances of this class exist to hold |
|
57 | is prone to race conditions. Instances of this class exist to hold | |
67 | mutable and race-free state for requests. |
|
58 | mutable and race-free state for requests. | |
68 | """ |
|
59 | """ | |
69 | def __init__(self, app): |
|
60 | def __init__(self, app, repo): | |
70 |
self.repo = |
|
61 | self.repo = repo | |
71 | self.reponame = app.reponame |
|
62 | self.reponame = app.reponame | |
72 |
|
63 | |||
73 | self.archives = ('zip', 'gz', 'bz2') |
|
64 | self.archives = ('zip', 'gz', 'bz2') | |
@@ -217,10 +208,8 b' class hgweb(object):' | |||||
217 | # break some wsgi implementation. |
|
208 | # break some wsgi implementation. | |
218 | r.ui.setconfig('progress', 'disable', 'true', 'hgweb') |
|
209 | r.ui.setconfig('progress', 'disable', 'true', 'hgweb') | |
219 | r.baseui.setconfig('progress', 'disable', 'true', 'hgweb') |
|
210 | r.baseui.setconfig('progress', 'disable', 'true', 'hgweb') | |
220 | self.repo = self._webifyrepo(r) |
|
211 | self._repo = hg.cachedlocalrepo(self._webifyrepo(r)) | |
221 | hook.redirect(True) |
|
212 | hook.redirect(True) | |
222 | self.repostate = None |
|
|||
223 | self.mtime = -1 |
|
|||
224 | self.reponame = name |
|
213 | self.reponame = name | |
225 |
|
214 | |||
226 | def _webifyrepo(self, repo): |
|
215 | def _webifyrepo(self, repo): | |
@@ -228,25 +217,13 b' class hgweb(object):' | |||||
228 | self.websubtable = webutil.getwebsubs(repo) |
|
217 | self.websubtable = webutil.getwebsubs(repo) | |
229 | return repo |
|
218 | return repo | |
230 |
|
219 | |||
231 |
def |
|
220 | def _getrepo(self): | |
232 | repostate = [] |
|
221 | r, created = self._repo.fetch() | |
233 | mtime = 0 |
|
222 | if created: | |
234 | # file of interrests mtime and size |
|
223 | r = self._webifyrepo(r) | |
235 | for meth, fname in foi: |
|
224 | ||
236 | prefix = getattr(self.repo, meth) |
|
225 | self.mtime = self._repo.mtime | |
237 | st = get_stat(prefix, fname) |
|
226 | return r | |
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 |
|
|||
250 |
|
227 | |||
251 | def run(self): |
|
228 | def run(self): | |
252 | """Start a server from CGI environment. |
|
229 | """Start a server from CGI environment. | |
@@ -274,8 +251,8 b' class hgweb(object):' | |||||
274 | This is typically only called by Mercurial. External consumers |
|
251 | This is typically only called by Mercurial. External consumers | |
275 | should be using instances of this class as the WSGI application. |
|
252 | should be using instances of this class as the WSGI application. | |
276 | """ |
|
253 | """ | |
277 |
self. |
|
254 | repo = self._getrepo() | |
278 | rctx = requestcontext(self) |
|
255 | rctx = requestcontext(self, repo) | |
279 |
|
256 | |||
280 | # This state is global across all threads. |
|
257 | # This state is global across all threads. | |
281 | encoding.encoding = rctx.config('web', 'encoding', encoding.encoding) |
|
258 | encoding.encoding = rctx.config('web', 'encoding', encoding.encoding) |
@@ -115,8 +115,6 b" Check hgweb's load order:" | |||||
115 | 3) bar extsetup |
|
115 | 3) bar extsetup | |
116 | 4) foo reposetup |
|
116 | 4) foo reposetup | |
117 | 4) bar reposetup |
|
117 | 4) bar reposetup | |
118 | 4) foo reposetup |
|
|||
119 | 4) bar reposetup |
|
|||
120 |
|
118 | |||
121 | $ echo 'foo = !' >> $HGRCPATH |
|
119 | $ echo 'foo = !' >> $HGRCPATH | |
122 | $ echo 'bar = !' >> $HGRCPATH |
|
120 | $ echo 'bar = !' >> $HGRCPATH |
@@ -64,7 +64,7 b' by the WSGI standard and strictly implem' | |||||
64 | > print '---- OS.ENVIRON wsgi variables' |
|
64 | > print '---- OS.ENVIRON wsgi variables' | |
65 | > print sorted([x for x in os.environ if x.startswith('wsgi')]) |
|
65 | > print sorted([x for x in os.environ if x.startswith('wsgi')]) | |
66 | > print '---- request.ENVIRON wsgi variables' |
|
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 | > EOF |
|
68 | > EOF | |
69 | $ python request.py |
|
69 | $ python request.py | |
70 | ---- STATUS |
|
70 | ---- STATUS |
General Comments 0
You need to be logged in to leave comments.
Login now