##// END OF EJS Templates
hgweb: add parents to json-log (issue5074)...
av6 -
r28709:94494031 default
parent child Browse files
Show More
@@ -1,607 +1,608 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 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import copy
11 import copy
12 import difflib
12 import difflib
13 import os
13 import os
14 import re
14 import re
15
15
16 from ..i18n import _
16 from ..i18n import _
17 from ..node import hex, nullid, short
17 from ..node import hex, nullid, short
18
18
19 from .common import (
19 from .common import (
20 ErrorResponse,
20 ErrorResponse,
21 HTTP_NOT_FOUND,
21 HTTP_NOT_FOUND,
22 paritygen,
22 paritygen,
23 )
23 )
24
24
25 from .. import (
25 from .. import (
26 context,
26 context,
27 error,
27 error,
28 match,
28 match,
29 patch,
29 patch,
30 pathutil,
30 pathutil,
31 templatefilters,
31 templatefilters,
32 ui as uimod,
32 ui as uimod,
33 util,
33 util,
34 )
34 )
35
35
36 def up(p):
36 def up(p):
37 if p[0] != "/":
37 if p[0] != "/":
38 p = "/" + p
38 p = "/" + p
39 if p[-1] == "/":
39 if p[-1] == "/":
40 p = p[:-1]
40 p = p[:-1]
41 up = os.path.dirname(p)
41 up = os.path.dirname(p)
42 if up == "/":
42 if up == "/":
43 return "/"
43 return "/"
44 return up + "/"
44 return up + "/"
45
45
46 def _navseq(step, firststep=None):
46 def _navseq(step, firststep=None):
47 if firststep:
47 if firststep:
48 yield firststep
48 yield firststep
49 if firststep >= 20 and firststep <= 40:
49 if firststep >= 20 and firststep <= 40:
50 firststep = 50
50 firststep = 50
51 yield firststep
51 yield firststep
52 assert step > 0
52 assert step > 0
53 assert firststep > 0
53 assert firststep > 0
54 while step <= firststep:
54 while step <= firststep:
55 step *= 10
55 step *= 10
56 while True:
56 while True:
57 yield 1 * step
57 yield 1 * step
58 yield 3 * step
58 yield 3 * step
59 step *= 10
59 step *= 10
60
60
61 class revnav(object):
61 class revnav(object):
62
62
63 def __init__(self, repo):
63 def __init__(self, repo):
64 """Navigation generation object
64 """Navigation generation object
65
65
66 :repo: repo object we generate nav for
66 :repo: repo object we generate nav for
67 """
67 """
68 # used for hex generation
68 # used for hex generation
69 self._revlog = repo.changelog
69 self._revlog = repo.changelog
70
70
71 def __nonzero__(self):
71 def __nonzero__(self):
72 """return True if any revision to navigate over"""
72 """return True if any revision to navigate over"""
73 return self._first() is not None
73 return self._first() is not None
74
74
75 def _first(self):
75 def _first(self):
76 """return the minimum non-filtered changeset or None"""
76 """return the minimum non-filtered changeset or None"""
77 try:
77 try:
78 return iter(self._revlog).next()
78 return iter(self._revlog).next()
79 except StopIteration:
79 except StopIteration:
80 return None
80 return None
81
81
82 def hex(self, rev):
82 def hex(self, rev):
83 return hex(self._revlog.node(rev))
83 return hex(self._revlog.node(rev))
84
84
85 def gen(self, pos, pagelen, limit):
85 def gen(self, pos, pagelen, limit):
86 """computes label and revision id for navigation link
86 """computes label and revision id for navigation link
87
87
88 :pos: is the revision relative to which we generate navigation.
88 :pos: is the revision relative to which we generate navigation.
89 :pagelen: the size of each navigation page
89 :pagelen: the size of each navigation page
90 :limit: how far shall we link
90 :limit: how far shall we link
91
91
92 The return is:
92 The return is:
93 - a single element tuple
93 - a single element tuple
94 - containing a dictionary with a `before` and `after` key
94 - containing a dictionary with a `before` and `after` key
95 - values are generator functions taking arbitrary number of kwargs
95 - values are generator functions taking arbitrary number of kwargs
96 - yield items are dictionaries with `label` and `node` keys
96 - yield items are dictionaries with `label` and `node` keys
97 """
97 """
98 if not self:
98 if not self:
99 # empty repo
99 # empty repo
100 return ({'before': (), 'after': ()},)
100 return ({'before': (), 'after': ()},)
101
101
102 targets = []
102 targets = []
103 for f in _navseq(1, pagelen):
103 for f in _navseq(1, pagelen):
104 if f > limit:
104 if f > limit:
105 break
105 break
106 targets.append(pos + f)
106 targets.append(pos + f)
107 targets.append(pos - f)
107 targets.append(pos - f)
108 targets.sort()
108 targets.sort()
109
109
110 first = self._first()
110 first = self._first()
111 navbefore = [("(%i)" % first, self.hex(first))]
111 navbefore = [("(%i)" % first, self.hex(first))]
112 navafter = []
112 navafter = []
113 for rev in targets:
113 for rev in targets:
114 if rev not in self._revlog:
114 if rev not in self._revlog:
115 continue
115 continue
116 if pos < rev < limit:
116 if pos < rev < limit:
117 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
117 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
118 if 0 < rev < pos:
118 if 0 < rev < pos:
119 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
119 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
120
120
121
121
122 navafter.append(("tip", "tip"))
122 navafter.append(("tip", "tip"))
123
123
124 data = lambda i: {"label": i[0], "node": i[1]}
124 data = lambda i: {"label": i[0], "node": i[1]}
125 return ({'before': lambda **map: (data(i) for i in navbefore),
125 return ({'before': lambda **map: (data(i) for i in navbefore),
126 'after': lambda **map: (data(i) for i in navafter)},)
126 'after': lambda **map: (data(i) for i in navafter)},)
127
127
128 class filerevnav(revnav):
128 class filerevnav(revnav):
129
129
130 def __init__(self, repo, path):
130 def __init__(self, repo, path):
131 """Navigation generation object
131 """Navigation generation object
132
132
133 :repo: repo object we generate nav for
133 :repo: repo object we generate nav for
134 :path: path of the file we generate nav for
134 :path: path of the file we generate nav for
135 """
135 """
136 # used for iteration
136 # used for iteration
137 self._changelog = repo.unfiltered().changelog
137 self._changelog = repo.unfiltered().changelog
138 # used for hex generation
138 # used for hex generation
139 self._revlog = repo.file(path)
139 self._revlog = repo.file(path)
140
140
141 def hex(self, rev):
141 def hex(self, rev):
142 return hex(self._changelog.node(self._revlog.linkrev(rev)))
142 return hex(self._changelog.node(self._revlog.linkrev(rev)))
143
143
144 class _siblings(object):
144 class _siblings(object):
145 def __init__(self, siblings=[], hiderev=None):
145 def __init__(self, siblings=[], hiderev=None):
146 self.siblings = [s for s in siblings if s.node() != nullid]
146 self.siblings = [s for s in siblings if s.node() != nullid]
147 if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
147 if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
148 self.siblings = []
148 self.siblings = []
149
149
150 def __iter__(self):
150 def __iter__(self):
151 for s in self.siblings:
151 for s in self.siblings:
152 d = {
152 d = {
153 'node': s.hex(),
153 'node': s.hex(),
154 'rev': s.rev(),
154 'rev': s.rev(),
155 'user': s.user(),
155 'user': s.user(),
156 'date': s.date(),
156 'date': s.date(),
157 'description': s.description(),
157 'description': s.description(),
158 'branch': s.branch(),
158 'branch': s.branch(),
159 }
159 }
160 if util.safehasattr(s, 'path'):
160 if util.safehasattr(s, 'path'):
161 d['file'] = s.path()
161 d['file'] = s.path()
162 yield d
162 yield d
163
163
164 def __len__(self):
164 def __len__(self):
165 return len(self.siblings)
165 return len(self.siblings)
166
166
167 def parents(ctx, hide=None):
167 def parents(ctx, hide=None):
168 if isinstance(ctx, context.basefilectx):
168 if isinstance(ctx, context.basefilectx):
169 introrev = ctx.introrev()
169 introrev = ctx.introrev()
170 if ctx.changectx().rev() != introrev:
170 if ctx.changectx().rev() != introrev:
171 return _siblings([ctx.repo()[introrev]], hide)
171 return _siblings([ctx.repo()[introrev]], hide)
172 return _siblings(ctx.parents(), hide)
172 return _siblings(ctx.parents(), hide)
173
173
174 def children(ctx, hide=None):
174 def children(ctx, hide=None):
175 return _siblings(ctx.children(), hide)
175 return _siblings(ctx.children(), hide)
176
176
177 def renamelink(fctx):
177 def renamelink(fctx):
178 r = fctx.renamed()
178 r = fctx.renamed()
179 if r:
179 if r:
180 return [{'file': r[0], 'node': hex(r[1])}]
180 return [{'file': r[0], 'node': hex(r[1])}]
181 return []
181 return []
182
182
183 def nodetagsdict(repo, node):
183 def nodetagsdict(repo, node):
184 return [{"name": i} for i in repo.nodetags(node)]
184 return [{"name": i} for i in repo.nodetags(node)]
185
185
186 def nodebookmarksdict(repo, node):
186 def nodebookmarksdict(repo, node):
187 return [{"name": i} for i in repo.nodebookmarks(node)]
187 return [{"name": i} for i in repo.nodebookmarks(node)]
188
188
189 def nodebranchdict(repo, ctx):
189 def nodebranchdict(repo, ctx):
190 branches = []
190 branches = []
191 branch = ctx.branch()
191 branch = ctx.branch()
192 # If this is an empty repo, ctx.node() == nullid,
192 # If this is an empty repo, ctx.node() == nullid,
193 # ctx.branch() == 'default'.
193 # ctx.branch() == 'default'.
194 try:
194 try:
195 branchnode = repo.branchtip(branch)
195 branchnode = repo.branchtip(branch)
196 except error.RepoLookupError:
196 except error.RepoLookupError:
197 branchnode = None
197 branchnode = None
198 if branchnode == ctx.node():
198 if branchnode == ctx.node():
199 branches.append({"name": branch})
199 branches.append({"name": branch})
200 return branches
200 return branches
201
201
202 def nodeinbranch(repo, ctx):
202 def nodeinbranch(repo, ctx):
203 branches = []
203 branches = []
204 branch = ctx.branch()
204 branch = ctx.branch()
205 try:
205 try:
206 branchnode = repo.branchtip(branch)
206 branchnode = repo.branchtip(branch)
207 except error.RepoLookupError:
207 except error.RepoLookupError:
208 branchnode = None
208 branchnode = None
209 if branch != 'default' and branchnode != ctx.node():
209 if branch != 'default' and branchnode != ctx.node():
210 branches.append({"name": branch})
210 branches.append({"name": branch})
211 return branches
211 return branches
212
212
213 def nodebranchnodefault(ctx):
213 def nodebranchnodefault(ctx):
214 branches = []
214 branches = []
215 branch = ctx.branch()
215 branch = ctx.branch()
216 if branch != 'default':
216 if branch != 'default':
217 branches.append({"name": branch})
217 branches.append({"name": branch})
218 return branches
218 return branches
219
219
220 def showtag(repo, tmpl, t1, node=nullid, **args):
220 def showtag(repo, tmpl, t1, node=nullid, **args):
221 for t in repo.nodetags(node):
221 for t in repo.nodetags(node):
222 yield tmpl(t1, tag=t, **args)
222 yield tmpl(t1, tag=t, **args)
223
223
224 def showbookmark(repo, tmpl, t1, node=nullid, **args):
224 def showbookmark(repo, tmpl, t1, node=nullid, **args):
225 for t in repo.nodebookmarks(node):
225 for t in repo.nodebookmarks(node):
226 yield tmpl(t1, bookmark=t, **args)
226 yield tmpl(t1, bookmark=t, **args)
227
227
228 def branchentries(repo, stripecount, limit=0):
228 def branchentries(repo, stripecount, limit=0):
229 tips = []
229 tips = []
230 heads = repo.heads()
230 heads = repo.heads()
231 parity = paritygen(stripecount)
231 parity = paritygen(stripecount)
232 sortkey = lambda item: (not item[1], item[0].rev())
232 sortkey = lambda item: (not item[1], item[0].rev())
233
233
234 def entries(**map):
234 def entries(**map):
235 count = 0
235 count = 0
236 if not tips:
236 if not tips:
237 for tag, hs, tip, closed in repo.branchmap().iterbranches():
237 for tag, hs, tip, closed in repo.branchmap().iterbranches():
238 tips.append((repo[tip], closed))
238 tips.append((repo[tip], closed))
239 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
239 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
240 if limit > 0 and count >= limit:
240 if limit > 0 and count >= limit:
241 return
241 return
242 count += 1
242 count += 1
243 if closed:
243 if closed:
244 status = 'closed'
244 status = 'closed'
245 elif ctx.node() not in heads:
245 elif ctx.node() not in heads:
246 status = 'inactive'
246 status = 'inactive'
247 else:
247 else:
248 status = 'open'
248 status = 'open'
249 yield {
249 yield {
250 'parity': parity.next(),
250 'parity': parity.next(),
251 'branch': ctx.branch(),
251 'branch': ctx.branch(),
252 'status': status,
252 'status': status,
253 'node': ctx.hex(),
253 'node': ctx.hex(),
254 'date': ctx.date()
254 'date': ctx.date()
255 }
255 }
256
256
257 return entries
257 return entries
258
258
259 def cleanpath(repo, path):
259 def cleanpath(repo, path):
260 path = path.lstrip('/')
260 path = path.lstrip('/')
261 return pathutil.canonpath(repo.root, '', path)
261 return pathutil.canonpath(repo.root, '', path)
262
262
263 def changeidctx(repo, changeid):
263 def changeidctx(repo, changeid):
264 try:
264 try:
265 ctx = repo[changeid]
265 ctx = repo[changeid]
266 except error.RepoError:
266 except error.RepoError:
267 man = repo.manifest
267 man = repo.manifest
268 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
268 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
269
269
270 return ctx
270 return ctx
271
271
272 def changectx(repo, req):
272 def changectx(repo, req):
273 changeid = "tip"
273 changeid = "tip"
274 if 'node' in req.form:
274 if 'node' in req.form:
275 changeid = req.form['node'][0]
275 changeid = req.form['node'][0]
276 ipos = changeid.find(':')
276 ipos = changeid.find(':')
277 if ipos != -1:
277 if ipos != -1:
278 changeid = changeid[(ipos + 1):]
278 changeid = changeid[(ipos + 1):]
279 elif 'manifest' in req.form:
279 elif 'manifest' in req.form:
280 changeid = req.form['manifest'][0]
280 changeid = req.form['manifest'][0]
281
281
282 return changeidctx(repo, changeid)
282 return changeidctx(repo, changeid)
283
283
284 def basechangectx(repo, req):
284 def basechangectx(repo, req):
285 if 'node' in req.form:
285 if 'node' in req.form:
286 changeid = req.form['node'][0]
286 changeid = req.form['node'][0]
287 ipos = changeid.find(':')
287 ipos = changeid.find(':')
288 if ipos != -1:
288 if ipos != -1:
289 changeid = changeid[:ipos]
289 changeid = changeid[:ipos]
290 return changeidctx(repo, changeid)
290 return changeidctx(repo, changeid)
291
291
292 return None
292 return None
293
293
294 def filectx(repo, req):
294 def filectx(repo, req):
295 if 'file' not in req.form:
295 if 'file' not in req.form:
296 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
296 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
297 path = cleanpath(repo, req.form['file'][0])
297 path = cleanpath(repo, req.form['file'][0])
298 if 'node' in req.form:
298 if 'node' in req.form:
299 changeid = req.form['node'][0]
299 changeid = req.form['node'][0]
300 elif 'filenode' in req.form:
300 elif 'filenode' in req.form:
301 changeid = req.form['filenode'][0]
301 changeid = req.form['filenode'][0]
302 else:
302 else:
303 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
303 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
304 try:
304 try:
305 fctx = repo[changeid][path]
305 fctx = repo[changeid][path]
306 except error.RepoError:
306 except error.RepoError:
307 fctx = repo.filectx(path, fileid=changeid)
307 fctx = repo.filectx(path, fileid=changeid)
308
308
309 return fctx
309 return fctx
310
310
311 def commonentry(repo, ctx):
311 def commonentry(repo, ctx):
312 node = ctx.node()
312 node = ctx.node()
313 return {
313 return {
314 'rev': ctx.rev(),
314 'rev': ctx.rev(),
315 'node': hex(node),
315 'node': hex(node),
316 'author': ctx.user(),
316 'author': ctx.user(),
317 'desc': ctx.description(),
317 'desc': ctx.description(),
318 'date': ctx.date(),
318 'date': ctx.date(),
319 'extra': ctx.extra(),
319 'extra': ctx.extra(),
320 'phase': ctx.phasestr(),
320 'phase': ctx.phasestr(),
321 'branch': nodebranchnodefault(ctx),
321 'branch': nodebranchnodefault(ctx),
322 'inbranch': nodeinbranch(repo, ctx),
322 'inbranch': nodeinbranch(repo, ctx),
323 'branches': nodebranchdict(repo, ctx),
323 'branches': nodebranchdict(repo, ctx),
324 'tags': nodetagsdict(repo, node),
324 'tags': nodetagsdict(repo, node),
325 'bookmarks': nodebookmarksdict(repo, node),
325 'bookmarks': nodebookmarksdict(repo, node),
326 'parent': lambda **x: parents(ctx),
326 'parent': lambda **x: parents(ctx),
327 'child': lambda **x: children(ctx),
327 'child': lambda **x: children(ctx),
328 }
328 }
329
329
330 def changelistentry(web, ctx, tmpl):
330 def changelistentry(web, ctx, tmpl):
331 '''Obtain a dictionary to be used for entries in a changelist.
331 '''Obtain a dictionary to be used for entries in a changelist.
332
332
333 This function is called when producing items for the "entries" list passed
333 This function is called when producing items for the "entries" list passed
334 to the "shortlog" and "changelog" templates.
334 to the "shortlog" and "changelog" templates.
335 '''
335 '''
336 repo = web.repo
336 repo = web.repo
337 rev = ctx.rev()
337 rev = ctx.rev()
338 n = ctx.node()
338 n = ctx.node()
339 showtags = showtag(repo, tmpl, 'changelogtag', n)
339 showtags = showtag(repo, tmpl, 'changelogtag', n)
340 files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
340 files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
341
341
342 entry = commonentry(repo, ctx)
342 entry = commonentry(repo, ctx)
343 entry.update(
343 entry.update(
344 allparents=lambda **x: parents(ctx),
344 parent=lambda **x: parents(ctx, rev - 1),
345 parent=lambda **x: parents(ctx, rev - 1),
345 child=lambda **x: children(ctx, rev + 1),
346 child=lambda **x: children(ctx, rev + 1),
346 changelogtag=showtags,
347 changelogtag=showtags,
347 files=files,
348 files=files,
348 )
349 )
349 return entry
350 return entry
350
351
351 def symrevorshortnode(req, ctx):
352 def symrevorshortnode(req, ctx):
352 if 'node' in req.form:
353 if 'node' in req.form:
353 return templatefilters.revescape(req.form['node'][0])
354 return templatefilters.revescape(req.form['node'][0])
354 else:
355 else:
355 return short(ctx.node())
356 return short(ctx.node())
356
357
357 def changesetentry(web, req, tmpl, ctx):
358 def changesetentry(web, req, tmpl, ctx):
358 '''Obtain a dictionary to be used to render the "changeset" template.'''
359 '''Obtain a dictionary to be used to render the "changeset" template.'''
359
360
360 showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
361 showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
361 showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
362 showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
362 ctx.node())
363 ctx.node())
363 showbranch = nodebranchnodefault(ctx)
364 showbranch = nodebranchnodefault(ctx)
364
365
365 files = []
366 files = []
366 parity = paritygen(web.stripecount)
367 parity = paritygen(web.stripecount)
367 for blockno, f in enumerate(ctx.files()):
368 for blockno, f in enumerate(ctx.files()):
368 template = f in ctx and 'filenodelink' or 'filenolink'
369 template = f in ctx and 'filenodelink' or 'filenolink'
369 files.append(tmpl(template,
370 files.append(tmpl(template,
370 node=ctx.hex(), file=f, blockno=blockno + 1,
371 node=ctx.hex(), file=f, blockno=blockno + 1,
371 parity=parity.next()))
372 parity=parity.next()))
372
373
373 basectx = basechangectx(web.repo, req)
374 basectx = basechangectx(web.repo, req)
374 if basectx is None:
375 if basectx is None:
375 basectx = ctx.p1()
376 basectx = ctx.p1()
376
377
377 style = web.config('web', 'style', 'paper')
378 style = web.config('web', 'style', 'paper')
378 if 'style' in req.form:
379 if 'style' in req.form:
379 style = req.form['style'][0]
380 style = req.form['style'][0]
380
381
381 parity = paritygen(web.stripecount)
382 parity = paritygen(web.stripecount)
382 diff = diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
383 diff = diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
383
384
384 parity = paritygen(web.stripecount)
385 parity = paritygen(web.stripecount)
385 diffstatsgen = diffstatgen(ctx, basectx)
386 diffstatsgen = diffstatgen(ctx, basectx)
386 diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
387 diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
387
388
388 return dict(
389 return dict(
389 diff=diff,
390 diff=diff,
390 symrev=symrevorshortnode(req, ctx),
391 symrev=symrevorshortnode(req, ctx),
391 basenode=basectx.hex(),
392 basenode=basectx.hex(),
392 changesettag=showtags,
393 changesettag=showtags,
393 changesetbookmark=showbookmarks,
394 changesetbookmark=showbookmarks,
394 changesetbranch=showbranch,
395 changesetbranch=showbranch,
395 files=files,
396 files=files,
396 diffsummary=lambda **x: diffsummary(diffstatsgen),
397 diffsummary=lambda **x: diffsummary(diffstatsgen),
397 diffstat=diffstats,
398 diffstat=diffstats,
398 archives=web.archivelist(ctx.hex()),
399 archives=web.archivelist(ctx.hex()),
399 **commonentry(web.repo, ctx))
400 **commonentry(web.repo, ctx))
400
401
401 def listfilediffs(tmpl, files, node, max):
402 def listfilediffs(tmpl, files, node, max):
402 for f in files[:max]:
403 for f in files[:max]:
403 yield tmpl('filedifflink', node=hex(node), file=f)
404 yield tmpl('filedifflink', node=hex(node), file=f)
404 if len(files) > max:
405 if len(files) > max:
405 yield tmpl('fileellipses')
406 yield tmpl('fileellipses')
406
407
407 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
408 def diffs(repo, tmpl, ctx, basectx, files, parity, style):
408
409
409 def countgen():
410 def countgen():
410 start = 1
411 start = 1
411 while True:
412 while True:
412 yield start
413 yield start
413 start += 1
414 start += 1
414
415
415 blockcount = countgen()
416 blockcount = countgen()
416 def prettyprintlines(diff, blockno):
417 def prettyprintlines(diff, blockno):
417 for lineno, l in enumerate(diff.splitlines(True)):
418 for lineno, l in enumerate(diff.splitlines(True)):
418 difflineno = "%d.%d" % (blockno, lineno + 1)
419 difflineno = "%d.%d" % (blockno, lineno + 1)
419 if l.startswith('+'):
420 if l.startswith('+'):
420 ltype = "difflineplus"
421 ltype = "difflineplus"
421 elif l.startswith('-'):
422 elif l.startswith('-'):
422 ltype = "difflineminus"
423 ltype = "difflineminus"
423 elif l.startswith('@'):
424 elif l.startswith('@'):
424 ltype = "difflineat"
425 ltype = "difflineat"
425 else:
426 else:
426 ltype = "diffline"
427 ltype = "diffline"
427 yield tmpl(ltype,
428 yield tmpl(ltype,
428 line=l,
429 line=l,
429 lineno=lineno + 1,
430 lineno=lineno + 1,
430 lineid="l%s" % difflineno,
431 lineid="l%s" % difflineno,
431 linenumber="% 8s" % difflineno)
432 linenumber="% 8s" % difflineno)
432
433
433 if files:
434 if files:
434 m = match.exact(repo.root, repo.getcwd(), files)
435 m = match.exact(repo.root, repo.getcwd(), files)
435 else:
436 else:
436 m = match.always(repo.root, repo.getcwd())
437 m = match.always(repo.root, repo.getcwd())
437
438
438 diffopts = patch.diffopts(repo.ui, untrusted=True)
439 diffopts = patch.diffopts(repo.ui, untrusted=True)
439 if basectx is None:
440 if basectx is None:
440 parents = ctx.parents()
441 parents = ctx.parents()
441 if parents:
442 if parents:
442 node1 = parents[0].node()
443 node1 = parents[0].node()
443 else:
444 else:
444 node1 = nullid
445 node1 = nullid
445 else:
446 else:
446 node1 = basectx.node()
447 node1 = basectx.node()
447 node2 = ctx.node()
448 node2 = ctx.node()
448
449
449 block = []
450 block = []
450 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
451 for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
451 if chunk.startswith('diff') and block:
452 if chunk.startswith('diff') and block:
452 blockno = blockcount.next()
453 blockno = blockcount.next()
453 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
454 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
454 lines=prettyprintlines(''.join(block), blockno))
455 lines=prettyprintlines(''.join(block), blockno))
455 block = []
456 block = []
456 if chunk.startswith('diff') and style != 'raw':
457 if chunk.startswith('diff') and style != 'raw':
457 chunk = ''.join(chunk.splitlines(True)[1:])
458 chunk = ''.join(chunk.splitlines(True)[1:])
458 block.append(chunk)
459 block.append(chunk)
459 blockno = blockcount.next()
460 blockno = blockcount.next()
460 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
461 yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
461 lines=prettyprintlines(''.join(block), blockno))
462 lines=prettyprintlines(''.join(block), blockno))
462
463
463 def compare(tmpl, context, leftlines, rightlines):
464 def compare(tmpl, context, leftlines, rightlines):
464 '''Generator function that provides side-by-side comparison data.'''
465 '''Generator function that provides side-by-side comparison data.'''
465
466
466 def compline(type, leftlineno, leftline, rightlineno, rightline):
467 def compline(type, leftlineno, leftline, rightlineno, rightline):
467 lineid = leftlineno and ("l%s" % leftlineno) or ''
468 lineid = leftlineno and ("l%s" % leftlineno) or ''
468 lineid += rightlineno and ("r%s" % rightlineno) or ''
469 lineid += rightlineno and ("r%s" % rightlineno) or ''
469 return tmpl('comparisonline',
470 return tmpl('comparisonline',
470 type=type,
471 type=type,
471 lineid=lineid,
472 lineid=lineid,
472 leftlineno=leftlineno,
473 leftlineno=leftlineno,
473 leftlinenumber="% 6s" % (leftlineno or ''),
474 leftlinenumber="% 6s" % (leftlineno or ''),
474 leftline=leftline or '',
475 leftline=leftline or '',
475 rightlineno=rightlineno,
476 rightlineno=rightlineno,
476 rightlinenumber="% 6s" % (rightlineno or ''),
477 rightlinenumber="% 6s" % (rightlineno or ''),
477 rightline=rightline or '')
478 rightline=rightline or '')
478
479
479 def getblock(opcodes):
480 def getblock(opcodes):
480 for type, llo, lhi, rlo, rhi in opcodes:
481 for type, llo, lhi, rlo, rhi in opcodes:
481 len1 = lhi - llo
482 len1 = lhi - llo
482 len2 = rhi - rlo
483 len2 = rhi - rlo
483 count = min(len1, len2)
484 count = min(len1, len2)
484 for i in xrange(count):
485 for i in xrange(count):
485 yield compline(type=type,
486 yield compline(type=type,
486 leftlineno=llo + i + 1,
487 leftlineno=llo + i + 1,
487 leftline=leftlines[llo + i],
488 leftline=leftlines[llo + i],
488 rightlineno=rlo + i + 1,
489 rightlineno=rlo + i + 1,
489 rightline=rightlines[rlo + i])
490 rightline=rightlines[rlo + i])
490 if len1 > len2:
491 if len1 > len2:
491 for i in xrange(llo + count, lhi):
492 for i in xrange(llo + count, lhi):
492 yield compline(type=type,
493 yield compline(type=type,
493 leftlineno=i + 1,
494 leftlineno=i + 1,
494 leftline=leftlines[i],
495 leftline=leftlines[i],
495 rightlineno=None,
496 rightlineno=None,
496 rightline=None)
497 rightline=None)
497 elif len2 > len1:
498 elif len2 > len1:
498 for i in xrange(rlo + count, rhi):
499 for i in xrange(rlo + count, rhi):
499 yield compline(type=type,
500 yield compline(type=type,
500 leftlineno=None,
501 leftlineno=None,
501 leftline=None,
502 leftline=None,
502 rightlineno=i + 1,
503 rightlineno=i + 1,
503 rightline=rightlines[i])
504 rightline=rightlines[i])
504
505
505 s = difflib.SequenceMatcher(None, leftlines, rightlines)
506 s = difflib.SequenceMatcher(None, leftlines, rightlines)
506 if context < 0:
507 if context < 0:
507 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
508 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
508 else:
509 else:
509 for oc in s.get_grouped_opcodes(n=context):
510 for oc in s.get_grouped_opcodes(n=context):
510 yield tmpl('comparisonblock', lines=getblock(oc))
511 yield tmpl('comparisonblock', lines=getblock(oc))
511
512
512 def diffstatgen(ctx, basectx):
513 def diffstatgen(ctx, basectx):
513 '''Generator function that provides the diffstat data.'''
514 '''Generator function that provides the diffstat data.'''
514
515
515 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
516 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
516 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
517 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
517 while True:
518 while True:
518 yield stats, maxname, maxtotal, addtotal, removetotal, binary
519 yield stats, maxname, maxtotal, addtotal, removetotal, binary
519
520
520 def diffsummary(statgen):
521 def diffsummary(statgen):
521 '''Return a short summary of the diff.'''
522 '''Return a short summary of the diff.'''
522
523
523 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
524 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
524 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
525 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
525 len(stats), addtotal, removetotal)
526 len(stats), addtotal, removetotal)
526
527
527 def diffstat(tmpl, ctx, statgen, parity):
528 def diffstat(tmpl, ctx, statgen, parity):
528 '''Return a diffstat template for each file in the diff.'''
529 '''Return a diffstat template for each file in the diff.'''
529
530
530 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
531 stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
531 files = ctx.files()
532 files = ctx.files()
532
533
533 def pct(i):
534 def pct(i):
534 if maxtotal == 0:
535 if maxtotal == 0:
535 return 0
536 return 0
536 return (float(i) / maxtotal) * 100
537 return (float(i) / maxtotal) * 100
537
538
538 fileno = 0
539 fileno = 0
539 for filename, adds, removes, isbinary in stats:
540 for filename, adds, removes, isbinary in stats:
540 template = filename in files and 'diffstatlink' or 'diffstatnolink'
541 template = filename in files and 'diffstatlink' or 'diffstatnolink'
541 total = adds + removes
542 total = adds + removes
542 fileno += 1
543 fileno += 1
543 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
544 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
544 total=total, addpct=pct(adds), removepct=pct(removes),
545 total=total, addpct=pct(adds), removepct=pct(removes),
545 parity=parity.next())
546 parity=parity.next())
546
547
547 class sessionvars(object):
548 class sessionvars(object):
548 def __init__(self, vars, start='?'):
549 def __init__(self, vars, start='?'):
549 self.start = start
550 self.start = start
550 self.vars = vars
551 self.vars = vars
551 def __getitem__(self, key):
552 def __getitem__(self, key):
552 return self.vars[key]
553 return self.vars[key]
553 def __setitem__(self, key, value):
554 def __setitem__(self, key, value):
554 self.vars[key] = value
555 self.vars[key] = value
555 def __copy__(self):
556 def __copy__(self):
556 return sessionvars(copy.copy(self.vars), self.start)
557 return sessionvars(copy.copy(self.vars), self.start)
557 def __iter__(self):
558 def __iter__(self):
558 separator = self.start
559 separator = self.start
559 for key, value in sorted(self.vars.iteritems()):
560 for key, value in sorted(self.vars.iteritems()):
560 yield {'name': key, 'value': str(value), 'separator': separator}
561 yield {'name': key, 'value': str(value), 'separator': separator}
561 separator = '&'
562 separator = '&'
562
563
563 class wsgiui(uimod.ui):
564 class wsgiui(uimod.ui):
564 # default termwidth breaks under mod_wsgi
565 # default termwidth breaks under mod_wsgi
565 def termwidth(self):
566 def termwidth(self):
566 return 80
567 return 80
567
568
568 def getwebsubs(repo):
569 def getwebsubs(repo):
569 websubtable = []
570 websubtable = []
570 websubdefs = repo.ui.configitems('websub')
571 websubdefs = repo.ui.configitems('websub')
571 # we must maintain interhg backwards compatibility
572 # we must maintain interhg backwards compatibility
572 websubdefs += repo.ui.configitems('interhg')
573 websubdefs += repo.ui.configitems('interhg')
573 for key, pattern in websubdefs:
574 for key, pattern in websubdefs:
574 # grab the delimiter from the character after the "s"
575 # grab the delimiter from the character after the "s"
575 unesc = pattern[1]
576 unesc = pattern[1]
576 delim = re.escape(unesc)
577 delim = re.escape(unesc)
577
578
578 # identify portions of the pattern, taking care to avoid escaped
579 # identify portions of the pattern, taking care to avoid escaped
579 # delimiters. the replace format and flags are optional, but
580 # delimiters. the replace format and flags are optional, but
580 # delimiters are required.
581 # delimiters are required.
581 match = re.match(
582 match = re.match(
582 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
583 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
583 % (delim, delim, delim), pattern)
584 % (delim, delim, delim), pattern)
584 if not match:
585 if not match:
585 repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
586 repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
586 % (key, pattern))
587 % (key, pattern))
587 continue
588 continue
588
589
589 # we need to unescape the delimiter for regexp and format
590 # we need to unescape the delimiter for regexp and format
590 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
591 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
591 regexp = delim_re.sub(unesc, match.group(1))
592 regexp = delim_re.sub(unesc, match.group(1))
592 format = delim_re.sub(unesc, match.group(2))
593 format = delim_re.sub(unesc, match.group(2))
593
594
594 # the pattern allows for 6 regexp flags, so set them if necessary
595 # the pattern allows for 6 regexp flags, so set them if necessary
595 flagin = match.group(3)
596 flagin = match.group(3)
596 flags = 0
597 flags = 0
597 if flagin:
598 if flagin:
598 for flag in flagin.upper():
599 for flag in flagin.upper():
599 flags |= re.__dict__[flag]
600 flags |= re.__dict__[flag]
600
601
601 try:
602 try:
602 regexp = re.compile(regexp, flags)
603 regexp = re.compile(regexp, flags)
603 websubtable.append((regexp, format))
604 websubtable.append((regexp, format))
604 except re.error:
605 except re.error:
605 repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
606 repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
606 % (key, regexp))
607 % (key, regexp))
607 return websubtable
608 return websubtable
@@ -1,183 +1,184 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 # changelog and shortlog are the same web API but with different
4 # changelog and shortlog are the same web API but with different
5 # number of entries.
5 # number of entries.
6 changelog = changelist.tmpl
6 changelog = changelist.tmpl
7 shortlog = changelist.tmpl
7 shortlog = changelist.tmpl
8 changelistentry = '\{
8 changelistentry = '\{
9 "node": {node|json},
9 "node": {node|json},
10 "date": {date|json},
10 "date": {date|json},
11 "desc": {desc|utf8|json},
11 "desc": {desc|utf8|json},
12 "bookmarks": [{join(bookmarks%changelistentryname, ", ")}],
12 "bookmarks": [{join(bookmarks%changelistentryname, ", ")}],
13 "tags": [{join(tags%changelistentryname, ", ")}],
13 "tags": [{join(tags%changelistentryname, ", ")}],
14 "user": {author|utf8|json}
14 "user": {author|utf8|json},
15 "parents": [{join(allparents%changesetparent, ", ")}]
15 }'
16 }'
16 changelistentryname = '{name|utf8|json}'
17 changelistentryname = '{name|utf8|json}'
17 changeset = '\{
18 changeset = '\{
18 "node": {node|json},
19 "node": {node|json},
19 "date": {date|json},
20 "date": {date|json},
20 "desc": {desc|utf8|json},
21 "desc": {desc|utf8|json},
21 "branch": {if(branch, branch%changesetbranch, "default"|json)},
22 "branch": {if(branch, branch%changesetbranch, "default"|json)},
22 "bookmarks": [{join(changesetbookmark, ", ")}],
23 "bookmarks": [{join(changesetbookmark, ", ")}],
23 "tags": [{join(changesettag, ", ")}],
24 "tags": [{join(changesettag, ", ")}],
24 "user": {author|utf8|json},
25 "user": {author|utf8|json},
25 "parents": [{join(parent%changesetparent, ", ")}],
26 "parents": [{join(parent%changesetparent, ", ")}],
26 "phase": {phase|json}
27 "phase": {phase|json}
27 }'
28 }'
28 changesetbranch = '{name|utf8|json}'
29 changesetbranch = '{name|utf8|json}'
29 changesetbookmark = '{bookmark|utf8|json}'
30 changesetbookmark = '{bookmark|utf8|json}'
30 changesettag = '{tag|utf8|json}'
31 changesettag = '{tag|utf8|json}'
31 changesetparent = '{node|json}'
32 changesetparent = '{node|json}'
32 manifest = '\{
33 manifest = '\{
33 "node": {node|json},
34 "node": {node|json},
34 "abspath": {path|json},
35 "abspath": {path|json},
35 "directories": [{join(dentries%direntry, ", ")}],
36 "directories": [{join(dentries%direntry, ", ")}],
36 "files": [{join(fentries%fileentry, ", ")}],
37 "files": [{join(fentries%fileentry, ", ")}],
37 "bookmarks": [{join(bookmarks%name, ", ")}],
38 "bookmarks": [{join(bookmarks%name, ", ")}],
38 "tags": [{join(tags%name, ", ")}]
39 "tags": [{join(tags%name, ", ")}]
39 }'
40 }'
40 name = '{name|utf8|json}'
41 name = '{name|utf8|json}'
41 direntry = '\{
42 direntry = '\{
42 "abspath": {path|json},
43 "abspath": {path|json},
43 "basename": {basename|json},
44 "basename": {basename|json},
44 "emptydirs": {emptydirs|json}
45 "emptydirs": {emptydirs|json}
45 }'
46 }'
46 fileentry = '\{
47 fileentry = '\{
47 "abspath": {file|json},
48 "abspath": {file|json},
48 "basename": {basename|json},
49 "basename": {basename|json},
49 "date": {date|json},
50 "date": {date|json},
50 "size": {size|json},
51 "size": {size|json},
51 "flags": {permissions|json}
52 "flags": {permissions|json}
52 }'
53 }'
53 tags = '\{
54 tags = '\{
54 "node": {node|json},
55 "node": {node|json},
55 "tags": [{join(entriesnotip%tagentry, ", ")}]
56 "tags": [{join(entriesnotip%tagentry, ", ")}]
56 }'
57 }'
57 tagentry = '\{
58 tagentry = '\{
58 "tag": {tag|utf8|json},
59 "tag": {tag|utf8|json},
59 "node": {node|json},
60 "node": {node|json},
60 "date": {date|json}
61 "date": {date|json}
61 }'
62 }'
62 bookmarks = '\{
63 bookmarks = '\{
63 "node": {node|json},
64 "node": {node|json},
64 "bookmarks": [{join(entries%bookmarkentry, ", ")}]
65 "bookmarks": [{join(entries%bookmarkentry, ", ")}]
65 }'
66 }'
66 bookmarkentry = '\{
67 bookmarkentry = '\{
67 "bookmark": {bookmark|utf8|json},
68 "bookmark": {bookmark|utf8|json},
68 "node": {node|json},
69 "node": {node|json},
69 "date": {date|json}
70 "date": {date|json}
70 }'
71 }'
71 branches = '\{
72 branches = '\{
72 "branches": [{join(entries%branchentry, ", ")}]
73 "branches": [{join(entries%branchentry, ", ")}]
73 }'
74 }'
74 branchentry = '\{
75 branchentry = '\{
75 "branch": {branch|utf8|json},
76 "branch": {branch|utf8|json},
76 "node": {node|json},
77 "node": {node|json},
77 "date": {date|json},
78 "date": {date|json},
78 "status": {status|json}
79 "status": {status|json}
79 }'
80 }'
80 summary = '"not yet implemented"'
81 summary = '"not yet implemented"'
81 filediff = '\{
82 filediff = '\{
82 "path": {file|json},
83 "path": {file|json},
83 "node": {node|json},
84 "node": {node|json},
84 "date": {date|json},
85 "date": {date|json},
85 "desc": {desc|utf8|json},
86 "desc": {desc|utf8|json},
86 "author": {author|utf8|json},
87 "author": {author|utf8|json},
87 "parents": [{join(parent%changesetparent, ", ")}],
88 "parents": [{join(parent%changesetparent, ", ")}],
88 "children": [{join(child%changesetparent, ", ")}],
89 "children": [{join(child%changesetparent, ", ")}],
89 "diff": [{join(diff%diffblock, ", ")}]
90 "diff": [{join(diff%diffblock, ", ")}]
90 }'
91 }'
91 diffblock = '\{
92 diffblock = '\{
92 "blockno": {blockno|json},
93 "blockno": {blockno|json},
93 "lines": [{join(lines, ", ")}]
94 "lines": [{join(lines, ", ")}]
94 }'
95 }'
95 difflineplus = '\{
96 difflineplus = '\{
96 "t": "+",
97 "t": "+",
97 "n": {lineno|json},
98 "n": {lineno|json},
98 "l": {line|json}
99 "l": {line|json}
99 }'
100 }'
100 difflineminus = '\{
101 difflineminus = '\{
101 "t": "-",
102 "t": "-",
102 "n": {lineno|json},
103 "n": {lineno|json},
103 "l": {line|json}
104 "l": {line|json}
104 }'
105 }'
105 difflineat = '\{
106 difflineat = '\{
106 "t": "@",
107 "t": "@",
107 "n": {lineno|json},
108 "n": {lineno|json},
108 "l": {line|json}
109 "l": {line|json}
109 }'
110 }'
110 diffline = '\{
111 diffline = '\{
111 "t": "",
112 "t": "",
112 "n": {lineno|json},
113 "n": {lineno|json},
113 "l": {line|json}
114 "l": {line|json}
114 }'
115 }'
115 filecomparison = '\{
116 filecomparison = '\{
116 "path": {file|json},
117 "path": {file|json},
117 "node": {node|json},
118 "node": {node|json},
118 "date": {date|json},
119 "date": {date|json},
119 "desc": {desc|utf8|json},
120 "desc": {desc|utf8|json},
120 "author": {author|utf8|json},
121 "author": {author|utf8|json},
121 "parents": [{join(parent%changesetparent, ", ")}],
122 "parents": [{join(parent%changesetparent, ", ")}],
122 "children": [{join(child%changesetparent, ", ")}],
123 "children": [{join(child%changesetparent, ", ")}],
123 "leftnode": {leftnode|json},
124 "leftnode": {leftnode|json},
124 "rightnode": {rightnode|json},
125 "rightnode": {rightnode|json},
125 "comparison": [{join(comparison, ", ")}]
126 "comparison": [{join(comparison, ", ")}]
126 }'
127 }'
127 comparisonblock = '\{
128 comparisonblock = '\{
128 "lines": [{join(lines, ", ")}]
129 "lines": [{join(lines, ", ")}]
129 }'
130 }'
130 comparisonline = '\{
131 comparisonline = '\{
131 "t": {type|json},
132 "t": {type|json},
132 "ln": {leftlineno|json},
133 "ln": {leftlineno|json},
133 "ll": {leftline|json},
134 "ll": {leftline|json},
134 "rn": {rightlineno|json},
135 "rn": {rightlineno|json},
135 "rl": {rightline|json}
136 "rl": {rightline|json}
136 }'
137 }'
137 fileannotate = '\{
138 fileannotate = '\{
138 "abspath": {file|json},
139 "abspath": {file|json},
139 "node": {node|json},
140 "node": {node|json},
140 "author": {author|utf8|json},
141 "author": {author|utf8|json},
141 "date": {date|json},
142 "date": {date|json},
142 "desc": {desc|utf8|json},
143 "desc": {desc|utf8|json},
143 "parents": [{join(parent%changesetparent, ", ")}],
144 "parents": [{join(parent%changesetparent, ", ")}],
144 "children": [{join(child%changesetparent, ", ")}],
145 "children": [{join(child%changesetparent, ", ")}],
145 "permissions": {permissions|json},
146 "permissions": {permissions|json},
146 "annotate": [{join(annotate%fileannotation, ", ")}]
147 "annotate": [{join(annotate%fileannotation, ", ")}]
147 }'
148 }'
148 fileannotation = '\{
149 fileannotation = '\{
149 "node": {node|json},
150 "node": {node|json},
150 "author": {author|utf8|json},
151 "author": {author|utf8|json},
151 "desc": {desc|utf8|json},
152 "desc": {desc|utf8|json},
152 "abspath": {file|json},
153 "abspath": {file|json},
153 "targetline": {targetline|json},
154 "targetline": {targetline|json},
154 "line": {line|json},
155 "line": {line|json},
155 "lineno": {lineno|json},
156 "lineno": {lineno|json},
156 "revdate": {revdate|json}
157 "revdate": {revdate|json}
157 }'
158 }'
158 filelog = '"not yet implemented"'
159 filelog = '"not yet implemented"'
159 graph = '"not yet implemented"'
160 graph = '"not yet implemented"'
160 helptopics = '\{
161 helptopics = '\{
161 "topics": [{join(topics%helptopicentry, ", ")}],
162 "topics": [{join(topics%helptopicentry, ", ")}],
162 "earlycommands": [{join(earlycommands%helptopicentry, ", ")}],
163 "earlycommands": [{join(earlycommands%helptopicentry, ", ")}],
163 "othercommands": [{join(othercommands%helptopicentry, ", ")}]
164 "othercommands": [{join(othercommands%helptopicentry, ", ")}]
164 }'
165 }'
165 helptopicentry = '\{
166 helptopicentry = '\{
166 "topic": {topic|utf8|json},
167 "topic": {topic|utf8|json},
167 "summary": {summary|utf8|json}
168 "summary": {summary|utf8|json}
168 }'
169 }'
169 help = '\{
170 help = '\{
170 "topic": {topic|utf8|json},
171 "topic": {topic|utf8|json},
171 "rawdoc": {doc|utf8|json}
172 "rawdoc": {doc|utf8|json}
172 }'
173 }'
173 filenodelink = ''
174 filenodelink = ''
174 filenolink = ''
175 filenolink = ''
175 index = '\{
176 index = '\{
176 "entries": [{join(entries%indexentry, ", ")}]
177 "entries": [{join(entries%indexentry, ", ")}]
177 }'
178 }'
178 indexentry = '\{
179 indexentry = '\{
179 "name": {name|utf8|json},
180 "name": {name|utf8|json},
180 "description": {description|utf8|json},
181 "description": {description|utf8|json},
181 "contact": {contact|utf8|json},
182 "contact": {contact|utf8|json},
182 "lastchange": {lastchange|json}
183 "lastchange": {lastchange|json}
183 }'
184 }'
@@ -1,1118 +1,1180 b''
1 #require serve
1 #require serve
2
2
3 $ request() {
3 $ request() {
4 > get-with-headers.py --json localhost:$HGPORT "$1"
4 > get-with-headers.py --json localhost:$HGPORT "$1"
5 > }
5 > }
6
6
7 $ hg init test
7 $ hg init test
8 $ cd test
8 $ cd test
9 $ mkdir da
9 $ mkdir da
10 $ echo foo > da/foo
10 $ echo foo > da/foo
11 $ echo foo > foo
11 $ echo foo > foo
12 $ hg -q ci -A -m initial
12 $ hg -q ci -A -m initial
13 $ echo bar > foo
13 $ echo bar > foo
14 $ hg ci -m 'modify foo'
14 $ hg ci -m 'modify foo'
15 $ echo bar > da/foo
15 $ echo bar > da/foo
16 $ hg ci -m 'modify da/foo'
16 $ hg ci -m 'modify da/foo'
17 $ hg bookmark bookmark1
17 $ hg bookmark bookmark1
18 $ hg up default
18 $ hg up default
19 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 (leaving bookmark bookmark1)
20 (leaving bookmark bookmark1)
21 $ hg mv foo foo-new
21 $ hg mv foo foo-new
22 $ hg commit -m 'move foo'
22 $ hg commit -m 'move foo'
23 $ hg tag -m 'create tag' tag1
23 $ hg tag -m 'create tag' tag1
24 $ hg phase --public -r .
24 $ hg phase --public -r .
25 $ echo baz > da/foo
25 $ echo baz > da/foo
26 $ hg commit -m 'another commit to da/foo'
26 $ hg commit -m 'another commit to da/foo'
27 $ hg tag -m 'create tag2' tag2
27 $ hg tag -m 'create tag2' tag2
28 $ hg bookmark bookmark2
28 $ hg bookmark bookmark2
29 $ hg -q up -r 0
29 $ hg -q up -r 0
30 $ hg -q branch test-branch
30 $ hg -q branch test-branch
31 $ echo branch > foo
31 $ echo branch > foo
32 $ hg commit -m 'create test branch'
32 $ hg commit -m 'create test branch'
33 $ echo branch_commit_2 > foo
33 $ echo branch_commit_2 > foo
34 $ hg commit -m 'another commit in test-branch'
34 $ hg commit -m 'another commit in test-branch'
35 $ hg -q up default
35 $ hg -q up default
36 $ hg merge --tool :local test-branch
36 $ hg merge --tool :local test-branch
37 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
37 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
38 (branch merge, don't forget to commit)
38 (branch merge, don't forget to commit)
39 $ hg commit -m 'merge test-branch into default'
39 $ hg commit -m 'merge test-branch into default'
40
40
41 $ hg log -G
41 $ hg log -G
42 @ changeset: 9:cc725e08502a
42 @ changeset: 9:cc725e08502a
43 |\ tag: tip
43 |\ tag: tip
44 | | parent: 6:ceed296fe500
44 | | parent: 6:ceed296fe500
45 | | parent: 8:ed66c30e87eb
45 | | parent: 8:ed66c30e87eb
46 | | user: test
46 | | user: test
47 | | date: Thu Jan 01 00:00:00 1970 +0000
47 | | date: Thu Jan 01 00:00:00 1970 +0000
48 | | summary: merge test-branch into default
48 | | summary: merge test-branch into default
49 | |
49 | |
50 | o changeset: 8:ed66c30e87eb
50 | o changeset: 8:ed66c30e87eb
51 | | branch: test-branch
51 | | branch: test-branch
52 | | user: test
52 | | user: test
53 | | date: Thu Jan 01 00:00:00 1970 +0000
53 | | date: Thu Jan 01 00:00:00 1970 +0000
54 | | summary: another commit in test-branch
54 | | summary: another commit in test-branch
55 | |
55 | |
56 | o changeset: 7:6ab967a8ab34
56 | o changeset: 7:6ab967a8ab34
57 | | branch: test-branch
57 | | branch: test-branch
58 | | parent: 0:06e557f3edf6
58 | | parent: 0:06e557f3edf6
59 | | user: test
59 | | user: test
60 | | date: Thu Jan 01 00:00:00 1970 +0000
60 | | date: Thu Jan 01 00:00:00 1970 +0000
61 | | summary: create test branch
61 | | summary: create test branch
62 | |
62 | |
63 o | changeset: 6:ceed296fe500
63 o | changeset: 6:ceed296fe500
64 | | bookmark: bookmark2
64 | | bookmark: bookmark2
65 | | user: test
65 | | user: test
66 | | date: Thu Jan 01 00:00:00 1970 +0000
66 | | date: Thu Jan 01 00:00:00 1970 +0000
67 | | summary: create tag2
67 | | summary: create tag2
68 | |
68 | |
69 o | changeset: 5:f2890a05fea4
69 o | changeset: 5:f2890a05fea4
70 | | tag: tag2
70 | | tag: tag2
71 | | user: test
71 | | user: test
72 | | date: Thu Jan 01 00:00:00 1970 +0000
72 | | date: Thu Jan 01 00:00:00 1970 +0000
73 | | summary: another commit to da/foo
73 | | summary: another commit to da/foo
74 | |
74 | |
75 o | changeset: 4:93a8ce14f891
75 o | changeset: 4:93a8ce14f891
76 | | user: test
76 | | user: test
77 | | date: Thu Jan 01 00:00:00 1970 +0000
77 | | date: Thu Jan 01 00:00:00 1970 +0000
78 | | summary: create tag
78 | | summary: create tag
79 | |
79 | |
80 o | changeset: 3:78896eb0e102
80 o | changeset: 3:78896eb0e102
81 | | tag: tag1
81 | | tag: tag1
82 | | user: test
82 | | user: test
83 | | date: Thu Jan 01 00:00:00 1970 +0000
83 | | date: Thu Jan 01 00:00:00 1970 +0000
84 | | summary: move foo
84 | | summary: move foo
85 | |
85 | |
86 o | changeset: 2:8d7c456572ac
86 o | changeset: 2:8d7c456572ac
87 | | bookmark: bookmark1
87 | | bookmark: bookmark1
88 | | user: test
88 | | user: test
89 | | date: Thu Jan 01 00:00:00 1970 +0000
89 | | date: Thu Jan 01 00:00:00 1970 +0000
90 | | summary: modify da/foo
90 | | summary: modify da/foo
91 | |
91 | |
92 o | changeset: 1:f8bbb9024b10
92 o | changeset: 1:f8bbb9024b10
93 |/ user: test
93 |/ user: test
94 | date: Thu Jan 01 00:00:00 1970 +0000
94 | date: Thu Jan 01 00:00:00 1970 +0000
95 | summary: modify foo
95 | summary: modify foo
96 |
96 |
97 o changeset: 0:06e557f3edf6
97 o changeset: 0:06e557f3edf6
98 user: test
98 user: test
99 date: Thu Jan 01 00:00:00 1970 +0000
99 date: Thu Jan 01 00:00:00 1970 +0000
100 summary: initial
100 summary: initial
101
101
102
102
103 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E error.log
103 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E error.log
104 $ cat hg.pid >> $DAEMON_PIDS
104 $ cat hg.pid >> $DAEMON_PIDS
105
105
106 (Try to keep these in roughly the order they are defined in webcommands.py)
106 (Try to keep these in roughly the order they are defined in webcommands.py)
107
107
108 (log is handled by filelog/ and changelog/ - ignore it)
108 (log is handled by filelog/ and changelog/ - ignore it)
109
109
110 (rawfile/ doesn't use templating - nothing to test)
110 (rawfile/ doesn't use templating - nothing to test)
111
111
112 file/{revision}/{path} shows file revision
112 file/{revision}/{path} shows file revision
113
113
114 $ request json-file/06e557f3edf6/foo
114 $ request json-file/06e557f3edf6/foo
115 200 Script output follows
115 200 Script output follows
116
116
117 "not yet implemented"
117 "not yet implemented"
118
118
119 file/{revision} shows root directory info
119 file/{revision} shows root directory info
120
120
121 $ request json-file/cc725e08502a
121 $ request json-file/cc725e08502a
122 200 Script output follows
122 200 Script output follows
123
123
124 {
124 {
125 "abspath": "/",
125 "abspath": "/",
126 "bookmarks": [],
126 "bookmarks": [],
127 "directories": [
127 "directories": [
128 {
128 {
129 "abspath": "/da",
129 "abspath": "/da",
130 "basename": "da",
130 "basename": "da",
131 "emptydirs": ""
131 "emptydirs": ""
132 }
132 }
133 ],
133 ],
134 "files": [
134 "files": [
135 {
135 {
136 "abspath": ".hgtags",
136 "abspath": ".hgtags",
137 "basename": ".hgtags",
137 "basename": ".hgtags",
138 "date": [
138 "date": [
139 0.0,
139 0.0,
140 0
140 0
141 ],
141 ],
142 "flags": "",
142 "flags": "",
143 "size": 92
143 "size": 92
144 },
144 },
145 {
145 {
146 "abspath": "foo-new",
146 "abspath": "foo-new",
147 "basename": "foo-new",
147 "basename": "foo-new",
148 "date": [
148 "date": [
149 0.0,
149 0.0,
150 0
150 0
151 ],
151 ],
152 "flags": "",
152 "flags": "",
153 "size": 4
153 "size": 4
154 }
154 }
155 ],
155 ],
156 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
156 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
157 "tags": [
157 "tags": [
158 "tip"
158 "tip"
159 ]
159 ]
160 }
160 }
161
161
162 changelog/ shows information about several changesets
162 changelog/ shows information about several changesets
163
163
164 $ request json-changelog
164 $ request json-changelog
165 200 Script output follows
165 200 Script output follows
166
166
167 {
167 {
168 "changeset_count": 10,
168 "changeset_count": 10,
169 "changesets": [
169 "changesets": [
170 {
170 {
171 "bookmarks": [],
171 "bookmarks": [],
172 "date": [
172 "date": [
173 0.0,
173 0.0,
174 0
174 0
175 ],
175 ],
176 "desc": "merge test-branch into default",
176 "desc": "merge test-branch into default",
177 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
177 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
178 "parents": [
179 "ceed296fe500c3fac9541e31dad860cb49c89e45",
180 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
181 ],
178 "tags": [
182 "tags": [
179 "tip"
183 "tip"
180 ],
184 ],
181 "user": "test"
185 "user": "test"
182 },
186 },
183 {
187 {
184 "bookmarks": [],
188 "bookmarks": [],
185 "date": [
189 "date": [
186 0.0,
190 0.0,
187 0
191 0
188 ],
192 ],
189 "desc": "another commit in test-branch",
193 "desc": "another commit in test-branch",
190 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
194 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
195 "parents": [
196 "6ab967a8ab3489227a83f80e920faa039a71819f"
197 ],
191 "tags": [],
198 "tags": [],
192 "user": "test"
199 "user": "test"
193 },
200 },
194 {
201 {
195 "bookmarks": [],
202 "bookmarks": [],
196 "date": [
203 "date": [
197 0.0,
204 0.0,
198 0
205 0
199 ],
206 ],
200 "desc": "create test branch",
207 "desc": "create test branch",
201 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
208 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
209 "parents": [
210 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
211 ],
202 "tags": [],
212 "tags": [],
203 "user": "test"
213 "user": "test"
204 },
214 },
205 {
215 {
206 "bookmarks": [
216 "bookmarks": [
207 "bookmark2"
217 "bookmark2"
208 ],
218 ],
209 "date": [
219 "date": [
210 0.0,
220 0.0,
211 0
221 0
212 ],
222 ],
213 "desc": "create tag2",
223 "desc": "create tag2",
214 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45",
224 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45",
225 "parents": [
226 "f2890a05fea49bfaf9fb27ed5490894eba32da78"
227 ],
215 "tags": [],
228 "tags": [],
216 "user": "test"
229 "user": "test"
217 },
230 },
218 {
231 {
219 "bookmarks": [],
232 "bookmarks": [],
220 "date": [
233 "date": [
221 0.0,
234 0.0,
222 0
235 0
223 ],
236 ],
224 "desc": "another commit to da/foo",
237 "desc": "another commit to da/foo",
225 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
238 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
239 "parents": [
240 "93a8ce14f89156426b7fa981af8042da53f03aa0"
241 ],
226 "tags": [
242 "tags": [
227 "tag2"
243 "tag2"
228 ],
244 ],
229 "user": "test"
245 "user": "test"
230 },
246 },
231 {
247 {
232 "bookmarks": [],
248 "bookmarks": [],
233 "date": [
249 "date": [
234 0.0,
250 0.0,
235 0
251 0
236 ],
252 ],
237 "desc": "create tag",
253 "desc": "create tag",
238 "node": "93a8ce14f89156426b7fa981af8042da53f03aa0",
254 "node": "93a8ce14f89156426b7fa981af8042da53f03aa0",
255 "parents": [
256 "78896eb0e102174ce9278438a95e12543e4367a7"
257 ],
239 "tags": [],
258 "tags": [],
240 "user": "test"
259 "user": "test"
241 },
260 },
242 {
261 {
243 "bookmarks": [],
262 "bookmarks": [],
244 "date": [
263 "date": [
245 0.0,
264 0.0,
246 0
265 0
247 ],
266 ],
248 "desc": "move foo",
267 "desc": "move foo",
249 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
268 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
269 "parents": [
270 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
271 ],
250 "tags": [
272 "tags": [
251 "tag1"
273 "tag1"
252 ],
274 ],
253 "user": "test"
275 "user": "test"
254 },
276 },
255 {
277 {
256 "bookmarks": [
278 "bookmarks": [
257 "bookmark1"
279 "bookmark1"
258 ],
280 ],
259 "date": [
281 "date": [
260 0.0,
282 0.0,
261 0
283 0
262 ],
284 ],
263 "desc": "modify da/foo",
285 "desc": "modify da/foo",
264 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
286 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
287 "parents": [
288 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
289 ],
265 "tags": [],
290 "tags": [],
266 "user": "test"
291 "user": "test"
267 },
292 },
268 {
293 {
269 "bookmarks": [],
294 "bookmarks": [],
270 "date": [
295 "date": [
271 0.0,
296 0.0,
272 0
297 0
273 ],
298 ],
274 "desc": "modify foo",
299 "desc": "modify foo",
275 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
300 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
301 "parents": [
302 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
303 ],
276 "tags": [],
304 "tags": [],
277 "user": "test"
305 "user": "test"
278 },
306 },
279 {
307 {
280 "bookmarks": [],
308 "bookmarks": [],
281 "date": [
309 "date": [
282 0.0,
310 0.0,
283 0
311 0
284 ],
312 ],
285 "desc": "initial",
313 "desc": "initial",
286 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
314 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
315 "parents": [],
287 "tags": [],
316 "tags": [],
288 "user": "test"
317 "user": "test"
289 }
318 }
290 ],
319 ],
291 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
320 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
292 }
321 }
293
322
294 changelog/{revision} shows information starting at a specific changeset
323 changelog/{revision} shows information starting at a specific changeset
295
324
296 $ request json-changelog/f8bbb9024b10
325 $ request json-changelog/f8bbb9024b10
297 200 Script output follows
326 200 Script output follows
298
327
299 {
328 {
300 "changeset_count": 10,
329 "changeset_count": 10,
301 "changesets": [
330 "changesets": [
302 {
331 {
303 "bookmarks": [],
332 "bookmarks": [],
304 "date": [
333 "date": [
305 0.0,
334 0.0,
306 0
335 0
307 ],
336 ],
308 "desc": "modify foo",
337 "desc": "modify foo",
309 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
338 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
339 "parents": [
340 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
341 ],
310 "tags": [],
342 "tags": [],
311 "user": "test"
343 "user": "test"
312 },
344 },
313 {
345 {
314 "bookmarks": [],
346 "bookmarks": [],
315 "date": [
347 "date": [
316 0.0,
348 0.0,
317 0
349 0
318 ],
350 ],
319 "desc": "initial",
351 "desc": "initial",
320 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
352 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
353 "parents": [],
321 "tags": [],
354 "tags": [],
322 "user": "test"
355 "user": "test"
323 }
356 }
324 ],
357 ],
325 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8"
358 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8"
326 }
359 }
327
360
328 shortlog/ shows information about a set of changesets
361 shortlog/ shows information about a set of changesets
329
362
330 $ request json-shortlog
363 $ request json-shortlog
331 200 Script output follows
364 200 Script output follows
332
365
333 {
366 {
334 "changeset_count": 10,
367 "changeset_count": 10,
335 "changesets": [
368 "changesets": [
336 {
369 {
337 "bookmarks": [],
370 "bookmarks": [],
338 "date": [
371 "date": [
339 0.0,
372 0.0,
340 0
373 0
341 ],
374 ],
342 "desc": "merge test-branch into default",
375 "desc": "merge test-branch into default",
343 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
376 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
377 "parents": [
378 "ceed296fe500c3fac9541e31dad860cb49c89e45",
379 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
380 ],
344 "tags": [
381 "tags": [
345 "tip"
382 "tip"
346 ],
383 ],
347 "user": "test"
384 "user": "test"
348 },
385 },
349 {
386 {
350 "bookmarks": [],
387 "bookmarks": [],
351 "date": [
388 "date": [
352 0.0,
389 0.0,
353 0
390 0
354 ],
391 ],
355 "desc": "another commit in test-branch",
392 "desc": "another commit in test-branch",
356 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
393 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
394 "parents": [
395 "6ab967a8ab3489227a83f80e920faa039a71819f"
396 ],
357 "tags": [],
397 "tags": [],
358 "user": "test"
398 "user": "test"
359 },
399 },
360 {
400 {
361 "bookmarks": [],
401 "bookmarks": [],
362 "date": [
402 "date": [
363 0.0,
403 0.0,
364 0
404 0
365 ],
405 ],
366 "desc": "create test branch",
406 "desc": "create test branch",
367 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
407 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
408 "parents": [
409 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
410 ],
368 "tags": [],
411 "tags": [],
369 "user": "test"
412 "user": "test"
370 },
413 },
371 {
414 {
372 "bookmarks": [
415 "bookmarks": [
373 "bookmark2"
416 "bookmark2"
374 ],
417 ],
375 "date": [
418 "date": [
376 0.0,
419 0.0,
377 0
420 0
378 ],
421 ],
379 "desc": "create tag2",
422 "desc": "create tag2",
380 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45",
423 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45",
424 "parents": [
425 "f2890a05fea49bfaf9fb27ed5490894eba32da78"
426 ],
381 "tags": [],
427 "tags": [],
382 "user": "test"
428 "user": "test"
383 },
429 },
384 {
430 {
385 "bookmarks": [],
431 "bookmarks": [],
386 "date": [
432 "date": [
387 0.0,
433 0.0,
388 0
434 0
389 ],
435 ],
390 "desc": "another commit to da/foo",
436 "desc": "another commit to da/foo",
391 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
437 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
438 "parents": [
439 "93a8ce14f89156426b7fa981af8042da53f03aa0"
440 ],
392 "tags": [
441 "tags": [
393 "tag2"
442 "tag2"
394 ],
443 ],
395 "user": "test"
444 "user": "test"
396 },
445 },
397 {
446 {
398 "bookmarks": [],
447 "bookmarks": [],
399 "date": [
448 "date": [
400 0.0,
449 0.0,
401 0
450 0
402 ],
451 ],
403 "desc": "create tag",
452 "desc": "create tag",
404 "node": "93a8ce14f89156426b7fa981af8042da53f03aa0",
453 "node": "93a8ce14f89156426b7fa981af8042da53f03aa0",
454 "parents": [
455 "78896eb0e102174ce9278438a95e12543e4367a7"
456 ],
405 "tags": [],
457 "tags": [],
406 "user": "test"
458 "user": "test"
407 },
459 },
408 {
460 {
409 "bookmarks": [],
461 "bookmarks": [],
410 "date": [
462 "date": [
411 0.0,
463 0.0,
412 0
464 0
413 ],
465 ],
414 "desc": "move foo",
466 "desc": "move foo",
415 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
467 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
468 "parents": [
469 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
470 ],
416 "tags": [
471 "tags": [
417 "tag1"
472 "tag1"
418 ],
473 ],
419 "user": "test"
474 "user": "test"
420 },
475 },
421 {
476 {
422 "bookmarks": [
477 "bookmarks": [
423 "bookmark1"
478 "bookmark1"
424 ],
479 ],
425 "date": [
480 "date": [
426 0.0,
481 0.0,
427 0
482 0
428 ],
483 ],
429 "desc": "modify da/foo",
484 "desc": "modify da/foo",
430 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
485 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
486 "parents": [
487 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
488 ],
431 "tags": [],
489 "tags": [],
432 "user": "test"
490 "user": "test"
433 },
491 },
434 {
492 {
435 "bookmarks": [],
493 "bookmarks": [],
436 "date": [
494 "date": [
437 0.0,
495 0.0,
438 0
496 0
439 ],
497 ],
440 "desc": "modify foo",
498 "desc": "modify foo",
441 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
499 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
500 "parents": [
501 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
502 ],
442 "tags": [],
503 "tags": [],
443 "user": "test"
504 "user": "test"
444 },
505 },
445 {
506 {
446 "bookmarks": [],
507 "bookmarks": [],
447 "date": [
508 "date": [
448 0.0,
509 0.0,
449 0
510 0
450 ],
511 ],
451 "desc": "initial",
512 "desc": "initial",
452 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
513 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
514 "parents": [],
453 "tags": [],
515 "tags": [],
454 "user": "test"
516 "user": "test"
455 }
517 }
456 ],
518 ],
457 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
519 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
458 }
520 }
459
521
460 changeset/ renders the tip changeset
522 changeset/ renders the tip changeset
461
523
462 $ request json-rev
524 $ request json-rev
463 200 Script output follows
525 200 Script output follows
464
526
465 {
527 {
466 "bookmarks": [],
528 "bookmarks": [],
467 "branch": "default",
529 "branch": "default",
468 "date": [
530 "date": [
469 0.0,
531 0.0,
470 0
532 0
471 ],
533 ],
472 "desc": "merge test-branch into default",
534 "desc": "merge test-branch into default",
473 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
535 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
474 "parents": [
536 "parents": [
475 "ceed296fe500c3fac9541e31dad860cb49c89e45",
537 "ceed296fe500c3fac9541e31dad860cb49c89e45",
476 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
538 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
477 ],
539 ],
478 "phase": "draft",
540 "phase": "draft",
479 "tags": [
541 "tags": [
480 "tip"
542 "tip"
481 ],
543 ],
482 "user": "test"
544 "user": "test"
483 }
545 }
484
546
485 changeset/{revision} shows tags
547 changeset/{revision} shows tags
486
548
487 $ request json-rev/78896eb0e102
549 $ request json-rev/78896eb0e102
488 200 Script output follows
550 200 Script output follows
489
551
490 {
552 {
491 "bookmarks": [],
553 "bookmarks": [],
492 "branch": "default",
554 "branch": "default",
493 "date": [
555 "date": [
494 0.0,
556 0.0,
495 0
557 0
496 ],
558 ],
497 "desc": "move foo",
559 "desc": "move foo",
498 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
560 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
499 "parents": [
561 "parents": [
500 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
562 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
501 ],
563 ],
502 "phase": "public",
564 "phase": "public",
503 "tags": [
565 "tags": [
504 "tag1"
566 "tag1"
505 ],
567 ],
506 "user": "test"
568 "user": "test"
507 }
569 }
508
570
509 changeset/{revision} shows bookmarks
571 changeset/{revision} shows bookmarks
510
572
511 $ request json-rev/8d7c456572ac
573 $ request json-rev/8d7c456572ac
512 200 Script output follows
574 200 Script output follows
513
575
514 {
576 {
515 "bookmarks": [
577 "bookmarks": [
516 "bookmark1"
578 "bookmark1"
517 ],
579 ],
518 "branch": "default",
580 "branch": "default",
519 "date": [
581 "date": [
520 0.0,
582 0.0,
521 0
583 0
522 ],
584 ],
523 "desc": "modify da/foo",
585 "desc": "modify da/foo",
524 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
586 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
525 "parents": [
587 "parents": [
526 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
588 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
527 ],
589 ],
528 "phase": "public",
590 "phase": "public",
529 "tags": [],
591 "tags": [],
530 "user": "test"
592 "user": "test"
531 }
593 }
532
594
533 changeset/{revision} shows branches
595 changeset/{revision} shows branches
534
596
535 $ request json-rev/6ab967a8ab34
597 $ request json-rev/6ab967a8ab34
536 200 Script output follows
598 200 Script output follows
537
599
538 {
600 {
539 "bookmarks": [],
601 "bookmarks": [],
540 "branch": "test-branch",
602 "branch": "test-branch",
541 "date": [
603 "date": [
542 0.0,
604 0.0,
543 0
605 0
544 ],
606 ],
545 "desc": "create test branch",
607 "desc": "create test branch",
546 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
608 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
547 "parents": [
609 "parents": [
548 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
610 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
549 ],
611 ],
550 "phase": "draft",
612 "phase": "draft",
551 "tags": [],
613 "tags": [],
552 "user": "test"
614 "user": "test"
553 }
615 }
554
616
555 manifest/{revision}/{path} shows info about a directory at a revision
617 manifest/{revision}/{path} shows info about a directory at a revision
556
618
557 $ request json-manifest/06e557f3edf6/
619 $ request json-manifest/06e557f3edf6/
558 200 Script output follows
620 200 Script output follows
559
621
560 {
622 {
561 "abspath": "/",
623 "abspath": "/",
562 "bookmarks": [],
624 "bookmarks": [],
563 "directories": [
625 "directories": [
564 {
626 {
565 "abspath": "/da",
627 "abspath": "/da",
566 "basename": "da",
628 "basename": "da",
567 "emptydirs": ""
629 "emptydirs": ""
568 }
630 }
569 ],
631 ],
570 "files": [
632 "files": [
571 {
633 {
572 "abspath": "foo",
634 "abspath": "foo",
573 "basename": "foo",
635 "basename": "foo",
574 "date": [
636 "date": [
575 0.0,
637 0.0,
576 0
638 0
577 ],
639 ],
578 "flags": "",
640 "flags": "",
579 "size": 4
641 "size": 4
580 }
642 }
581 ],
643 ],
582 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
644 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
583 "tags": []
645 "tags": []
584 }
646 }
585
647
586 tags/ shows tags info
648 tags/ shows tags info
587
649
588 $ request json-tags
650 $ request json-tags
589 200 Script output follows
651 200 Script output follows
590
652
591 {
653 {
592 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
654 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
593 "tags": [
655 "tags": [
594 {
656 {
595 "date": [
657 "date": [
596 0.0,
658 0.0,
597 0
659 0
598 ],
660 ],
599 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
661 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
600 "tag": "tag2"
662 "tag": "tag2"
601 },
663 },
602 {
664 {
603 "date": [
665 "date": [
604 0.0,
666 0.0,
605 0
667 0
606 ],
668 ],
607 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
669 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
608 "tag": "tag1"
670 "tag": "tag1"
609 }
671 }
610 ]
672 ]
611 }
673 }
612
674
613 bookmarks/ shows bookmarks info
675 bookmarks/ shows bookmarks info
614
676
615 $ request json-bookmarks
677 $ request json-bookmarks
616 200 Script output follows
678 200 Script output follows
617
679
618 {
680 {
619 "bookmarks": [
681 "bookmarks": [
620 {
682 {
621 "bookmark": "bookmark1",
683 "bookmark": "bookmark1",
622 "date": [
684 "date": [
623 0.0,
685 0.0,
624 0
686 0
625 ],
687 ],
626 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5"
688 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5"
627 },
689 },
628 {
690 {
629 "bookmark": "bookmark2",
691 "bookmark": "bookmark2",
630 "date": [
692 "date": [
631 0.0,
693 0.0,
632 0
694 0
633 ],
695 ],
634 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45"
696 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45"
635 }
697 }
636 ],
698 ],
637 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
699 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
638 }
700 }
639
701
640 branches/ shows branches info
702 branches/ shows branches info
641
703
642 $ request json-branches
704 $ request json-branches
643 200 Script output follows
705 200 Script output follows
644
706
645 {
707 {
646 "branches": [
708 "branches": [
647 {
709 {
648 "branch": "default",
710 "branch": "default",
649 "date": [
711 "date": [
650 0.0,
712 0.0,
651 0
713 0
652 ],
714 ],
653 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
715 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
654 "status": "open"
716 "status": "open"
655 },
717 },
656 {
718 {
657 "branch": "test-branch",
719 "branch": "test-branch",
658 "date": [
720 "date": [
659 0.0,
721 0.0,
660 0
722 0
661 ],
723 ],
662 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
724 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
663 "status": "inactive"
725 "status": "inactive"
664 }
726 }
665 ]
727 ]
666 }
728 }
667
729
668 summary/ shows a summary of repository state
730 summary/ shows a summary of repository state
669
731
670 $ request json-summary
732 $ request json-summary
671 200 Script output follows
733 200 Script output follows
672
734
673 "not yet implemented"
735 "not yet implemented"
674
736
675 filediff/{revision}/{path} shows changes to a file in a revision
737 filediff/{revision}/{path} shows changes to a file in a revision
676
738
677 $ request json-diff/f8bbb9024b10/foo
739 $ request json-diff/f8bbb9024b10/foo
678 200 Script output follows
740 200 Script output follows
679
741
680 {
742 {
681 "author": "test",
743 "author": "test",
682 "children": [],
744 "children": [],
683 "date": [
745 "date": [
684 0.0,
746 0.0,
685 0
747 0
686 ],
748 ],
687 "desc": "modify foo",
749 "desc": "modify foo",
688 "diff": [
750 "diff": [
689 {
751 {
690 "blockno": 1,
752 "blockno": 1,
691 "lines": [
753 "lines": [
692 {
754 {
693 "l": "--- a/foo\tThu Jan 01 00:00:00 1970 +0000\n",
755 "l": "--- a/foo\tThu Jan 01 00:00:00 1970 +0000\n",
694 "n": 1,
756 "n": 1,
695 "t": "-"
757 "t": "-"
696 },
758 },
697 {
759 {
698 "l": "+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n",
760 "l": "+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n",
699 "n": 2,
761 "n": 2,
700 "t": "+"
762 "t": "+"
701 },
763 },
702 {
764 {
703 "l": "@@ -1,1 +1,1 @@\n",
765 "l": "@@ -1,1 +1,1 @@\n",
704 "n": 3,
766 "n": 3,
705 "t": "@"
767 "t": "@"
706 },
768 },
707 {
769 {
708 "l": "-foo\n",
770 "l": "-foo\n",
709 "n": 4,
771 "n": 4,
710 "t": "-"
772 "t": "-"
711 },
773 },
712 {
774 {
713 "l": "+bar\n",
775 "l": "+bar\n",
714 "n": 5,
776 "n": 5,
715 "t": "+"
777 "t": "+"
716 }
778 }
717 ]
779 ]
718 }
780 }
719 ],
781 ],
720 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
782 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
721 "parents": [
783 "parents": [
722 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
784 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
723 ],
785 ],
724 "path": "foo"
786 "path": "foo"
725 }
787 }
726
788
727 comparison/{revision}/{path} shows information about before and after for a file
789 comparison/{revision}/{path} shows information about before and after for a file
728
790
729 $ request json-comparison/f8bbb9024b10/foo
791 $ request json-comparison/f8bbb9024b10/foo
730 200 Script output follows
792 200 Script output follows
731
793
732 {
794 {
733 "author": "test",
795 "author": "test",
734 "children": [],
796 "children": [],
735 "comparison": [
797 "comparison": [
736 {
798 {
737 "lines": [
799 "lines": [
738 {
800 {
739 "ll": "foo",
801 "ll": "foo",
740 "ln": 1,
802 "ln": 1,
741 "rl": "bar",
803 "rl": "bar",
742 "rn": 1,
804 "rn": 1,
743 "t": "replace"
805 "t": "replace"
744 }
806 }
745 ]
807 ]
746 }
808 }
747 ],
809 ],
748 "date": [
810 "date": [
749 0.0,
811 0.0,
750 0
812 0
751 ],
813 ],
752 "desc": "modify foo",
814 "desc": "modify foo",
753 "leftnode": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
815 "leftnode": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
754 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
816 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
755 "parents": [
817 "parents": [
756 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
818 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
757 ],
819 ],
758 "path": "foo",
820 "path": "foo",
759 "rightnode": "f8bbb9024b10f93cdbb8d940337398291d40dea8"
821 "rightnode": "f8bbb9024b10f93cdbb8d940337398291d40dea8"
760 }
822 }
761
823
762 annotate/{revision}/{path} shows annotations for each line
824 annotate/{revision}/{path} shows annotations for each line
763
825
764 $ request json-annotate/f8bbb9024b10/foo
826 $ request json-annotate/f8bbb9024b10/foo
765 200 Script output follows
827 200 Script output follows
766
828
767 {
829 {
768 "abspath": "foo",
830 "abspath": "foo",
769 "annotate": [
831 "annotate": [
770 {
832 {
771 "abspath": "foo",
833 "abspath": "foo",
772 "author": "test",
834 "author": "test",
773 "desc": "modify foo",
835 "desc": "modify foo",
774 "line": "bar\n",
836 "line": "bar\n",
775 "lineno": 1,
837 "lineno": 1,
776 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
838 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
777 "revdate": [
839 "revdate": [
778 0.0,
840 0.0,
779 0
841 0
780 ],
842 ],
781 "targetline": 1
843 "targetline": 1
782 }
844 }
783 ],
845 ],
784 "author": "test",
846 "author": "test",
785 "children": [],
847 "children": [],
786 "date": [
848 "date": [
787 0.0,
849 0.0,
788 0
850 0
789 ],
851 ],
790 "desc": "modify foo",
852 "desc": "modify foo",
791 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
853 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
792 "parents": [
854 "parents": [
793 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
855 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
794 ],
856 ],
795 "permissions": ""
857 "permissions": ""
796 }
858 }
797
859
798 filelog/{revision}/{path} shows history of a single file
860 filelog/{revision}/{path} shows history of a single file
799
861
800 $ request json-filelog/f8bbb9024b10/foo
862 $ request json-filelog/f8bbb9024b10/foo
801 200 Script output follows
863 200 Script output follows
802
864
803 "not yet implemented"
865 "not yet implemented"
804
866
805 (archive/ doesn't use templating, so ignore it)
867 (archive/ doesn't use templating, so ignore it)
806
868
807 (static/ doesn't use templating, so ignore it)
869 (static/ doesn't use templating, so ignore it)
808
870
809 graph/ shows information that can be used to render a graph of the DAG
871 graph/ shows information that can be used to render a graph of the DAG
810
872
811 $ request json-graph
873 $ request json-graph
812 200 Script output follows
874 200 Script output follows
813
875
814 "not yet implemented"
876 "not yet implemented"
815
877
816 help/ shows help topics
878 help/ shows help topics
817
879
818 $ request json-help
880 $ request json-help
819 200 Script output follows
881 200 Script output follows
820
882
821 {
883 {
822 "earlycommands": [
884 "earlycommands": [
823 {
885 {
824 "summary": "add the specified files on the next commit",
886 "summary": "add the specified files on the next commit",
825 "topic": "add"
887 "topic": "add"
826 },
888 },
827 {
889 {
828 "summary": "show changeset information by line for each file",
890 "summary": "show changeset information by line for each file",
829 "topic": "annotate"
891 "topic": "annotate"
830 },
892 },
831 {
893 {
832 "summary": "make a copy of an existing repository",
894 "summary": "make a copy of an existing repository",
833 "topic": "clone"
895 "topic": "clone"
834 },
896 },
835 {
897 {
836 "summary": "commit the specified files or all outstanding changes",
898 "summary": "commit the specified files or all outstanding changes",
837 "topic": "commit"
899 "topic": "commit"
838 },
900 },
839 {
901 {
840 "summary": "diff repository (or selected files)",
902 "summary": "diff repository (or selected files)",
841 "topic": "diff"
903 "topic": "diff"
842 },
904 },
843 {
905 {
844 "summary": "dump the header and diffs for one or more changesets",
906 "summary": "dump the header and diffs for one or more changesets",
845 "topic": "export"
907 "topic": "export"
846 },
908 },
847 {
909 {
848 "summary": "forget the specified files on the next commit",
910 "summary": "forget the specified files on the next commit",
849 "topic": "forget"
911 "topic": "forget"
850 },
912 },
851 {
913 {
852 "summary": "create a new repository in the given directory",
914 "summary": "create a new repository in the given directory",
853 "topic": "init"
915 "topic": "init"
854 },
916 },
855 {
917 {
856 "summary": "show revision history of entire repository or files",
918 "summary": "show revision history of entire repository or files",
857 "topic": "log"
919 "topic": "log"
858 },
920 },
859 {
921 {
860 "summary": "merge another revision into working directory",
922 "summary": "merge another revision into working directory",
861 "topic": "merge"
923 "topic": "merge"
862 },
924 },
863 {
925 {
864 "summary": "pull changes from the specified source",
926 "summary": "pull changes from the specified source",
865 "topic": "pull"
927 "topic": "pull"
866 },
928 },
867 {
929 {
868 "summary": "push changes to the specified destination",
930 "summary": "push changes to the specified destination",
869 "topic": "push"
931 "topic": "push"
870 },
932 },
871 {
933 {
872 "summary": "remove the specified files on the next commit",
934 "summary": "remove the specified files on the next commit",
873 "topic": "remove"
935 "topic": "remove"
874 },
936 },
875 {
937 {
876 "summary": "start stand-alone webserver",
938 "summary": "start stand-alone webserver",
877 "topic": "serve"
939 "topic": "serve"
878 },
940 },
879 {
941 {
880 "summary": "show changed files in the working directory",
942 "summary": "show changed files in the working directory",
881 "topic": "status"
943 "topic": "status"
882 },
944 },
883 {
945 {
884 "summary": "summarize working directory state",
946 "summary": "summarize working directory state",
885 "topic": "summary"
947 "topic": "summary"
886 },
948 },
887 {
949 {
888 "summary": "update working directory (or switch revisions)",
950 "summary": "update working directory (or switch revisions)",
889 "topic": "update"
951 "topic": "update"
890 }
952 }
891 ],
953 ],
892 "othercommands": [
954 "othercommands": [
893 {
955 {
894 "summary": "add all new files, delete all missing files",
956 "summary": "add all new files, delete all missing files",
895 "topic": "addremove"
957 "topic": "addremove"
896 },
958 },
897 {
959 {
898 "summary": "create an unversioned archive of a repository revision",
960 "summary": "create an unversioned archive of a repository revision",
899 "topic": "archive"
961 "topic": "archive"
900 },
962 },
901 {
963 {
902 "summary": "reverse effect of earlier changeset",
964 "summary": "reverse effect of earlier changeset",
903 "topic": "backout"
965 "topic": "backout"
904 },
966 },
905 {
967 {
906 "summary": "subdivision search of changesets",
968 "summary": "subdivision search of changesets",
907 "topic": "bisect"
969 "topic": "bisect"
908 },
970 },
909 {
971 {
910 "summary": "create a new bookmark or list existing bookmarks",
972 "summary": "create a new bookmark or list existing bookmarks",
911 "topic": "bookmarks"
973 "topic": "bookmarks"
912 },
974 },
913 {
975 {
914 "summary": "set or show the current branch name",
976 "summary": "set or show the current branch name",
915 "topic": "branch"
977 "topic": "branch"
916 },
978 },
917 {
979 {
918 "summary": "list repository named branches",
980 "summary": "list repository named branches",
919 "topic": "branches"
981 "topic": "branches"
920 },
982 },
921 {
983 {
922 "summary": "create a changegroup file",
984 "summary": "create a changegroup file",
923 "topic": "bundle"
985 "topic": "bundle"
924 },
986 },
925 {
987 {
926 "summary": "output the current or given revision of files",
988 "summary": "output the current or given revision of files",
927 "topic": "cat"
989 "topic": "cat"
928 },
990 },
929 {
991 {
930 "summary": "show combined config settings from all hgrc files",
992 "summary": "show combined config settings from all hgrc files",
931 "topic": "config"
993 "topic": "config"
932 },
994 },
933 {
995 {
934 "summary": "mark files as copied for the next commit",
996 "summary": "mark files as copied for the next commit",
935 "topic": "copy"
997 "topic": "copy"
936 },
998 },
937 {
999 {
938 "summary": "list tracked files",
1000 "summary": "list tracked files",
939 "topic": "files"
1001 "topic": "files"
940 },
1002 },
941 {
1003 {
942 "summary": "copy changes from other branches onto the current branch",
1004 "summary": "copy changes from other branches onto the current branch",
943 "topic": "graft"
1005 "topic": "graft"
944 },
1006 },
945 {
1007 {
946 "summary": "search for a pattern in specified files and revisions",
1008 "summary": "search for a pattern in specified files and revisions",
947 "topic": "grep"
1009 "topic": "grep"
948 },
1010 },
949 {
1011 {
950 "summary": "show branch heads",
1012 "summary": "show branch heads",
951 "topic": "heads"
1013 "topic": "heads"
952 },
1014 },
953 {
1015 {
954 "summary": "show help for a given topic or a help overview",
1016 "summary": "show help for a given topic or a help overview",
955 "topic": "help"
1017 "topic": "help"
956 },
1018 },
957 {
1019 {
958 "summary": "identify the working directory or specified revision",
1020 "summary": "identify the working directory or specified revision",
959 "topic": "identify"
1021 "topic": "identify"
960 },
1022 },
961 {
1023 {
962 "summary": "import an ordered set of patches",
1024 "summary": "import an ordered set of patches",
963 "topic": "import"
1025 "topic": "import"
964 },
1026 },
965 {
1027 {
966 "summary": "show new changesets found in source",
1028 "summary": "show new changesets found in source",
967 "topic": "incoming"
1029 "topic": "incoming"
968 },
1030 },
969 {
1031 {
970 "summary": "output the current or given revision of the project manifest",
1032 "summary": "output the current or given revision of the project manifest",
971 "topic": "manifest"
1033 "topic": "manifest"
972 },
1034 },
973 {
1035 {
974 "summary": "show changesets not found in the destination",
1036 "summary": "show changesets not found in the destination",
975 "topic": "outgoing"
1037 "topic": "outgoing"
976 },
1038 },
977 {
1039 {
978 "summary": "show aliases for remote repositories",
1040 "summary": "show aliases for remote repositories",
979 "topic": "paths"
1041 "topic": "paths"
980 },
1042 },
981 {
1043 {
982 "summary": "set or show the current phase name",
1044 "summary": "set or show the current phase name",
983 "topic": "phase"
1045 "topic": "phase"
984 },
1046 },
985 {
1047 {
986 "summary": "roll back an interrupted transaction",
1048 "summary": "roll back an interrupted transaction",
987 "topic": "recover"
1049 "topic": "recover"
988 },
1050 },
989 {
1051 {
990 "summary": "rename files; equivalent of copy + remove",
1052 "summary": "rename files; equivalent of copy + remove",
991 "topic": "rename"
1053 "topic": "rename"
992 },
1054 },
993 {
1055 {
994 "summary": "redo merges or set/view the merge status of files",
1056 "summary": "redo merges or set/view the merge status of files",
995 "topic": "resolve"
1057 "topic": "resolve"
996 },
1058 },
997 {
1059 {
998 "summary": "restore files to their checkout state",
1060 "summary": "restore files to their checkout state",
999 "topic": "revert"
1061 "topic": "revert"
1000 },
1062 },
1001 {
1063 {
1002 "summary": "print the root (top) of the current working directory",
1064 "summary": "print the root (top) of the current working directory",
1003 "topic": "root"
1065 "topic": "root"
1004 },
1066 },
1005 {
1067 {
1006 "summary": "add one or more tags for the current or given revision",
1068 "summary": "add one or more tags for the current or given revision",
1007 "topic": "tag"
1069 "topic": "tag"
1008 },
1070 },
1009 {
1071 {
1010 "summary": "list repository tags",
1072 "summary": "list repository tags",
1011 "topic": "tags"
1073 "topic": "tags"
1012 },
1074 },
1013 {
1075 {
1014 "summary": "apply one or more changegroup files",
1076 "summary": "apply one or more changegroup files",
1015 "topic": "unbundle"
1077 "topic": "unbundle"
1016 },
1078 },
1017 {
1079 {
1018 "summary": "verify the integrity of the repository",
1080 "summary": "verify the integrity of the repository",
1019 "topic": "verify"
1081 "topic": "verify"
1020 },
1082 },
1021 {
1083 {
1022 "summary": "output version and copyright information",
1084 "summary": "output version and copyright information",
1023 "topic": "version"
1085 "topic": "version"
1024 }
1086 }
1025 ],
1087 ],
1026 "topics": [
1088 "topics": [
1027 {
1089 {
1028 "summary": "Configuration Files",
1090 "summary": "Configuration Files",
1029 "topic": "config"
1091 "topic": "config"
1030 },
1092 },
1031 {
1093 {
1032 "summary": "Date Formats",
1094 "summary": "Date Formats",
1033 "topic": "dates"
1095 "topic": "dates"
1034 },
1096 },
1035 {
1097 {
1036 "summary": "Diff Formats",
1098 "summary": "Diff Formats",
1037 "topic": "diffs"
1099 "topic": "diffs"
1038 },
1100 },
1039 {
1101 {
1040 "summary": "Environment Variables",
1102 "summary": "Environment Variables",
1041 "topic": "environment"
1103 "topic": "environment"
1042 },
1104 },
1043 {
1105 {
1044 "summary": "Using Additional Features",
1106 "summary": "Using Additional Features",
1045 "topic": "extensions"
1107 "topic": "extensions"
1046 },
1108 },
1047 {
1109 {
1048 "summary": "Specifying File Sets",
1110 "summary": "Specifying File Sets",
1049 "topic": "filesets"
1111 "topic": "filesets"
1050 },
1112 },
1051 {
1113 {
1052 "summary": "Glossary",
1114 "summary": "Glossary",
1053 "topic": "glossary"
1115 "topic": "glossary"
1054 },
1116 },
1055 {
1117 {
1056 "summary": "Syntax for Mercurial Ignore Files",
1118 "summary": "Syntax for Mercurial Ignore Files",
1057 "topic": "hgignore"
1119 "topic": "hgignore"
1058 },
1120 },
1059 {
1121 {
1060 "summary": "Configuring hgweb",
1122 "summary": "Configuring hgweb",
1061 "topic": "hgweb"
1123 "topic": "hgweb"
1062 },
1124 },
1063 {
1125 {
1064 "summary": "Technical implementation topics",
1126 "summary": "Technical implementation topics",
1065 "topic": "internals"
1127 "topic": "internals"
1066 },
1128 },
1067 {
1129 {
1068 "summary": "Merge Tools",
1130 "summary": "Merge Tools",
1069 "topic": "merge-tools"
1131 "topic": "merge-tools"
1070 },
1132 },
1071 {
1133 {
1072 "summary": "Specifying Multiple Revisions",
1134 "summary": "Specifying Multiple Revisions",
1073 "topic": "multirevs"
1135 "topic": "multirevs"
1074 },
1136 },
1075 {
1137 {
1076 "summary": "File Name Patterns",
1138 "summary": "File Name Patterns",
1077 "topic": "patterns"
1139 "topic": "patterns"
1078 },
1140 },
1079 {
1141 {
1080 "summary": "Working with Phases",
1142 "summary": "Working with Phases",
1081 "topic": "phases"
1143 "topic": "phases"
1082 },
1144 },
1083 {
1145 {
1084 "summary": "Specifying Single Revisions",
1146 "summary": "Specifying Single Revisions",
1085 "topic": "revisions"
1147 "topic": "revisions"
1086 },
1148 },
1087 {
1149 {
1088 "summary": "Specifying Revision Sets",
1150 "summary": "Specifying Revision Sets",
1089 "topic": "revsets"
1151 "topic": "revsets"
1090 },
1152 },
1091 {
1153 {
1092 "summary": "Using Mercurial from scripts and automation",
1154 "summary": "Using Mercurial from scripts and automation",
1093 "topic": "scripting"
1155 "topic": "scripting"
1094 },
1156 },
1095 {
1157 {
1096 "summary": "Subrepositories",
1158 "summary": "Subrepositories",
1097 "topic": "subrepos"
1159 "topic": "subrepos"
1098 },
1160 },
1099 {
1161 {
1100 "summary": "Template Usage",
1162 "summary": "Template Usage",
1101 "topic": "templating"
1163 "topic": "templating"
1102 },
1164 },
1103 {
1165 {
1104 "summary": "URL Paths",
1166 "summary": "URL Paths",
1105 "topic": "urls"
1167 "topic": "urls"
1106 }
1168 }
1107 ]
1169 ]
1108 }
1170 }
1109
1171
1110 help/{topic} shows an individual help topic
1172 help/{topic} shows an individual help topic
1111
1173
1112 $ request json-help/phases
1174 $ request json-help/phases
1113 200 Script output follows
1175 200 Script output follows
1114
1176
1115 {
1177 {
1116 "rawdoc": "Working with Phases\n*", (glob)
1178 "rawdoc": "Working with Phases\n*", (glob)
1117 "topic": "phases"
1179 "topic": "phases"
1118 }
1180 }
General Comments 0
You need to be logged in to leave comments. Login now