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