Show More
@@ -677,9 +677,26 b' class dirstate(object):' | |||
|
677 | 677 | # step 3: report unseen items in the dmap hash |
|
678 | 678 | if not skipstep3 and not exact: |
|
679 | 679 | visit = sorted([f for f in dmap if f not in results and matchfn(f)]) |
|
680 | nf = iter(visit).next | |
|
681 | for st in util.statfiles([join(i) for i in visit]): | |
|
682 | results[nf()] = st | |
|
680 | if unknown: | |
|
681 | # unknown == True means we walked the full directory tree above. | |
|
682 | # So if a file is not seen it was either a) not matching matchfn | |
|
683 | # b) ignored, c) missing, or d) under a symlink directory. | |
|
684 | audit_path = scmutil.pathauditor(self._root) | |
|
685 | ||
|
686 | for nf in iter(visit): | |
|
687 | # Report ignored items in the dmap as long as they are not | |
|
688 | # under a symlink directory. | |
|
689 | if ignore(nf) and audit_path.check(nf): | |
|
690 | results[nf] = util.statfiles([join(nf)])[0] | |
|
691 | else: | |
|
692 | # It's either missing or under a symlink directory | |
|
693 | results[nf] = None | |
|
694 | else: | |
|
695 | # We may not have walked the full directory tree above, | |
|
696 | # so stat everything we missed. | |
|
697 | nf = iter(visit).next | |
|
698 | for st in util.statfiles([join(i) for i in visit]): | |
|
699 | results[nf()] = st | |
|
683 | 700 | for s in subrepos: |
|
684 | 701 | del results[s] |
|
685 | 702 | del results['.hg'] |
@@ -11,7 +11,7 b' from i18n import _, gettext' | |||
|
11 | 11 | |
|
12 | 12 | _extensions = {} |
|
13 | 13 | _order = [] |
|
14 | _ignore = ['hbisect', 'bookmarks', 'parentrevspec'] | |
|
14 | _ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg'] | |
|
15 | 15 | |
|
16 | 16 | def extensions(): |
|
17 | 17 | for name in _order: |
@@ -1463,6 +1463,39 b' The full set of options is:' | |||
|
1463 | 1463 | ``templates`` |
|
1464 | 1464 | Where to find the HTML templates. Default is install path. |
|
1465 | 1465 | |
|
1466 | ``websub`` | |
|
1467 | ---------- | |
|
1468 | ||
|
1469 | Web substitution filter definition. You can use this section to | |
|
1470 | define a set of regular expression substitution patterns which | |
|
1471 | let you automatically modify the hgweb server output. | |
|
1472 | ||
|
1473 | The default hgweb templates only apply these substitution patterns | |
|
1474 | on the revision description fields. You can apply them anywhere | |
|
1475 | you want when you create your own templates by adding calls to the | |
|
1476 | "websub" filter (usually after calling the "escape" filter). | |
|
1477 | ||
|
1478 | This can be used, for example, to convert issue references to links | |
|
1479 | to your issue tracker, or to convert "markdown-like" syntax into | |
|
1480 | HTML (see the examples below). | |
|
1481 | ||
|
1482 | Each entry in this section names a substitution filter. | |
|
1483 | The value of each entry defines the substitution expression itself. | |
|
1484 | The websub expressions follow the old interhg extension syntax, | |
|
1485 | which in turn imitates the Unix sed replacement syntax:: | |
|
1486 | ||
|
1487 | pattername = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i] | |
|
1488 | ||
|
1489 | You can use any separator other than "/". The final "i" is optional | |
|
1490 | and indicates that the search must be case insensitive. | |
|
1491 | ||
|
1492 | Examples:: | |
|
1493 | ||
|
1494 | [websub] | |
|
1495 | issues = s|issue(\d+)|<a href="http://bts.example.org/issue\1">issue\1</a>|i | |
|
1496 | italic = s/\b_(\S+)_\b/<i>\1<\/i>/ | |
|
1497 | bold = s/\*\b(\S+)\b\*/<b>\1<\/b>/ | |
|
1498 | ||
|
1466 | 1499 | ``worker`` |
|
1467 | 1500 | ---------- |
|
1468 | 1501 |
@@ -8,11 +8,13 b'' | |||
|
8 | 8 | |
|
9 | 9 | import os |
|
10 | 10 | from mercurial import ui, hg, hook, error, encoding, templater, util, repoview |
|
11 | from mercurial.templatefilters import websub | |
|
12 | from mercurial.i18n import _ | |
|
11 | 13 | from common import get_stat, ErrorResponse, permhooks, caching |
|
12 | 14 | from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST |
|
13 | 15 | from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR |
|
14 | 16 | from request import wsgirequest |
|
15 | import webcommands, protocol, webutil | |
|
17 | import webcommands, protocol, webutil, re | |
|
16 | 18 | |
|
17 | 19 | perms = { |
|
18 | 20 | 'changegroup': 'pull', |
@@ -73,6 +75,7 b' class hgweb(object):' | |||
|
73 | 75 | # a repo owner may set web.templates in .hg/hgrc to get any file |
|
74 | 76 | # readable by the user running the CGI script |
|
75 | 77 | self.templatepath = self.config('web', 'templates') |
|
78 | self.websubtable = self.loadwebsub() | |
|
76 | 79 | |
|
77 | 80 | # The CGI scripts are often run by a user different from the repo owner. |
|
78 | 81 | # Trust the settings from the .hg/hgrc files by default. |
@@ -258,6 +261,47 b' class hgweb(object):' | |||
|
258 | 261 | return [''] |
|
259 | 262 | return tmpl('error', error=inst.message) |
|
260 | 263 | |
|
264 | def loadwebsub(self): | |
|
265 | websubtable = [] | |
|
266 | websubdefs = self.repo.ui.configitems('websub') | |
|
267 | # we must maintain interhg backwards compatibility | |
|
268 | websubdefs += self.repo.ui.configitems('interhg') | |
|
269 | for key, pattern in websubdefs: | |
|
270 | # grab the delimiter from the character after the "s" | |
|
271 | unesc = pattern[1] | |
|
272 | delim = re.escape(unesc) | |
|
273 | ||
|
274 | # identify portions of the pattern, taking care to avoid escaped | |
|
275 | # delimiters. the replace format and flags are optional, but | |
|
276 | # delimiters are required. | |
|
277 | match = re.match( | |
|
278 | r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$' | |
|
279 | % (delim, delim, delim), pattern) | |
|
280 | if not match: | |
|
281 | self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n") | |
|
282 | % (key, pattern)) | |
|
283 | continue | |
|
284 | ||
|
285 | # we need to unescape the delimiter for regexp and format | |
|
286 | delim_re = re.compile(r'(?<!\\)\\%s' % delim) | |
|
287 | regexp = delim_re.sub(unesc, match.group(1)) | |
|
288 | format = delim_re.sub(unesc, match.group(2)) | |
|
289 | ||
|
290 | # the pattern allows for 6 regexp flags, so set them if necessary | |
|
291 | flagin = match.group(3) | |
|
292 | flags = 0 | |
|
293 | if flagin: | |
|
294 | for flag in flagin.upper(): | |
|
295 | flags |= re.__dict__[flag] | |
|
296 | ||
|
297 | try: | |
|
298 | regexp = re.compile(regexp, flags) | |
|
299 | websubtable.append((regexp, format)) | |
|
300 | except re.error: | |
|
301 | self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n") | |
|
302 | % (key, regexp)) | |
|
303 | return websubtable | |
|
304 | ||
|
261 | 305 | def templater(self, req): |
|
262 | 306 | |
|
263 | 307 | # determine scheme, port and server name |
@@ -311,9 +355,13 b' class hgweb(object):' | |||
|
311 | 355 | or req.env.get('REPO_NAME') |
|
312 | 356 | or req.url.strip('/') or self.repo.root) |
|
313 | 357 | |
|
358 | def websubfilter(text): | |
|
359 | return websub(text, self.websubtable) | |
|
360 | ||
|
314 | 361 | # create the templater |
|
315 | 362 | |
|
316 | 363 | tmpl = templater.templater(mapfile, |
|
364 | filters={"websub": websubfilter}, | |
|
317 | 365 | defaults={"url": req.url, |
|
318 | 366 | "logourl": logourl, |
|
319 | 367 | "logoimg": logoimg, |
@@ -184,6 +184,13 b' class pathauditor(object):' | |||
|
184 | 184 | # want to add "foo/bar/baz" before checking if there's a "foo/.hg" |
|
185 | 185 | self.auditeddir.update(prefixes) |
|
186 | 186 | |
|
187 | def check(self, path): | |
|
188 | try: | |
|
189 | self(path) | |
|
190 | return True | |
|
191 | except (OSError, util.Abort): | |
|
192 | return False | |
|
193 | ||
|
187 | 194 | class abstractvfs(object): |
|
188 | 195 | """Abstract base class; cannot be instantiated""" |
|
189 | 196 | |
@@ -745,21 +752,14 b' def addremove(repo, pats=[], opts={}, dr' | |||
|
745 | 752 | ctx = repo[None] |
|
746 | 753 | walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False) |
|
747 | 754 | for abs in sorted(walkresults): |
|
748 | good = True | |
|
749 | try: | |
|
750 | audit_path(abs) | |
|
751 | except (OSError, util.Abort): | |
|
752 | good = False | |
|
753 | ||
|
754 | 755 | st = walkresults[abs] |
|
755 | 756 | dstate = repo.dirstate[abs] |
|
756 | if good and dstate == '?': | |
|
757 | if dstate == '?' and audit_path.check(abs): | |
|
757 | 758 | unknown.append(abs) |
|
758 | 759 | if repo.ui.verbose or not m.exact(abs): |
|
759 | 760 | rel = m.rel(abs) |
|
760 | 761 | repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) |
|
761 | elif (dstate != 'r' and | |
|
762 | (not good or not st or | |
|
762 | elif (dstate != 'r' and (not st or | |
|
763 | 763 | (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))): |
|
764 | 764 | deleted.append(abs) |
|
765 | 765 | if repo.ui.verbose or not m.exact(abs): |
@@ -391,6 +391,15 b' filters = {' | |||
|
391 | 391 | "xmlescape": xmlescape, |
|
392 | 392 | } |
|
393 | 393 | |
|
394 | def websub(text, websubtable): | |
|
395 | """:websub: Any text. Only applies to hgweb. Applies the regular | |
|
396 | expression replacements defined in the websub section. | |
|
397 | """ | |
|
398 | if websubtable: | |
|
399 | for regexp, format in websubtable: | |
|
400 | text = regexp.sub(format, text) | |
|
401 | return text | |
|
402 | ||
|
394 | 403 | def fillfunc(context, mapping, args): |
|
395 | 404 | if not (1 <= len(args) <= 2): |
|
396 | 405 | raise error.ParseError(_("fill expects one or two arguments")) |
@@ -8,7 +8,7 b'' | |||
|
8 | 8 | <i>{author|obfuscate} [{date|rfc822date}] rev {rev}</i><br/> |
|
9 | 9 | </div> |
|
10 | 10 | <div class="log_body"> |
|
11 | {desc|strip|escape|addbreaks|nonempty} | |
|
11 | {desc|strip|escape|websub|addbreaks|nonempty} | |
|
12 | 12 | <br/> |
|
13 | 13 | <br/> |
|
14 | 14 | </div> |
@@ -41,7 +41,7 b' changeset |' | |||
|
41 | 41 | </table></div> |
|
42 | 42 | |
|
43 | 43 | <div class="page_body"> |
|
44 | {desc|strip|escape|addbreaks|nonempty} | |
|
44 | {desc|strip|escape|websub|addbreaks|nonempty} | |
|
45 | 45 | </div> |
|
46 | 46 | <div class="list_head"></div> |
|
47 | 47 | <div class="title_text"> |
@@ -56,7 +56,7 b' annotate |' | |||
|
56 | 56 | </div> |
|
57 | 57 | |
|
58 | 58 | <div class="page_path"> |
|
59 | {desc|strip|escape|addbreaks|nonempty} | |
|
59 | {desc|strip|escape|websub|addbreaks|nonempty} | |
|
60 | 60 | </div> |
|
61 | 61 | <div class="page_body"> |
|
62 | 62 | <table> |
@@ -56,7 +56,7 b' file |' | |||
|
56 | 56 | </div> |
|
57 | 57 | |
|
58 | 58 | <div class="page_path"> |
|
59 | {desc|strip|escape|addbreaks|nonempty} | |
|
59 | {desc|strip|escape|websub|addbreaks|nonempty} | |
|
60 | 60 | </div> |
|
61 | 61 | |
|
62 | 62 | <div class="page_body"> |
@@ -2,5 +2,5 b'' | |||
|
2 | 2 | <ul class="changelog-entry"> |
|
3 | 3 | <li class="age">{date|rfc822date}</li> |
|
4 | 4 | <li>by <span class="name">{author|obfuscate}</span> <span class="revdate">[{date|rfc822date}] rev {rev}</span></li> |
|
5 | <li class="description">{desc|strip|escape|addbreaks|nonempty}</li> | |
|
5 | <li class="description">{desc|strip|escape|websub|addbreaks|nonempty}</li> | |
|
6 | 6 | </ul> |
@@ -52,7 +52,7 b'' | |||
|
52 | 52 | {child%changesetchild} |
|
53 | 53 | </dl> |
|
54 | 54 | |
|
55 | <p class="description">{desc|strip|escape|addbreaks|nonempty}</p> | |
|
55 | <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p> | |
|
56 | 56 | |
|
57 | 57 | <table> |
|
58 | 58 | {files} |
@@ -57,7 +57,7 b'' | |||
|
57 | 57 | <dd>{permissions|permissions}</dd> |
|
58 | 58 | </dl> |
|
59 | 59 | |
|
60 | <p class="description">{desc|strip|escape|addbreaks|nonempty}</p> | |
|
60 | <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p> | |
|
61 | 61 | |
|
62 | 62 | <table class="annotated"> |
|
63 | 63 | {annotate%annotateline} |
@@ -57,7 +57,7 b'' | |||
|
57 | 57 | <dd>{permissions|permissions}</dd> |
|
58 | 58 | </dl> |
|
59 | 59 | |
|
60 | <p class="description">{desc|strip|escape|addbreaks|nonempty}</p> | |
|
60 | <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p> | |
|
61 | 61 | |
|
62 | 62 | <div class="source"> |
|
63 | 63 | {text%fileline} |
@@ -40,7 +40,7 b'' | |||
|
40 | 40 | files, or words in the commit message</div> |
|
41 | 41 | </form> |
|
42 | 42 | |
|
43 | <div class="description">{desc|strip|escape|nonempty}</div> | |
|
43 | <div class="description">{desc|strip|escape|websub|nonempty}</div> | |
|
44 | 44 | |
|
45 | 45 | <table id="changesetEntry"> |
|
46 | 46 | <tr> |
@@ -46,7 +46,7 b'' | |||
|
46 | 46 | files, or words in the commit message</div> |
|
47 | 47 | </form> |
|
48 | 48 | |
|
49 | <div class="description">{desc|strip|escape|nonempty}</div> | |
|
49 | <div class="description">{desc|strip|escape|websub|nonempty}</div> | |
|
50 | 50 | |
|
51 | 51 | <table id="changesetEntry"> |
|
52 | 52 | <tr> |
@@ -45,7 +45,7 b'' | |||
|
45 | 45 | files, or words in the commit message</div> |
|
46 | 46 | </form> |
|
47 | 47 | |
|
48 | <div class="description">{desc|strip|escape|nonempty}</div> | |
|
48 | <div class="description">{desc|strip|escape|websub|nonempty}</div> | |
|
49 | 49 | |
|
50 | 50 | <table id="changesetEntry"> |
|
51 | 51 | <tr> |
@@ -45,7 +45,7 b'' | |||
|
45 | 45 | files, or words in the commit message</div> |
|
46 | 46 | </form> |
|
47 | 47 | |
|
48 | <div class="description">{desc|strip|escape|nonempty}</div> | |
|
48 | <div class="description">{desc|strip|escape|websub|nonempty}</div> | |
|
49 | 49 | |
|
50 | 50 | <table id="changesetEntry"> |
|
51 | 51 | <tr> |
@@ -44,7 +44,7 b'' | |||
|
44 | 44 | files, or words in the commit message</div> |
|
45 | 45 | </form> |
|
46 | 46 | |
|
47 | <div class="description">{desc|strip|escape|nonempty}</div> | |
|
47 | <div class="description">{desc|strip|escape|websub|nonempty}</div> | |
|
48 | 48 | |
|
49 | 49 | <table id="changesetEntry"> |
|
50 | 50 | <tr> |
@@ -39,7 +39,7 b'' | |||
|
39 | 39 | </tr> |
|
40 | 40 | <tr> |
|
41 | 41 | <th class="description">description:</th> |
|
42 | <td class="description">{desc|strip|escape|addbreaks|nonempty}</td> | |
|
42 | <td class="description">{desc|strip|escape|websub|addbreaks|nonempty}</td> | |
|
43 | 43 | </tr> |
|
44 | 44 | </table> |
|
45 | 45 |
@@ -38,7 +38,7 b'' | |||
|
38 | 38 | </tr> |
|
39 | 39 | <tr> |
|
40 | 40 | <td class="metatag">description:</td> |
|
41 | <td>{desc|strip|escape|addbreaks|nonempty}</td> | |
|
41 | <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> | |
|
42 | 42 | </tr> |
|
43 | 43 | </table> |
|
44 | 44 |
@@ -36,7 +36,7 b'' | |||
|
36 | 36 | <td>{permissions|permissions}</td></tr> |
|
37 | 37 | <tr> |
|
38 | 38 | <td class="metatag">description:</td> |
|
39 | <td>{desc|strip|escape|addbreaks|nonempty}</td> | |
|
39 | <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> | |
|
40 | 40 | </tr> |
|
41 | 41 | </table> |
|
42 | 42 |
@@ -149,6 +149,10 b' directory moved and symlinked' | |||
|
149 | 149 | adding foo/a |
|
150 | 150 | $ mv foo bar |
|
151 | 151 | $ ln -s bar foo |
|
152 | $ hg status | |
|
153 | ! foo/a | |
|
154 | ? bar/a | |
|
155 | ? foo | |
|
152 | 156 | |
|
153 | 157 |
|
|
154 | 158 |
@@ -5,11 +5,15 b'' | |||
|
5 | 5 | |
|
6 | 6 | $ cat > .hg/hgrc <<EOF |
|
7 | 7 | > [extensions] |
|
8 | > # this is only necessary to check that the mapping from | |
|
9 | > # interhg to websub works | |
|
8 | 10 | > interhg = |
|
9 | 11 | > |
|
12 | > [websub] | |
|
13 | > issues = s|Issue(\d+)|<a href="http://bts.example.org/issue\1">Issue\1</a>| | |
|
14 | > | |
|
10 | 15 | > [interhg] |
|
11 | > issues = s|Issue(\d+)|<a href="http://bts.example.org/issue\1">Issue\1</a>| | |
|
12 | > | |
|
16 | > # check that we maintain some interhg backwards compatibility... | |
|
13 | 17 |
> # |
|
14 | 18 | > markbugs = sxbugx<i class="\x">bug</i>x |
|
15 | 19 | > EOF |
@@ -23,9 +27,8 b'' | |||
|
23 | 27 | |
|
24 | 28 | log |
|
25 | 29 | |
|
26 |
$ "$TESTDIR/get-with-headers.py" localhost:$HGPORT |
|
|
27 |
|
|
|
28 | ||
|
30 | $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "rev/tip" | grep bts | |
|
31 | <div class="description"><a href="http://bts.example.org/issue123">Issue123</a>: fixed the <i class="x">bug</i>!</div> | |
|
29 | 32 | errors |
|
30 | 33 | |
|
31 | 34 | $ cat errors.log |
General Comments 0
You need to be logged in to leave comments.
Login now