##// END OF EJS Templates
hgweb: make annotate line revisions point to annotation for that rev
Brendan Cully -
r3175:fc379b91 default
parent child Browse files
Show More
@@ -1,964 +1,964 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os
9 import os
10 import os.path
10 import os.path
11 import mimetypes
11 import mimetypes
12 from mercurial.demandload import demandload
12 from mercurial.demandload import demandload
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
15 demandload(globals(), "mercurial:templater")
15 demandload(globals(), "mercurial:templater")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 from mercurial.node import *
17 from mercurial.node import *
18 from mercurial.i18n import gettext as _
18 from mercurial.i18n import gettext as _
19
19
20 def _up(p):
20 def _up(p):
21 if p[0] != "/":
21 if p[0] != "/":
22 p = "/" + p
22 p = "/" + p
23 if p[-1] == "/":
23 if p[-1] == "/":
24 p = p[:-1]
24 p = p[:-1]
25 up = os.path.dirname(p)
25 up = os.path.dirname(p)
26 if up == "/":
26 if up == "/":
27 return "/"
27 return "/"
28 return up + "/"
28 return up + "/"
29
29
30 class hgweb(object):
30 class hgweb(object):
31 def __init__(self, repo, name=None):
31 def __init__(self, repo, name=None):
32 if type(repo) == type(""):
32 if type(repo) == type(""):
33 self.repo = hg.repository(ui.ui(), repo)
33 self.repo = hg.repository(ui.ui(), repo)
34 else:
34 else:
35 self.repo = repo
35 self.repo = repo
36
36
37 self.mtime = -1
37 self.mtime = -1
38 self.reponame = name
38 self.reponame = name
39 self.archives = 'zip', 'gz', 'bz2'
39 self.archives = 'zip', 'gz', 'bz2'
40 self.stripecount = 1
40 self.stripecount = 1
41 self.templatepath = self.repo.ui.config("web", "templates",
41 self.templatepath = self.repo.ui.config("web", "templates",
42 templater.templatepath())
42 templater.templatepath())
43
43
44 def refresh(self):
44 def refresh(self):
45 mtime = get_mtime(self.repo.root)
45 mtime = get_mtime(self.repo.root)
46 if mtime != self.mtime:
46 if mtime != self.mtime:
47 self.mtime = mtime
47 self.mtime = mtime
48 self.repo = hg.repository(self.repo.ui, self.repo.root)
48 self.repo = hg.repository(self.repo.ui, self.repo.root)
49 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
49 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
50 self.stripecount = int(self.repo.ui.config("web", "stripes", 1))
50 self.stripecount = int(self.repo.ui.config("web", "stripes", 1))
51 self.maxshortchanges = int(self.repo.ui.config("web", "maxshortchanges", 60))
51 self.maxshortchanges = int(self.repo.ui.config("web", "maxshortchanges", 60))
52 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
52 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
53 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
53 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
54
54
55 def archivelist(self, nodeid):
55 def archivelist(self, nodeid):
56 allowed = self.repo.ui.configlist("web", "allow_archive")
56 allowed = self.repo.ui.configlist("web", "allow_archive")
57 for i in self.archives:
57 for i in self.archives:
58 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
58 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
59 yield {"type" : i, "node" : nodeid, "url": ""}
59 yield {"type" : i, "node" : nodeid, "url": ""}
60
60
61 def listfiles(self, files, mf):
61 def listfiles(self, files, mf):
62 for f in files[:self.maxfiles]:
62 for f in files[:self.maxfiles]:
63 yield self.t("filenodelink", node=hex(mf[f]), file=f)
63 yield self.t("filenodelink", node=hex(mf[f]), file=f)
64 if len(files) > self.maxfiles:
64 if len(files) > self.maxfiles:
65 yield self.t("fileellipses")
65 yield self.t("fileellipses")
66
66
67 def listfilediffs(self, files, changeset):
67 def listfilediffs(self, files, changeset):
68 for f in files[:self.maxfiles]:
68 for f in files[:self.maxfiles]:
69 yield self.t("filedifflink", node=hex(changeset), file=f)
69 yield self.t("filedifflink", node=hex(changeset), file=f)
70 if len(files) > self.maxfiles:
70 if len(files) > self.maxfiles:
71 yield self.t("fileellipses")
71 yield self.t("fileellipses")
72
72
73 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
73 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
74 if not rev:
74 if not rev:
75 rev = lambda x: ""
75 rev = lambda x: ""
76 siblings = [s for s in siblings if s != nullid]
76 siblings = [s for s in siblings if s != nullid]
77 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
77 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
78 return
78 return
79 for s in siblings:
79 for s in siblings:
80 yield dict(node=hex(s), rev=rev(s), **args)
80 yield dict(node=hex(s), rev=rev(s), **args)
81
81
82 def renamelink(self, fl, node):
82 def renamelink(self, fl, node):
83 r = fl.renamed(node)
83 r = fl.renamed(node)
84 if r:
84 if r:
85 return [dict(file=r[0], node=hex(r[1]))]
85 return [dict(file=r[0], node=hex(r[1]))]
86 return []
86 return []
87
87
88 def showtag(self, t1, node=nullid, **args):
88 def showtag(self, t1, node=nullid, **args):
89 for t in self.repo.nodetags(node):
89 for t in self.repo.nodetags(node):
90 yield self.t(t1, tag=t, **args)
90 yield self.t(t1, tag=t, **args)
91
91
92 def diff(self, node1, node2, files):
92 def diff(self, node1, node2, files):
93 def filterfiles(filters, files):
93 def filterfiles(filters, files):
94 l = [x for x in files if x in filters]
94 l = [x for x in files if x in filters]
95
95
96 for t in filters:
96 for t in filters:
97 if t and t[-1] != os.sep:
97 if t and t[-1] != os.sep:
98 t += os.sep
98 t += os.sep
99 l += [x for x in files if x.startswith(t)]
99 l += [x for x in files if x.startswith(t)]
100 return l
100 return l
101
101
102 parity = [0]
102 parity = [0]
103 def diffblock(diff, f, fn):
103 def diffblock(diff, f, fn):
104 yield self.t("diffblock",
104 yield self.t("diffblock",
105 lines=prettyprintlines(diff),
105 lines=prettyprintlines(diff),
106 parity=parity[0],
106 parity=parity[0],
107 file=f,
107 file=f,
108 filenode=hex(fn or nullid))
108 filenode=hex(fn or nullid))
109 parity[0] = 1 - parity[0]
109 parity[0] = 1 - parity[0]
110
110
111 def prettyprintlines(diff):
111 def prettyprintlines(diff):
112 for l in diff.splitlines(1):
112 for l in diff.splitlines(1):
113 if l.startswith('+'):
113 if l.startswith('+'):
114 yield self.t("difflineplus", line=l)
114 yield self.t("difflineplus", line=l)
115 elif l.startswith('-'):
115 elif l.startswith('-'):
116 yield self.t("difflineminus", line=l)
116 yield self.t("difflineminus", line=l)
117 elif l.startswith('@'):
117 elif l.startswith('@'):
118 yield self.t("difflineat", line=l)
118 yield self.t("difflineat", line=l)
119 else:
119 else:
120 yield self.t("diffline", line=l)
120 yield self.t("diffline", line=l)
121
121
122 r = self.repo
122 r = self.repo
123 cl = r.changelog
123 cl = r.changelog
124 mf = r.manifest
124 mf = r.manifest
125 change1 = cl.read(node1)
125 change1 = cl.read(node1)
126 change2 = cl.read(node2)
126 change2 = cl.read(node2)
127 mmap1 = mf.read(change1[0])
127 mmap1 = mf.read(change1[0])
128 mmap2 = mf.read(change2[0])
128 mmap2 = mf.read(change2[0])
129 date1 = util.datestr(change1[2])
129 date1 = util.datestr(change1[2])
130 date2 = util.datestr(change2[2])
130 date2 = util.datestr(change2[2])
131
131
132 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
132 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
133 if files:
133 if files:
134 modified, added, removed = map(lambda x: filterfiles(files, x),
134 modified, added, removed = map(lambda x: filterfiles(files, x),
135 (modified, added, removed))
135 (modified, added, removed))
136
136
137 diffopts = patch.diffopts(self.repo.ui)
137 diffopts = patch.diffopts(self.repo.ui)
138 for f in modified:
138 for f in modified:
139 to = r.file(f).read(mmap1[f])
139 to = r.file(f).read(mmap1[f])
140 tn = r.file(f).read(mmap2[f])
140 tn = r.file(f).read(mmap2[f])
141 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
141 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
142 opts=diffopts), f, tn)
142 opts=diffopts), f, tn)
143 for f in added:
143 for f in added:
144 to = None
144 to = None
145 tn = r.file(f).read(mmap2[f])
145 tn = r.file(f).read(mmap2[f])
146 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
146 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
147 opts=diffopts), f, tn)
147 opts=diffopts), f, tn)
148 for f in removed:
148 for f in removed:
149 to = r.file(f).read(mmap1[f])
149 to = r.file(f).read(mmap1[f])
150 tn = None
150 tn = None
151 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
151 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
152 opts=diffopts), f, tn)
152 opts=diffopts), f, tn)
153
153
154 def changelog(self, pos, shortlog=False):
154 def changelog(self, pos, shortlog=False):
155 def changenav(**map):
155 def changenav(**map):
156 def seq(factor, maxchanges=None):
156 def seq(factor, maxchanges=None):
157 if maxchanges:
157 if maxchanges:
158 yield maxchanges
158 yield maxchanges
159 if maxchanges >= 20 and maxchanges <= 40:
159 if maxchanges >= 20 and maxchanges <= 40:
160 yield 50
160 yield 50
161 else:
161 else:
162 yield 1 * factor
162 yield 1 * factor
163 yield 3 * factor
163 yield 3 * factor
164 for f in seq(factor * 10):
164 for f in seq(factor * 10):
165 yield f
165 yield f
166
166
167 l = []
167 l = []
168 last = 0
168 last = 0
169 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
169 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
170 for f in seq(1, maxchanges):
170 for f in seq(1, maxchanges):
171 if f < maxchanges or f <= last:
171 if f < maxchanges or f <= last:
172 continue
172 continue
173 if f > count:
173 if f > count:
174 break
174 break
175 last = f
175 last = f
176 r = "%d" % f
176 r = "%d" % f
177 if pos + f < count:
177 if pos + f < count:
178 l.append(("+" + r, pos + f))
178 l.append(("+" + r, pos + f))
179 if pos - f >= 0:
179 if pos - f >= 0:
180 l.insert(0, ("-" + r, pos - f))
180 l.insert(0, ("-" + r, pos - f))
181
181
182 yield {"rev": 0, "label": "(0)"}
182 yield {"rev": 0, "label": "(0)"}
183
183
184 for label, rev in l:
184 for label, rev in l:
185 yield {"label": label, "rev": rev}
185 yield {"label": label, "rev": rev}
186
186
187 yield {"label": "tip", "rev": "tip"}
187 yield {"label": "tip", "rev": "tip"}
188
188
189 def changelist(**map):
189 def changelist(**map):
190 parity = (start - end) & 1
190 parity = (start - end) & 1
191 cl = self.repo.changelog
191 cl = self.repo.changelog
192 l = [] # build a list in forward order for efficiency
192 l = [] # build a list in forward order for efficiency
193 for i in range(start, end):
193 for i in range(start, end):
194 n = cl.node(i)
194 n = cl.node(i)
195 changes = cl.read(n)
195 changes = cl.read(n)
196 hn = hex(n)
196 hn = hex(n)
197
197
198 l.insert(0, {"parity": parity,
198 l.insert(0, {"parity": parity,
199 "author": changes[1],
199 "author": changes[1],
200 "parent": self.siblings(cl.parents(n), cl.rev,
200 "parent": self.siblings(cl.parents(n), cl.rev,
201 cl.rev(n) - 1),
201 cl.rev(n) - 1),
202 "child": self.siblings(cl.children(n), cl.rev,
202 "child": self.siblings(cl.children(n), cl.rev,
203 cl.rev(n) + 1),
203 cl.rev(n) + 1),
204 "changelogtag": self.showtag("changelogtag",n),
204 "changelogtag": self.showtag("changelogtag",n),
205 "manifest": hex(changes[0]),
205 "manifest": hex(changes[0]),
206 "desc": changes[4],
206 "desc": changes[4],
207 "date": changes[2],
207 "date": changes[2],
208 "files": self.listfilediffs(changes[3], n),
208 "files": self.listfilediffs(changes[3], n),
209 "rev": i,
209 "rev": i,
210 "node": hn})
210 "node": hn})
211 parity = 1 - parity
211 parity = 1 - parity
212
212
213 for e in l:
213 for e in l:
214 yield e
214 yield e
215
215
216 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
216 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
217 cl = self.repo.changelog
217 cl = self.repo.changelog
218 mf = cl.read(cl.tip())[0]
218 mf = cl.read(cl.tip())[0]
219 count = cl.count()
219 count = cl.count()
220 start = max(0, pos - maxchanges + 1)
220 start = max(0, pos - maxchanges + 1)
221 end = min(count, start + maxchanges)
221 end = min(count, start + maxchanges)
222 pos = end - 1
222 pos = end - 1
223
223
224 yield self.t(shortlog and 'shortlog' or 'changelog',
224 yield self.t(shortlog and 'shortlog' or 'changelog',
225 changenav=changenav,
225 changenav=changenav,
226 manifest=hex(mf),
226 manifest=hex(mf),
227 rev=pos, changesets=count, entries=changelist,
227 rev=pos, changesets=count, entries=changelist,
228 archives=self.archivelist("tip"))
228 archives=self.archivelist("tip"))
229
229
230 def search(self, query):
230 def search(self, query):
231
231
232 def changelist(**map):
232 def changelist(**map):
233 cl = self.repo.changelog
233 cl = self.repo.changelog
234 count = 0
234 count = 0
235 qw = query.lower().split()
235 qw = query.lower().split()
236
236
237 def revgen():
237 def revgen():
238 for i in range(cl.count() - 1, 0, -100):
238 for i in range(cl.count() - 1, 0, -100):
239 l = []
239 l = []
240 for j in range(max(0, i - 100), i):
240 for j in range(max(0, i - 100), i):
241 n = cl.node(j)
241 n = cl.node(j)
242 changes = cl.read(n)
242 changes = cl.read(n)
243 l.append((n, j, changes))
243 l.append((n, j, changes))
244 l.reverse()
244 l.reverse()
245 for e in l:
245 for e in l:
246 yield e
246 yield e
247
247
248 for n, i, changes in revgen():
248 for n, i, changes in revgen():
249 miss = 0
249 miss = 0
250 for q in qw:
250 for q in qw:
251 if not (q in changes[1].lower() or
251 if not (q in changes[1].lower() or
252 q in changes[4].lower() or
252 q in changes[4].lower() or
253 q in " ".join(changes[3][:20]).lower()):
253 q in " ".join(changes[3][:20]).lower()):
254 miss = 1
254 miss = 1
255 break
255 break
256 if miss:
256 if miss:
257 continue
257 continue
258
258
259 count += 1
259 count += 1
260 hn = hex(n)
260 hn = hex(n)
261
261
262 yield self.t('searchentry',
262 yield self.t('searchentry',
263 parity=self.stripes(count),
263 parity=self.stripes(count),
264 author=changes[1],
264 author=changes[1],
265 parent=self.siblings(cl.parents(n), cl.rev),
265 parent=self.siblings(cl.parents(n), cl.rev),
266 child=self.siblings(cl.children(n), cl.rev),
266 child=self.siblings(cl.children(n), cl.rev),
267 changelogtag=self.showtag("changelogtag",n),
267 changelogtag=self.showtag("changelogtag",n),
268 manifest=hex(changes[0]),
268 manifest=hex(changes[0]),
269 desc=changes[4],
269 desc=changes[4],
270 date=changes[2],
270 date=changes[2],
271 files=self.listfilediffs(changes[3], n),
271 files=self.listfilediffs(changes[3], n),
272 rev=i,
272 rev=i,
273 node=hn)
273 node=hn)
274
274
275 if count >= self.maxchanges:
275 if count >= self.maxchanges:
276 break
276 break
277
277
278 cl = self.repo.changelog
278 cl = self.repo.changelog
279 mf = cl.read(cl.tip())[0]
279 mf = cl.read(cl.tip())[0]
280
280
281 yield self.t('search',
281 yield self.t('search',
282 query=query,
282 query=query,
283 manifest=hex(mf),
283 manifest=hex(mf),
284 entries=changelist)
284 entries=changelist)
285
285
286 def changeset(self, nodeid):
286 def changeset(self, nodeid):
287 cl = self.repo.changelog
287 cl = self.repo.changelog
288 n = self.repo.lookup(nodeid)
288 n = self.repo.lookup(nodeid)
289 nodeid = hex(n)
289 nodeid = hex(n)
290 changes = cl.read(n)
290 changes = cl.read(n)
291 p1 = cl.parents(n)[0]
291 p1 = cl.parents(n)[0]
292
292
293 files = []
293 files = []
294 mf = self.repo.manifest.read(changes[0])
294 mf = self.repo.manifest.read(changes[0])
295 for f in changes[3]:
295 for f in changes[3]:
296 files.append(self.t("filenodelink",
296 files.append(self.t("filenodelink",
297 filenode=hex(mf.get(f, nullid)), file=f))
297 filenode=hex(mf.get(f, nullid)), file=f))
298
298
299 def diff(**map):
299 def diff(**map):
300 yield self.diff(p1, n, None)
300 yield self.diff(p1, n, None)
301
301
302 yield self.t('changeset',
302 yield self.t('changeset',
303 diff=diff,
303 diff=diff,
304 rev=cl.rev(n),
304 rev=cl.rev(n),
305 node=nodeid,
305 node=nodeid,
306 parent=self.siblings(cl.parents(n), cl.rev),
306 parent=self.siblings(cl.parents(n), cl.rev),
307 child=self.siblings(cl.children(n), cl.rev),
307 child=self.siblings(cl.children(n), cl.rev),
308 changesettag=self.showtag("changesettag",n),
308 changesettag=self.showtag("changesettag",n),
309 manifest=hex(changes[0]),
309 manifest=hex(changes[0]),
310 author=changes[1],
310 author=changes[1],
311 desc=changes[4],
311 desc=changes[4],
312 date=changes[2],
312 date=changes[2],
313 files=files,
313 files=files,
314 archives=self.archivelist(nodeid))
314 archives=self.archivelist(nodeid))
315
315
316 def filelog(self, f, filenode):
316 def filelog(self, f, filenode):
317 cl = self.repo.changelog
317 cl = self.repo.changelog
318 fl = self.repo.file(f)
318 fl = self.repo.file(f)
319 filenode = hex(fl.lookup(filenode))
319 filenode = hex(fl.lookup(filenode))
320 count = fl.count()
320 count = fl.count()
321
321
322 def entries(**map):
322 def entries(**map):
323 l = []
323 l = []
324 parity = (count - 1) & 1
324 parity = (count - 1) & 1
325
325
326 for i in range(count):
326 for i in range(count):
327 n = fl.node(i)
327 n = fl.node(i)
328 lr = fl.linkrev(n)
328 lr = fl.linkrev(n)
329 cn = cl.node(lr)
329 cn = cl.node(lr)
330 cs = cl.read(cl.node(lr))
330 cs = cl.read(cl.node(lr))
331
331
332 l.insert(0, {"parity": parity,
332 l.insert(0, {"parity": parity,
333 "filenode": hex(n),
333 "filenode": hex(n),
334 "filerev": i,
334 "filerev": i,
335 "file": f,
335 "file": f,
336 "node": hex(cn),
336 "node": hex(cn),
337 "author": cs[1],
337 "author": cs[1],
338 "date": cs[2],
338 "date": cs[2],
339 "rename": self.renamelink(fl, n),
339 "rename": self.renamelink(fl, n),
340 "parent": self.siblings(fl.parents(n),
340 "parent": self.siblings(fl.parents(n),
341 fl.rev, file=f),
341 fl.rev, file=f),
342 "child": self.siblings(fl.children(n),
342 "child": self.siblings(fl.children(n),
343 fl.rev, file=f),
343 fl.rev, file=f),
344 "desc": cs[4]})
344 "desc": cs[4]})
345 parity = 1 - parity
345 parity = 1 - parity
346
346
347 for e in l:
347 for e in l:
348 yield e
348 yield e
349
349
350 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
350 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
351
351
352 def filerevision(self, f, node):
352 def filerevision(self, f, node):
353 fl = self.repo.file(f)
353 fl = self.repo.file(f)
354 n = fl.lookup(node)
354 n = fl.lookup(node)
355 node = hex(n)
355 node = hex(n)
356 text = fl.read(n)
356 text = fl.read(n)
357 changerev = fl.linkrev(n)
357 changerev = fl.linkrev(n)
358 cl = self.repo.changelog
358 cl = self.repo.changelog
359 cn = cl.node(changerev)
359 cn = cl.node(changerev)
360 cs = cl.read(cn)
360 cs = cl.read(cn)
361 mfn = cs[0]
361 mfn = cs[0]
362
362
363 mt = mimetypes.guess_type(f)[0]
363 mt = mimetypes.guess_type(f)[0]
364 rawtext = text
364 rawtext = text
365 if util.binary(text):
365 if util.binary(text):
366 mt = mt or 'application/octet-stream'
366 mt = mt or 'application/octet-stream'
367 text = "(binary:%s)" % mt
367 text = "(binary:%s)" % mt
368 mt = mt or 'text/plain'
368 mt = mt or 'text/plain'
369
369
370 def lines():
370 def lines():
371 for l, t in enumerate(text.splitlines(1)):
371 for l, t in enumerate(text.splitlines(1)):
372 yield {"line": t,
372 yield {"line": t,
373 "linenumber": "% 6d" % (l + 1),
373 "linenumber": "% 6d" % (l + 1),
374 "parity": self.stripes(l)}
374 "parity": self.stripes(l)}
375
375
376 yield self.t("filerevision",
376 yield self.t("filerevision",
377 file=f,
377 file=f,
378 filenode=node,
378 filenode=node,
379 path=_up(f),
379 path=_up(f),
380 text=lines(),
380 text=lines(),
381 raw=rawtext,
381 raw=rawtext,
382 mimetype=mt,
382 mimetype=mt,
383 rev=changerev,
383 rev=changerev,
384 node=hex(cn),
384 node=hex(cn),
385 manifest=hex(mfn),
385 manifest=hex(mfn),
386 author=cs[1],
386 author=cs[1],
387 date=cs[2],
387 date=cs[2],
388 parent=self.siblings(fl.parents(n), fl.rev, file=f),
388 parent=self.siblings(fl.parents(n), fl.rev, file=f),
389 child=self.siblings(fl.children(n), fl.rev, file=f),
389 child=self.siblings(fl.children(n), fl.rev, file=f),
390 rename=self.renamelink(fl, n),
390 rename=self.renamelink(fl, n),
391 permissions=self.repo.manifest.read(mfn).execf(f))
391 permissions=self.repo.manifest.read(mfn).execf(f))
392
392
393 def fileannotate(self, f, node):
393 def fileannotate(self, f, node):
394 fctx = self.repo.filectx(f, fileid=node)
394 fctx = self.repo.filectx(f, fileid=node)
395 n = fctx.filenode()
395 n = fctx.filenode()
396 fl = fctx.filelog()
396 fl = fctx.filelog()
397
397
398 def annotate(**map):
398 def annotate(**map):
399 parity = 0
399 parity = 0
400 last = None
400 last = None
401 for f, l in fctx.annotate():
401 for f, l in fctx.annotate(follow=True):
402 cnode = f.node()
402 fnode = f.filenode()
403 name = self.repo.ui.shortuser(f.user())
403 name = self.repo.ui.shortuser(f.user())
404
404
405 if last != cnode:
405 if last != fnode:
406 parity = 1 - parity
406 parity = 1 - parity
407 last = cnode
407 last = fnode
408
408
409 yield {"parity": parity,
409 yield {"parity": parity,
410 "node": hex(cnode),
410 "node": hex(fnode),
411 "rev": f.rev(),
411 "rev": f.rev(),
412 "author": name,
412 "author": name,
413 "file": f.path(),
413 "file": f.path(),
414 "line": l}
414 "line": l}
415
415
416 yield self.t("fileannotate",
416 yield self.t("fileannotate",
417 file=f,
417 file=f,
418 filenode=node,
418 filenode=node,
419 annotate=annotate,
419 annotate=annotate,
420 path=_up(f),
420 path=_up(f),
421 rev=fctx.rev(),
421 rev=fctx.rev(),
422 node=hex(n),
422 node=hex(n),
423 manifest=hex(fctx.changectx().changeset()[0]),
423 manifest=hex(fctx.changectx().changeset()[0]),
424 author=fctx.user(),
424 author=fctx.user(),
425 date=fctx.date(),
425 date=fctx.date(),
426 rename=self.renamelink(fl, n),
426 rename=self.renamelink(fl, n),
427 parent=self.siblings(fl.parents(n), fl.rev, file=f),
427 parent=self.siblings(fl.parents(n), fl.rev, file=f),
428 child=self.siblings(fl.children(n), fl.rev, file=f),
428 child=self.siblings(fl.children(n), fl.rev, file=f),
429 permissions=fctx.manifest().execf(f))
429 permissions=fctx.manifest().execf(f))
430
430
431 def manifest(self, mnode, path):
431 def manifest(self, mnode, path):
432 man = self.repo.manifest
432 man = self.repo.manifest
433 mn = man.lookup(mnode)
433 mn = man.lookup(mnode)
434 mnode = hex(mn)
434 mnode = hex(mn)
435 mf = man.read(mn)
435 mf = man.read(mn)
436 rev = man.rev(mn)
436 rev = man.rev(mn)
437 changerev = man.linkrev(mn)
437 changerev = man.linkrev(mn)
438 node = self.repo.changelog.node(changerev)
438 node = self.repo.changelog.node(changerev)
439
439
440 files = {}
440 files = {}
441
441
442 p = path[1:]
442 p = path[1:]
443 if p and p[-1] != "/":
443 if p and p[-1] != "/":
444 p += "/"
444 p += "/"
445 l = len(p)
445 l = len(p)
446
446
447 for f,n in mf.items():
447 for f,n in mf.items():
448 if f[:l] != p:
448 if f[:l] != p:
449 continue
449 continue
450 remain = f[l:]
450 remain = f[l:]
451 if "/" in remain:
451 if "/" in remain:
452 short = remain[:remain.index("/") + 1] # bleah
452 short = remain[:remain.index("/") + 1] # bleah
453 files[short] = (f, None)
453 files[short] = (f, None)
454 else:
454 else:
455 short = os.path.basename(remain)
455 short = os.path.basename(remain)
456 files[short] = (f, n)
456 files[short] = (f, n)
457
457
458 def filelist(**map):
458 def filelist(**map):
459 parity = 0
459 parity = 0
460 fl = files.keys()
460 fl = files.keys()
461 fl.sort()
461 fl.sort()
462 for f in fl:
462 for f in fl:
463 full, fnode = files[f]
463 full, fnode = files[f]
464 if not fnode:
464 if not fnode:
465 continue
465 continue
466
466
467 yield {"file": full,
467 yield {"file": full,
468 "manifest": mnode,
468 "manifest": mnode,
469 "filenode": hex(fnode),
469 "filenode": hex(fnode),
470 "parity": self.stripes(parity),
470 "parity": self.stripes(parity),
471 "basename": f,
471 "basename": f,
472 "permissions": mf.execf(full)}
472 "permissions": mf.execf(full)}
473 parity += 1
473 parity += 1
474
474
475 def dirlist(**map):
475 def dirlist(**map):
476 parity = 0
476 parity = 0
477 fl = files.keys()
477 fl = files.keys()
478 fl.sort()
478 fl.sort()
479 for f in fl:
479 for f in fl:
480 full, fnode = files[f]
480 full, fnode = files[f]
481 if fnode:
481 if fnode:
482 continue
482 continue
483
483
484 yield {"parity": self.stripes(parity),
484 yield {"parity": self.stripes(parity),
485 "path": os.path.join(path, f),
485 "path": os.path.join(path, f),
486 "manifest": mnode,
486 "manifest": mnode,
487 "basename": f[:-1]}
487 "basename": f[:-1]}
488 parity += 1
488 parity += 1
489
489
490 yield self.t("manifest",
490 yield self.t("manifest",
491 manifest=mnode,
491 manifest=mnode,
492 rev=rev,
492 rev=rev,
493 node=hex(node),
493 node=hex(node),
494 path=path,
494 path=path,
495 up=_up(path),
495 up=_up(path),
496 fentries=filelist,
496 fentries=filelist,
497 dentries=dirlist,
497 dentries=dirlist,
498 archives=self.archivelist(hex(node)))
498 archives=self.archivelist(hex(node)))
499
499
500 def tags(self):
500 def tags(self):
501 cl = self.repo.changelog
501 cl = self.repo.changelog
502 mf = cl.read(cl.tip())[0]
502 mf = cl.read(cl.tip())[0]
503
503
504 i = self.repo.tagslist()
504 i = self.repo.tagslist()
505 i.reverse()
505 i.reverse()
506
506
507 def entries(notip=False, **map):
507 def entries(notip=False, **map):
508 parity = 0
508 parity = 0
509 for k,n in i:
509 for k,n in i:
510 if notip and k == "tip": continue
510 if notip and k == "tip": continue
511 yield {"parity": self.stripes(parity),
511 yield {"parity": self.stripes(parity),
512 "tag": k,
512 "tag": k,
513 "tagmanifest": hex(cl.read(n)[0]),
513 "tagmanifest": hex(cl.read(n)[0]),
514 "date": cl.read(n)[2],
514 "date": cl.read(n)[2],
515 "node": hex(n)}
515 "node": hex(n)}
516 parity += 1
516 parity += 1
517
517
518 yield self.t("tags",
518 yield self.t("tags",
519 manifest=hex(mf),
519 manifest=hex(mf),
520 entries=lambda **x: entries(False, **x),
520 entries=lambda **x: entries(False, **x),
521 entriesnotip=lambda **x: entries(True, **x))
521 entriesnotip=lambda **x: entries(True, **x))
522
522
523 def summary(self):
523 def summary(self):
524 cl = self.repo.changelog
524 cl = self.repo.changelog
525 mf = cl.read(cl.tip())[0]
525 mf = cl.read(cl.tip())[0]
526
526
527 i = self.repo.tagslist()
527 i = self.repo.tagslist()
528 i.reverse()
528 i.reverse()
529
529
530 def tagentries(**map):
530 def tagentries(**map):
531 parity = 0
531 parity = 0
532 count = 0
532 count = 0
533 for k,n in i:
533 for k,n in i:
534 if k == "tip": # skip tip
534 if k == "tip": # skip tip
535 continue;
535 continue;
536
536
537 count += 1
537 count += 1
538 if count > 10: # limit to 10 tags
538 if count > 10: # limit to 10 tags
539 break;
539 break;
540
540
541 c = cl.read(n)
541 c = cl.read(n)
542 m = c[0]
542 m = c[0]
543 t = c[2]
543 t = c[2]
544
544
545 yield self.t("tagentry",
545 yield self.t("tagentry",
546 parity = self.stripes(parity),
546 parity = self.stripes(parity),
547 tag = k,
547 tag = k,
548 node = hex(n),
548 node = hex(n),
549 date = t,
549 date = t,
550 tagmanifest = hex(m))
550 tagmanifest = hex(m))
551 parity += 1
551 parity += 1
552
552
553 def changelist(**map):
553 def changelist(**map):
554 parity = 0
554 parity = 0
555 cl = self.repo.changelog
555 cl = self.repo.changelog
556 l = [] # build a list in forward order for efficiency
556 l = [] # build a list in forward order for efficiency
557 for i in range(start, end):
557 for i in range(start, end):
558 n = cl.node(i)
558 n = cl.node(i)
559 changes = cl.read(n)
559 changes = cl.read(n)
560 hn = hex(n)
560 hn = hex(n)
561 t = changes[2]
561 t = changes[2]
562
562
563 l.insert(0, self.t(
563 l.insert(0, self.t(
564 'shortlogentry',
564 'shortlogentry',
565 parity = parity,
565 parity = parity,
566 author = changes[1],
566 author = changes[1],
567 manifest = hex(changes[0]),
567 manifest = hex(changes[0]),
568 desc = changes[4],
568 desc = changes[4],
569 date = t,
569 date = t,
570 rev = i,
570 rev = i,
571 node = hn))
571 node = hn))
572 parity = 1 - parity
572 parity = 1 - parity
573
573
574 yield l
574 yield l
575
575
576 cl = self.repo.changelog
576 cl = self.repo.changelog
577 mf = cl.read(cl.tip())[0]
577 mf = cl.read(cl.tip())[0]
578 count = cl.count()
578 count = cl.count()
579 start = max(0, count - self.maxchanges)
579 start = max(0, count - self.maxchanges)
580 end = min(count, start + self.maxchanges)
580 end = min(count, start + self.maxchanges)
581
581
582 yield self.t("summary",
582 yield self.t("summary",
583 desc = self.repo.ui.config("web", "description", "unknown"),
583 desc = self.repo.ui.config("web", "description", "unknown"),
584 owner = (self.repo.ui.config("ui", "username") or # preferred
584 owner = (self.repo.ui.config("ui", "username") or # preferred
585 self.repo.ui.config("web", "contact") or # deprecated
585 self.repo.ui.config("web", "contact") or # deprecated
586 self.repo.ui.config("web", "author", "unknown")), # also
586 self.repo.ui.config("web", "author", "unknown")), # also
587 lastchange = (0, 0), # FIXME
587 lastchange = (0, 0), # FIXME
588 manifest = hex(mf),
588 manifest = hex(mf),
589 tags = tagentries,
589 tags = tagentries,
590 shortlog = changelist,
590 shortlog = changelist,
591 archives=self.archivelist("tip"))
591 archives=self.archivelist("tip"))
592
592
593 def filediff(self, file, changeset):
593 def filediff(self, file, changeset):
594 cl = self.repo.changelog
594 cl = self.repo.changelog
595 n = self.repo.lookup(changeset)
595 n = self.repo.lookup(changeset)
596 changeset = hex(n)
596 changeset = hex(n)
597 p1 = cl.parents(n)[0]
597 p1 = cl.parents(n)[0]
598 cs = cl.read(n)
598 cs = cl.read(n)
599 mf = self.repo.manifest.read(cs[0])
599 mf = self.repo.manifest.read(cs[0])
600
600
601 def diff(**map):
601 def diff(**map):
602 yield self.diff(p1, n, [file])
602 yield self.diff(p1, n, [file])
603
603
604 yield self.t("filediff",
604 yield self.t("filediff",
605 file=file,
605 file=file,
606 filenode=hex(mf.get(file, nullid)),
606 filenode=hex(mf.get(file, nullid)),
607 node=changeset,
607 node=changeset,
608 rev=self.repo.changelog.rev(n),
608 rev=self.repo.changelog.rev(n),
609 parent=self.siblings(cl.parents(n), cl.rev),
609 parent=self.siblings(cl.parents(n), cl.rev),
610 child=self.siblings(cl.children(n), cl.rev),
610 child=self.siblings(cl.children(n), cl.rev),
611 diff=diff)
611 diff=diff)
612
612
613 archive_specs = {
613 archive_specs = {
614 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
614 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
615 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
615 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
616 'zip': ('application/zip', 'zip', '.zip', None),
616 'zip': ('application/zip', 'zip', '.zip', None),
617 }
617 }
618
618
619 def archive(self, req, cnode, type_):
619 def archive(self, req, cnode, type_):
620 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
620 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
621 name = "%s-%s" % (reponame, short(cnode))
621 name = "%s-%s" % (reponame, short(cnode))
622 mimetype, artype, extension, encoding = self.archive_specs[type_]
622 mimetype, artype, extension, encoding = self.archive_specs[type_]
623 headers = [('Content-type', mimetype),
623 headers = [('Content-type', mimetype),
624 ('Content-disposition', 'attachment; filename=%s%s' %
624 ('Content-disposition', 'attachment; filename=%s%s' %
625 (name, extension))]
625 (name, extension))]
626 if encoding:
626 if encoding:
627 headers.append(('Content-encoding', encoding))
627 headers.append(('Content-encoding', encoding))
628 req.header(headers)
628 req.header(headers)
629 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
629 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
630
630
631 # add tags to things
631 # add tags to things
632 # tags -> list of changesets corresponding to tags
632 # tags -> list of changesets corresponding to tags
633 # find tag, changeset, file
633 # find tag, changeset, file
634
634
635 def cleanpath(self, path):
635 def cleanpath(self, path):
636 p = util.normpath(path)
636 p = util.normpath(path)
637 if p[:2] == "..":
637 if p[:2] == "..":
638 raise Exception("suspicious path")
638 raise Exception("suspicious path")
639 return p
639 return p
640
640
641 def run(self):
641 def run(self):
642 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
642 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
643 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
643 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
644 import mercurial.hgweb.wsgicgi as wsgicgi
644 import mercurial.hgweb.wsgicgi as wsgicgi
645 from request import wsgiapplication
645 from request import wsgiapplication
646 def make_web_app():
646 def make_web_app():
647 return self
647 return self
648 wsgicgi.launch(wsgiapplication(make_web_app))
648 wsgicgi.launch(wsgiapplication(make_web_app))
649
649
650 def run_wsgi(self, req):
650 def run_wsgi(self, req):
651 def header(**map):
651 def header(**map):
652 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
652 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
653 msg = mimetools.Message(header_file, 0)
653 msg = mimetools.Message(header_file, 0)
654 req.header(msg.items())
654 req.header(msg.items())
655 yield header_file.read()
655 yield header_file.read()
656
656
657 def rawfileheader(**map):
657 def rawfileheader(**map):
658 req.header([('Content-type', map['mimetype']),
658 req.header([('Content-type', map['mimetype']),
659 ('Content-disposition', 'filename=%s' % map['file']),
659 ('Content-disposition', 'filename=%s' % map['file']),
660 ('Content-length', str(len(map['raw'])))])
660 ('Content-length', str(len(map['raw'])))])
661 yield ''
661 yield ''
662
662
663 def footer(**map):
663 def footer(**map):
664 yield self.t("footer",
664 yield self.t("footer",
665 motd=self.repo.ui.config("web", "motd", ""),
665 motd=self.repo.ui.config("web", "motd", ""),
666 **map)
666 **map)
667
667
668 def expand_form(form):
668 def expand_form(form):
669 shortcuts = {
669 shortcuts = {
670 'cl': [('cmd', ['changelog']), ('rev', None)],
670 'cl': [('cmd', ['changelog']), ('rev', None)],
671 'sl': [('cmd', ['shortlog']), ('rev', None)],
671 'sl': [('cmd', ['shortlog']), ('rev', None)],
672 'cs': [('cmd', ['changeset']), ('node', None)],
672 'cs': [('cmd', ['changeset']), ('node', None)],
673 'f': [('cmd', ['file']), ('filenode', None)],
673 'f': [('cmd', ['file']), ('filenode', None)],
674 'fl': [('cmd', ['filelog']), ('filenode', None)],
674 'fl': [('cmd', ['filelog']), ('filenode', None)],
675 'fd': [('cmd', ['filediff']), ('node', None)],
675 'fd': [('cmd', ['filediff']), ('node', None)],
676 'fa': [('cmd', ['annotate']), ('filenode', None)],
676 'fa': [('cmd', ['annotate']), ('filenode', None)],
677 'mf': [('cmd', ['manifest']), ('manifest', None)],
677 'mf': [('cmd', ['manifest']), ('manifest', None)],
678 'ca': [('cmd', ['archive']), ('node', None)],
678 'ca': [('cmd', ['archive']), ('node', None)],
679 'tags': [('cmd', ['tags'])],
679 'tags': [('cmd', ['tags'])],
680 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
680 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
681 'static': [('cmd', ['static']), ('file', None)]
681 'static': [('cmd', ['static']), ('file', None)]
682 }
682 }
683
683
684 for k in shortcuts.iterkeys():
684 for k in shortcuts.iterkeys():
685 if form.has_key(k):
685 if form.has_key(k):
686 for name, value in shortcuts[k]:
686 for name, value in shortcuts[k]:
687 if value is None:
687 if value is None:
688 value = form[k]
688 value = form[k]
689 form[name] = value
689 form[name] = value
690 del form[k]
690 del form[k]
691
691
692 self.refresh()
692 self.refresh()
693
693
694 expand_form(req.form)
694 expand_form(req.form)
695
695
696 m = os.path.join(self.templatepath, "map")
696 m = os.path.join(self.templatepath, "map")
697 style = self.repo.ui.config("web", "style", "")
697 style = self.repo.ui.config("web", "style", "")
698 if req.form.has_key('style'):
698 if req.form.has_key('style'):
699 style = req.form['style'][0]
699 style = req.form['style'][0]
700 if style:
700 if style:
701 b = os.path.basename("map-" + style)
701 b = os.path.basename("map-" + style)
702 p = os.path.join(self.templatepath, b)
702 p = os.path.join(self.templatepath, b)
703 if os.path.isfile(p):
703 if os.path.isfile(p):
704 m = p
704 m = p
705
705
706 port = req.env["SERVER_PORT"]
706 port = req.env["SERVER_PORT"]
707 port = port != "80" and (":" + port) or ""
707 port = port != "80" and (":" + port) or ""
708 uri = req.env["REQUEST_URI"]
708 uri = req.env["REQUEST_URI"]
709 if "?" in uri:
709 if "?" in uri:
710 uri = uri.split("?")[0]
710 uri = uri.split("?")[0]
711 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
711 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
712 if not self.reponame:
712 if not self.reponame:
713 self.reponame = (self.repo.ui.config("web", "name")
713 self.reponame = (self.repo.ui.config("web", "name")
714 or uri.strip('/') or self.repo.root)
714 or uri.strip('/') or self.repo.root)
715
715
716 self.t = templater.templater(m, templater.common_filters,
716 self.t = templater.templater(m, templater.common_filters,
717 defaults={"url": url,
717 defaults={"url": url,
718 "repo": self.reponame,
718 "repo": self.reponame,
719 "header": header,
719 "header": header,
720 "footer": footer,
720 "footer": footer,
721 "rawfileheader": rawfileheader,
721 "rawfileheader": rawfileheader,
722 })
722 })
723
723
724 if not req.form.has_key('cmd'):
724 if not req.form.has_key('cmd'):
725 req.form['cmd'] = [self.t.cache['default'],]
725 req.form['cmd'] = [self.t.cache['default'],]
726
726
727 cmd = req.form['cmd'][0]
727 cmd = req.form['cmd'][0]
728
728
729 method = getattr(self, 'do_' + cmd, None)
729 method = getattr(self, 'do_' + cmd, None)
730 if method:
730 if method:
731 method(req)
731 method(req)
732 else:
732 else:
733 req.write(self.t("error"))
733 req.write(self.t("error"))
734
734
735 def stripes(self, parity):
735 def stripes(self, parity):
736 "make horizontal stripes for easier reading"
736 "make horizontal stripes for easier reading"
737 if self.stripecount:
737 if self.stripecount:
738 return (1 + parity / self.stripecount) & 1
738 return (1 + parity / self.stripecount) & 1
739 else:
739 else:
740 return 0
740 return 0
741
741
742 def do_changelog(self, req):
742 def do_changelog(self, req):
743 hi = self.repo.changelog.count() - 1
743 hi = self.repo.changelog.count() - 1
744 if req.form.has_key('rev'):
744 if req.form.has_key('rev'):
745 hi = req.form['rev'][0]
745 hi = req.form['rev'][0]
746 try:
746 try:
747 hi = self.repo.changelog.rev(self.repo.lookup(hi))
747 hi = self.repo.changelog.rev(self.repo.lookup(hi))
748 except hg.RepoError:
748 except hg.RepoError:
749 req.write(self.search(hi)) # XXX redirect to 404 page?
749 req.write(self.search(hi)) # XXX redirect to 404 page?
750 return
750 return
751
751
752 req.write(self.changelog(hi))
752 req.write(self.changelog(hi))
753
753
754 def do_shortlog(self, req):
754 def do_shortlog(self, req):
755 hi = self.repo.changelog.count() - 1
755 hi = self.repo.changelog.count() - 1
756 if req.form.has_key('rev'):
756 if req.form.has_key('rev'):
757 hi = req.form['rev'][0]
757 hi = req.form['rev'][0]
758 try:
758 try:
759 hi = self.repo.changelog.rev(self.repo.lookup(hi))
759 hi = self.repo.changelog.rev(self.repo.lookup(hi))
760 except hg.RepoError:
760 except hg.RepoError:
761 req.write(self.search(hi)) # XXX redirect to 404 page?
761 req.write(self.search(hi)) # XXX redirect to 404 page?
762 return
762 return
763
763
764 req.write(self.changelog(hi, shortlog = True))
764 req.write(self.changelog(hi, shortlog = True))
765
765
766 def do_changeset(self, req):
766 def do_changeset(self, req):
767 req.write(self.changeset(req.form['node'][0]))
767 req.write(self.changeset(req.form['node'][0]))
768
768
769 def do_manifest(self, req):
769 def do_manifest(self, req):
770 req.write(self.manifest(req.form['manifest'][0],
770 req.write(self.manifest(req.form['manifest'][0],
771 self.cleanpath(req.form['path'][0])))
771 self.cleanpath(req.form['path'][0])))
772
772
773 def do_tags(self, req):
773 def do_tags(self, req):
774 req.write(self.tags())
774 req.write(self.tags())
775
775
776 def do_summary(self, req):
776 def do_summary(self, req):
777 req.write(self.summary())
777 req.write(self.summary())
778
778
779 def do_filediff(self, req):
779 def do_filediff(self, req):
780 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
780 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
781 req.form['node'][0]))
781 req.form['node'][0]))
782
782
783 def do_file(self, req):
783 def do_file(self, req):
784 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
784 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
785 req.form['filenode'][0]))
785 req.form['filenode'][0]))
786
786
787 def do_annotate(self, req):
787 def do_annotate(self, req):
788 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
788 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
789 req.form['filenode'][0]))
789 req.form['filenode'][0]))
790
790
791 def do_filelog(self, req):
791 def do_filelog(self, req):
792 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
792 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
793 req.form['filenode'][0]))
793 req.form['filenode'][0]))
794
794
795 def do_heads(self, req):
795 def do_heads(self, req):
796 resp = " ".join(map(hex, self.repo.heads())) + "\n"
796 resp = " ".join(map(hex, self.repo.heads())) + "\n"
797 req.httphdr("application/mercurial-0.1", length=len(resp))
797 req.httphdr("application/mercurial-0.1", length=len(resp))
798 req.write(resp)
798 req.write(resp)
799
799
800 def do_branches(self, req):
800 def do_branches(self, req):
801 nodes = []
801 nodes = []
802 if req.form.has_key('nodes'):
802 if req.form.has_key('nodes'):
803 nodes = map(bin, req.form['nodes'][0].split(" "))
803 nodes = map(bin, req.form['nodes'][0].split(" "))
804 resp = cStringIO.StringIO()
804 resp = cStringIO.StringIO()
805 for b in self.repo.branches(nodes):
805 for b in self.repo.branches(nodes):
806 resp.write(" ".join(map(hex, b)) + "\n")
806 resp.write(" ".join(map(hex, b)) + "\n")
807 resp = resp.getvalue()
807 resp = resp.getvalue()
808 req.httphdr("application/mercurial-0.1", length=len(resp))
808 req.httphdr("application/mercurial-0.1", length=len(resp))
809 req.write(resp)
809 req.write(resp)
810
810
811 def do_between(self, req):
811 def do_between(self, req):
812 if req.form.has_key('pairs'):
812 if req.form.has_key('pairs'):
813 pairs = [map(bin, p.split("-"))
813 pairs = [map(bin, p.split("-"))
814 for p in req.form['pairs'][0].split(" ")]
814 for p in req.form['pairs'][0].split(" ")]
815 resp = cStringIO.StringIO()
815 resp = cStringIO.StringIO()
816 for b in self.repo.between(pairs):
816 for b in self.repo.between(pairs):
817 resp.write(" ".join(map(hex, b)) + "\n")
817 resp.write(" ".join(map(hex, b)) + "\n")
818 resp = resp.getvalue()
818 resp = resp.getvalue()
819 req.httphdr("application/mercurial-0.1", length=len(resp))
819 req.httphdr("application/mercurial-0.1", length=len(resp))
820 req.write(resp)
820 req.write(resp)
821
821
822 def do_changegroup(self, req):
822 def do_changegroup(self, req):
823 req.httphdr("application/mercurial-0.1")
823 req.httphdr("application/mercurial-0.1")
824 nodes = []
824 nodes = []
825 if not self.allowpull:
825 if not self.allowpull:
826 return
826 return
827
827
828 if req.form.has_key('roots'):
828 if req.form.has_key('roots'):
829 nodes = map(bin, req.form['roots'][0].split(" "))
829 nodes = map(bin, req.form['roots'][0].split(" "))
830
830
831 z = zlib.compressobj()
831 z = zlib.compressobj()
832 f = self.repo.changegroup(nodes, 'serve')
832 f = self.repo.changegroup(nodes, 'serve')
833 while 1:
833 while 1:
834 chunk = f.read(4096)
834 chunk = f.read(4096)
835 if not chunk:
835 if not chunk:
836 break
836 break
837 req.write(z.compress(chunk))
837 req.write(z.compress(chunk))
838
838
839 req.write(z.flush())
839 req.write(z.flush())
840
840
841 def do_archive(self, req):
841 def do_archive(self, req):
842 changeset = self.repo.lookup(req.form['node'][0])
842 changeset = self.repo.lookup(req.form['node'][0])
843 type_ = req.form['type'][0]
843 type_ = req.form['type'][0]
844 allowed = self.repo.ui.configlist("web", "allow_archive")
844 allowed = self.repo.ui.configlist("web", "allow_archive")
845 if (type_ in self.archives and (type_ in allowed or
845 if (type_ in self.archives and (type_ in allowed or
846 self.repo.ui.configbool("web", "allow" + type_, False))):
846 self.repo.ui.configbool("web", "allow" + type_, False))):
847 self.archive(req, changeset, type_)
847 self.archive(req, changeset, type_)
848 return
848 return
849
849
850 req.write(self.t("error"))
850 req.write(self.t("error"))
851
851
852 def do_static(self, req):
852 def do_static(self, req):
853 fname = req.form['file'][0]
853 fname = req.form['file'][0]
854 static = self.repo.ui.config("web", "static",
854 static = self.repo.ui.config("web", "static",
855 os.path.join(self.templatepath,
855 os.path.join(self.templatepath,
856 "static"))
856 "static"))
857 req.write(staticfile(static, fname, req)
857 req.write(staticfile(static, fname, req)
858 or self.t("error", error="%r not found" % fname))
858 or self.t("error", error="%r not found" % fname))
859
859
860 def do_capabilities(self, req):
860 def do_capabilities(self, req):
861 caps = ['unbundle']
861 caps = ['unbundle']
862 if self.repo.ui.configbool('server', 'uncompressed'):
862 if self.repo.ui.configbool('server', 'uncompressed'):
863 caps.append('stream=%d' % self.repo.revlogversion)
863 caps.append('stream=%d' % self.repo.revlogversion)
864 resp = ' '.join(caps)
864 resp = ' '.join(caps)
865 req.httphdr("application/mercurial-0.1", length=len(resp))
865 req.httphdr("application/mercurial-0.1", length=len(resp))
866 req.write(resp)
866 req.write(resp)
867
867
868 def check_perm(self, req, op, default):
868 def check_perm(self, req, op, default):
869 '''check permission for operation based on user auth.
869 '''check permission for operation based on user auth.
870 return true if op allowed, else false.
870 return true if op allowed, else false.
871 default is policy to use if no config given.'''
871 default is policy to use if no config given.'''
872
872
873 user = req.env.get('REMOTE_USER')
873 user = req.env.get('REMOTE_USER')
874
874
875 deny = self.repo.ui.configlist('web', 'deny_' + op)
875 deny = self.repo.ui.configlist('web', 'deny_' + op)
876 if deny and (not user or deny == ['*'] or user in deny):
876 if deny and (not user or deny == ['*'] or user in deny):
877 return False
877 return False
878
878
879 allow = self.repo.ui.configlist('web', 'allow_' + op)
879 allow = self.repo.ui.configlist('web', 'allow_' + op)
880 return (allow and (allow == ['*'] or user in allow)) or default
880 return (allow and (allow == ['*'] or user in allow)) or default
881
881
882 def do_unbundle(self, req):
882 def do_unbundle(self, req):
883 def bail(response, headers={}):
883 def bail(response, headers={}):
884 length = int(req.env['CONTENT_LENGTH'])
884 length = int(req.env['CONTENT_LENGTH'])
885 for s in util.filechunkiter(req, limit=length):
885 for s in util.filechunkiter(req, limit=length):
886 # drain incoming bundle, else client will not see
886 # drain incoming bundle, else client will not see
887 # response when run outside cgi script
887 # response when run outside cgi script
888 pass
888 pass
889 req.httphdr("application/mercurial-0.1", headers=headers)
889 req.httphdr("application/mercurial-0.1", headers=headers)
890 req.write('0\n')
890 req.write('0\n')
891 req.write(response)
891 req.write(response)
892
892
893 # require ssl by default, auth info cannot be sniffed and
893 # require ssl by default, auth info cannot be sniffed and
894 # replayed
894 # replayed
895 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
895 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
896 if ssl_req:
896 if ssl_req:
897 if not req.env.get('HTTPS'):
897 if not req.env.get('HTTPS'):
898 bail(_('ssl required\n'))
898 bail(_('ssl required\n'))
899 return
899 return
900 proto = 'https'
900 proto = 'https'
901 else:
901 else:
902 proto = 'http'
902 proto = 'http'
903
903
904 # do not allow push unless explicitly allowed
904 # do not allow push unless explicitly allowed
905 if not self.check_perm(req, 'push', False):
905 if not self.check_perm(req, 'push', False):
906 bail(_('push not authorized\n'),
906 bail(_('push not authorized\n'),
907 headers={'status': '401 Unauthorized'})
907 headers={'status': '401 Unauthorized'})
908 return
908 return
909
909
910 req.httphdr("application/mercurial-0.1")
910 req.httphdr("application/mercurial-0.1")
911
911
912 their_heads = req.form['heads'][0].split(' ')
912 their_heads = req.form['heads'][0].split(' ')
913
913
914 def check_heads():
914 def check_heads():
915 heads = map(hex, self.repo.heads())
915 heads = map(hex, self.repo.heads())
916 return their_heads == [hex('force')] or their_heads == heads
916 return their_heads == [hex('force')] or their_heads == heads
917
917
918 # fail early if possible
918 # fail early if possible
919 if not check_heads():
919 if not check_heads():
920 bail(_('unsynced changes\n'))
920 bail(_('unsynced changes\n'))
921 return
921 return
922
922
923 # do not lock repo until all changegroup data is
923 # do not lock repo until all changegroup data is
924 # streamed. save to temporary file.
924 # streamed. save to temporary file.
925
925
926 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
926 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
927 fp = os.fdopen(fd, 'wb+')
927 fp = os.fdopen(fd, 'wb+')
928 try:
928 try:
929 length = int(req.env['CONTENT_LENGTH'])
929 length = int(req.env['CONTENT_LENGTH'])
930 for s in util.filechunkiter(req, limit=length):
930 for s in util.filechunkiter(req, limit=length):
931 fp.write(s)
931 fp.write(s)
932
932
933 lock = self.repo.lock()
933 lock = self.repo.lock()
934 try:
934 try:
935 if not check_heads():
935 if not check_heads():
936 req.write('0\n')
936 req.write('0\n')
937 req.write(_('unsynced changes\n'))
937 req.write(_('unsynced changes\n'))
938 return
938 return
939
939
940 fp.seek(0)
940 fp.seek(0)
941
941
942 # send addchangegroup output to client
942 # send addchangegroup output to client
943
943
944 old_stdout = sys.stdout
944 old_stdout = sys.stdout
945 sys.stdout = cStringIO.StringIO()
945 sys.stdout = cStringIO.StringIO()
946
946
947 try:
947 try:
948 url = 'remote:%s:%s' % (proto,
948 url = 'remote:%s:%s' % (proto,
949 req.env.get('REMOTE_HOST', ''))
949 req.env.get('REMOTE_HOST', ''))
950 ret = self.repo.addchangegroup(fp, 'serve', url)
950 ret = self.repo.addchangegroup(fp, 'serve', url)
951 finally:
951 finally:
952 val = sys.stdout.getvalue()
952 val = sys.stdout.getvalue()
953 sys.stdout = old_stdout
953 sys.stdout = old_stdout
954 req.write('%d\n' % ret)
954 req.write('%d\n' % ret)
955 req.write(val)
955 req.write(val)
956 finally:
956 finally:
957 lock.release()
957 lock.release()
958 finally:
958 finally:
959 fp.close()
959 fp.close()
960 os.unlink(tempname)
960 os.unlink(tempname)
961
961
962 def do_stream_out(self, req):
962 def do_stream_out(self, req):
963 req.httphdr("application/mercurial-0.1")
963 req.httphdr("application/mercurial-0.1")
964 streamclone.stream_out(self.repo, req)
964 streamclone.stream_out(self.repo, req)
@@ -1,53 +1,53 b''
1 default = 'changelog'
1 default = 'changelog'
2 header = header.tmpl
2 header = header.tmpl
3 footer = footer.tmpl
3 footer = footer.tmpl
4 search = search.tmpl
4 search = search.tmpl
5 changelog = changelog.tmpl
5 changelog = changelog.tmpl
6 shortlog = shortlog.tmpl
6 shortlog = shortlog.tmpl
7 shortlogentry = shortlogentry.tmpl
7 shortlogentry = shortlogentry.tmpl
8 naventry = '<a href="?cl=#rev#">#label|escape#</a> '
8 naventry = '<a href="?cl=#rev#">#label|escape#</a> '
9 navshortentry = '<a href="?sl=#rev#">#label|escape#</a> '
9 navshortentry = '<a href="?sl=#rev#">#label|escape#</a> '
10 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
10 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
11 filenodelink = '<a href="?f=#filenode|short#;file=#file|urlescape#">#file|escape#</a> '
11 filenodelink = '<a href="?f=#filenode|short#;file=#file|urlescape#">#file|escape#</a> '
12 fileellipses = '...'
12 fileellipses = '...'
13 changelogentry = changelogentry.tmpl
13 changelogentry = changelogentry.tmpl
14 searchentry = changelogentry.tmpl
14 searchentry = changelogentry.tmpl
15 changeset = changeset.tmpl
15 changeset = changeset.tmpl
16 manifest = manifest.tmpl
16 manifest = manifest.tmpl
17 manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td><a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#">#basename|escape#/</a>'
17 manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td><a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#">#basename|escape#/</a>'
18 manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td><a href="?f=#filenode|short#;file=#file|urlescape#">#basename|escape#</a>'
18 manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td><a href="?f=#filenode|short#;file=#file|urlescape#">#basename|escape#</a>'
19 filerevision = filerevision.tmpl
19 filerevision = filerevision.tmpl
20 fileannotate = fileannotate.tmpl
20 fileannotate = fileannotate.tmpl
21 filediff = filediff.tmpl
21 filediff = filediff.tmpl
22 filelog = filelog.tmpl
22 filelog = filelog.tmpl
23 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
23 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
24 filelogentry = filelogentry.tmpl
24 filelogentry = filelogentry.tmpl
25 annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="?cs=#node|short#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
25 annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="?fa=#node|short#;file=#file|urlescape#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
26 difflineplus = '<span class="plusline">#line|escape#</span>'
26 difflineplus = '<span class="plusline">#line|escape#</span>'
27 difflineminus = '<span class="minusline">#line|escape#</span>'
27 difflineminus = '<span class="minusline">#line|escape#</span>'
28 difflineat = '<span class="atline">#line|escape#</span>'
28 difflineat = '<span class="atline">#line|escape#</span>'
29 diffline = '#line|escape#'
29 diffline = '#line|escape#'
30 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
30 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
31 changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
31 changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
32 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
32 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
33 filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
33 filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
34 filelogrename = '<tr><th>base:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
34 filelogrename = '<tr><th>base:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
35 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
35 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
36 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
36 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
37 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
37 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
38 filerevchild = '<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
38 filerevchild = '<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
39 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
39 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
40 tags = tags.tmpl
40 tags = tags.tmpl
41 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="?cs=#node|short#">#tag|escape#</a></li>'
41 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="?cs=#node|short#">#tag|escape#</a></li>'
42 diffblock = '<pre class="parity#parity#">#lines#</pre>'
42 diffblock = '<pre class="parity#parity#">#lines#</pre>'
43 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
43 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
44 changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
44 changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
45 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
45 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
46 filelogparent = '<tr><th>parent #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
46 filelogparent = '<tr><th>parent #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
47 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
47 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
48 filelogchild = '<tr><th>child #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
48 filelogchild = '<tr><th>child #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
49 indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#?cl=tip;style=rss">RSS</a> #archives%archiveentry#</td></tr>'
49 indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#?cl=tip;style=rss">RSS</a> #archives%archiveentry#</td></tr>'
50 index = index.tmpl
50 index = index.tmpl
51 archiveentry = '<a href="#url#?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
51 archiveentry = '<a href="#url#?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
52 notfound = notfound.tmpl
52 notfound = notfound.tmpl
53 error = error.tmpl
53 error = error.tmpl
@@ -1,50 +1,50 b''
1 default = 'summary'
1 default = 'summary'
2 header = header-gitweb.tmpl
2 header = header-gitweb.tmpl
3 footer = footer-gitweb.tmpl
3 footer = footer-gitweb.tmpl
4 search = search-gitweb.tmpl
4 search = search-gitweb.tmpl
5 changelog = changelog-gitweb.tmpl
5 changelog = changelog-gitweb.tmpl
6 summary = summary-gitweb.tmpl
6 summary = summary-gitweb.tmpl
7 error = error-gitweb.tmpl
7 error = error-gitweb.tmpl
8 naventry = '<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label|escape#</a> '
8 naventry = '<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label|escape#</a> '
9 navshortentry = '<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label|escape#</a> '
9 navshortentry = '<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label|escape#</a> '
10 filedifflink = '<a href="?cmd=filediff;node=#node#;file=#file|urlescape#;style=gitweb">#file|escape#</a> '
10 filedifflink = '<a href="?cmd=filediff;node=#node#;file=#file|urlescape#;style=gitweb">#file|escape#</a> '
11 filenodelink = '<tr class="light"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
11 filenodelink = '<tr class="light"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
12 fileellipses = '...'
12 fileellipses = '...'
13 changelogentry = changelogentry-gitweb.tmpl
13 changelogentry = changelogentry-gitweb.tmpl
14 searchentry = changelogentry-gitweb.tmpl
14 searchentry = changelogentry-gitweb.tmpl
15 changeset = changeset-gitweb.tmpl
15 changeset = changeset-gitweb.tmpl
16 manifest = manifest-gitweb.tmpl
16 manifest = manifest-gitweb.tmpl
17 manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td><a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">#basename|escape#/</a></td><td class="link"><a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a></td></tr>'
17 manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td><a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">#basename|escape#/</a></td><td class="link"><a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a></td></tr>'
18 manifestfileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td class="list"><a class="list" href="?f=#filenode|short#;file=#file|urlescape#;style=gitweb">#basename|escape#</a></td><td class="link"><a href="?f=#filenode|short#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a></td></tr>'
18 manifestfileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td class="list"><a class="list" href="?f=#filenode|short#;file=#file|urlescape#;style=gitweb">#basename|escape#</a></td><td class="link"><a href="?f=#filenode|short#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a></td></tr>'
19 filerevision = filerevision-gitweb.tmpl
19 filerevision = filerevision-gitweb.tmpl
20 fileannotate = fileannotate-gitweb.tmpl
20 fileannotate = fileannotate-gitweb.tmpl
21 filelog = filelog-gitweb.tmpl
21 filelog = filelog-gitweb.tmpl
22 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr"> #linenumber#</span> #line|escape#</pre></div>'
22 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr"> #linenumber#</span> #line|escape#</pre></div>'
23 annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="?cs=#node|short#;style=gitweb">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
23 annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="?fa=#node|short#;file=#file|urlescape#;style=gitweb">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
24 difflineplus = '<div style="color:#008800;">#line|escape#</div>'
24 difflineplus = '<div style="color:#008800;">#line|escape#</div>'
25 difflineminus = '<div style="color:#cc0000;">#line|escape#</div>'
25 difflineminus = '<div style="color:#cc0000;">#line|escape#</div>'
26 difflineat = '<div style="color:#990099;">#line|escape#</div>'
26 difflineat = '<div style="color:#990099;">#line|escape#</div>'
27 diffline = '<div>#line|escape#</div>'
27 diffline = '<div>#line|escape#</div>'
28 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
28 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
29 changesetparent = '<tr><td>parent</td><td style="font-family:monospace"><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb">#node|short#</a></td></tr>'
29 changesetparent = '<tr><td>parent</td><td style="font-family:monospace"><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb">#node|short#</a></td></tr>'
30 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
30 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
31 filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">#file|escape#@#node|short#</a></td></tr>'
31 filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">#file|escape#@#node|short#</a></td></tr>'
32 filelogrename = '| <a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">base</a>'
32 filelogrename = '| <a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">base</a>'
33 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?cmd=annotate;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
33 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?cmd=annotate;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
34 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
34 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
35 changesetchild = '<tr><td>child</td><td style="font-family:monospace"><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb">#node|short#</a></td></tr>'
35 changesetchild = '<tr><td>child</td><td style="font-family:monospace"><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb">#node|short#</a></td></tr>'
36 filerevchild = '<tr><td class="metatag">child:</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
36 filerevchild = '<tr><td class="metatag">child:</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
37 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?cmd=annotate;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
37 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?cmd=annotate;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
38 tags = tags-gitweb.tmpl
38 tags = tags-gitweb.tmpl
39 tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#tag|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=changelog;rev=#node|short#;style=gitweb">changelog</a> | <a href="?mf=#tagmanifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
39 tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#tag|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=changelog;rev=#node|short#;style=gitweb">changelog</a> | <a href="?mf=#tagmanifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
40 diffblock = '<pre>#lines#</pre>'
40 diffblock = '<pre>#lines#</pre>'
41 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
41 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
42 changesettag = '<tr><td>tag</td><td>#tag|escape#</td></tr>'
42 changesettag = '<tr><td>tag</td><td>#tag|escape#</td></tr>'
43 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
43 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
44 filelogparent = '<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
44 filelogparent = '<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
45 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
45 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
46 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
46 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
47 shortlog = shortlog-gitweb.tmpl
47 shortlog = shortlog-gitweb.tmpl
48 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
48 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
49 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">file</a> | <!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>'
49 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">file</a> | <!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>'
50 archiveentry = ' | <a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
50 archiveentry = ' | <a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
General Comments 0
You need to be logged in to leave comments. Login now