##// END OF EJS Templates
hgweb: replace 'ctx._repo' with 'ctx.repo()'
Matt Harbison -
r24340:567ae536 default
parent child Browse files
Show More
@@ -1,499 +1,499 b''
1 # hgweb/webutil.py - utility library for the web interface.
1 # hgweb/webutil.py - utility library for the web interface.
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-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os, copy
9 import os, copy
10 from mercurial import match, patch, error, ui, util, pathutil, context
10 from mercurial import match, patch, error, ui, util, pathutil, context
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial.node import hex, nullid
12 from mercurial.node import hex, nullid
13 from common import ErrorResponse, paritygen
13 from common import ErrorResponse, paritygen
14 from common import HTTP_NOT_FOUND
14 from common import HTTP_NOT_FOUND
15 import difflib
15 import difflib
16
16
17 def up(p):
17 def up(p):
18 if p[0] != "/":
18 if p[0] != "/":
19 p = "/" + p
19 p = "/" + p
20 if p[-1] == "/":
20 if p[-1] == "/":
21 p = p[:-1]
21 p = p[:-1]
22 up = os.path.dirname(p)
22 up = os.path.dirname(p)
23 if up == "/":
23 if up == "/":
24 return "/"
24 return "/"
25 return up + "/"
25 return up + "/"
26
26
27 def _navseq(step, firststep=None):
27 def _navseq(step, firststep=None):
28 if firststep:
28 if firststep:
29 yield firststep
29 yield firststep
30 if firststep >= 20 and firststep <= 40:
30 if firststep >= 20 and firststep <= 40:
31 firststep = 50
31 firststep = 50
32 yield firststep
32 yield firststep
33 assert step > 0
33 assert step > 0
34 assert firststep > 0
34 assert firststep > 0
35 while step <= firststep:
35 while step <= firststep:
36 step *= 10
36 step *= 10
37 while True:
37 while True:
38 yield 1 * step
38 yield 1 * step
39 yield 3 * step
39 yield 3 * step
40 step *= 10
40 step *= 10
41
41
42 class revnav(object):
42 class revnav(object):
43
43
44 def __init__(self, repo):
44 def __init__(self, repo):
45 """Navigation generation object
45 """Navigation generation object
46
46
47 :repo: repo object we generate nav for
47 :repo: repo object we generate nav for
48 """
48 """
49 # used for hex generation
49 # used for hex generation
50 self._revlog = repo.changelog
50 self._revlog = repo.changelog
51
51
52 def __nonzero__(self):
52 def __nonzero__(self):
53 """return True if any revision to navigate over"""
53 """return True if any revision to navigate over"""
54 return self._first() is not None
54 return self._first() is not None
55
55
56 def _first(self):
56 def _first(self):
57 """return the minimum non-filtered changeset or None"""
57 """return the minimum non-filtered changeset or None"""
58 try:
58 try:
59 return iter(self._revlog).next()
59 return iter(self._revlog).next()
60 except StopIteration:
60 except StopIteration:
61 return None
61 return None
62
62
63 def hex(self, rev):
63 def hex(self, rev):
64 return hex(self._revlog.node(rev))
64 return hex(self._revlog.node(rev))
65
65
66 def gen(self, pos, pagelen, limit):
66 def gen(self, pos, pagelen, limit):
67 """computes label and revision id for navigation link
67 """computes label and revision id for navigation link
68
68
69 :pos: is the revision relative to which we generate navigation.
69 :pos: is the revision relative to which we generate navigation.
70 :pagelen: the size of each navigation page
70 :pagelen: the size of each navigation page
71 :limit: how far shall we link
71 :limit: how far shall we link
72
72
73 The return is:
73 The return is:
74 - a single element tuple
74 - a single element tuple
75 - containing a dictionary with a `before` and `after` key
75 - containing a dictionary with a `before` and `after` key
76 - values are generator functions taking arbitrary number of kwargs
76 - values are generator functions taking arbitrary number of kwargs
77 - yield items are dictionaries with `label` and `node` keys
77 - yield items are dictionaries with `label` and `node` keys
78 """
78 """
79 if not self:
79 if not self:
80 # empty repo
80 # empty repo
81 return ({'before': (), 'after': ()},)
81 return ({'before': (), 'after': ()},)
82
82
83 targets = []
83 targets = []
84 for f in _navseq(1, pagelen):
84 for f in _navseq(1, pagelen):
85 if f > limit:
85 if f > limit:
86 break
86 break
87 targets.append(pos + f)
87 targets.append(pos + f)
88 targets.append(pos - f)
88 targets.append(pos - f)
89 targets.sort()
89 targets.sort()
90
90
91 first = self._first()
91 first = self._first()
92 navbefore = [("(%i)" % first, self.hex(first))]
92 navbefore = [("(%i)" % first, self.hex(first))]
93 navafter = []
93 navafter = []
94 for rev in targets:
94 for rev in targets:
95 if rev not in self._revlog:
95 if rev not in self._revlog:
96 continue
96 continue
97 if pos < rev < limit:
97 if pos < rev < limit:
98 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
98 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
99 if 0 < rev < pos:
99 if 0 < rev < pos:
100 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
100 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
101
101
102
102
103 navafter.append(("tip", "tip"))
103 navafter.append(("tip", "tip"))
104
104
105 data = lambda i: {"label": i[0], "node": i[1]}
105 data = lambda i: {"label": i[0], "node": i[1]}
106 return ({'before': lambda **map: (data(i) for i in navbefore),
106 return ({'before': lambda **map: (data(i) for i in navbefore),
107 'after': lambda **map: (data(i) for i in navafter)},)
107 'after': lambda **map: (data(i) for i in navafter)},)
108
108
109 class filerevnav(revnav):
109 class filerevnav(revnav):
110
110
111 def __init__(self, repo, path):
111 def __init__(self, repo, path):
112 """Navigation generation object
112 """Navigation generation object
113
113
114 :repo: repo object we generate nav for
114 :repo: repo object we generate nav for
115 :path: path of the file we generate nav for
115 :path: path of the file we generate nav for
116 """
116 """
117 # used for iteration
117 # used for iteration
118 self._changelog = repo.unfiltered().changelog
118 self._changelog = repo.unfiltered().changelog
119 # used for hex generation
119 # used for hex generation
120 self._revlog = repo.file(path)
120 self._revlog = repo.file(path)
121
121
122 def hex(self, rev):
122 def hex(self, rev):
123 return hex(self._changelog.node(self._revlog.linkrev(rev)))
123 return hex(self._changelog.node(self._revlog.linkrev(rev)))
124
124
125
125
126 def _siblings(siblings=[], hiderev=None):
126 def _siblings(siblings=[], hiderev=None):
127 siblings = [s for s in siblings if s.node() != nullid]
127 siblings = [s for s in siblings if s.node() != nullid]
128 if len(siblings) == 1 and siblings[0].rev() == hiderev:
128 if len(siblings) == 1 and siblings[0].rev() == hiderev:
129 return
129 return
130 for s in siblings:
130 for s in siblings:
131 d = {'node': s.hex(), 'rev': s.rev()}
131 d = {'node': s.hex(), 'rev': s.rev()}
132 d['user'] = s.user()
132 d['user'] = s.user()
133 d['date'] = s.date()
133 d['date'] = s.date()
134 d['description'] = s.description()
134 d['description'] = s.description()
135 d['branch'] = s.branch()
135 d['branch'] = s.branch()
136 if util.safehasattr(s, 'path'):
136 if util.safehasattr(s, 'path'):
137 d['file'] = s.path()
137 d['file'] = s.path()
138 yield d
138 yield d
139
139
140 def parents(ctx, hide=None):
140 def parents(ctx, hide=None):
141 if isinstance(ctx, context.basefilectx):
141 if isinstance(ctx, context.basefilectx):
142 introrev = ctx.introrev()
142 introrev = ctx.introrev()
143 if ctx.changectx().rev() != introrev:
143 if ctx.changectx().rev() != introrev:
144 return _siblings([ctx._repo[introrev]], hide)
144 return _siblings([ctx.repo()[introrev]], hide)
145 return _siblings(ctx.parents(), hide)
145 return _siblings(ctx.parents(), hide)
146
146
147 def children(ctx, hide=None):
147 def children(ctx, hide=None):
148 return _siblings(ctx.children(), hide)
148 return _siblings(ctx.children(), hide)
149
149
150 def renamelink(fctx):
150 def renamelink(fctx):
151 r = fctx.renamed()
151 r = fctx.renamed()
152 if r:
152 if r:
153 return [{'file': r[0], 'node': hex(r[1])}]
153 return [{'file': r[0], 'node': hex(r[1])}]
154 return []
154 return []
155
155
156 def nodetagsdict(repo, node):
156 def nodetagsdict(repo, node):
157 return [{"name": i} for i in repo.nodetags(node)]
157 return [{"name": i} for i in repo.nodetags(node)]
158
158
159 def nodebookmarksdict(repo, node):
159 def nodebookmarksdict(repo, node):
160 return [{"name": i} for i in repo.nodebookmarks(node)]
160 return [{"name": i} for i in repo.nodebookmarks(node)]
161
161
162 def nodebranchdict(repo, ctx):
162 def nodebranchdict(repo, ctx):
163 branches = []
163 branches = []
164 branch = ctx.branch()
164 branch = ctx.branch()
165 # If this is an empty repo, ctx.node() == nullid,
165 # If this is an empty repo, ctx.node() == nullid,
166 # ctx.branch() == 'default'.
166 # ctx.branch() == 'default'.
167 try:
167 try:
168 branchnode = repo.branchtip(branch)
168 branchnode = repo.branchtip(branch)
169 except error.RepoLookupError:
169 except error.RepoLookupError:
170 branchnode = None
170 branchnode = None
171 if branchnode == ctx.node():
171 if branchnode == ctx.node():
172 branches.append({"name": branch})
172 branches.append({"name": branch})
173 return branches
173 return branches
174
174
175 def nodeinbranch(repo, ctx):
175 def nodeinbranch(repo, ctx):
176 branches = []
176 branches = []
177 branch = ctx.branch()
177 branch = ctx.branch()
178 try:
178 try:
179 branchnode = repo.branchtip(branch)
179 branchnode = repo.branchtip(branch)
180 except error.RepoLookupError:
180 except error.RepoLookupError:
181 branchnode = None
181 branchnode = None
182 if branch != 'default' and branchnode != ctx.node():
182 if branch != 'default' and branchnode != ctx.node():
183 branches.append({"name": branch})
183 branches.append({"name": branch})
184 return branches
184 return branches
185
185
186 def nodebranchnodefault(ctx):
186 def nodebranchnodefault(ctx):
187 branches = []
187 branches = []
188 branch = ctx.branch()
188 branch = ctx.branch()
189 if branch != 'default':
189 if branch != 'default':
190 branches.append({"name": branch})
190 branches.append({"name": branch})
191 return branches
191 return branches
192
192
193 def showtag(repo, tmpl, t1, node=nullid, **args):
193 def showtag(repo, tmpl, t1, node=nullid, **args):
194 for t in repo.nodetags(node):
194 for t in repo.nodetags(node):
195 yield tmpl(t1, tag=t, **args)
195 yield tmpl(t1, tag=t, **args)
196
196
197 def showbookmark(repo, tmpl, t1, node=nullid, **args):
197 def showbookmark(repo, tmpl, t1, node=nullid, **args):
198 for t in repo.nodebookmarks(node):
198 for t in repo.nodebookmarks(node):
199 yield tmpl(t1, bookmark=t, **args)
199 yield tmpl(t1, bookmark=t, **args)
200
200
201 def cleanpath(repo, path):
201 def cleanpath(repo, path):
202 path = path.lstrip('/')
202 path = path.lstrip('/')
203 return pathutil.canonpath(repo.root, '', path)
203 return pathutil.canonpath(repo.root, '', path)
204
204
205 def changeidctx (repo, changeid):
205 def changeidctx (repo, changeid):
206 try:
206 try:
207 ctx = repo[changeid]
207 ctx = repo[changeid]
208 except error.RepoError:
208 except error.RepoError:
209 man = repo.manifest
209 man = repo.manifest
210 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
210 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
211
211
212 return ctx
212 return ctx
213
213
214 def changectx (repo, req):
214 def changectx (repo, req):
215 changeid = "tip"
215 changeid = "tip"
216 if 'node' in req.form:
216 if 'node' in req.form:
217 changeid = req.form['node'][0]
217 changeid = req.form['node'][0]
218 ipos=changeid.find(':')
218 ipos=changeid.find(':')
219 if ipos != -1:
219 if ipos != -1:
220 changeid = changeid[(ipos + 1):]
220 changeid = changeid[(ipos + 1):]
221 elif 'manifest' in req.form:
221 elif 'manifest' in req.form:
222 changeid = req.form['manifest'][0]
222 changeid = req.form['manifest'][0]
223
223
224 return changeidctx(repo, changeid)
224 return changeidctx(repo, changeid)
225
225
226 def basechangectx(repo, req):
226 def basechangectx(repo, req):
227 if 'node' in req.form:
227 if 'node' in req.form:
228 changeid = req.form['node'][0]
228 changeid = req.form['node'][0]
229 ipos=changeid.find(':')
229 ipos=changeid.find(':')
230 if ipos != -1:
230 if ipos != -1:
231 changeid = changeid[:ipos]
231 changeid = changeid[:ipos]
232 return changeidctx(repo, changeid)
232 return changeidctx(repo, changeid)
233
233
234 return None
234 return None
235
235
236 def filectx(repo, req):
236 def filectx(repo, req):
237 if 'file' not in req.form:
237 if 'file' not in req.form:
238 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
238 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
239 path = cleanpath(repo, req.form['file'][0])
239 path = cleanpath(repo, req.form['file'][0])
240 if 'node' in req.form:
240 if 'node' in req.form:
241 changeid = req.form['node'][0]
241 changeid = req.form['node'][0]
242 elif 'filenode' in req.form:
242 elif 'filenode' in req.form:
243 changeid = req.form['filenode'][0]
243 changeid = req.form['filenode'][0]
244 else:
244 else:
245 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
245 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
246 try:
246 try:
247 fctx = repo[changeid][path]
247 fctx = repo[changeid][path]
248 except error.RepoError:
248 except error.RepoError:
249 fctx = repo.filectx(path, fileid=changeid)
249 fctx = repo.filectx(path, fileid=changeid)
250
250
251 return fctx
251 return fctx
252
252
253 def changelistentry(web, ctx, tmpl):
253 def changelistentry(web, ctx, tmpl):
254 '''Obtain a dictionary to be used for entries in a changelist.
254 '''Obtain a dictionary to be used for entries in a changelist.
255
255
256 This function is called when producing items for the "entries" list passed
256 This function is called when producing items for the "entries" list passed
257 to the "shortlog" and "changelog" templates.
257 to the "shortlog" and "changelog" templates.
258 '''
258 '''
259 repo = web.repo
259 repo = web.repo
260 rev = ctx.rev()
260 rev = ctx.rev()
261 n = ctx.node()
261 n = ctx.node()
262 showtags = showtag(repo, tmpl, 'changelogtag', n)
262 showtags = showtag(repo, tmpl, 'changelogtag', n)
263 files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
263 files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
264
264
265 return {
265 return {
266 "author": ctx.user(),
266 "author": ctx.user(),
267 "parent": parents(ctx, rev - 1),
267 "parent": parents(ctx, rev - 1),
268 "child": children(ctx, rev + 1),
268 "child": children(ctx, rev + 1),
269 "changelogtag": showtags,
269 "changelogtag": showtags,
270 "desc": ctx.description(),
270 "desc": ctx.description(),
271 "extra": ctx.extra(),
271 "extra": ctx.extra(),
272 "date": ctx.date(),
272 "date": ctx.date(),
273 "files": files,
273 "files": files,
274 "rev": rev,
274 "rev": rev,
275 "node": hex(n),
275 "node": hex(n),
276 "tags": nodetagsdict(repo, n),
276 "tags": nodetagsdict(repo, n),
277 "bookmarks": nodebookmarksdict(repo, n),
277 "bookmarks": nodebookmarksdict(repo, n),
278 "inbranch": nodeinbranch(repo, ctx),
278 "inbranch": nodeinbranch(repo, ctx),
279 "branches": nodebranchdict(repo, ctx)
279 "branches": nodebranchdict(repo, ctx)
280 }
280 }
281
281
282 def changesetentry(web, req, tmpl, ctx):
282 def changesetentry(web, req, tmpl, ctx):
283 '''Obtain a dictionary to be used to render the "changeset" template.'''
283 '''Obtain a dictionary to be used to render the "changeset" template.'''
284
284
285 showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
285 showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
286 showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
286 showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
287 ctx.node())
287 ctx.node())
288 showbranch = nodebranchnodefault(ctx)
288 showbranch = nodebranchnodefault(ctx)
289
289
290 files = []
290 files = []
291 parity = paritygen(web.stripecount)
291 parity = paritygen(web.stripecount)
292 for blockno, f in enumerate(ctx.files()):
292 for blockno, f in enumerate(ctx.files()):
293 template = f in ctx and 'filenodelink' or 'filenolink'
293 template = f in ctx and 'filenodelink' or 'filenolink'
294 files.append(tmpl(template,
294 files.append(tmpl(template,
295 node=ctx.hex(), file=f, blockno=blockno + 1,
295 node=ctx.hex(), file=f, blockno=blockno + 1,
296 parity=parity.next()))
296 parity=parity.next()))
297
297
298 basectx = basechangectx(web.repo, req)
298 basectx = basechangectx(web.repo, req)
299 if basectx is None:
299 if basectx is None:
300 basectx = ctx.p1()
300 basectx = ctx.p1()
301
301
302 style = web.config('web', 'style', 'paper')
302 style = web.config('web', 'style', 'paper')
303 if 'style' in req.form:
303 if 'style' in req.form:
304 style = req.form['style'][0]
304 style = req.form['style'][0]
305
305
306 parity = paritygen(web.stripecount)
306 parity = paritygen(web.stripecount)
307 diff = diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
307 diff = diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
308
308
309 parity = paritygen(web.stripecount)
309 parity = paritygen(web.stripecount)
310 diffstatsgen = diffstatgen(ctx, basectx)
310 diffstatsgen = diffstatgen(ctx, basectx)
311 diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
311 diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
312
312
313 return dict(
313 return dict(
314 diff=diff,
314 diff=diff,
315 rev=ctx.rev(),
315 rev=ctx.rev(),
316 node=ctx.hex(),
316 node=ctx.hex(),
317 parent=tuple(parents(ctx)),
317 parent=tuple(parents(ctx)),
318 child=children(ctx),
318 child=children(ctx),
319 basenode=basectx.hex(),
319 basenode=basectx.hex(),
320 changesettag=showtags,
320 changesettag=showtags,
321 changesetbookmark=showbookmarks,
321 changesetbookmark=showbookmarks,
322 changesetbranch=showbranch,
322 changesetbranch=showbranch,
323 author=ctx.user(),
323 author=ctx.user(),
324 desc=ctx.description(),
324 desc=ctx.description(),
325 extra=ctx.extra(),
325 extra=ctx.extra(),
326 date=ctx.date(),
326 date=ctx.date(),
327 files=files,
327 files=files,
328 diffsummary=lambda **x: diffsummary(diffstatsgen),
328 diffsummary=lambda **x: diffsummary(diffstatsgen),
329 diffstat=diffstats,
329 diffstat=diffstats,
330 archives=web.archivelist(ctx.hex()),
330 archives=web.archivelist(ctx.hex()),
331 tags=nodetagsdict(web.repo, ctx.node()),
331 tags=nodetagsdict(web.repo, ctx.node()),
332 bookmarks=nodebookmarksdict(web.repo, ctx.node()),
332 bookmarks=nodebookmarksdict(web.repo, ctx.node()),
333 branch=nodebranchnodefault(ctx),
333 branch=nodebranchnodefault(ctx),
334 inbranch=nodeinbranch(web.repo, ctx),
334 inbranch=nodeinbranch(web.repo, ctx),
335 branches=nodebranchdict(web.repo, ctx))
335 branches=nodebranchdict(web.repo, ctx))
336
336
337 def listfilediffs(tmpl, files, node, max):
337 def listfilediffs(tmpl, files, node, max):
338 for f in files[:max]:
338 for f in files[:max]:
339 yield tmpl('filedifflink', node=hex(node), file=f)
339 yield tmpl('filedifflink', node=hex(node), file=f)
340 if len(files) > max:
340 if len(files) > max:
341 yield tmpl('fileellipses')
341 yield tmpl('fileellipses')
342
342
343 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
343 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
344
344
345 def countgen():
345 def countgen():
346 start = 1
346 start = 1
347 while True:
347 while True:
348 yield start
348 yield start
349 start += 1
349 start += 1
350
350
351 blockcount = countgen()
351 blockcount = countgen()
352 def prettyprintlines(diff, blockno):
352 def prettyprintlines(diff, blockno):
353 for lineno, l in enumerate(diff.splitlines(True)):
353 for lineno, l in enumerate(diff.splitlines(True)):
354 lineno = "%d.%d" % (blockno, lineno + 1)
354 lineno = "%d.%d" % (blockno, lineno + 1)
355 if l.startswith('+'):
355 if l.startswith('+'):
356 ltype = "difflineplus"
356 ltype = "difflineplus"
357 elif l.startswith('-'):
357 elif l.startswith('-'):
358 ltype = "difflineminus"
358 ltype = "difflineminus"
359 elif l.startswith('@'):
359 elif l.startswith('@'):
360 ltype = "difflineat"
360 ltype = "difflineat"
361 else:
361 else:
362 ltype = "diffline"
362 ltype = "diffline"
363 yield tmpl(ltype,
363 yield tmpl(ltype,
364 line=l,
364 line=l,
365 lineid="l%s" % lineno,
365 lineid="l%s" % lineno,
366 linenumber="% 8s" % lineno)
366 linenumber="% 8s" % lineno)
367
367
368 if files:
368 if files:
369 m = match.exact(repo.root, repo.getcwd(), files)
369 m = match.exact(repo.root, repo.getcwd(), files)
370 else:
370 else:
371 m = match.always(repo.root, repo.getcwd())
371 m = match.always(repo.root, repo.getcwd())
372
372
373 diffopts = patch.diffopts(repo.ui, untrusted=True)
373 diffopts = patch.diffopts(repo.ui, untrusted=True)
374 if basectx is None:
374 if basectx is None:
375 parents = ctx.parents()
375 parents = ctx.parents()
376 if parents:
376 if parents:
377 node1 = parents[0].node()
377 node1 = parents[0].node()
378 else:
378 else:
379 node1 = nullid
379 node1 = nullid
380 else:
380 else:
381 node1 = basectx.node()
381 node1 = basectx.node()
382 node2 = ctx.node()
382 node2 = ctx.node()
383
383
384 block = []
384 block = []
385 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
385 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
386 if chunk.startswith('diff') and block:
386 if chunk.startswith('diff') and block:
387 blockno = blockcount.next()
387 blockno = blockcount.next()
388 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
388 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
389 lines=prettyprintlines(''.join(block), blockno))
389 lines=prettyprintlines(''.join(block), blockno))
390 block = []
390 block = []
391 if chunk.startswith('diff') and style != 'raw':
391 if chunk.startswith('diff') and style != 'raw':
392 chunk = ''.join(chunk.splitlines(True)[1:])
392 chunk = ''.join(chunk.splitlines(True)[1:])
393 block.append(chunk)
393 block.append(chunk)
394 blockno = blockcount.next()
394 blockno = blockcount.next()
395 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
395 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
396 lines=prettyprintlines(''.join(block), blockno))
396 lines=prettyprintlines(''.join(block), blockno))
397
397
398 def compare(tmpl, context, leftlines, rightlines):
398 def compare(tmpl, context, leftlines, rightlines):
399 '''Generator function that provides side-by-side comparison data.'''
399 '''Generator function that provides side-by-side comparison data.'''
400
400
401 def compline(type, leftlineno, leftline, rightlineno, rightline):
401 def compline(type, leftlineno, leftline, rightlineno, rightline):
402 lineid = leftlineno and ("l%s" % leftlineno) or ''
402 lineid = leftlineno and ("l%s" % leftlineno) or ''
403 lineid += rightlineno and ("r%s" % rightlineno) or ''
403 lineid += rightlineno and ("r%s" % rightlineno) or ''
404 return tmpl('comparisonline',
404 return tmpl('comparisonline',
405 type=type,
405 type=type,
406 lineid=lineid,
406 lineid=lineid,
407 leftlinenumber="% 6s" % (leftlineno or ''),
407 leftlinenumber="% 6s" % (leftlineno or ''),
408 leftline=leftline or '',
408 leftline=leftline or '',
409 rightlinenumber="% 6s" % (rightlineno or ''),
409 rightlinenumber="% 6s" % (rightlineno or ''),
410 rightline=rightline or '')
410 rightline=rightline or '')
411
411
412 def getblock(opcodes):
412 def getblock(opcodes):
413 for type, llo, lhi, rlo, rhi in opcodes:
413 for type, llo, lhi, rlo, rhi in opcodes:
414 len1 = lhi - llo
414 len1 = lhi - llo
415 len2 = rhi - rlo
415 len2 = rhi - rlo
416 count = min(len1, len2)
416 count = min(len1, len2)
417 for i in xrange(count):
417 for i in xrange(count):
418 yield compline(type=type,
418 yield compline(type=type,
419 leftlineno=llo + i + 1,
419 leftlineno=llo + i + 1,
420 leftline=leftlines[llo + i],
420 leftline=leftlines[llo + i],
421 rightlineno=rlo + i + 1,
421 rightlineno=rlo + i + 1,
422 rightline=rightlines[rlo + i])
422 rightline=rightlines[rlo + i])
423 if len1 > len2:
423 if len1 > len2:
424 for i in xrange(llo + count, lhi):
424 for i in xrange(llo + count, lhi):
425 yield compline(type=type,
425 yield compline(type=type,
426 leftlineno=i + 1,
426 leftlineno=i + 1,
427 leftline=leftlines[i],
427 leftline=leftlines[i],
428 rightlineno=None,
428 rightlineno=None,
429 rightline=None)
429 rightline=None)
430 elif len2 > len1:
430 elif len2 > len1:
431 for i in xrange(rlo + count, rhi):
431 for i in xrange(rlo + count, rhi):
432 yield compline(type=type,
432 yield compline(type=type,
433 leftlineno=None,
433 leftlineno=None,
434 leftline=None,
434 leftline=None,
435 rightlineno=i + 1,
435 rightlineno=i + 1,
436 rightline=rightlines[i])
436 rightline=rightlines[i])
437
437
438 s = difflib.SequenceMatcher(None, leftlines, rightlines)
438 s = difflib.SequenceMatcher(None, leftlines, rightlines)
439 if context < 0:
439 if context < 0:
440 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
440 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
441 else:
441 else:
442 for oc in s.get_grouped_opcodes(n=context):
442 for oc in s.get_grouped_opcodes(n=context):
443 yield tmpl('comparisonblock', lines=getblock(oc))
443 yield tmpl('comparisonblock', lines=getblock(oc))
444
444
445 def diffstatgen(ctx, basectx):
445 def diffstatgen(ctx, basectx):
446 '''Generator function that provides the diffstat data.'''
446 '''Generator function that provides the diffstat data.'''
447
447
448 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
448 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
449 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
449 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
450 while True:
450 while True:
451 yield stats, maxname, maxtotal, addtotal, removetotal, binary
451 yield stats, maxname, maxtotal, addtotal, removetotal, binary
452
452
453 def diffsummary(statgen):
453 def diffsummary(statgen):
454 '''Return a short summary of the diff.'''
454 '''Return a short summary of the diff.'''
455
455
456 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
456 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
457 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
457 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
458 len(stats), addtotal, removetotal)
458 len(stats), addtotal, removetotal)
459
459
460 def diffstat(tmpl, ctx, statgen, parity):
460 def diffstat(tmpl, ctx, statgen, parity):
461 '''Return a diffstat template for each file in the diff.'''
461 '''Return a diffstat template for each file in the diff.'''
462
462
463 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
463 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
464 files = ctx.files()
464 files = ctx.files()
465
465
466 def pct(i):
466 def pct(i):
467 if maxtotal == 0:
467 if maxtotal == 0:
468 return 0
468 return 0
469 return (float(i) / maxtotal) * 100
469 return (float(i) / maxtotal) * 100
470
470
471 fileno = 0
471 fileno = 0
472 for filename, adds, removes, isbinary in stats:
472 for filename, adds, removes, isbinary in stats:
473 template = filename in files and 'diffstatlink' or 'diffstatnolink'
473 template = filename in files and 'diffstatlink' or 'diffstatnolink'
474 total = adds + removes
474 total = adds + removes
475 fileno += 1
475 fileno += 1
476 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
476 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
477 total=total, addpct=pct(adds), removepct=pct(removes),
477 total=total, addpct=pct(adds), removepct=pct(removes),
478 parity=parity.next())
478 parity=parity.next())
479
479
480 class sessionvars(object):
480 class sessionvars(object):
481 def __init__(self, vars, start='?'):
481 def __init__(self, vars, start='?'):
482 self.start = start
482 self.start = start
483 self.vars = vars
483 self.vars = vars
484 def __getitem__(self, key):
484 def __getitem__(self, key):
485 return self.vars[key]
485 return self.vars[key]
486 def __setitem__(self, key, value):
486 def __setitem__(self, key, value):
487 self.vars[key] = value
487 self.vars[key] = value
488 def __copy__(self):
488 def __copy__(self):
489 return sessionvars(copy.copy(self.vars), self.start)
489 return sessionvars(copy.copy(self.vars), self.start)
490 def __iter__(self):
490 def __iter__(self):
491 separator = self.start
491 separator = self.start
492 for key, value in sorted(self.vars.iteritems()):
492 for key, value in sorted(self.vars.iteritems()):
493 yield {'name': key, 'value': str(value), 'separator': separator}
493 yield {'name': key, 'value': str(value), 'separator': separator}
494 separator = '&'
494 separator = '&'
495
495
496 class wsgiui(ui.ui):
496 class wsgiui(ui.ui):
497 # default termwidth breaks under mod_wsgi
497 # default termwidth breaks under mod_wsgi
498 def termwidth(self):
498 def termwidth(self):
499 return 80
499 return 80
General Comments 0
You need to be logged in to leave comments. Login now