##// END OF EJS Templates
Merge from crew-stable.
Dirkjan Ochtman -
r6457:7ef281e7 merge default
parent child Browse files
Show More
@@ -0,0 +1,21 b''
1 #!/usr/bin/env python
2 # Dump revlogs as raw data stream
3 # $ find .hg/store/ -name "*.i" | xargs dumprevlog > repo.dump
4
5 import sys
6 from mercurial import revlog, node
7
8 for f in sys.argv[1:]:
9 r = revlog.revlog(open, f)
10 print "file:", f
11 for i in xrange(r.count()):
12 n = r.node(i)
13 p = r.parents(n)
14 d = r.revision(n)
15 print "node:", node.hex(n)
16 print "linkrev:", r.linkrev(n)
17 print "parents:", node.hex(p[0]), node.hex(p[1])
18 print "length:", len(d)
19 print "-start-"
20 print d
21 print "-end-"
@@ -0,0 +1,34 b''
1 #!/usr/bin/env python
2 # Undump a dump from dumprevlog
3 # $ hg init
4 # $ undumprevlog < repo.dump
5
6 import sys
7 from mercurial import revlog, node, util, transaction
8
9 opener = util.opener('.', False)
10 tr = transaction.transaction(sys.stderr.write, opener, "undump.journal")
11 while 1:
12 l = sys.stdin.readline()
13 if not l:
14 break
15 if l.startswith("file:"):
16 f = l[6:-1]
17 r = revlog.revlog(opener, f)
18 print f
19 elif l.startswith("node:"):
20 n = node.bin(l[6:-1])
21 elif l.startswith("linkrev:"):
22 lr = int(l[9:-1])
23 elif l.startswith("parents:"):
24 p = l[9:-1].split()
25 p1 = node.bin(p[0])
26 p2 = node.bin(p[1])
27 elif l.startswith("length:"):
28 length = int(l[8:-1])
29 sys.stdin.readline() # start marker
30 d = sys.stdin.read(length)
31 sys.stdin.readline() # end marker
32 r.addrevision(d, tr, lr, p1, p2)
33
34 tr.close()
@@ -0,0 +1,143 b''
1 # hgweb/webutil.py - utility library for the web interface.
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
8
9 import os
10 from mercurial.node import hex, nullid
11 from mercurial.repo import RepoError
12 from mercurial import util
13
14 def up(p):
15 if p[0] != "/":
16 p = "/" + p
17 if p[-1] == "/":
18 p = p[:-1]
19 up = os.path.dirname(p)
20 if up == "/":
21 return "/"
22 return up + "/"
23
24 def revnavgen(pos, pagelen, limit, nodefunc):
25 def seq(factor, limit=None):
26 if limit:
27 yield limit
28 if limit >= 20 and limit <= 40:
29 yield 50
30 else:
31 yield 1 * factor
32 yield 3 * factor
33 for f in seq(factor * 10):
34 yield f
35
36 def nav(**map):
37 l = []
38 last = 0
39 for f in seq(1, pagelen):
40 if f < pagelen or f <= last:
41 continue
42 if f > limit:
43 break
44 last = f
45 if pos + f < limit:
46 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
47 if pos - f >= 0:
48 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
49
50 try:
51 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
52
53 for label, node in l:
54 yield {"label": label, "node": node}
55
56 yield {"label": "tip", "node": "tip"}
57 except RepoError:
58 pass
59
60 return nav
61
62 def siblings(siblings=[], hiderev=None, **args):
63 siblings = [s for s in siblings if s.node() != nullid]
64 if len(siblings) == 1 and siblings[0].rev() == hiderev:
65 return
66 for s in siblings:
67 d = {'node': hex(s.node()), 'rev': s.rev()}
68 if hasattr(s, 'path'):
69 d['file'] = s.path()
70 d.update(args)
71 yield d
72
73 def renamelink(fctx):
74 r = fctx.renamed()
75 if r:
76 return [dict(file=r[0], node=hex(r[1]))]
77 return []
78
79 def nodetagsdict(repo, node):
80 return [{"name": i} for i in repo.nodetags(node)]
81
82 def nodebranchdict(repo, ctx):
83 branches = []
84 branch = ctx.branch()
85 # If this is an empty repo, ctx.node() == nullid,
86 # ctx.branch() == 'default', but branchtags() is
87 # an empty dict. Using dict.get avoids a traceback.
88 if repo.branchtags().get(branch) == ctx.node():
89 branches.append({"name": branch})
90 return branches
91
92 def nodeinbranch(repo, ctx):
93 branches = []
94 branch = ctx.branch()
95 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
96 branches.append({"name": branch})
97 return branches
98
99 def nodebranchnodefault(ctx):
100 branches = []
101 branch = ctx.branch()
102 if branch != 'default':
103 branches.append({"name": branch})
104 return branches
105
106 def showtag(repo, tmpl, t1, node=nullid, **args):
107 for t in repo.nodetags(node):
108 yield tmpl(t1, tag=t, **args)
109
110 def cleanpath(repo, path):
111 path = path.lstrip('/')
112 return util.canonpath(repo.root, '', path)
113
114 def changectx(repo, req):
115 if 'node' in req.form:
116 changeid = req.form['node'][0]
117 elif 'manifest' in req.form:
118 changeid = req.form['manifest'][0]
119 else:
120 changeid = repo.changelog.count() - 1
121
122 try:
123 ctx = repo.changectx(changeid)
124 except RepoError:
125 man = repo.manifest
126 mn = man.lookup(changeid)
127 ctx = repo.changectx(man.linkrev(mn))
128
129 return ctx
130
131 def filectx(repo, req):
132 path = cleanpath(repo, req.form['file'][0])
133 if 'node' in req.form:
134 changeid = req.form['node'][0]
135 else:
136 changeid = req.form['filenode'][0]
137 try:
138 ctx = repo.changectx(changeid)
139 fctx = ctx.filectx(path)
140 except RepoError:
141 fctx = repo.filectx(path, fileid=changeid)
142
143 return fctx
@@ -0,0 +1,169 b''
1 /*
2 parsers.c - efficient content parsing
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
8 */
9
10 #include <Python.h>
11 #include <ctype.h>
12 #include <string.h>
13
14 static int hexdigit(char c)
15 {
16 if (c >= '0' && c <= '9')
17 return c - '0';
18
19 if (c >= 'A' && c <= 'F')
20 return c - 'A' + 10;
21
22 if (c >= 'a' && c <= 'f')
23 return c - 'a' + 10;
24
25 return -1;
26 }
27
28 /*
29 * Turn a hex-encoded string into binary.
30 */
31 static PyObject *unhexlify(const char *str, int len)
32 {
33 PyObject *ret = NULL;
34 const char *c;
35 char *d;
36
37 if (len % 2) {
38 PyErr_SetString(PyExc_ValueError,
39 "input is not even in length");
40 goto bail;
41 }
42
43 ret = PyString_FromStringAndSize(NULL, len / 2);
44 if (!ret)
45 goto bail;
46
47 d = PyString_AsString(ret);
48 if (!d)
49 goto bail;
50
51 for (c = str; c < str + len;) {
52 int hi = hexdigit(*c++);
53 int lo = hexdigit(*c++);
54
55 if (hi == -1 || lo == -1) {
56 PyErr_SetString(PyExc_ValueError,
57 "input contains non-hex character");
58 goto bail;
59 }
60
61 *d++ = (hi << 4) | lo;
62 }
63
64 goto done;
65
66 bail:
67 Py_XDECREF(ret);
68 ret = NULL;
69 done:
70 return ret;
71 }
72
73 /*
74 * This code assumes that a manifest is stitched together with newline
75 * ('\n') characters.
76 */
77 static PyObject *parse_manifest(PyObject *self, PyObject *args)
78 {
79 PyObject *mfdict, *fdict;
80 char *str, *cur, *start, *zero;
81 int len;
82
83 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
84 &PyDict_Type, &mfdict,
85 &PyDict_Type, &fdict,
86 &str, &len))
87 goto quit;
88
89 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
90 PyObject *file = NULL, *node = NULL;
91 PyObject *flags = NULL;
92 int nlen;
93
94 if (!*cur) {
95 zero = cur;
96 continue;
97 }
98 else if (*cur != '\n')
99 continue;
100
101 if (!zero) {
102 PyErr_SetString(PyExc_ValueError,
103 "manifest entry has no separator");
104 goto quit;
105 }
106
107 file = PyString_FromStringAndSize(start, zero - start);
108 if (!file)
109 goto bail;
110
111 nlen = cur - zero - 1;
112
113 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
114 if (!node)
115 goto bail;
116
117 if (nlen > 40) {
118 PyObject *flags;
119
120 flags = PyString_FromStringAndSize(zero + 41,
121 nlen - 40);
122 if (!flags)
123 goto bail;
124
125 if (PyDict_SetItem(fdict, file, flags) == -1)
126 goto bail;
127 }
128
129 if (PyDict_SetItem(mfdict, file, node) == -1)
130 goto bail;
131
132 start = cur + 1;
133 zero = NULL;
134
135 Py_XDECREF(flags);
136 Py_XDECREF(node);
137 Py_XDECREF(file);
138 continue;
139 bail:
140 Py_XDECREF(flags);
141 Py_XDECREF(node);
142 Py_XDECREF(file);
143 goto quit;
144 }
145
146 if (len > 0 && *(cur - 1) != '\n') {
147 PyErr_SetString(PyExc_ValueError,
148 "manifest contains trailing garbage");
149 goto quit;
150 }
151
152 Py_INCREF(Py_None);
153 return Py_None;
154
155 quit:
156 return NULL;
157 }
158
159 static char parsers_doc[] = "Efficient content parsing.";
160
161 static PyMethodDef methods[] = {
162 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
163 {NULL, NULL}
164 };
165
166 PyMODINIT_FUNC initparsers(void)
167 {
168 Py_InitModule3("parsers", methods, parsers_doc);
169 }
@@ -0,0 +1,71 b''
1 {header}
2 <title>{repo|escape}: {node|short}</title>
3 </head>
4 <body>
5 <div class="container">
6 <div class="menu">
7 <div class="logo">
8 <a href="http://www.selenic.com/mercurial/">
9 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
10 </div>
11 <ul>
12 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
13 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
14 </ul>
15 <ul>
16 <li class="active">changeset</li>
17 <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a></li>
18 </ul>
19 <ul>
20 {archives%archiveentry}</ul>
21 </ul>
22 </div>
23
24 <div class="main">
25
26 <h2>{repo|escape}</h2>
27 <h3>changeset {rev}:{node|short} {changesettag}</h3>
28
29 <form class="search" action="{url}log">
30 {sessionvars%hiddenformentry}
31 <p><input name="rev" id="search1" type="text" size="30"></p>
32 </form>
33
34 <div class="description">{desc|strip|escape|addbreaks}</div>
35
36 <table id="changesetEntry">
37 <tr>
38 <th class="author">author</th>
39 <td class="author">{author|obfuscate}</td>
40 </tr>
41 <tr>
42 <th class="date">date</th>
43 <td class="date">{date|date} ({date|age} ago)</td></tr>
44 <tr>
45 <th class="author">parents</th>
46 <td class="author">{parent%changesetparent}</td>
47 </tr>
48 <tr>
49 <th class="author">children</th>
50 <td class="author">{child%changesetchild}</td>
51 </tr>
52 <tr>
53 <th class="files">files</th>
54 <td class="files">{files}</td></tr>
55 </tr>
56 </table>
57 <tr>
58
59 <div class="overflow">
60 <table class="bigtable">
61 <tr>
62 <th class="lineno">Line</th>
63 <th class="source">Diff</th>
64 </tr>
65 </table>
66 {diff}
67 </div>
68 </div>
69 {footer}
70
71
@@ -0,0 +1,39 b''
1 {header}
2 <title>{repo|escape}: error</title>
3 </head>
4 <body>
5
6 <div class="content">
7 <div class="menu">
8 <div class="logo">
9 <a href="http://www.selenic.com/mercurial/">
10 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
11 </div>
12 <ul>
13 <li><a href="{url}log{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
15 </ul>
16 </div>
17
18 <div class="main">
19
20 <h2>{repo|escape}</h2>
21 <h3>error</h3>
22
23 <form class="search" action="{url}log">
24 {sessionvars%hiddenformentry}
25 <p><input name="rev" id="search1" type="text" size="30"></p>
26 </form>
27
28 <div class="description">
29 <p>
30 An error occurred while processing your request:
31 </p>
32 <p>
33 {error|escape}
34 </p>
35 </div>
36 </div>
37 </div>
38
39 {footer}
@@ -0,0 +1,76 b''
1 {header}
2 <title>{repo|escape}: {file|escape} annotate</title>
3 </head>
4 <body>
5
6 <div class="container">
7 <div class="menu">
8 <div class="logo">
9 <a href="http://www.selenic.com/mercurial/">
10 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
11 </div>
12 <ul>
13 <li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
15 </ul>
16
17 <ul>
18 <li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
19 <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
20 </ul>
21 <ul>
22 <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
23 <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
24 <li class="active">annotate</li>
25 <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
26 <li><a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
27 </ul>
28 </div>
29
30 <div class="main">
31 <h2>{repo|escape}</h2>
32 <h3>Annotate {file|escape} @ {diff}:{node|short}</h2>
33
34 <form class="search" action="{url}log">
35 {sessionvars%hiddenformentry}
36 <p><input name="rev" id="search1" type="text" size="30"></p>
37 </form>
38
39 <div class="description">{desc|strip|escape|addbreaks}</div>
40
41 <table id="changesetEntry">
42 <tr>
43 <th class="author">author</th>
44 <td class="author">{author|obfuscate}</td>
45 </tr>
46 <tr>
47 <th class="date">date</th>
48 <td class="date">{date|date} ({date|age} ago)</td>
49 </tr>
50 <tr>
51 <th class="author">parents</th>
52 <td class="author">{parent%filerevparent}</td>
53 </tr>
54 <tr>
55 <th class="author">children</th>
56 <td class="author">{child%filerevchild}</td>
57 </tr>
58 {changesettag}
59 </table>
60
61 <br/>
62
63 <div class="overflow">
64 <table class="bigtable">
65 <tr>
66 <th class="annotate">Rev</th>
67 <th class="lineno">Line</th>
68 <th class="line">Source</th>
69 </tr>
70 {annotate%annotateline}
71 </table>
72 </div>
73 </div>
74 </div>
75
76 {footer}
@@ -0,0 +1,74 b''
1 {header}
2 <title>{repo|escape}: {file|escape} diff</title>
3 </head>
4 <body>
5
6 <div class="container">
7 <div class="menu">
8 <div class="logo">
9 <a href="http://www.selenic.com/mercurial/">
10 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
11 </div>
12 <ul>
13 <li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
15 </ul>
16 <ul>
17 <li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
18 <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
19 </ul>
20 <ul>
21 <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
22 <li class="active">diff</li>
23 <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
24 <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
25 <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
26 </ul>
27 </div>
28
29 <div class="main">
30 <h2>{repo|escape}</h2>
31 <h3>diff {file|escape} @ {rev}:{node|short}</h3>
32
33 <form class="search" action="{url}log">
34 {sessionvars%hiddenformentry}
35 <p><input name="rev" id="search1" type="text" size="30"></p>
36 </form>
37
38 <div class="description">{desc|strip|escape|addbreaks}</div>
39
40 <table id="changesetEntry">
41 <tr>
42 <th>author</th>
43 <td>{author|obfuscate}</td>
44 </tr>
45 <tr>
46 <th>date</th>
47 <td>{date|date} ({date|age} ago)</td>
48 </tr>
49 <tr>
50 <th>parents</th>
51 <td>{parent%filerevparent}</td>
52 </tr>
53 <tr>
54 <th>children</th>
55 <td>{child%filerevchild}</td>
56 </tr>
57 {changesettag}
58 </table>
59
60 <div class="overflow">
61 <table class="bigtable">
62 <tr>
63 <th class="lineno">Line</th>
64 <th class="source">Diff</th>
65 </tr>
66 <table>
67 {diff}
68 </div>
69 </div>
70 </div>
71
72 {footer}
73
74
@@ -0,0 +1,58 b''
1 {header}
2 <title>{repo|escape}: {file|escape} history</title>
3 <link rel="alternate" type="application/atom+xml"
4 href="{url}atom-log/tip/{file|urlescape}" title="Atom feed for {repo|escape}:{file}">
5 <link rel="alternate" type="application/rss+xml"
6 href="{url}rss-log/tip/{file|urlescape}" title="RSS feed for {repo|escape}:{file}">
7 </head>
8 </head>
9 <body>
10
11 <div class="container">
12 <div class="menu">
13 <div class="logo">
14 <a href="http://www.selenic.com/mercurial/">
15 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
16 </div>
17 <ul>
18 <li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
19 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
20 </ul>
21 <ul>
22 <li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
23 <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
24 </ul>
25 <ul>
26 <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
27 <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
28 <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
29 <li class="active">file log</li>
30 <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
31 </ul>
32 </div>
33
34 <div class="main">
35
36 <h2>{repo|escape}</h2>
37 <h3>log {file|escape}</h3>
38
39 <form class="search" action="{url}log">
40 {sessionvars%hiddenformentry}
41 <p><input name="rev" id="search1" type="text" size="30"></p>
42 </form>
43
44 <div class="navigate">{nav%filenaventry}</div>
45
46 <table class="bigtable">
47 <tr>
48 <th class="age">Age</td>
49 <th class="author">Author</td>
50 <th class="description">Description</td>
51 </tr>
52 {entries%filelogentry}
53 </table>
54
55 </div>
56 </div>
57
58 {footer}
@@ -0,0 +1,5 b''
1 <tr class="parity{parity}">
2 <td class="age">{date|age}</td>
3 <td class="author">{author|person}</td>
4 <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape}</a></td>
5 </tr>
@@ -0,0 +1,73 b''
1 {header}
2 <title>{repo|escape}: {node|short} {file|escape}</title>
3 </head>
4 <body>
5
6 <div class="container">
7 <div class="menu">
8 <div class="logo">
9 <a href="http://www.selenic.com/mercurial/">
10 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
11 </div>
12 <ul>
13 <li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
15 </ul>
16 <ul>
17 <li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
18 <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
19 </ul>
20 <ul>
21 <li class="active">file</li>
22 <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
23 <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
24 <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
25 <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
26 </ul>
27 </div>
28
29 <div class="main">
30
31 <h2>{repo|escape}</h2>
32 <h3>view {file|escape} @ {rev}:{node|short}</h3>
33
34 <form class="search" action="{url}log">
35 {sessionvars%hiddenformentry}
36 <p><input name="rev" id="search1" type="text" size="30"></p>
37 </form>
38
39 <div class="description">{desc|strip|escape|addbreaks}</div>
40
41 <table id="changesetEntry">
42 <tr>
43 <th class="author">author</th>
44 <td class="author">{author|obfuscate}</td>
45 </tr>
46 <tr>
47 <th class="date">date</th>
48 <td class="date">{date|date} ({date|age} ago)</td>
49 </tr>
50 <tr>
51 <th class="author">parents</th>
52 <td class="author">{parent%filerevparent}</td>
53 </tr>
54 <tr>
55 <th class="author">children</th>
56 <td class="author">{child%filerevchild}</td>
57 </tr>
58 {changesettag}
59 </table>
60
61 <div class="overflow">
62 <table class="bigtable">
63 <tr>
64 <th class="lineno">Line</th>
65 <th class="source">Source</th>
66 </tr>
67 {text%fileline}
68 </table>
69 </div>
70 </div>
71 </div>
72
73 {footer}
@@ -0,0 +1,4 b''
1 {motd}
2
3 </body>
4 </html>
@@ -0,0 +1,7 b''
1 <!-- quirksmode -->
2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
3 <html>
4 <head>
5 <link rel="icon" href="{staticurl}hgicon.png" type="image/png">
6 <meta name="robots" content="index, nofollow" />
7 <link rel="stylesheet" href="{staticurl}style-coal.css" type="text/css" />
@@ -0,0 +1,19 b''
1 {header}
2 <title>Mercurial repositories index</title>
3 </head>
4 <body>
5
6 <h2>Mercurial Repositories</h2>
7
8 <table>
9 <tr>
10 <td><a href="?sort={sort_name}">Name</a></td>
11 <td><a href="?sort={sort_description}">Description</a></td>
12 <td><a href="?sort={sort_contact}">Contact</a></td>
13 <td><a href="?sort={sort_lastchange}">Last change</a></td>
14 <td>&nbsp;</td>
15 <tr>
16 {entries%indexentry}
17 </table>
18
19 {footer}
@@ -0,0 +1,53 b''
1 {header}
2 <title>{repo|escape}: {node|short} {path|escape}</title>
3 </head>
4 <body>
5
6 <div class="container">
7 <div class="menu">
8 <div class="logo">
9 <a href="http://www.selenic.com/mercurial/">
10 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
11 </div>
12 <ul>
13 <li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
15 </ul>
16 <ul>
17 <li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
18 <li class="active">browse</li>
19 </ul>
20 <ul>
21 {archives%archiveentry}
22 </ul>
23 </div>
24
25 <div class="main">
26
27 <h2>{repo|escape}</h2>
28 <h3>directory {path|escape} @ {rev}:{node|short} {tags%changelogtag}</h3>
29
30 <form class="search" action="{url}log">
31 {sessionvars%hiddenformentry}
32 <p><input name="rev" id="search1" type="text" size="30"></p>
33 </form>
34
35 <table class="bigtable">
36 <tr>
37 <th class="permissions">permissions</th>
38 <th class="date">date</th>
39 <th class="size">size</th>
40 <th class="name">name</th>
41 </tr>
42 <tr class="parity{upparity}">
43 <td class="permissions">drwxr-xr-x
44 <td class="date">
45 <td class="size">
46 <td class="name"><a href="{url}file/{node|short}{up|urlescape}{sessionvars%urlparameter}">[up]</a>
47 </tr>
48 {dentries%direntry}
49 {fentries%fileentry}
50 </table>
51 </div>
52 </div>
53 {footer}
@@ -0,0 +1,69 b''
1 default = 'shortlog'
2
3 mimetype = 'text/html; charset={encoding}'
4 header = header.tmpl
5 footer = footer.tmpl
6 search = search.tmpl
7
8 changelog = shortlog.tmpl
9 shortlog = shortlog.tmpl
10 shortlogentry = shortlogentry.tmpl
11
12 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
13 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
14 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
15 filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
16 filenodelink = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
17 fileellipses = '...'
18 changelogentry = shortlogentry.tmpl
19 searchentry = shortlogentry.tmpl
20 changeset = changeset.tmpl
21 manifest = manifest.tmpl
22
23 direntry = '<tr class="parity{parity}"><td class="permissions">drwxr-xr-x</td><td class="date"></td><td class="size"></td><td class="name"><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}/</a></td></tr>'
24 fileentry = '<tr class="parity{parity}"><td class="permissions">{permissions|permissions}&nbsp;</td><td class="date">{date|isodate}&nbsp;</td><td class="size">{size}&nbsp;</td><td clase="filename"><a href="{url}file/{node|short}/{file|urlescape}#l1{sessionvars%urlparameter}">{basename|escape}</a></td></tr>'
25
26 filerevision = filerevision.tmpl
27 fileannotate = fileannotate.tmpl
28 filediff = filediff.tmpl
29 filelog = filelog.tmpl
30 fileline = '<tr class="parity{parity}"><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source">{line|escape}</td></tr>'
31 filelogentry = filelogentry.tmpl
32
33 annotateline = '<tr class="parity{parity}"><td class="annotate"><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#{targetline}">{author|obfuscate}@{rev}</a></td><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source">{line|escape}</td></tr>'
34
35 diffblock = '<table class="bigtable parity{parity}">{lines}</table>'
36 difflineplus = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source plusline">{line|escape}</td></tr>'
37 difflineminus = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source minusline">{line|escape}</td></tr>'
38 difflineat = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source atline">{line|escape}</td></tr>'
39 diffline = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source">{line|escape}</td></tr>'
40
41 changelogparent = '<tr><th class="parent">parent {rev}:</th><td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
42
43 changesetparent = '<a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
44
45 filerevparent = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
46 filerevchild = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
47
48 filerename = '{file|escape}@'
49 filelogrename = '<tr><th>base:</th><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}@{node|short}</a></td></tr>'
50 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
51 changesetchild = '<a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
52 changelogchild = '<tr><th class="child">child</th><td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
53 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
54 tags = tags.tmpl
55 tagentry = '<tr class="tagEntry parity{parity}"><td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{tag|escape}</a></td><td class="node">{node|short}</td></tr>'
56 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">{tag|escape}</td></tr>'
57 changelogtag = '<span class="tag">{name|escape}</span> '
58 changesettag = '<span class="tag">{tag|escape}</span> '
59 filediffparent = '<tr><th class="parent">parent {rev}:</th><td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
60 filelogparent = '<tr><th>parent {rev}:</th><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
61 filediffchild = '<tr><th class="child">child {rev}:</th><td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
62 filelogchild = '<tr><th>child {rev}:</th><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
63 indexentry = '<tr class="parity{parity}"><td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td><td>{description}</td><td>{contact|obfuscate}</td><td class="age">{lastchange|age} ago</td><td class="indexlinks"><a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a> {archives%archiveentry}</td></tr>'
64 index = index.tmpl
65 archiveentry = '<li><a href="{url}archive/{node|short}{extension|urlescape}">{type|escape}</a></li>'
66 notfound = notfound.tmpl
67 error = error.tmpl
68 urlparameter = '{separator}{name}={value|urlescape}'
69 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
@@ -0,0 +1,12 b''
1 {header}
2 <title>Mercurial repository not found</title>
3 </head>
4 <body>
5
6 <h2>Mercurial repository not found</h2>
7
8 The specified repository "{repo|escape}" is unknown, sorry.
9
10 Please go back to the main repository list page.
11
12 {footer}
@@ -0,0 +1,40 b''
1 {header}
2 <title>{repo|escape}: searching for {query|escape}</title>
3 </head>
4 <body>
5
6 <div class="content">
7 <div class="menu">
8 <div class="logo">
9 <a href="http://www.selenic.com/mercurial/">
10 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
11 </div>
12 <ul>
13 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
14 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
15 </ul>
16 </div>
17
18 <div class="main">
19
20 <h2>{repo|escape}</h2>
21 <h3>searching for '{query|escape}'</h3>
22
23 <form class="search" action="{url}log">
24 {sessionvars%hiddenformentry}
25 <p><input name="rev" id="search1" type="text" size="30"></p>
26 </form>
27
28 <table class="bigtable">
29 <tr>
30 <th class="age">Age</td>
31 <th class="author">Author</td>
32 <th class="description">Description</td>
33 </tr>
34 {entries}
35 </table>
36
37 </div>
38 </div>
39
40 {footer}
@@ -0,0 +1,54 b''
1 {header}
2 <title>{repo|escape}: log</title>
3 <link rel="alternate" type="application/atom+xml"
4 href="{url}atom-log" title="Atom feed for {repo|escape}">
5 <link rel="alternate" type="application/rss+xml"
6 href="{url}rss-log" title="RSS feed for {repo|escape}">
7 </head>
8 <body>
9
10 <div class="container">
11 <div class="menu">
12 <div class="logo">
13 <a href="http://www.selenic.com/mercurial/">
14 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
15 </div>
16 <ul>
17 <li class="active">log</li>
18 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
19 </ul>
20 <ul>
21 <li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
22 <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
23 </ul>
24 <ul>
25 {archives%archiveentry}
26 </ul>
27 </div>
28
29 <div class="main">
30
31 <h2>{repo|escape}</h2>
32 <h3>log</h3>
33
34 <form class="search" action="{url}log">
35 {sessionvars%hiddenformentry}
36 <p><input name="rev" id="search1" type="text" size="30"></p>
37 </form>
38
39 <div class="navigate">rev {rev}: {changenav%navshortentry}</div>
40
41 <table class="bigtable">
42 <tr>
43 <th class="age">Age</td>
44 <th class="author">Author</td>
45 <th class="description">Description</td>
46 </tr>
47 {entries%shortlogentry}
48 </table>
49
50 <div class="navigate">rev {rev}: {changenav%navshortentry}</div>
51 </div>
52 </div>
53
54 {footer}
@@ -0,0 +1,5 b''
1 <tr class="parity{parity}">
2 <td class="age">{date|age}</td>
3 <td class="author">{author|person}</td>
4 <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape}</a>{tags%changelogtag}</td>
5 </tr>
@@ -0,0 +1,41 b''
1 {header}
2 <title>{repo|escape}: tags</title>
3 <link rel="alternate" type="application/atom+xml"
4 href="{url}atom-tags" title="Atom feed for {repo|escape}: tags">
5 <link rel="alternate" type="application/rss+xml"
6 href="{url}rss-tags" title="RSS feed for {repo|escape}: tags">
7 </head>
8 <body>
9
10 <div class="container">
11 <div class="menu">
12 <div class="logo">
13 <a href="http://www.selenic.com/mercurial/">
14 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
15 </div>
16 <ul>
17 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
18 <li class="active">tags</li>
19 </ul>
20 </div>
21
22 <div class="main">
23 <h2>{repo|escape}</h2>
24 <h3>tags</h3>
25
26 <form class="search" action="{url}log">
27 {sessionvars%hiddenformentry}
28 <p><input name="rev" id="search1" type="text" size="30"></p>
29 </form>
30
31 <table class="bigtable">
32 <tr>
33 <th>tag</th>
34 <th>node</th>
35 </tr>
36 {entries%tagentry}
37 </table>
38 </div>
39 </div>
40
41 {footer}
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,160 b''
1 body {
2 margin: 0;
3 padding: 0;
4 background: black url(background.png) repeat-x;
5 font-family: sans-serif;
6 }
7
8 .container {
9 padding-right: 150px;
10 }
11
12 .main {
13 position: relative;
14 background: white;
15 padding: 2em;
16 border-right: 15px solid black;
17 border-bottom: 15px solid black;
18 }
19
20 .overflow {
21 width: 100%;
22 overflow: auto;
23 }
24
25 .menu {
26 background: #999;
27 padding: 10px;
28 width: 75px;
29 margin: 0;
30 font-size: 80% /*smaller*/;
31 text-align: left;
32 position: fixed;
33 top: 27px;
34 left: auto;
35 right: 27px;
36 }
37
38 .menu ul {
39 list-style: none;
40 padding: 0;
41 margin: 10px 0 0 0;
42 }
43
44 .menu li {
45 margin-bottom: 3px;
46 padding: 2px 4px;
47 background: white;
48 color: black;
49 font-weight: normal;
50 }
51
52 .menu li.active {
53 border-left: 3px solid black;
54 }
55
56 .search {
57 position: absolute;
58 top: .7em;
59 right: 2em;
60 }
61
62 .menu a { color: black; display: block; }
63
64 a { text-decoration:none; }
65 .age { white-space:nowrap; }
66 .date { white-space:nowrap; }
67 .indexlinks { white-space:nowrap; }
68 .parity0 { background-color: #f5f5f5; }
69 .parity1 { background-color: white; }
70 .plusline { color: green; }
71 .minusline { color: red; }
72 .atline { color: purple; }
73
74 .navigate {
75 text-align: right;
76 font-size: 60%;
77 margin: 1em 0 1em 0;
78 }
79
80 .tag {
81 color: #999;
82 font-size: 70%;
83 font-weight: normal;
84 margin-left: .5em;
85 vertical-align: text-baseline;
86 }
87
88 .navigate a {
89 #padding: 2pt;
90 #background-color: #f5f5f5;
91 }
92
93 /* Common */
94 pre { margin: 0; }
95
96 h2 { font-size: 120%; border-bottom: 1px solid #999; }
97 h3 {
98 margin-top: -.7em;
99 font-size: 100%;
100 }
101
102 /* log and tags tables */
103 .bigtable {
104 border-bottom: 1px solid #999;
105 border-collapse: collapse;
106 font-size: 90%;
107 width: 100%;
108 font-weight: normal;
109 text-align: left;
110 }
111
112 .bigtable td {
113 padding: 1px 4px 1px 4px;
114 vertical-align: top;
115 }
116
117 .bigtable th {
118 padding: 1px 4px 1px 4px;
119 border-bottom: 1px solid #999;
120 font-size: smaller;
121 }
122 .bigtable tr { border: none; }
123 .bigtable .age { width: 6em; }
124 .bigtable .author { width: 10em; }
125 .bigtable .description { }
126 .bigtable .node { width: 5em; font-family: monospace;}
127 .bigtable .lineno { width: 2em; text-align: right;}
128 .bigtable .lineno a { color: #999; font-size: smaller; font-family: monospace;}
129 .bigtable td.source { font-family: monospace; white-space: pre; }
130 .bigtable .permissions { width: 8em; text-align: left;}
131 .bigtable td.permissions { font-family: monospace; }
132 .bigtable .date { width: 10em; text-align: left;}
133 .bigtable .size { width: 5em; text-align: right; }
134 .bigtable td.size { font-family: monospace; }
135 .bigtable .annotate { text-align: right; padding-right: }
136 .bigtable td.annotate { font-size: smaller; }
137
138 /* Changeset entry */
139 #changesetEntry {
140 border-collapse: collapse;
141 font-size: 90%;
142 width: 100%;
143 margin-bottom: 1em;
144 }
145
146 #changesetEntry th {
147 padding: 1px 4px 1px 4px;
148 width: 4em;
149 text-align: right;
150 font-weight: normal;
151 color: #999;
152 margin-right: .5em;
153 vertical-align: top;
154 }
155
156 div.description {
157 border-left: 3px solid #999;
158 margin: 1em 0 1em 0;
159 padding: .3em;
160 }
@@ -35,8 +35,10 b''
35 35 ;; This code has been developed under XEmacs 21.5, and may not work as
36 36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
37 37 ;; enhance the portability of this code, fix bugs, and add features
38 ;; are most welcome. You can clone a Mercurial repository for this
39 ;; package from http://www.serpentine.com/hg/hg-emacs
38 ;; are most welcome.
39
40 ;; As of version 22.3, GNU Emacs's VC mode has direct support for
41 ;; Mercurial, so this package may not prove as useful there.
40 42
41 43 ;; Please send problem reports and suggestions to bos@serpentine.com.
42 44
@@ -281,6 +281,7 b' merge-tools::'
281 281 myHtmlTool.priority = 1
282 282
283 283 Supported arguments:
284
284 285 priority;;
285 286 The priority in which to evaluate this tool.
286 287 Default: 0.
@@ -313,7 +314,7 b' merge-tools::'
313 314 fixeol;;
314 315 Attempt to fix up EOL changes caused by the merge tool.
315 316 Default: False
316 gui:;
317 gui;;
317 318 This tool requires a graphical interface to run. Default: False
318 319 regkey;;
319 320 Windows registry key which describes install location of this tool.
@@ -563,6 +564,7 b' ui::'
563 564 fail to merge
564 565
565 566 See the merge-tools section for more information on configuring tools.
567
566 568 patch;;
567 569 command to use to apply patches. Look for 'gpatch' or 'patch' in PATH if
568 570 unset.
@@ -20,18 +20,13 b' file should be re-generated by running'
20 20
21 21 # pygmentize -f html -S <newstyle>
22 22
23
24 23 -- Adam Hupp <adam@hupp.org>
25
26
27 24 """
28 25
29 26 from mercurial import demandimport
30 demandimport.ignore.extend(['pkgutil',
31 'pkg_resources',
32 '__main__',])
27 demandimport.ignore.extend(['pkgutil', 'pkg_resources', '__main__',])
33 28
34 from mercurial.hgweb.hgweb_mod import hgweb
29 from mercurial.hgweb import webcommands, webutil
35 30 from mercurial import util
36 31 from mercurial.templatefilters import filters
37 32
@@ -43,7 +38,8 b' from pygments.formatters import HtmlForm'
43 38 SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" '
44 39 'type="text/css" />')
45 40
46 def pygmentize(self, tmpl, fctx, field):
41 def pygmentize(field, fctx, style, tmpl):
42
47 43 # append a <link ...> to the syntax highlighting css
48 44 old_header = ''.join(tmpl('header'))
49 45 if SYNTAX_CSS not in old_header:
@@ -54,7 +50,6 b' def pygmentize(self, tmpl, fctx, field):'
54 50 if util.binary(text):
55 51 return
56 52
57 style = self.config("web", "pygments_style", "colorful")
58 53 # To get multi-line strings right, we can't format line-by-line
59 54 try:
60 55 lexer = guess_lexer_for_filename(fctx.path(), text,
@@ -79,20 +74,21 b' def pygmentize(self, tmpl, fctx, field):'
79 74 newl = oldl.replace('line|escape', 'line|colorize')
80 75 tmpl.cache[field] = newl
81 76
82 def filerevision_highlight(self, tmpl, fctx):
83 pygmentize(self, tmpl, fctx, 'fileline')
84
85 return realrevision(self, tmpl, fctx)
77 web_filerevision = webcommands._filerevision
78 web_annotate = webcommands.annotate
86 79
87 def fileannotate_highlight(self, tmpl, fctx):
88 pygmentize(self, tmpl, fctx, 'annotateline')
80 def filerevision_highlight(web, tmpl, fctx):
81 style = web.config('web', 'pygments_style', 'colorful')
82 pygmentize('fileline', fctx, style, tmpl)
83 return web_filerevision(web, tmpl, fctx)
89 84
90 return realannotate(self, tmpl, fctx)
85 def annotate_highlight(web, req, tmpl):
86 fctx = webutil.filectx(web.repo, req)
87 style = web.config('web', 'pygments_style', 'colorful')
88 pygmentize('annotateline', fctx, style, tmpl)
89 return web_annotate(web, req, tmpl)
91 90
92 91 # monkeypatch in the new version
93 # should be safer than overriding the method in a derived class
94 # and then patching the class
95 realrevision = hgweb.filerevision
96 hgweb.filerevision = filerevision_highlight
97 realannotate = hgweb.fileannotate
98 hgweb.fileannotate = fileannotate_highlight
92
93 webcommands._filerevision = filerevision_highlight
94 webcommands.annotate = annotate_highlight
@@ -108,6 +108,8 b" kwtools = {'templater': None, 'hgcmd': N"
108 108 _patchfile_init = patch.patchfile.__init__
109 109 _patch_diff = patch.diff
110 110 _dispatch_parse = dispatch._parse
111 _webcommands_changeset = webcommands.changeset
112 _webcommands_filediff = webcommands.filediff
111 113
112 114 def _kwpatchfile_init(self, ui, fname, missing=False):
113 115 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
@@ -131,12 +133,12 b' def _kw_diff(repo, node1=None, node2=Non'
131 133 def _kwweb_changeset(web, req, tmpl):
132 134 '''Wraps webcommands.changeset turning off keyword expansion.'''
133 135 kwtools['templater'].matcher = util.never
134 return web.changeset(tmpl, web.changectx(req))
136 return _webcommands_changeset(web, req, tmpl)
135 137
136 138 def _kwweb_filediff(web, req, tmpl):
137 139 '''Wraps webcommands.filediff turning off keyword expansion.'''
138 140 kwtools['templater'].matcher = util.never
139 return web.filediff(tmpl, web.filectx(req))
141 return _webcommands_filediff(web, req, tmpl)
140 142
141 143 def _kwdispatch_parse(ui, args):
142 144 '''Monkeypatch dispatch._parse to obtain running hg command.'''
@@ -145,6 +147,7 b' def _kwdispatch_parse(ui, args):'
145 147 return cmd, func, args, options, cmdoptions
146 148
147 149 # dispatch._parse is run before reposetup, so wrap it here
150 # all other actual monkey patching is done at end of reposetup
148 151 dispatch._parse = _kwdispatch_parse
149 152
150 153
@@ -24,12 +24,37 b''
24 24 #
25 25 # [pager]
26 26 # quiet = True
27 #
28 # You can disable the pager for certain commands by adding them to the
29 # pager.ignore list:
30 #
31 # [pager]
32 # ignore = version, help, update
33 #
34 # You can also enable the pager only for certain commands using pager.attend:
35 #
36 # [pager]
37 # attend = log
38 #
39 # If pager.attend is present, pager.ignore will be ignored.
40 #
41 # To ignore global commands like 'hg version' or 'hg help', you have to specify them
42 # in the global .hgrc
27 43
28 44 import sys, os, signal
45 from mercurial import dispatch
29 46
30 47 def uisetup(ui):
48 def pagecmd(ui, options, cmd, cmdfunc):
31 49 p = ui.config("pager", "pager", os.environ.get("PAGER"))
32 50 if p and sys.stdout.isatty() and '--debugger' not in sys.argv:
51 attend = ui.configlist('pager', 'attend')
52 if (cmd in attend or
53 (cmd not in ui.configlist('pager', 'ignore') and not attend)):
54 sys.stderr = sys.stdout = os.popen(p, "wb")
33 55 if ui.configbool('pager', 'quiet'):
34 56 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
35 sys.stderr = sys.stdout = os.popen(p, "wb")
57 return oldrun(ui, options, cmd, cmdfunc)
58
59 oldrun = dispatch._runcommand
60 dispatch._runcommand = pagecmd
@@ -66,7 +66,8 b''
66 66
67 67 import os, errno, socket, tempfile
68 68 import email.MIMEMultipart, email.MIMEText, email.MIMEBase
69 import email.Utils, email.Encoders
69 import email.Utils, email.Encoders, email.Generator
70 import cStringIO.StringIO
70 71 from mercurial import cmdutil, commands, hg, mail, patch, util
71 72 from mercurial.i18n import _
72 73 from mercurial.node import bin
@@ -407,8 +408,9 b' def patchbomb(ui, repo, *revs, **opts):'
407 408 fp = os.popen(os.environ['PAGER'], 'w')
408 409 else:
409 410 fp = ui
411 generator = email.Generator.Generator(fp, mangle_from_=False)
410 412 try:
411 fp.write(m.as_string(0))
413 generator.flatten(m, 0)
412 414 fp.write('\n')
413 415 except IOError, inst:
414 416 if inst.errno != errno.EPIPE:
@@ -418,9 +420,10 b' def patchbomb(ui, repo, *revs, **opts):'
418 420 elif opts.get('mbox'):
419 421 ui.status('Writing ', m['Subject'], ' ...\n')
420 422 fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
423 generator = email.Generator.Generator(fp, mangle_from_=True)
421 424 date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y')
422 425 fp.write('From %s %s\n' % (sender_addr, date))
423 fp.write(m.as_string(0))
426 generator.flatten(m, 0)
424 427 fp.write('\n\n')
425 428 fp.close()
426 429 else:
@@ -429,7 +432,10 b' def patchbomb(ui, repo, *revs, **opts):'
429 432 ui.status('Sending ', m['Subject'], ' ...\n')
430 433 # Exim does not remove the Bcc field
431 434 del m['Bcc']
432 sendmail(sender, to + bcc + cc, m.as_string(0))
435 fp = cStringIO.StringIO()
436 generator = email.Generator.Generator(fp, mangle_from_=False)
437 generator.flatten(m, 0)
438 sendmail(sender, to + bcc + cc, fp.getvalue())
433 439
434 440 cmdtable = {
435 441 "email":
@@ -2530,8 +2530,17 b' def serve(ui, repo, **opts):'
2530 2530 if port == ':80':
2531 2531 port = ''
2532 2532
2533 ui.status(_('listening at http://%s%s/%s (%s:%d)\n') %
2534 (self.httpd.fqaddr, port, prefix, self.httpd.addr, self.httpd.port))
2533 bindaddr = self.httpd.addr
2534 if bindaddr == '0.0.0.0':
2535 bindaddr = '*'
2536 elif ':' in bindaddr: # IPv6
2537 bindaddr = '[%s]' % bindaddr
2538
2539 fqaddr = self.httpd.fqaddr
2540 if ':' in fqaddr:
2541 fqaddr = '[%s]' % fqaddr
2542 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2543 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2535 2544
2536 2545 def run(self):
2537 2546 self.httpd.serve_forever()
This diff has been collapsed as it changes many lines, (626 lines changed) Show them Hide them
@@ -6,16 +6,15 b''
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 import os, mimetypes, re, mimetools, cStringIO
10 from mercurial.node import hex, nullid, short
9 import os, mimetypes
10 from mercurial.node import hex, nullid
11 11 from mercurial.repo import RepoError
12 from mercurial import mdiff, ui, hg, util, archival, patch, hook
12 from mercurial import mdiff, ui, hg, util, patch, hook
13 13 from mercurial import revlog, templater, templatefilters, changegroup
14 from common import get_mtime, style_map, paritygen, countgen, get_contact
15 from common import ErrorResponse
14 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse
16 15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
17 16 from request import wsgirequest
18 import webcommands, protocol
17 import webcommands, protocol, webutil
19 18
20 19 shortcuts = {
21 20 'cl': [('cmd', ['changelog']), ('rev', None)],
@@ -32,54 +31,6 b' shortcuts = {'
32 31 'static': [('cmd', ['static']), ('file', None)]
33 32 }
34 33
35 def _up(p):
36 if p[0] != "/":
37 p = "/" + p
38 if p[-1] == "/":
39 p = p[:-1]
40 up = os.path.dirname(p)
41 if up == "/":
42 return "/"
43 return up + "/"
44
45 def revnavgen(pos, pagelen, limit, nodefunc):
46 def seq(factor, limit=None):
47 if limit:
48 yield limit
49 if limit >= 20 and limit <= 40:
50 yield 50
51 else:
52 yield 1 * factor
53 yield 3 * factor
54 for f in seq(factor * 10):
55 yield f
56
57 def nav(**map):
58 l = []
59 last = 0
60 for f in seq(1, pagelen):
61 if f < pagelen or f <= last:
62 continue
63 if f > limit:
64 break
65 last = f
66 if pos + f < limit:
67 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
68 if pos - f >= 0:
69 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
70
71 try:
72 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
73
74 for label, node in l:
75 yield {"label": label, "node": node}
76
77 yield {"label": "tip", "node": "tip"}
78 except RepoError:
79 pass
80
81 return nav
82
83 34 class hgweb(object):
84 35 def __init__(self, repo, name=None):
85 36 if isinstance(repo, str):
@@ -226,17 +177,8 b' class hgweb(object):'
226 177 try:
227 178
228 179 tmpl = self.templater(req)
229 try:
230 180 ctype = tmpl('mimetype', encoding=self.encoding)
231 181 ctype = templater.stringify(ctype)
232 except KeyError:
233 # old templates with inline HTTP headers?
234 if 'mimetype' in tmpl:
235 raise
236 header = tmpl('header', encoding=self.encoding)
237 header_file = cStringIO.StringIO(templater.stringify(header))
238 msg = mimetools.Message(header_file, 0)
239 ctype = msg['content-type']
240 182
241 183 if cmd == '':
242 184 req.form['cmd'] = [tmpl.cache['default']]
@@ -291,13 +233,7 b' class hgweb(object):'
291 233 # some functions for the templater
292 234
293 235 def header(**map):
294 header = tmpl('header', encoding=self.encoding, **map)
295 if 'mimetype' not in tmpl:
296 # old template with inline HTTP headers
297 header_file = cStringIO.StringIO(templater.stringify(header))
298 msg = mimetools.Message(header_file, 0)
299 header = header_file.read()
300 yield header
236 yield tmpl('header', encoding=self.encoding, **map)
301 237
302 238 def footer(**map):
303 239 yield tmpl("footer", **map)
@@ -355,54 +291,6 b' class hgweb(object):'
355 291 if len(files) > self.maxfiles:
356 292 yield tmpl("fileellipses")
357 293
358 def siblings(self, siblings=[], hiderev=None, **args):
359 siblings = [s for s in siblings if s.node() != nullid]
360 if len(siblings) == 1 and siblings[0].rev() == hiderev:
361 return
362 for s in siblings:
363 d = {'node': hex(s.node()), 'rev': s.rev()}
364 if hasattr(s, 'path'):
365 d['file'] = s.path()
366 d.update(args)
367 yield d
368
369 def renamelink(self, fl, node):
370 r = fl.renamed(node)
371 if r:
372 return [dict(file=r[0], node=hex(r[1]))]
373 return []
374
375 def nodetagsdict(self, node):
376 return [{"name": i} for i in self.repo.nodetags(node)]
377
378 def nodebranchdict(self, ctx):
379 branches = []
380 branch = ctx.branch()
381 # If this is an empty repo, ctx.node() == nullid,
382 # ctx.branch() == 'default', but branchtags() is
383 # an empty dict. Using dict.get avoids a traceback.
384 if self.repo.branchtags().get(branch) == ctx.node():
385 branches.append({"name": branch})
386 return branches
387
388 def nodeinbranch(self, ctx):
389 branches = []
390 branch = ctx.branch()
391 if branch != 'default' and self.repo.branchtags().get(branch) != ctx.node():
392 branches.append({"name": branch})
393 return branches
394
395 def nodebranchnodefault(self, ctx):
396 branches = []
397 branch = ctx.branch()
398 if branch != 'default':
399 branches.append({"name": branch})
400 return branches
401
402 def showtag(self, tmpl, t1, node=nullid, **args):
403 for t in self.repo.nodetags(node):
404 yield tmpl(t1, tag=t, **args)
405
406 294 def diff(self, tmpl, node1, node2, files):
407 295 def filterfiles(filters, files):
408 296 l = [x for x in files if x in filters]
@@ -470,514 +358,12 b' class hgweb(object):'
470 358 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
471 359 opts=diffopts), f, tn)
472 360
473 def changelog(self, tmpl, ctx, shortlog=False):
474 def changelist(limit=0,**map):
475 cl = self.repo.changelog
476 l = [] # build a list in forward order for efficiency
477 for i in xrange(start, end):
478 ctx = self.repo.changectx(i)
479 n = ctx.node()
480 showtags = self.showtag(tmpl, 'changelogtag', n)
481
482 l.insert(0, {"parity": parity.next(),
483 "author": ctx.user(),
484 "parent": self.siblings(ctx.parents(), i - 1),
485 "child": self.siblings(ctx.children(), i + 1),
486 "changelogtag": showtags,
487 "desc": ctx.description(),
488 "date": ctx.date(),
489 "files": self.listfilediffs(tmpl, ctx.files(), n),
490 "rev": i,
491 "node": hex(n),
492 "tags": self.nodetagsdict(n),
493 "inbranch": self.nodeinbranch(ctx),
494 "branches": self.nodebranchdict(ctx)})
495
496 if limit > 0:
497 l = l[:limit]
498
499 for e in l:
500 yield e
501
502 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
503 cl = self.repo.changelog
504 count = cl.count()
505 pos = ctx.rev()
506 start = max(0, pos - maxchanges + 1)
507 end = min(count, start + maxchanges)
508 pos = end - 1
509 parity = paritygen(self.stripecount, offset=start-end)
510
511 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
512
513 return tmpl(shortlog and 'shortlog' or 'changelog',
514 changenav=changenav,
515 node=hex(cl.tip()),
516 rev=pos, changesets=count,
517 entries=lambda **x: changelist(limit=0,**x),
518 latestentry=lambda **x: changelist(limit=1,**x),
519 archives=self.archivelist("tip"))
520
521 def search(self, tmpl, query):
522
523 def changelist(**map):
524 cl = self.repo.changelog
525 count = 0
526 qw = query.lower().split()
527
528 def revgen():
529 for i in xrange(cl.count() - 1, 0, -100):
530 l = []
531 for j in xrange(max(0, i - 100), i + 1):
532 ctx = self.repo.changectx(j)
533 l.append(ctx)
534 l.reverse()
535 for e in l:
536 yield e
537
538 for ctx in revgen():
539 miss = 0
540 for q in qw:
541 if not (q in ctx.user().lower() or
542 q in ctx.description().lower() or
543 q in " ".join(ctx.files()).lower()):
544 miss = 1
545 break
546 if miss:
547 continue
548
549 count += 1
550 n = ctx.node()
551 showtags = self.showtag(tmpl, 'changelogtag', n)
552
553 yield tmpl('searchentry',
554 parity=parity.next(),
555 author=ctx.user(),
556 parent=self.siblings(ctx.parents()),
557 child=self.siblings(ctx.children()),
558 changelogtag=showtags,
559 desc=ctx.description(),
560 date=ctx.date(),
561 files=self.listfilediffs(tmpl, ctx.files(), n),
562 rev=ctx.rev(),
563 node=hex(n),
564 tags=self.nodetagsdict(n),
565 inbranch=self.nodeinbranch(ctx),
566 branches=self.nodebranchdict(ctx))
567
568 if count >= self.maxchanges:
569 break
570
571 cl = self.repo.changelog
572 parity = paritygen(self.stripecount)
573
574 return tmpl('search',
575 query=query,
576 node=hex(cl.tip()),
577 entries=changelist,
578 archives=self.archivelist("tip"))
579
580 def changeset(self, tmpl, ctx):
581 n = ctx.node()
582 showtags = self.showtag(tmpl, 'changesettag', n)
583 parents = ctx.parents()
584 p1 = parents[0].node()
585
586 files = []
587 parity = paritygen(self.stripecount)
588 for f in ctx.files():
589 files.append(tmpl("filenodelink",
590 node=hex(n), file=f,
591 parity=parity.next()))
592
593 def diff(**map):
594 yield self.diff(tmpl, p1, n, None)
595
596 return tmpl('changeset',
597 diff=diff,
598 rev=ctx.rev(),
599 node=hex(n),
600 parent=self.siblings(parents),
601 child=self.siblings(ctx.children()),
602 changesettag=showtags,
603 author=ctx.user(),
604 desc=ctx.description(),
605 date=ctx.date(),
606 files=files,
607 archives=self.archivelist(hex(n)),
608 tags=self.nodetagsdict(n),
609 branch=self.nodebranchnodefault(ctx),
610 inbranch=self.nodeinbranch(ctx),
611 branches=self.nodebranchdict(ctx))
612
613 def filelog(self, tmpl, fctx):
614 f = fctx.path()
615 fl = fctx.filelog()
616 count = fl.count()
617 pagelen = self.maxshortchanges
618 pos = fctx.filerev()
619 start = max(0, pos - pagelen + 1)
620 end = min(count, start + pagelen)
621 pos = end - 1
622 parity = paritygen(self.stripecount, offset=start-end)
623
624 def entries(limit=0, **map):
625 l = []
626
627 for i in xrange(start, end):
628 ctx = fctx.filectx(i)
629 n = fl.node(i)
630
631 l.insert(0, {"parity": parity.next(),
632 "filerev": i,
633 "file": f,
634 "node": hex(ctx.node()),
635 "author": ctx.user(),
636 "date": ctx.date(),
637 "rename": self.renamelink(fl, n),
638 "parent": self.siblings(fctx.parents()),
639 "child": self.siblings(fctx.children()),
640 "desc": ctx.description()})
641
642 if limit > 0:
643 l = l[:limit]
644
645 for e in l:
646 yield e
647
648 nodefunc = lambda x: fctx.filectx(fileid=x)
649 nav = revnavgen(pos, pagelen, count, nodefunc)
650 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
651 entries=lambda **x: entries(limit=0, **x),
652 latestentry=lambda **x: entries(limit=1, **x))
653
654 def filerevision(self, tmpl, fctx):
655 f = fctx.path()
656 text = fctx.data()
657 fl = fctx.filelog()
658 n = fctx.filenode()
659 parity = paritygen(self.stripecount)
660
661 if util.binary(text):
662 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
663 text = '(binary:%s)' % mt
664
665 def lines():
666 for lineno, t in enumerate(text.splitlines(1)):
667 yield {"line": t,
668 "lineid": "l%d" % (lineno + 1),
669 "linenumber": "% 6d" % (lineno + 1),
670 "parity": parity.next()}
671
672 return tmpl("filerevision",
673 file=f,
674 path=_up(f),
675 text=lines(),
676 rev=fctx.rev(),
677 node=hex(fctx.node()),
678 author=fctx.user(),
679 date=fctx.date(),
680 desc=fctx.description(),
681 branch=self.nodebranchnodefault(fctx),
682 parent=self.siblings(fctx.parents()),
683 child=self.siblings(fctx.children()),
684 rename=self.renamelink(fl, n),
685 permissions=fctx.manifest().flags(f))
686
687 def fileannotate(self, tmpl, fctx):
688 f = fctx.path()
689 n = fctx.filenode()
690 fl = fctx.filelog()
691 parity = paritygen(self.stripecount)
692
693 def annotate(**map):
694 last = None
695 if util.binary(fctx.data()):
696 mt = (mimetypes.guess_type(fctx.path())[0]
697 or 'application/octet-stream')
698 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
699 '(binary:%s)' % mt)])
700 else:
701 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
702 for lineno, ((f, targetline), l) in lines:
703 fnode = f.filenode()
704 name = self.repo.ui.shortuser(f.user())
705
706 if last != fnode:
707 last = fnode
708
709 yield {"parity": parity.next(),
710 "node": hex(f.node()),
711 "rev": f.rev(),
712 "author": name,
713 "file": f.path(),
714 "targetline": targetline,
715 "line": l,
716 "lineid": "l%d" % (lineno + 1),
717 "linenumber": "% 6d" % (lineno + 1)}
718
719 return tmpl("fileannotate",
720 file=f,
721 annotate=annotate,
722 path=_up(f),
723 rev=fctx.rev(),
724 node=hex(fctx.node()),
725 author=fctx.user(),
726 date=fctx.date(),
727 desc=fctx.description(),
728 rename=self.renamelink(fl, n),
729 branch=self.nodebranchnodefault(fctx),
730 parent=self.siblings(fctx.parents()),
731 child=self.siblings(fctx.children()),
732 permissions=fctx.manifest().flags(f))
733
734 def manifest(self, tmpl, ctx, path):
735 mf = ctx.manifest()
736 node = ctx.node()
737
738 files = {}
739 parity = paritygen(self.stripecount)
740
741 if path and path[-1] != "/":
742 path += "/"
743 l = len(path)
744 abspath = "/" + path
745
746 for f, n in mf.items():
747 if f[:l] != path:
748 continue
749 remain = f[l:]
750 if "/" in remain:
751 short = remain[:remain.index("/") + 1] # bleah
752 files[short] = (f, None)
753 else:
754 short = os.path.basename(remain)
755 files[short] = (f, n)
756
757 if not files:
758 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
759
760 def filelist(**map):
761 fl = files.keys()
762 fl.sort()
763 for f in fl:
764 full, fnode = files[f]
765 if not fnode:
766 continue
767
768 fctx = ctx.filectx(full)
769 yield {"file": full,
770 "parity": parity.next(),
771 "basename": f,
772 "date": fctx.changectx().date(),
773 "size": fctx.size(),
774 "permissions": mf.flags(full)}
775
776 def dirlist(**map):
777 fl = files.keys()
778 fl.sort()
779 for f in fl:
780 full, fnode = files[f]
781 if fnode:
782 continue
783
784 yield {"parity": parity.next(),
785 "path": "%s%s" % (abspath, f),
786 "basename": f[:-1]}
787
788 return tmpl("manifest",
789 rev=ctx.rev(),
790 node=hex(node),
791 path=abspath,
792 up=_up(abspath),
793 upparity=parity.next(),
794 fentries=filelist,
795 dentries=dirlist,
796 archives=self.archivelist(hex(node)),
797 tags=self.nodetagsdict(node),
798 inbranch=self.nodeinbranch(ctx),
799 branches=self.nodebranchdict(ctx))
800
801 def tags(self, tmpl):
802 i = self.repo.tagslist()
803 i.reverse()
804 parity = paritygen(self.stripecount)
805
806 def entries(notip=False,limit=0, **map):
807 count = 0
808 for k, n in i:
809 if notip and k == "tip":
810 continue
811 if limit > 0 and count >= limit:
812 continue
813 count = count + 1
814 yield {"parity": parity.next(),
815 "tag": k,
816 "date": self.repo.changectx(n).date(),
817 "node": hex(n)}
818
819 return tmpl("tags",
820 node=hex(self.repo.changelog.tip()),
821 entries=lambda **x: entries(False,0, **x),
822 entriesnotip=lambda **x: entries(True,0, **x),
823 latestentry=lambda **x: entries(True,1, **x))
824
825 def summary(self, tmpl):
826 i = self.repo.tagslist()
827 i.reverse()
828
829 def tagentries(**map):
830 parity = paritygen(self.stripecount)
831 count = 0
832 for k, n in i:
833 if k == "tip": # skip tip
834 continue;
835
836 count += 1
837 if count > 10: # limit to 10 tags
838 break;
839
840 yield tmpl("tagentry",
841 parity=parity.next(),
842 tag=k,
843 node=hex(n),
844 date=self.repo.changectx(n).date())
845
846
847 def branches(**map):
848 parity = paritygen(self.stripecount)
849
850 b = self.repo.branchtags()
851 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
852 l.sort()
853
854 for r,n,t in l:
855 ctx = self.repo.changectx(n)
856
857 yield {'parity': parity.next(),
858 'branch': t,
859 'node': hex(n),
860 'date': ctx.date()}
861
862 def changelist(**map):
863 parity = paritygen(self.stripecount, offset=start-end)
864 l = [] # build a list in forward order for efficiency
865 for i in xrange(start, end):
866 ctx = self.repo.changectx(i)
867 n = ctx.node()
868 hn = hex(n)
869
870 l.insert(0, tmpl(
871 'shortlogentry',
872 parity=parity.next(),
873 author=ctx.user(),
874 desc=ctx.description(),
875 date=ctx.date(),
876 rev=i,
877 node=hn,
878 tags=self.nodetagsdict(n),
879 inbranch=self.nodeinbranch(ctx),
880 branches=self.nodebranchdict(ctx)))
881
882 yield l
883
884 cl = self.repo.changelog
885 count = cl.count()
886 start = max(0, count - self.maxchanges)
887 end = min(count, start + self.maxchanges)
888
889 return tmpl("summary",
890 desc=self.config("web", "description", "unknown"),
891 owner=get_contact(self.config) or "unknown",
892 lastchange=cl.read(cl.tip())[2],
893 tags=tagentries,
894 branches=branches,
895 shortlog=changelist,
896 node=hex(cl.tip()),
897 archives=self.archivelist("tip"))
898
899 def filediff(self, tmpl, fctx):
900 n = fctx.node()
901 path = fctx.path()
902 parents = fctx.parents()
903 p1 = parents and parents[0].node() or nullid
904
905 def diff(**map):
906 yield self.diff(tmpl, p1, n, [path])
907
908 return tmpl("filediff",
909 file=path,
910 node=hex(n),
911 rev=fctx.rev(),
912 branch=self.nodebranchnodefault(fctx),
913 parent=self.siblings(parents),
914 child=self.siblings(fctx.children()),
915 diff=diff)
916
917 361 archive_specs = {
918 362 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
919 363 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
920 364 'zip': ('application/zip', 'zip', '.zip', None),
921 365 }
922 366
923 def archive(self, tmpl, req, key, type_):
924 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
925 cnode = self.repo.lookup(key)
926 arch_version = key
927 if cnode == key or key == 'tip':
928 arch_version = short(cnode)
929 name = "%s-%s" % (reponame, arch_version)
930 mimetype, artype, extension, encoding = self.archive_specs[type_]
931 headers = [
932 ('Content-Type', mimetype),
933 ('Content-Disposition', 'attachment; filename=%s%s' %
934 (name, extension))
935 ]
936 if encoding:
937 headers.append(('Content-Encoding', encoding))
938 req.header(headers)
939 req.respond(HTTP_OK)
940 archival.archive(self.repo, req, cnode, artype, prefix=name)
941
942 # add tags to things
943 # tags -> list of changesets corresponding to tags
944 # find tag, changeset, file
945
946 def cleanpath(self, path):
947 path = path.lstrip('/')
948 return util.canonpath(self.repo.root, '', path)
949
950 def changectx(self, req):
951 if 'node' in req.form:
952 changeid = req.form['node'][0]
953 elif 'manifest' in req.form:
954 changeid = req.form['manifest'][0]
955 else:
956 changeid = self.repo.changelog.count() - 1
957
958 try:
959 ctx = self.repo.changectx(changeid)
960 except RepoError:
961 man = self.repo.manifest
962 mn = man.lookup(changeid)
963 ctx = self.repo.changectx(man.linkrev(mn))
964
965 return ctx
966
967 def filectx(self, req):
968 path = self.cleanpath(req.form['file'][0])
969 if 'node' in req.form:
970 changeid = req.form['node'][0]
971 else:
972 changeid = req.form['filenode'][0]
973 try:
974 ctx = self.repo.changectx(changeid)
975 fctx = ctx.filectx(path)
976 except RepoError:
977 fctx = self.repo.filectx(path, fileid=changeid)
978
979 return fctx
980
981 367 def check_perm(self, req, op, default):
982 368 '''check permission for operation based on user auth.
983 369 return true if op allowed, else false.
@@ -6,7 +6,7 b''
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 import os, mimetools, cStringIO
9 import os
10 10 from mercurial.i18n import gettext as _
11 11 from mercurial.repo import RepoError
12 12 from mercurial import ui, hg, util, templater, templatefilters
@@ -81,17 +81,8 b' class hgwebdir(object):'
81 81
82 82 virtual = req.env.get("PATH_INFO", "").strip('/')
83 83 tmpl = self.templater(req)
84 try:
85 84 ctype = tmpl('mimetype', encoding=util._encoding)
86 85 ctype = templater.stringify(ctype)
87 except KeyError:
88 # old templates with inline HTTP headers?
89 if 'mimetype' in tmpl:
90 raise
91 header = tmpl('header', encoding=util._encoding)
92 header_file = cStringIO.StringIO(templater.stringify(header))
93 msg = mimetools.Message(header_file, 0)
94 ctype = msg['content-type']
95 86
96 87 # a static file
97 88 if virtual.startswith('static/') or 'static' in req.form:
@@ -255,13 +246,7 b' class hgwebdir(object):'
255 246 def templater(self, req):
256 247
257 248 def header(**map):
258 header = tmpl('header', encoding=util._encoding, **map)
259 if 'mimetype' not in tmpl:
260 # old template with inline HTTP headers
261 header_file = cStringIO.StringIO(templater.stringify(header))
262 msg = mimetools.Message(header_file, 0)
263 header = header_file.read()
264 yield header
249 yield tmpl('header', encoding=util._encoding, **map)
265 250
266 251 def footer(**map):
267 252 yield tmpl("footer", **map)
@@ -268,12 +268,7 b' def create_server(ui, repo):'
268 268
269 269 self.addr, self.port = self.socket.getsockname()[0:2]
270 270 self.prefix = prefix
271
272 271 self.fqaddr = socket.getfqdn(address)
273 try:
274 socket.getaddrbyhost(self.fqaddr)
275 except:
276 fqaddr = address
277 272
278 273 class IPv6HTTPServer(MercurialHTTPServer):
279 274 address_family = getattr(socket, 'AF_INET6', None)
@@ -5,10 +5,14 b''
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 import os, mimetypes
9 from mercurial import revlog, util
8 import os, mimetypes, re
9 import webutil
10 from mercurial import revlog, archival
11 from mercurial.node import short, hex, nullid
12 from mercurial.util import binary
10 13 from mercurial.repo import RepoError
11 from common import staticfile, ErrorResponse, HTTP_OK, HTTP_NOT_FOUND
14 from common import paritygen, staticfile, get_contact, ErrorResponse
15 from common import HTTP_OK, HTTP_NOT_FOUND
12 16
13 17 # __all__ is populated with the allowed commands. Be sure to add to it if
14 18 # you're adding a new command, or the new command won't work.
@@ -26,17 +30,17 b' def log(web, req, tmpl):'
26 30 return changelog(web, req, tmpl)
27 31
28 32 def rawfile(web, req, tmpl):
29 path = web.cleanpath(req.form.get('file', [''])[0])
33 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
30 34 if not path:
31 content = web.manifest(tmpl, web.changectx(req), path)
35 content = manifest(web, req, tmpl)
32 36 req.respond(HTTP_OK, web.ctype)
33 37 return content
34 38
35 39 try:
36 fctx = web.filectx(req)
40 fctx = webutil.filectx(web.repo, req)
37 41 except revlog.LookupError, inst:
38 42 try:
39 content = web.manifest(tmpl, web.changectx(req), path)
43 content = manifest(web, req, tmpl)
40 44 req.respond(HTTP_OK, web.ctype)
41 45 return content
42 46 except ErrorResponse:
@@ -45,28 +49,120 b' def rawfile(web, req, tmpl):'
45 49 path = fctx.path()
46 50 text = fctx.data()
47 51 mt = mimetypes.guess_type(path)[0]
48 if mt is None or util.binary(text):
52 if mt is None or binary(text):
49 53 mt = mt or 'application/octet-stream'
50 54
51 55 req.respond(HTTP_OK, mt, path, len(text))
52 56 return [text]
53 57
58 def _filerevision(web, tmpl, fctx):
59 f = fctx.path()
60 text = fctx.data()
61 fl = fctx.filelog()
62 n = fctx.filenode()
63 parity = paritygen(web.stripecount)
64
65 if binary(text):
66 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
67 text = '(binary:%s)' % mt
68
69 def lines():
70 for lineno, t in enumerate(text.splitlines(1)):
71 yield {"line": t,
72 "lineid": "l%d" % (lineno + 1),
73 "linenumber": "% 6d" % (lineno + 1),
74 "parity": parity.next()}
75
76 return tmpl("filerevision",
77 file=f,
78 path=webutil.up(f),
79 text=lines(),
80 rev=fctx.rev(),
81 node=hex(fctx.node()),
82 author=fctx.user(),
83 date=fctx.date(),
84 desc=fctx.description(),
85 branch=webutil.nodebranchnodefault(fctx),
86 parent=webutil.siblings(fctx.parents()),
87 child=webutil.siblings(fctx.children()),
88 rename=webutil.renamelink(fctx),
89 permissions=fctx.manifest().flags(f))
90
54 91 def file(web, req, tmpl):
55 path = web.cleanpath(req.form.get('file', [''])[0])
92 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
56 93 if path:
57 94 try:
58 return web.filerevision(tmpl, web.filectx(req))
95 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
59 96 except revlog.LookupError, inst:
60 97 pass
61 98
62 99 try:
63 return web.manifest(tmpl, web.changectx(req), path)
100 return manifest(web, req, tmpl)
64 101 except ErrorResponse:
65 102 raise inst
66 103
104 def _search(web, tmpl, query):
105
106 def changelist(**map):
107 cl = web.repo.changelog
108 count = 0
109 qw = query.lower().split()
110
111 def revgen():
112 for i in xrange(cl.count() - 1, 0, -100):
113 l = []
114 for j in xrange(max(0, i - 100), i + 1):
115 ctx = web.repo.changectx(j)
116 l.append(ctx)
117 l.reverse()
118 for e in l:
119 yield e
120
121 for ctx in revgen():
122 miss = 0
123 for q in qw:
124 if not (q in ctx.user().lower() or
125 q in ctx.description().lower() or
126 q in " ".join(ctx.files()).lower()):
127 miss = 1
128 break
129 if miss:
130 continue
131
132 count = 1
133 n = ctx.node()
134 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
135
136 yield tmpl('searchentry',
137 parity=parity.next(),
138 author=ctx.user(),
139 parent=webutil.siblings(ctx.parents()),
140 child=webutil.siblings(ctx.children()),
141 changelogtag=showtags,
142 desc=ctx.description(),
143 date=ctx.date(),
144 files=web.listfilediffs(tmpl, ctx.files(), n),
145 rev=ctx.rev(),
146 node=hex(n),
147 tags=webutil.nodetagsdict(web.repo, n),
148 inbranch=webutil.nodeinbranch(web.repo, ctx),
149 branches=webutil.nodebranchdict(web.repo, ctx))
150
151 if count >= web.maxchanges:
152 break
153
154 cl = web.repo.changelog
155 parity = paritygen(web.stripecount)
156
157 return tmpl('search',
158 query=query,
159 node=hex(cl.tip()),
160 entries=changelist,
161 archives=web.archivelist("tip"))
162
67 163 def changelog(web, req, tmpl, shortlog = False):
68 164 if 'node' in req.form:
69 ctx = web.changectx(req)
165 ctx = webutil.changectx(web.repo, req)
70 166 else:
71 167 if 'rev' in req.form:
72 168 hi = req.form['rev'][0]
@@ -75,47 +171,400 b' def changelog(web, req, tmpl, shortlog ='
75 171 try:
76 172 ctx = web.repo.changectx(hi)
77 173 except RepoError:
78 return web.search(tmpl, hi) # XXX redirect to 404 page?
174 return _search(web, tmpl, hi) # XXX redirect to 404 page?
175
176 def changelist(limit=0, **map):
177 cl = web.repo.changelog
178 l = [] # build a list in forward order for efficiency
179 for i in xrange(start, end):
180 ctx = web.repo.changectx(i)
181 n = ctx.node()
182 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
183
184 l.insert(0, {"parity": parity.next(),
185 "author": ctx.user(),
186 "parent": webutil.siblings(ctx.parents(), i - 1),
187 "child": webutil.siblings(ctx.children(), i + 1),
188 "changelogtag": showtags,
189 "desc": ctx.description(),
190 "date": ctx.date(),
191 "files": web.listfilediffs(tmpl, ctx.files(), n),
192 "rev": i,
193 "node": hex(n),
194 "tags": webutil.nodetagsdict(web.repo, n),
195 "inbranch": webutil.nodeinbranch(web.repo, ctx),
196 "branches": webutil.nodebranchdict(web.repo, ctx)
197 })
79 198
80 return web.changelog(tmpl, ctx, shortlog = shortlog)
199 if limit > 0:
200 l = l[:limit]
201
202 for e in l:
203 yield e
204
205 maxchanges = shortlog and web.maxshortchanges or web.maxchanges
206 cl = web.repo.changelog
207 count = cl.count()
208 pos = ctx.rev()
209 start = max(0, pos - maxchanges + 1)
210 end = min(count, start + maxchanges)
211 pos = end - 1
212 parity = paritygen(web.stripecount, offset=start-end)
213
214 changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
215
216 return tmpl(shortlog and 'shortlog' or 'changelog',
217 changenav=changenav,
218 node=hex(ctx.node()),
219 rev=pos, changesets=count,
220 entries=lambda **x: changelist(limit=0,**x),
221 latestentry=lambda **x: changelist(limit=1,**x),
222 archives=web.archivelist("tip"))
81 223
82 224 def shortlog(web, req, tmpl):
83 225 return changelog(web, req, tmpl, shortlog = True)
84 226
85 227 def changeset(web, req, tmpl):
86 return web.changeset(tmpl, web.changectx(req))
228 ctx = webutil.changectx(web.repo, req)
229 n = ctx.node()
230 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n)
231 parents = ctx.parents()
232 p1 = parents[0].node()
233
234 files = []
235 parity = paritygen(web.stripecount)
236 for f in ctx.files():
237 files.append(tmpl("filenodelink",
238 node=hex(n), file=f,
239 parity=parity.next()))
240
241 diffs = web.diff(tmpl, p1, n, None)
242 return tmpl('changeset',
243 diff=diffs,
244 rev=ctx.rev(),
245 node=hex(n),
246 parent=webutil.siblings(parents),
247 child=webutil.siblings(ctx.children()),
248 changesettag=showtags,
249 author=ctx.user(),
250 desc=ctx.description(),
251 date=ctx.date(),
252 files=files,
253 archives=web.archivelist(hex(n)),
254 tags=webutil.nodetagsdict(web.repo, n),
255 branch=webutil.nodebranchnodefault(ctx),
256 inbranch=webutil.nodeinbranch(web.repo, ctx),
257 branches=webutil.nodebranchdict(web.repo, ctx))
87 258
88 259 rev = changeset
89 260
90 261 def manifest(web, req, tmpl):
91 return web.manifest(tmpl, web.changectx(req),
92 web.cleanpath(req.form['path'][0]))
262 ctx = webutil.changectx(web.repo, req)
263 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
264 mf = ctx.manifest()
265 node = ctx.node()
266
267 files = {}
268 parity = paritygen(web.stripecount)
269
270 if path and path[-1] != "/":
271 path += "/"
272 l = len(path)
273 abspath = "/" + path
274
275 for f, n in mf.items():
276 if f[:l] != path:
277 continue
278 remain = f[l:]
279 if "/" in remain:
280 short = remain[:remain.index("/") + 1] # bleah
281 files[short] = (f, None)
282 else:
283 short = os.path.basename(remain)
284 files[short] = (f, n)
285
286 if not files:
287 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
288
289 def filelist(**map):
290 fl = files.keys()
291 fl.sort()
292 for f in fl:
293 full, fnode = files[f]
294 if not fnode:
295 continue
296
297 fctx = ctx.filectx(full)
298 yield {"file": full,
299 "parity": parity.next(),
300 "basename": f,
301 "date": fctx.changectx().date(),
302 "size": fctx.size(),
303 "permissions": mf.flags(full)}
304
305 def dirlist(**map):
306 fl = files.keys()
307 fl.sort()
308 for f in fl:
309 full, fnode = files[f]
310 if fnode:
311 continue
312
313 yield {"parity": parity.next(),
314 "path": "%s%s" % (abspath, f),
315 "basename": f[:-1]}
316
317 return tmpl("manifest",
318 rev=ctx.rev(),
319 node=hex(node),
320 path=abspath,
321 up=webutil.up(abspath),
322 upparity=parity.next(),
323 fentries=filelist,
324 dentries=dirlist,
325 archives=web.archivelist(hex(node)),
326 tags=webutil.nodetagsdict(web.repo, node),
327 inbranch=webutil.nodeinbranch(web.repo, ctx),
328 branches=webutil.nodebranchdict(web.repo, ctx))
93 329
94 330 def tags(web, req, tmpl):
95 return web.tags(tmpl)
331 i = web.repo.tagslist()
332 i.reverse()
333 parity = paritygen(web.stripecount)
334
335 def entries(notip=False,limit=0, **map):
336 count = 0
337 for k, n in i:
338 if notip and k == "tip":
339 continue
340 if limit > 0 and count >= limit:
341 continue
342 count = count + 1
343 yield {"parity": parity.next(),
344 "tag": k,
345 "date": web.repo.changectx(n).date(),
346 "node": hex(n)}
347
348 return tmpl("tags",
349 node=hex(web.repo.changelog.tip()),
350 entries=lambda **x: entries(False,0, **x),
351 entriesnotip=lambda **x: entries(True,0, **x),
352 latestentry=lambda **x: entries(True,1, **x))
96 353
97 354 def summary(web, req, tmpl):
98 return web.summary(tmpl)
355 i = web.repo.tagslist()
356 i.reverse()
357
358 def tagentries(**map):
359 parity = paritygen(web.stripecount)
360 count = 0
361 for k, n in i:
362 if k == "tip": # skip tip
363 continue
364
365 count = 1
366 if count > 10: # limit to 10 tags
367 break
368
369 yield tmpl("tagentry",
370 parity=parity.next(),
371 tag=k,
372 node=hex(n),
373 date=web.repo.changectx(n).date())
374
375 def branches(**map):
376 parity = paritygen(web.stripecount)
377
378 b = web.repo.branchtags()
379 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
380 l.sort()
381
382 for r,n,t in l:
383 ctx = web.repo.changectx(n)
384 yield {'parity': parity.next(),
385 'branch': t,
386 'node': hex(n),
387 'date': ctx.date()}
388
389 def changelist(**map):
390 parity = paritygen(web.stripecount, offset=start-end)
391 l = [] # build a list in forward order for efficiency
392 for i in xrange(start, end):
393 ctx = web.repo.changectx(i)
394 n = ctx.node()
395 hn = hex(n)
396
397 l.insert(0, tmpl(
398 'shortlogentry',
399 parity=parity.next(),
400 author=ctx.user(),
401 desc=ctx.description(),
402 date=ctx.date(),
403 rev=i,
404 node=hn,
405 tags=webutil.nodetagsdict(web.repo, n),
406 inbranch=webutil.nodeinbranch(web.repo, ctx),
407 branches=webutil.nodebranchdict(web.repo, ctx)))
408
409 yield l
410
411 cl = web.repo.changelog
412 count = cl.count()
413 start = max(0, count - web.maxchanges)
414 end = min(count, start + web.maxchanges)
415
416 return tmpl("summary",
417 desc=web.config("web", "description", "unknown"),
418 owner=get_contact(web.config) or "unknown",
419 lastchange=cl.read(cl.tip())[2],
420 tags=tagentries,
421 branches=branches,
422 shortlog=changelist,
423 node=hex(cl.tip()),
424 archives=web.archivelist("tip"))
99 425
100 426 def filediff(web, req, tmpl):
101 return web.filediff(tmpl, web.filectx(req))
427 fctx = webutil.filectx(web.repo, req)
428 n = fctx.node()
429 path = fctx.path()
430 parents = fctx.parents()
431 p1 = parents and parents[0].node() or nullid
432
433 diffs = web.diff(tmpl, p1, n, [path])
434 return tmpl("filediff",
435 file=path,
436 node=hex(n),
437 rev=fctx.rev(),
438 date=fctx.date(),
439 desc=fctx.description(),
440 author=fctx.user(),
441 rename=webutil.renamelink(fctx),
442 branch=webutil.nodebranchnodefault(fctx),
443 parent=webutil.siblings(parents),
444 child=webutil.siblings(fctx.children()),
445 diff=diffs)
102 446
103 447 diff = filediff
104 448
105 449 def annotate(web, req, tmpl):
106 return web.fileannotate(tmpl, web.filectx(req))
450 fctx = webutil.filectx(web.repo, req)
451 f = fctx.path()
452 n = fctx.filenode()
453 fl = fctx.filelog()
454 parity = paritygen(web.stripecount)
455
456 def annotate(**map):
457 last = None
458 if binary(fctx.data()):
459 mt = (mimetypes.guess_type(fctx.path())[0]
460 or 'application/octet-stream')
461 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
462 '(binary:%s)' % mt)])
463 else:
464 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
465 for lineno, ((f, targetline), l) in lines:
466 fnode = f.filenode()
467 name = web.repo.ui.shortuser(f.user())
468
469 if last != fnode:
470 last = fnode
471
472 yield {"parity": parity.next(),
473 "node": hex(f.node()),
474 "rev": f.rev(),
475 "author": name,
476 "file": f.path(),
477 "targetline": targetline,
478 "line": l,
479 "lineid": "l%d" % (lineno + 1),
480 "linenumber": "% 6d" % (lineno + 1)}
481
482 return tmpl("fileannotate",
483 file=f,
484 annotate=annotate,
485 path=webutil.up(f),
486 rev=fctx.rev(),
487 node=hex(fctx.node()),
488 author=fctx.user(),
489 date=fctx.date(),
490 desc=fctx.description(),
491 rename=webutil.renamelink(fctx),
492 branch=webutil.nodebranchnodefault(fctx),
493 parent=webutil.siblings(fctx.parents()),
494 child=webutil.siblings(fctx.children()),
495 permissions=fctx.manifest().flags(f))
107 496
108 497 def filelog(web, req, tmpl):
109 return web.filelog(tmpl, web.filectx(req))
498 fctx = webutil.filectx(web.repo, req)
499 f = fctx.path()
500 fl = fctx.filelog()
501 count = fl.count()
502 pagelen = web.maxshortchanges
503 pos = fctx.filerev()
504 start = max(0, pos - pagelen + 1)
505 end = min(count, start + pagelen)
506 pos = end - 1
507 parity = paritygen(web.stripecount, offset=start-end)
508
509 def entries(limit=0, **map):
510 l = []
511
512 for i in xrange(start, end):
513 ctx = fctx.filectx(i)
514 n = fl.node(i)
515
516 l.insert(0, {"parity": parity.next(),
517 "filerev": i,
518 "file": f,
519 "node": hex(ctx.node()),
520 "author": ctx.user(),
521 "date": ctx.date(),
522 "rename": webutil.renamelink(fctx),
523 "parent": webutil.siblings(fctx.parents()),
524 "child": webutil.siblings(fctx.children()),
525 "desc": ctx.description()})
526
527 if limit > 0:
528 l = l[:limit]
529
530 for e in l:
531 yield e
532
533 nodefunc = lambda x: fctx.filectx(fileid=x)
534 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
535 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
536 entries=lambda **x: entries(limit=0, **x),
537 latestentry=lambda **x: entries(limit=1, **x))
538
110 539
111 540 def archive(web, req, tmpl):
112 541 type_ = req.form['type'][0]
113 542 allowed = web.configlist("web", "allow_archive")
114 if (type_ in web.archives and (type_ in allowed or
543 key = req.form['node'][0]
544
545 if not (type_ in web.archives and (type_ in allowed or
115 546 web.configbool("web", "allow" + type_, False))):
116 web.archive(tmpl, req, req.form['node'][0], type_)
547 msg = 'Unsupported archive type: %s' % type_
548 raise ErrorResponse(HTTP_NOT_FOUND, msg)
549
550 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
551 cnode = web.repo.lookup(key)
552 arch_version = key
553 if cnode == key or key == 'tip':
554 arch_version = short(cnode)
555 name = "%s-%s" % (reponame, arch_version)
556 mimetype, artype, extension, encoding = web.archive_specs[type_]
557 headers = [
558 ('Content-Type', mimetype),
559 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
560 ]
561 if encoding:
562 headers.append(('Content-Encoding', encoding))
563 req.header(headers)
564 req.respond(HTTP_OK)
565 archival.archive(web.repo, req, cnode, artype, prefix=name)
117 566 return []
118 raise ErrorResponse(HTTP_NOT_FOUND, 'unsupported archive type: %s' % type_)
567
119 568
120 569 def static(web, req, tmpl):
121 570 fname = req.form['file'][0]
@@ -8,7 +8,7 b''
8 8 from node import bin, hex, nullid
9 9 from revlog import revlog, RevlogError
10 10 from i18n import _
11 import array, struct, mdiff
11 import array, struct, mdiff, parsers
12 12
13 13 class manifestdict(dict):
14 14 def __init__(self, mapping=None, flags=None):
@@ -39,14 +39,7 b' class manifest(revlog):'
39 39
40 40 def parse(self, lines):
41 41 mfdict = manifestdict()
42 fdict = mfdict._flags
43 for l in lines.splitlines():
44 f, n = l.split('\0')
45 if len(n) > 40:
46 fdict[f] = n[40:]
47 mfdict[f] = bin(n[:40])
48 else:
49 mfdict[f] = bin(n)
42 parsers.parse_manifest(mfdict, mfdict._flags, lines)
50 43 return mfdict
51 44
52 45 def readdelta(self, node):
@@ -72,7 +72,6 b' def _collectextranodes(repo, files, link'
72 72 def strip(ui, repo, node, backup="all"):
73 73 cl = repo.changelog
74 74 # TODO delete the undo files, and handle undo of merge sets
75 pp = cl.parents(node)
76 75 striprev = cl.rev(node)
77 76
78 77 # Some revisions with rev > striprev may not be descendants of striprev.
@@ -114,7 +114,7 b' class templater(object):'
114 114 v = v(**map)
115 115 if format:
116 116 if not hasattr(v, '__iter__'):
117 raise SyntaxError(_("Error expanding '%s%s'")
117 raise SyntaxError(_("Error expanding '%s%%%s'")
118 118 % (key, format))
119 119 lm = map.copy()
120 120 for i in v:
@@ -96,9 +96,13 b' def rollback(opener, file):'
96 96 files = {}
97 97 for l in open(file).readlines():
98 98 f, o = l.split('\0')
99 files[f] = o
99 files[f] = int(o)
100 100 for f in files:
101 101 o = files[f]
102 if o:
102 103 opener(f, "a").truncate(int(o))
104 else:
105 fn = opener(f).name
106 os.unlink(fn)
103 107 os.unlink(file)
104 108
@@ -88,10 +88,11 b' mercurial.version.remember_version(versi'
88 88 cmdclass = {'install_data': install_package_data}
89 89
90 90 ext_modules=[
91 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
91 Extension('mercurial.base85', ['mercurial/base85.c']),
92 92 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
93 Extension('mercurial.base85', ['mercurial/base85.c']),
94 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'])
93 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
94 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
95 Extension('mercurial.parsers', ['mercurial/parsers.c']),
95 96 ]
96 97
97 98 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert']
1 NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: modified file, binary diff hidden
@@ -3,6 +3,8 b''
3 3 echo "[extensions]" >> $HGRCPATH
4 4 echo "churn=" >> $HGRCPATH
5 5
6 COLUMNS=80; export COLUMNS
7
6 8 echo % create test repository
7 9 hg init repo
8 10 cd repo
@@ -1,12 +1,12 b''
1 1 % Without -v
2 2 access log created - .hg/hgrc respected
3 3 % With -v
4 listening at http://localhost/ (127.0.0.1)
4 listening at http://localhost/ (bound to 127.0.0.1)
5 5 % With --prefix foo
6 listening at http://localhost/foo/ (127.0.0.1)
6 listening at http://localhost/foo/ (bound to 127.0.0.1)
7 7 % With --prefix /foo
8 listening at http://localhost/foo/ (127.0.0.1)
8 listening at http://localhost/foo/ (bound to 127.0.0.1)
9 9 % With --prefix foo/
10 listening at http://localhost/foo/ (127.0.0.1)
10 listening at http://localhost/foo/ (bound to 127.0.0.1)
11 11 % With --prefix /foo/
12 listening at http://localhost/foo/ (127.0.0.1)
12 listening at http://localhost/foo/ (bound to 127.0.0.1)
General Comments 0
You need to be logged in to leave comments. Login now