##// END OF EJS Templates
hgweb: add phase to {changeset} template...
Gregory Szorc -
r24564:5ec4bda3 default
parent child Browse files
Show More
@@ -1,499 +1,500 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 phase=ctx.phasestr(),
327 files=files,
328 files=files,
328 diffsummary=lambda **x: diffsummary(diffstatsgen),
329 diffsummary=lambda **x: diffsummary(diffstatsgen),
329 diffstat=diffstats,
330 diffstat=diffstats,
330 archives=web.archivelist(ctx.hex()),
331 archives=web.archivelist(ctx.hex()),
331 tags=nodetagsdict(web.repo, ctx.node()),
332 tags=nodetagsdict(web.repo, ctx.node()),
332 bookmarks=nodebookmarksdict(web.repo, ctx.node()),
333 bookmarks=nodebookmarksdict(web.repo, ctx.node()),
333 branch=nodebranchnodefault(ctx),
334 branch=nodebranchnodefault(ctx),
334 inbranch=nodeinbranch(web.repo, ctx),
335 inbranch=nodeinbranch(web.repo, ctx),
335 branches=nodebranchdict(web.repo, ctx))
336 branches=nodebranchdict(web.repo, ctx))
336
337
337 def listfilediffs(tmpl, files, node, max):
338 def listfilediffs(tmpl, files, node, max):
338 for f in files[:max]:
339 for f in files[:max]:
339 yield tmpl('filedifflink', node=hex(node), file=f)
340 yield tmpl('filedifflink', node=hex(node), file=f)
340 if len(files) > max:
341 if len(files) > max:
341 yield tmpl('fileellipses')
342 yield tmpl('fileellipses')
342
343
343 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
344 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
344
345
345 def countgen():
346 def countgen():
346 start = 1
347 start = 1
347 while True:
348 while True:
348 yield start
349 yield start
349 start += 1
350 start += 1
350
351
351 blockcount = countgen()
352 blockcount = countgen()
352 def prettyprintlines(diff, blockno):
353 def prettyprintlines(diff, blockno):
353 for lineno, l in enumerate(diff.splitlines(True)):
354 for lineno, l in enumerate(diff.splitlines(True)):
354 lineno = "%d.%d" % (blockno, lineno + 1)
355 lineno = "%d.%d" % (blockno, lineno + 1)
355 if l.startswith('+'):
356 if l.startswith('+'):
356 ltype = "difflineplus"
357 ltype = "difflineplus"
357 elif l.startswith('-'):
358 elif l.startswith('-'):
358 ltype = "difflineminus"
359 ltype = "difflineminus"
359 elif l.startswith('@'):
360 elif l.startswith('@'):
360 ltype = "difflineat"
361 ltype = "difflineat"
361 else:
362 else:
362 ltype = "diffline"
363 ltype = "diffline"
363 yield tmpl(ltype,
364 yield tmpl(ltype,
364 line=l,
365 line=l,
365 lineid="l%s" % lineno,
366 lineid="l%s" % lineno,
366 linenumber="% 8s" % lineno)
367 linenumber="% 8s" % lineno)
367
368
368 if files:
369 if files:
369 m = match.exact(repo.root, repo.getcwd(), files)
370 m = match.exact(repo.root, repo.getcwd(), files)
370 else:
371 else:
371 m = match.always(repo.root, repo.getcwd())
372 m = match.always(repo.root, repo.getcwd())
372
373
373 diffopts = patch.diffopts(repo.ui, untrusted=True)
374 diffopts = patch.diffopts(repo.ui, untrusted=True)
374 if basectx is None:
375 if basectx is None:
375 parents = ctx.parents()
376 parents = ctx.parents()
376 if parents:
377 if parents:
377 node1 = parents[0].node()
378 node1 = parents[0].node()
378 else:
379 else:
379 node1 = nullid
380 node1 = nullid
380 else:
381 else:
381 node1 = basectx.node()
382 node1 = basectx.node()
382 node2 = ctx.node()
383 node2 = ctx.node()
383
384
384 block = []
385 block = []
385 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
386 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
386 if chunk.startswith('diff') and block:
387 if chunk.startswith('diff') and block:
387 blockno = blockcount.next()
388 blockno = blockcount.next()
388 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
389 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
389 lines=prettyprintlines(''.join(block), blockno))
390 lines=prettyprintlines(''.join(block), blockno))
390 block = []
391 block = []
391 if chunk.startswith('diff') and style != 'raw':
392 if chunk.startswith('diff') and style != 'raw':
392 chunk = ''.join(chunk.splitlines(True)[1:])
393 chunk = ''.join(chunk.splitlines(True)[1:])
393 block.append(chunk)
394 block.append(chunk)
394 blockno = blockcount.next()
395 blockno = blockcount.next()
395 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
396 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
396 lines=prettyprintlines(''.join(block), blockno))
397 lines=prettyprintlines(''.join(block), blockno))
397
398
398 def compare(tmpl, context, leftlines, rightlines):
399 def compare(tmpl, context, leftlines, rightlines):
399 '''Generator function that provides side-by-side comparison data.'''
400 '''Generator function that provides side-by-side comparison data.'''
400
401
401 def compline(type, leftlineno, leftline, rightlineno, rightline):
402 def compline(type, leftlineno, leftline, rightlineno, rightline):
402 lineid = leftlineno and ("l%s" % leftlineno) or ''
403 lineid = leftlineno and ("l%s" % leftlineno) or ''
403 lineid += rightlineno and ("r%s" % rightlineno) or ''
404 lineid += rightlineno and ("r%s" % rightlineno) or ''
404 return tmpl('comparisonline',
405 return tmpl('comparisonline',
405 type=type,
406 type=type,
406 lineid=lineid,
407 lineid=lineid,
407 leftlinenumber="% 6s" % (leftlineno or ''),
408 leftlinenumber="% 6s" % (leftlineno or ''),
408 leftline=leftline or '',
409 leftline=leftline or '',
409 rightlinenumber="% 6s" % (rightlineno or ''),
410 rightlinenumber="% 6s" % (rightlineno or ''),
410 rightline=rightline or '')
411 rightline=rightline or '')
411
412
412 def getblock(opcodes):
413 def getblock(opcodes):
413 for type, llo, lhi, rlo, rhi in opcodes:
414 for type, llo, lhi, rlo, rhi in opcodes:
414 len1 = lhi - llo
415 len1 = lhi - llo
415 len2 = rhi - rlo
416 len2 = rhi - rlo
416 count = min(len1, len2)
417 count = min(len1, len2)
417 for i in xrange(count):
418 for i in xrange(count):
418 yield compline(type=type,
419 yield compline(type=type,
419 leftlineno=llo + i + 1,
420 leftlineno=llo + i + 1,
420 leftline=leftlines[llo + i],
421 leftline=leftlines[llo + i],
421 rightlineno=rlo + i + 1,
422 rightlineno=rlo + i + 1,
422 rightline=rightlines[rlo + i])
423 rightline=rightlines[rlo + i])
423 if len1 > len2:
424 if len1 > len2:
424 for i in xrange(llo + count, lhi):
425 for i in xrange(llo + count, lhi):
425 yield compline(type=type,
426 yield compline(type=type,
426 leftlineno=i + 1,
427 leftlineno=i + 1,
427 leftline=leftlines[i],
428 leftline=leftlines[i],
428 rightlineno=None,
429 rightlineno=None,
429 rightline=None)
430 rightline=None)
430 elif len2 > len1:
431 elif len2 > len1:
431 for i in xrange(rlo + count, rhi):
432 for i in xrange(rlo + count, rhi):
432 yield compline(type=type,
433 yield compline(type=type,
433 leftlineno=None,
434 leftlineno=None,
434 leftline=None,
435 leftline=None,
435 rightlineno=i + 1,
436 rightlineno=i + 1,
436 rightline=rightlines[i])
437 rightline=rightlines[i])
437
438
438 s = difflib.SequenceMatcher(None, leftlines, rightlines)
439 s = difflib.SequenceMatcher(None, leftlines, rightlines)
439 if context < 0:
440 if context < 0:
440 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
441 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
441 else:
442 else:
442 for oc in s.get_grouped_opcodes(n=context):
443 for oc in s.get_grouped_opcodes(n=context):
443 yield tmpl('comparisonblock', lines=getblock(oc))
444 yield tmpl('comparisonblock', lines=getblock(oc))
444
445
445 def diffstatgen(ctx, basectx):
446 def diffstatgen(ctx, basectx):
446 '''Generator function that provides the diffstat data.'''
447 '''Generator function that provides the diffstat data.'''
447
448
448 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
449 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
449 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
450 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
450 while True:
451 while True:
451 yield stats, maxname, maxtotal, addtotal, removetotal, binary
452 yield stats, maxname, maxtotal, addtotal, removetotal, binary
452
453
453 def diffsummary(statgen):
454 def diffsummary(statgen):
454 '''Return a short summary of the diff.'''
455 '''Return a short summary of the diff.'''
455
456
456 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
457 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
457 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
458 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
458 len(stats), addtotal, removetotal)
459 len(stats), addtotal, removetotal)
459
460
460 def diffstat(tmpl, ctx, statgen, parity):
461 def diffstat(tmpl, ctx, statgen, parity):
461 '''Return a diffstat template for each file in the diff.'''
462 '''Return a diffstat template for each file in the diff.'''
462
463
463 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
464 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
464 files = ctx.files()
465 files = ctx.files()
465
466
466 def pct(i):
467 def pct(i):
467 if maxtotal == 0:
468 if maxtotal == 0:
468 return 0
469 return 0
469 return (float(i) / maxtotal) * 100
470 return (float(i) / maxtotal) * 100
470
471
471 fileno = 0
472 fileno = 0
472 for filename, adds, removes, isbinary in stats:
473 for filename, adds, removes, isbinary in stats:
473 template = filename in files and 'diffstatlink' or 'diffstatnolink'
474 template = filename in files and 'diffstatlink' or 'diffstatnolink'
474 total = adds + removes
475 total = adds + removes
475 fileno += 1
476 fileno += 1
476 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
477 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
477 total=total, addpct=pct(adds), removepct=pct(removes),
478 total=total, addpct=pct(adds), removepct=pct(removes),
478 parity=parity.next())
479 parity=parity.next())
479
480
480 class sessionvars(object):
481 class sessionvars(object):
481 def __init__(self, vars, start='?'):
482 def __init__(self, vars, start='?'):
482 self.start = start
483 self.start = start
483 self.vars = vars
484 self.vars = vars
484 def __getitem__(self, key):
485 def __getitem__(self, key):
485 return self.vars[key]
486 return self.vars[key]
486 def __setitem__(self, key, value):
487 def __setitem__(self, key, value):
487 self.vars[key] = value
488 self.vars[key] = value
488 def __copy__(self):
489 def __copy__(self):
489 return sessionvars(copy.copy(self.vars), self.start)
490 return sessionvars(copy.copy(self.vars), self.start)
490 def __iter__(self):
491 def __iter__(self):
491 separator = self.start
492 separator = self.start
492 for key, value in sorted(self.vars.iteritems()):
493 for key, value in sorted(self.vars.iteritems()):
493 yield {'name': key, 'value': str(value), 'separator': separator}
494 yield {'name': key, 'value': str(value), 'separator': separator}
494 separator = '&'
495 separator = '&'
495
496
496 class wsgiui(ui.ui):
497 class wsgiui(ui.ui):
497 # default termwidth breaks under mod_wsgi
498 # default termwidth breaks under mod_wsgi
498 def termwidth(self):
499 def termwidth(self):
499 return 80
500 return 80
@@ -1,57 +1,58 b''
1 mimetype = 'application/json'
1 mimetype = 'application/json'
2 filerevision = '"not yet implemented"'
2 filerevision = '"not yet implemented"'
3 search = '"not yet implemented"'
3 search = '"not yet implemented"'
4 shortlog = '"not yet implemented"'
4 shortlog = '"not yet implemented"'
5 changelog = '"not yet implemented"'
5 changelog = '"not yet implemented"'
6 changeset = '\{
6 changeset = '\{
7 "node": {node|json},
7 "node": {node|json},
8 "date": {date|json},
8 "date": {date|json},
9 "desc": {desc|json},
9 "desc": {desc|json},
10 "branch": {if(branch, branch%changesetbranch, "default"|json)},
10 "branch": {if(branch, branch%changesetbranch, "default"|json)},
11 "bookmarks": [{join(changesetbookmark, ", ")}],
11 "bookmarks": [{join(changesetbookmark, ", ")}],
12 "tags": [{join(changesettag, ", ")}],
12 "tags": [{join(changesettag, ", ")}],
13 "user": {author|json},
13 "user": {author|json},
14 "parents": [{join(parent%changesetparent, ", ")}]
14 "parents": [{join(parent%changesetparent, ", ")}],
15 "phase": {phase|json}
15 }'
16 }'
16 changesetbranch = '{name|json}'
17 changesetbranch = '{name|json}'
17 changesetbookmark = '{bookmark|json}'
18 changesetbookmark = '{bookmark|json}'
18 changesettag = '{tag|json}'
19 changesettag = '{tag|json}'
19 changesetparent = '{node|json}'
20 changesetparent = '{node|json}'
20 manifest = '"not yet implemented"'
21 manifest = '"not yet implemented"'
21 tags = '\{
22 tags = '\{
22 "node": {node|json},
23 "node": {node|json},
23 "tags": [{join(entriesnotip%tagentry, ", ")}]
24 "tags": [{join(entriesnotip%tagentry, ", ")}]
24 }'
25 }'
25 tagentry = '\{
26 tagentry = '\{
26 "tag": {tag|json},
27 "tag": {tag|json},
27 "node": {node|json},
28 "node": {node|json},
28 "date": {date|json}
29 "date": {date|json}
29 }'
30 }'
30 bookmarks = '\{
31 bookmarks = '\{
31 "node": {node|json},
32 "node": {node|json},
32 "bookmarks": [{join(entries%bookmarkentry, ", ")}]
33 "bookmarks": [{join(entries%bookmarkentry, ", ")}]
33 }'
34 }'
34 bookmarkentry = '\{
35 bookmarkentry = '\{
35 "bookmark": {bookmark|json},
36 "bookmark": {bookmark|json},
36 "node": {node|json},
37 "node": {node|json},
37 "date": {date|json}
38 "date": {date|json}
38 }'
39 }'
39 branches = '\{
40 branches = '\{
40 "branches": [{join(entries%branchentry, ", ")}]
41 "branches": [{join(entries%branchentry, ", ")}]
41 }'
42 }'
42 branchentry = '\{
43 branchentry = '\{
43 "branch": {branch|json},
44 "branch": {branch|json},
44 "node": {node|json},
45 "node": {node|json},
45 "date": {date|json},
46 "date": {date|json},
46 "status": {status|json}
47 "status": {status|json}
47 }'
48 }'
48 summary = '"not yet implemented"'
49 summary = '"not yet implemented"'
49 filediff = '"not yet implemented"'
50 filediff = '"not yet implemented"'
50 filecomparison = '"not yet implemented"'
51 filecomparison = '"not yet implemented"'
51 fileannotate = '"not yet implemented"'
52 fileannotate = '"not yet implemented"'
52 filelog = '"not yet implemented"'
53 filelog = '"not yet implemented"'
53 graph = '"not yet implemented"'
54 graph = '"not yet implemented"'
54 helptopics = '"not yet implemented"'
55 helptopics = '"not yet implemented"'
55 help = '"not yet implemented"'
56 help = '"not yet implemented"'
56 filenodelink = ''
57 filenodelink = ''
57 filenolink = ''
58 filenolink = ''
@@ -1,385 +1,390 b''
1 #require json
1 #require json
2 #require serve
2 #require serve
3
3
4 $ request() {
4 $ request() {
5 > $TESTDIR/get-with-headers.py --json localhost:$HGPORT "$1"
5 > $TESTDIR/get-with-headers.py --json localhost:$HGPORT "$1"
6 > }
6 > }
7
7
8 $ hg init test
8 $ hg init test
9 $ cd test
9 $ cd test
10 $ mkdir da
10 $ mkdir da
11 $ echo foo > da/foo
11 $ echo foo > da/foo
12 $ echo foo > foo
12 $ echo foo > foo
13 $ hg -q ci -A -m initial
13 $ hg -q ci -A -m initial
14 $ echo bar > foo
14 $ echo bar > foo
15 $ hg ci -m 'modify foo'
15 $ hg ci -m 'modify foo'
16 $ echo bar > da/foo
16 $ echo bar > da/foo
17 $ hg ci -m 'modify da/foo'
17 $ hg ci -m 'modify da/foo'
18 $ hg bookmark bookmark1
18 $ hg bookmark bookmark1
19 $ hg up default
19 $ hg up default
20 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 (leaving bookmark bookmark1)
21 (leaving bookmark bookmark1)
22 $ hg mv foo foo-new
22 $ hg mv foo foo-new
23 $ hg commit -m 'move foo'
23 $ hg commit -m 'move foo'
24 $ hg tag -m 'create tag' tag1
24 $ hg tag -m 'create tag' tag1
25 $ hg phase --public -r .
25 $ echo baz > da/foo
26 $ echo baz > da/foo
26 $ hg commit -m 'another commit to da/foo'
27 $ hg commit -m 'another commit to da/foo'
27 $ hg tag -m 'create tag2' tag2
28 $ hg tag -m 'create tag2' tag2
28 $ hg bookmark bookmark2
29 $ hg bookmark bookmark2
29 $ hg -q up -r 0
30 $ hg -q up -r 0
30 $ hg -q branch test-branch
31 $ hg -q branch test-branch
31 $ echo branch > foo
32 $ echo branch > foo
32 $ hg commit -m 'create test branch'
33 $ hg commit -m 'create test branch'
33 $ echo branch_commit_2 > foo
34 $ echo branch_commit_2 > foo
34 $ hg commit -m 'another commit in test-branch'
35 $ hg commit -m 'another commit in test-branch'
35 $ hg -q up default
36 $ hg -q up default
36 $ hg merge --tool :local test-branch
37 $ hg merge --tool :local test-branch
37 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
38 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
38 (branch merge, don't forget to commit)
39 (branch merge, don't forget to commit)
39 $ hg commit -m 'merge test-branch into default'
40 $ hg commit -m 'merge test-branch into default'
40
41
41 $ hg log -G
42 $ hg log -G
42 @ changeset: 9:cc725e08502a
43 @ changeset: 9:cc725e08502a
43 |\ tag: tip
44 |\ tag: tip
44 | | parent: 6:ceed296fe500
45 | | parent: 6:ceed296fe500
45 | | parent: 8:ed66c30e87eb
46 | | parent: 8:ed66c30e87eb
46 | | user: test
47 | | user: test
47 | | date: Thu Jan 01 00:00:00 1970 +0000
48 | | date: Thu Jan 01 00:00:00 1970 +0000
48 | | summary: merge test-branch into default
49 | | summary: merge test-branch into default
49 | |
50 | |
50 | o changeset: 8:ed66c30e87eb
51 | o changeset: 8:ed66c30e87eb
51 | | branch: test-branch
52 | | branch: test-branch
52 | | user: test
53 | | user: test
53 | | date: Thu Jan 01 00:00:00 1970 +0000
54 | | date: Thu Jan 01 00:00:00 1970 +0000
54 | | summary: another commit in test-branch
55 | | summary: another commit in test-branch
55 | |
56 | |
56 | o changeset: 7:6ab967a8ab34
57 | o changeset: 7:6ab967a8ab34
57 | | branch: test-branch
58 | | branch: test-branch
58 | | parent: 0:06e557f3edf6
59 | | parent: 0:06e557f3edf6
59 | | user: test
60 | | user: test
60 | | date: Thu Jan 01 00:00:00 1970 +0000
61 | | date: Thu Jan 01 00:00:00 1970 +0000
61 | | summary: create test branch
62 | | summary: create test branch
62 | |
63 | |
63 o | changeset: 6:ceed296fe500
64 o | changeset: 6:ceed296fe500
64 | | bookmark: bookmark2
65 | | bookmark: bookmark2
65 | | user: test
66 | | user: test
66 | | date: Thu Jan 01 00:00:00 1970 +0000
67 | | date: Thu Jan 01 00:00:00 1970 +0000
67 | | summary: create tag2
68 | | summary: create tag2
68 | |
69 | |
69 o | changeset: 5:f2890a05fea4
70 o | changeset: 5:f2890a05fea4
70 | | tag: tag2
71 | | tag: tag2
71 | | user: test
72 | | user: test
72 | | date: Thu Jan 01 00:00:00 1970 +0000
73 | | date: Thu Jan 01 00:00:00 1970 +0000
73 | | summary: another commit to da/foo
74 | | summary: another commit to da/foo
74 | |
75 | |
75 o | changeset: 4:93a8ce14f891
76 o | changeset: 4:93a8ce14f891
76 | | user: test
77 | | user: test
77 | | date: Thu Jan 01 00:00:00 1970 +0000
78 | | date: Thu Jan 01 00:00:00 1970 +0000
78 | | summary: create tag
79 | | summary: create tag
79 | |
80 | |
80 o | changeset: 3:78896eb0e102
81 o | changeset: 3:78896eb0e102
81 | | tag: tag1
82 | | tag: tag1
82 | | user: test
83 | | user: test
83 | | date: Thu Jan 01 00:00:00 1970 +0000
84 | | date: Thu Jan 01 00:00:00 1970 +0000
84 | | summary: move foo
85 | | summary: move foo
85 | |
86 | |
86 o | changeset: 2:8d7c456572ac
87 o | changeset: 2:8d7c456572ac
87 | | bookmark: bookmark1
88 | | bookmark: bookmark1
88 | | user: test
89 | | user: test
89 | | date: Thu Jan 01 00:00:00 1970 +0000
90 | | date: Thu Jan 01 00:00:00 1970 +0000
90 | | summary: modify da/foo
91 | | summary: modify da/foo
91 | |
92 | |
92 o | changeset: 1:f8bbb9024b10
93 o | changeset: 1:f8bbb9024b10
93 |/ user: test
94 |/ user: test
94 | date: Thu Jan 01 00:00:00 1970 +0000
95 | date: Thu Jan 01 00:00:00 1970 +0000
95 | summary: modify foo
96 | summary: modify foo
96 |
97 |
97 o changeset: 0:06e557f3edf6
98 o changeset: 0:06e557f3edf6
98 user: test
99 user: test
99 date: Thu Jan 01 00:00:00 1970 +0000
100 date: Thu Jan 01 00:00:00 1970 +0000
100 summary: initial
101 summary: initial
101
102
102
103
103 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E error.log
104 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E error.log
104 $ cat hg.pid >> $DAEMON_PIDS
105 $ cat hg.pid >> $DAEMON_PIDS
105
106
106 (Try to keep these in roughly the order they are defined in webcommands.py)
107 (Try to keep these in roughly the order they are defined in webcommands.py)
107
108
108 (log is handled by filelog/ and changelog/ - ignore it)
109 (log is handled by filelog/ and changelog/ - ignore it)
109
110
110 (rawfile/ doesn't use templating - nothing to test)
111 (rawfile/ doesn't use templating - nothing to test)
111
112
112 file/{revision}/{path} shows file revision
113 file/{revision}/{path} shows file revision
113
114
114 $ request json-file/06e557f3edf6/foo
115 $ request json-file/06e557f3edf6/foo
115 200 Script output follows
116 200 Script output follows
116
117
117 "not yet implemented"
118 "not yet implemented"
118
119
119 file/{revision} shows root directory info
120 file/{revision} shows root directory info
120
121
121 $ request json-file/06e557f3edf6
122 $ request json-file/06e557f3edf6
122 200 Script output follows
123 200 Script output follows
123
124
124 "not yet implemented"
125 "not yet implemented"
125
126
126 changelog/ shows information about several changesets
127 changelog/ shows information about several changesets
127
128
128 $ request json-changelog
129 $ request json-changelog
129 200 Script output follows
130 200 Script output follows
130
131
131 "not yet implemented"
132 "not yet implemented"
132
133
133 changelog/{revision} shows information about a single changeset
134 changelog/{revision} shows information about a single changeset
134
135
135 $ request json-changelog/06e557f3edf6
136 $ request json-changelog/06e557f3edf6
136 200 Script output follows
137 200 Script output follows
137
138
138 "not yet implemented"
139 "not yet implemented"
139
140
140 shortlog/ shows information about a set of changesets
141 shortlog/ shows information about a set of changesets
141
142
142 $ request json-shortlog
143 $ request json-shortlog
143 200 Script output follows
144 200 Script output follows
144
145
145 "not yet implemented"
146 "not yet implemented"
146
147
147 changeset/ renders the tip changeset
148 changeset/ renders the tip changeset
148
149
149 $ request json-rev
150 $ request json-rev
150 200 Script output follows
151 200 Script output follows
151
152
152 {
153 {
153 "bookmarks": [],
154 "bookmarks": [],
154 "branch": "default",
155 "branch": "default",
155 "date": [
156 "date": [
156 0.0,
157 0.0,
157 0
158 0
158 ],
159 ],
159 "desc": "merge test-branch into default",
160 "desc": "merge test-branch into default",
160 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
161 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
161 "parents": [
162 "parents": [
162 "ceed296fe500c3fac9541e31dad860cb49c89e45",
163 "ceed296fe500c3fac9541e31dad860cb49c89e45",
163 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
164 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
164 ],
165 ],
166 "phase": "draft",
165 "tags": [
167 "tags": [
166 "tip"
168 "tip"
167 ],
169 ],
168 "user": "test"
170 "user": "test"
169 }
171 }
170
172
171 changeset/{revision} shows tags
173 changeset/{revision} shows tags
172
174
173 $ request json-rev/78896eb0e102
175 $ request json-rev/78896eb0e102
174 200 Script output follows
176 200 Script output follows
175
177
176 {
178 {
177 "bookmarks": [],
179 "bookmarks": [],
178 "branch": "default",
180 "branch": "default",
179 "date": [
181 "date": [
180 0.0,
182 0.0,
181 0
183 0
182 ],
184 ],
183 "desc": "move foo",
185 "desc": "move foo",
184 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
186 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
185 "parents": [
187 "parents": [
186 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
188 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
187 ],
189 ],
190 "phase": "public",
188 "tags": [
191 "tags": [
189 "tag1"
192 "tag1"
190 ],
193 ],
191 "user": "test"
194 "user": "test"
192 }
195 }
193
196
194 changeset/{revision} shows bookmarks
197 changeset/{revision} shows bookmarks
195
198
196 $ request json-rev/8d7c456572ac
199 $ request json-rev/8d7c456572ac
197 200 Script output follows
200 200 Script output follows
198
201
199 {
202 {
200 "bookmarks": [
203 "bookmarks": [
201 "bookmark1"
204 "bookmark1"
202 ],
205 ],
203 "branch": "default",
206 "branch": "default",
204 "date": [
207 "date": [
205 0.0,
208 0.0,
206 0
209 0
207 ],
210 ],
208 "desc": "modify da/foo",
211 "desc": "modify da/foo",
209 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
212 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
210 "parents": [
213 "parents": [
211 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
214 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
212 ],
215 ],
216 "phase": "public",
213 "tags": [],
217 "tags": [],
214 "user": "test"
218 "user": "test"
215 }
219 }
216
220
217 changeset/{revision} shows branches
221 changeset/{revision} shows branches
218
222
219 $ request json-rev/6ab967a8ab34
223 $ request json-rev/6ab967a8ab34
220 200 Script output follows
224 200 Script output follows
221
225
222 {
226 {
223 "bookmarks": [],
227 "bookmarks": [],
224 "branch": "test-branch",
228 "branch": "test-branch",
225 "date": [
229 "date": [
226 0.0,
230 0.0,
227 0
231 0
228 ],
232 ],
229 "desc": "create test branch",
233 "desc": "create test branch",
230 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
234 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
231 "parents": [
235 "parents": [
232 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
236 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
233 ],
237 ],
238 "phase": "draft",
234 "tags": [],
239 "tags": [],
235 "user": "test"
240 "user": "test"
236 }
241 }
237
242
238 manifest/{revision}/{path} shows info about a directory at a revision
243 manifest/{revision}/{path} shows info about a directory at a revision
239
244
240 $ request json-manifest/06e557f3edf6/
245 $ request json-manifest/06e557f3edf6/
241 200 Script output follows
246 200 Script output follows
242
247
243 "not yet implemented"
248 "not yet implemented"
244
249
245 tags/ shows tags info
250 tags/ shows tags info
246
251
247 $ request json-tags
252 $ request json-tags
248 200 Script output follows
253 200 Script output follows
249
254
250 {
255 {
251 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
256 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
252 "tags": [
257 "tags": [
253 {
258 {
254 "date": [
259 "date": [
255 0.0,
260 0.0,
256 0
261 0
257 ],
262 ],
258 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
263 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
259 "tag": "tag2"
264 "tag": "tag2"
260 },
265 },
261 {
266 {
262 "date": [
267 "date": [
263 0.0,
268 0.0,
264 0
269 0
265 ],
270 ],
266 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
271 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
267 "tag": "tag1"
272 "tag": "tag1"
268 }
273 }
269 ]
274 ]
270 }
275 }
271
276
272 bookmarks/ shows bookmarks info
277 bookmarks/ shows bookmarks info
273
278
274 $ request json-bookmarks
279 $ request json-bookmarks
275 200 Script output follows
280 200 Script output follows
276
281
277 {
282 {
278 "bookmarks": [
283 "bookmarks": [
279 {
284 {
280 "bookmark": "bookmark1",
285 "bookmark": "bookmark1",
281 "date": [
286 "date": [
282 0.0,
287 0.0,
283 0
288 0
284 ],
289 ],
285 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5"
290 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5"
286 },
291 },
287 {
292 {
288 "bookmark": "bookmark2",
293 "bookmark": "bookmark2",
289 "date": [
294 "date": [
290 0.0,
295 0.0,
291 0
296 0
292 ],
297 ],
293 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45"
298 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45"
294 }
299 }
295 ],
300 ],
296 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
301 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
297 }
302 }
298
303
299 branches/ shows branches info
304 branches/ shows branches info
300
305
301 $ request json-branches
306 $ request json-branches
302 200 Script output follows
307 200 Script output follows
303
308
304 {
309 {
305 "branches": [
310 "branches": [
306 {
311 {
307 "branch": "default",
312 "branch": "default",
308 "date": [
313 "date": [
309 0.0,
314 0.0,
310 0
315 0
311 ],
316 ],
312 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
317 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
313 "status": "open"
318 "status": "open"
314 },
319 },
315 {
320 {
316 "branch": "test-branch",
321 "branch": "test-branch",
317 "date": [
322 "date": [
318 0.0,
323 0.0,
319 0
324 0
320 ],
325 ],
321 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
326 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
322 "status": "inactive"
327 "status": "inactive"
323 }
328 }
324 ]
329 ]
325 }
330 }
326
331
327 summary/ shows a summary of repository state
332 summary/ shows a summary of repository state
328
333
329 $ request json-summary
334 $ request json-summary
330 200 Script output follows
335 200 Script output follows
331
336
332 "not yet implemented"
337 "not yet implemented"
333
338
334 filediff/{revision}/{path} shows changes to a file in a revision
339 filediff/{revision}/{path} shows changes to a file in a revision
335
340
336 $ request json-diff/f8bbb9024b10/foo
341 $ request json-diff/f8bbb9024b10/foo
337 200 Script output follows
342 200 Script output follows
338
343
339 "not yet implemented"
344 "not yet implemented"
340
345
341 comparison/{revision}/{path} shows information about before and after for a file
346 comparison/{revision}/{path} shows information about before and after for a file
342
347
343 $ request json-comparison/f8bbb9024b10/foo
348 $ request json-comparison/f8bbb9024b10/foo
344 200 Script output follows
349 200 Script output follows
345
350
346 "not yet implemented"
351 "not yet implemented"
347
352
348 annotate/{revision}/{path} shows annotations for each line
353 annotate/{revision}/{path} shows annotations for each line
349
354
350 $ request json-annotate/f8bbb9024b10/foo
355 $ request json-annotate/f8bbb9024b10/foo
351 200 Script output follows
356 200 Script output follows
352
357
353 "not yet implemented"
358 "not yet implemented"
354
359
355 filelog/{revision}/{path} shows history of a single file
360 filelog/{revision}/{path} shows history of a single file
356
361
357 $ request json-filelog/f8bbb9024b10/foo
362 $ request json-filelog/f8bbb9024b10/foo
358 200 Script output follows
363 200 Script output follows
359
364
360 "not yet implemented"
365 "not yet implemented"
361
366
362 (archive/ doesn't use templating, so ignore it)
367 (archive/ doesn't use templating, so ignore it)
363
368
364 (static/ doesn't use templating, so ignore it)
369 (static/ doesn't use templating, so ignore it)
365
370
366 graph/ shows information that can be used to render a graph of the DAG
371 graph/ shows information that can be used to render a graph of the DAG
367
372
368 $ request json-graph
373 $ request json-graph
369 200 Script output follows
374 200 Script output follows
370
375
371 "not yet implemented"
376 "not yet implemented"
372
377
373 help/ shows help topics
378 help/ shows help topics
374
379
375 $ request json-help
380 $ request json-help
376 200 Script output follows
381 200 Script output follows
377
382
378 "not yet implemented"
383 "not yet implemented"
379
384
380 help/{topic} shows an individual help topic
385 help/{topic} shows an individual help topic
381
386
382 $ request json-help/phases
387 $ request json-help/phases
383 200 Script output follows
388 200 Script output follows
384
389
385 "not yet implemented"
390 "not yet implemented"
General Comments 0
You need to be logged in to leave comments. Login now