##// END OF EJS Templates
templates: add support for summary webcommand in json style...
Laura Médioni -
r29382:e4b777fe default
parent child Browse files
Show More
@@ -1,1298 +1,1298 b''
1 1 #
2 2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import cgi
11 11 import copy
12 12 import mimetypes
13 13 import os
14 14 import re
15 15
16 16 from ..i18n import _
17 17 from ..node import hex, short
18 18
19 19 from .common import (
20 20 ErrorResponse,
21 21 HTTP_FORBIDDEN,
22 22 HTTP_NOT_FOUND,
23 23 HTTP_OK,
24 24 get_contact,
25 25 paritygen,
26 26 staticfile,
27 27 )
28 28
29 29 from .. import (
30 30 archival,
31 31 encoding,
32 32 error,
33 33 graphmod,
34 34 patch,
35 35 revset,
36 36 scmutil,
37 37 templatefilters,
38 38 templater,
39 39 util,
40 40 )
41 41
42 42 from . import (
43 43 webutil,
44 44 )
45 45
46 46 __all__ = []
47 47 commands = {}
48 48
49 49 class webcommand(object):
50 50 """Decorator used to register a web command handler.
51 51
52 52 The decorator takes as its positional arguments the name/path the
53 53 command should be accessible under.
54 54
55 55 Usage:
56 56
57 57 @webcommand('mycommand')
58 58 def mycommand(web, req, tmpl):
59 59 pass
60 60 """
61 61
62 62 def __init__(self, name):
63 63 self.name = name
64 64
65 65 def __call__(self, func):
66 66 __all__.append(self.name)
67 67 commands[self.name] = func
68 68 return func
69 69
70 70 @webcommand('log')
71 71 def log(web, req, tmpl):
72 72 """
73 73 /log[/{revision}[/{path}]]
74 74 --------------------------
75 75
76 76 Show repository or file history.
77 77
78 78 For URLs of the form ``/log/{revision}``, a list of changesets starting at
79 79 the specified changeset identifier is shown. If ``{revision}`` is not
80 80 defined, the default is ``tip``. This form is equivalent to the
81 81 ``changelog`` handler.
82 82
83 83 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
84 84 file will be shown. This form is equivalent to the ``filelog`` handler.
85 85 """
86 86
87 87 if 'file' in req.form and req.form['file'][0]:
88 88 return filelog(web, req, tmpl)
89 89 else:
90 90 return changelog(web, req, tmpl)
91 91
92 92 @webcommand('rawfile')
93 93 def rawfile(web, req, tmpl):
94 94 guessmime = web.configbool('web', 'guessmime', False)
95 95
96 96 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
97 97 if not path:
98 98 content = manifest(web, req, tmpl)
99 99 req.respond(HTTP_OK, web.ctype)
100 100 return content
101 101
102 102 try:
103 103 fctx = webutil.filectx(web.repo, req)
104 104 except error.LookupError as inst:
105 105 try:
106 106 content = manifest(web, req, tmpl)
107 107 req.respond(HTTP_OK, web.ctype)
108 108 return content
109 109 except ErrorResponse:
110 110 raise inst
111 111
112 112 path = fctx.path()
113 113 text = fctx.data()
114 114 mt = 'application/binary'
115 115 if guessmime:
116 116 mt = mimetypes.guess_type(path)[0]
117 117 if mt is None:
118 118 if util.binary(text):
119 119 mt = 'application/binary'
120 120 else:
121 121 mt = 'text/plain'
122 122 if mt.startswith('text/'):
123 123 mt += '; charset="%s"' % encoding.encoding
124 124
125 125 req.respond(HTTP_OK, mt, path, body=text)
126 126 return []
127 127
128 128 def _filerevision(web, req, tmpl, fctx):
129 129 f = fctx.path()
130 130 text = fctx.data()
131 131 parity = paritygen(web.stripecount)
132 132
133 133 if util.binary(text):
134 134 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
135 135 text = '(binary:%s)' % mt
136 136
137 137 def lines():
138 138 for lineno, t in enumerate(text.splitlines(True)):
139 139 yield {"line": t,
140 140 "lineid": "l%d" % (lineno + 1),
141 141 "linenumber": "% 6d" % (lineno + 1),
142 142 "parity": next(parity)}
143 143
144 144 return tmpl("filerevision",
145 145 file=f,
146 146 path=webutil.up(f),
147 147 text=lines(),
148 148 symrev=webutil.symrevorshortnode(req, fctx),
149 149 rename=webutil.renamelink(fctx),
150 150 permissions=fctx.manifest().flags(f),
151 151 **webutil.commonentry(web.repo, fctx))
152 152
153 153 @webcommand('file')
154 154 def file(web, req, tmpl):
155 155 """
156 156 /file/{revision}[/{path}]
157 157 -------------------------
158 158
159 159 Show information about a directory or file in the repository.
160 160
161 161 Info about the ``path`` given as a URL parameter will be rendered.
162 162
163 163 If ``path`` is a directory, information about the entries in that
164 164 directory will be rendered. This form is equivalent to the ``manifest``
165 165 handler.
166 166
167 167 If ``path`` is a file, information about that file will be shown via
168 168 the ``filerevision`` template.
169 169
170 170 If ``path`` is not defined, information about the root directory will
171 171 be rendered.
172 172 """
173 173 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
174 174 if not path:
175 175 return manifest(web, req, tmpl)
176 176 try:
177 177 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
178 178 except error.LookupError as inst:
179 179 try:
180 180 return manifest(web, req, tmpl)
181 181 except ErrorResponse:
182 182 raise inst
183 183
184 184 def _search(web, req, tmpl):
185 185 MODE_REVISION = 'rev'
186 186 MODE_KEYWORD = 'keyword'
187 187 MODE_REVSET = 'revset'
188 188
189 189 def revsearch(ctx):
190 190 yield ctx
191 191
192 192 def keywordsearch(query):
193 193 lower = encoding.lower
194 194 qw = lower(query).split()
195 195
196 196 def revgen():
197 197 cl = web.repo.changelog
198 198 for i in xrange(len(web.repo) - 1, 0, -100):
199 199 l = []
200 200 for j in cl.revs(max(0, i - 99), i):
201 201 ctx = web.repo[j]
202 202 l.append(ctx)
203 203 l.reverse()
204 204 for e in l:
205 205 yield e
206 206
207 207 for ctx in revgen():
208 208 miss = 0
209 209 for q in qw:
210 210 if not (q in lower(ctx.user()) or
211 211 q in lower(ctx.description()) or
212 212 q in lower(" ".join(ctx.files()))):
213 213 miss = 1
214 214 break
215 215 if miss:
216 216 continue
217 217
218 218 yield ctx
219 219
220 220 def revsetsearch(revs):
221 221 for r in revs:
222 222 yield web.repo[r]
223 223
224 224 searchfuncs = {
225 225 MODE_REVISION: (revsearch, 'exact revision search'),
226 226 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
227 227 MODE_REVSET: (revsetsearch, 'revset expression search'),
228 228 }
229 229
230 230 def getsearchmode(query):
231 231 try:
232 232 ctx = web.repo[query]
233 233 except (error.RepoError, error.LookupError):
234 234 # query is not an exact revision pointer, need to
235 235 # decide if it's a revset expression or keywords
236 236 pass
237 237 else:
238 238 return MODE_REVISION, ctx
239 239
240 240 revdef = 'reverse(%s)' % query
241 241 try:
242 242 tree = revset.parse(revdef)
243 243 except error.ParseError:
244 244 # can't parse to a revset tree
245 245 return MODE_KEYWORD, query
246 246
247 247 if revset.depth(tree) <= 2:
248 248 # no revset syntax used
249 249 return MODE_KEYWORD, query
250 250
251 251 if any((token, (value or '')[:3]) == ('string', 're:')
252 252 for token, value, pos in revset.tokenize(revdef)):
253 253 return MODE_KEYWORD, query
254 254
255 255 funcsused = revset.funcsused(tree)
256 256 if not funcsused.issubset(revset.safesymbols):
257 257 return MODE_KEYWORD, query
258 258
259 259 mfunc = revset.match(web.repo.ui, revdef)
260 260 try:
261 261 revs = mfunc(web.repo)
262 262 return MODE_REVSET, revs
263 263 # ParseError: wrongly placed tokens, wrongs arguments, etc
264 264 # RepoLookupError: no such revision, e.g. in 'revision:'
265 265 # Abort: bookmark/tag not exists
266 266 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
267 267 except (error.ParseError, error.RepoLookupError, error.Abort,
268 268 LookupError):
269 269 return MODE_KEYWORD, query
270 270
271 271 def changelist(**map):
272 272 count = 0
273 273
274 274 for ctx in searchfunc[0](funcarg):
275 275 count += 1
276 276 n = ctx.node()
277 277 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
278 278 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
279 279
280 280 yield tmpl('searchentry',
281 281 parity=next(parity),
282 282 changelogtag=showtags,
283 283 files=files,
284 284 **webutil.commonentry(web.repo, ctx))
285 285
286 286 if count >= revcount:
287 287 break
288 288
289 289 query = req.form['rev'][0]
290 290 revcount = web.maxchanges
291 291 if 'revcount' in req.form:
292 292 try:
293 293 revcount = int(req.form.get('revcount', [revcount])[0])
294 294 revcount = max(revcount, 1)
295 295 tmpl.defaults['sessionvars']['revcount'] = revcount
296 296 except ValueError:
297 297 pass
298 298
299 299 lessvars = copy.copy(tmpl.defaults['sessionvars'])
300 300 lessvars['revcount'] = max(revcount / 2, 1)
301 301 lessvars['rev'] = query
302 302 morevars = copy.copy(tmpl.defaults['sessionvars'])
303 303 morevars['revcount'] = revcount * 2
304 304 morevars['rev'] = query
305 305
306 306 mode, funcarg = getsearchmode(query)
307 307
308 308 if 'forcekw' in req.form:
309 309 showforcekw = ''
310 310 showunforcekw = searchfuncs[mode][1]
311 311 mode = MODE_KEYWORD
312 312 funcarg = query
313 313 else:
314 314 if mode != MODE_KEYWORD:
315 315 showforcekw = searchfuncs[MODE_KEYWORD][1]
316 316 else:
317 317 showforcekw = ''
318 318 showunforcekw = ''
319 319
320 320 searchfunc = searchfuncs[mode]
321 321
322 322 tip = web.repo['tip']
323 323 parity = paritygen(web.stripecount)
324 324
325 325 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
326 326 entries=changelist, archives=web.archivelist("tip"),
327 327 morevars=morevars, lessvars=lessvars,
328 328 modedesc=searchfunc[1],
329 329 showforcekw=showforcekw, showunforcekw=showunforcekw)
330 330
331 331 @webcommand('changelog')
332 332 def changelog(web, req, tmpl, shortlog=False):
333 333 """
334 334 /changelog[/{revision}]
335 335 -----------------------
336 336
337 337 Show information about multiple changesets.
338 338
339 339 If the optional ``revision`` URL argument is absent, information about
340 340 all changesets starting at ``tip`` will be rendered. If the ``revision``
341 341 argument is present, changesets will be shown starting from the specified
342 342 revision.
343 343
344 344 If ``revision`` is absent, the ``rev`` query string argument may be
345 345 defined. This will perform a search for changesets.
346 346
347 347 The argument for ``rev`` can be a single revision, a revision set,
348 348 or a literal keyword to search for in changeset data (equivalent to
349 349 :hg:`log -k`).
350 350
351 351 The ``revcount`` query string argument defines the maximum numbers of
352 352 changesets to render.
353 353
354 354 For non-searches, the ``changelog`` template will be rendered.
355 355 """
356 356
357 357 query = ''
358 358 if 'node' in req.form:
359 359 ctx = webutil.changectx(web.repo, req)
360 360 symrev = webutil.symrevorshortnode(req, ctx)
361 361 elif 'rev' in req.form:
362 362 return _search(web, req, tmpl)
363 363 else:
364 364 ctx = web.repo['tip']
365 365 symrev = 'tip'
366 366
367 367 def changelist():
368 368 revs = []
369 369 if pos != -1:
370 370 revs = web.repo.changelog.revs(pos, 0)
371 371 curcount = 0
372 372 for rev in revs:
373 373 curcount += 1
374 374 if curcount > revcount + 1:
375 375 break
376 376
377 377 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
378 378 entry['parity'] = next(parity)
379 379 yield entry
380 380
381 381 if shortlog:
382 382 revcount = web.maxshortchanges
383 383 else:
384 384 revcount = web.maxchanges
385 385
386 386 if 'revcount' in req.form:
387 387 try:
388 388 revcount = int(req.form.get('revcount', [revcount])[0])
389 389 revcount = max(revcount, 1)
390 390 tmpl.defaults['sessionvars']['revcount'] = revcount
391 391 except ValueError:
392 392 pass
393 393
394 394 lessvars = copy.copy(tmpl.defaults['sessionvars'])
395 395 lessvars['revcount'] = max(revcount / 2, 1)
396 396 morevars = copy.copy(tmpl.defaults['sessionvars'])
397 397 morevars['revcount'] = revcount * 2
398 398
399 399 count = len(web.repo)
400 400 pos = ctx.rev()
401 401 parity = paritygen(web.stripecount)
402 402
403 403 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
404 404
405 405 entries = list(changelist())
406 406 latestentry = entries[:1]
407 407 if len(entries) > revcount:
408 408 nextentry = entries[-1:]
409 409 entries = entries[:-1]
410 410 else:
411 411 nextentry = []
412 412
413 413 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
414 414 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
415 415 entries=entries,
416 416 latestentry=latestentry, nextentry=nextentry,
417 417 archives=web.archivelist("tip"), revcount=revcount,
418 418 morevars=morevars, lessvars=lessvars, query=query)
419 419
420 420 @webcommand('shortlog')
421 421 def shortlog(web, req, tmpl):
422 422 """
423 423 /shortlog
424 424 ---------
425 425
426 426 Show basic information about a set of changesets.
427 427
428 428 This accepts the same parameters as the ``changelog`` handler. The only
429 429 difference is the ``shortlog`` template will be rendered instead of the
430 430 ``changelog`` template.
431 431 """
432 432 return changelog(web, req, tmpl, shortlog=True)
433 433
434 434 @webcommand('changeset')
435 435 def changeset(web, req, tmpl):
436 436 """
437 437 /changeset[/{revision}]
438 438 -----------------------
439 439
440 440 Show information about a single changeset.
441 441
442 442 A URL path argument is the changeset identifier to show. See ``hg help
443 443 revisions`` for possible values. If not defined, the ``tip`` changeset
444 444 will be shown.
445 445
446 446 The ``changeset`` template is rendered. Contents of the ``changesettag``,
447 447 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
448 448 templates related to diffs may all be used to produce the output.
449 449 """
450 450 ctx = webutil.changectx(web.repo, req)
451 451
452 452 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
453 453
454 454 rev = webcommand('rev')(changeset)
455 455
456 456 def decodepath(path):
457 457 """Hook for mapping a path in the repository to a path in the
458 458 working copy.
459 459
460 460 Extensions (e.g., largefiles) can override this to remap files in
461 461 the virtual file system presented by the manifest command below."""
462 462 return path
463 463
464 464 @webcommand('manifest')
465 465 def manifest(web, req, tmpl):
466 466 """
467 467 /manifest[/{revision}[/{path}]]
468 468 -------------------------------
469 469
470 470 Show information about a directory.
471 471
472 472 If the URL path arguments are omitted, information about the root
473 473 directory for the ``tip`` changeset will be shown.
474 474
475 475 Because this handler can only show information for directories, it
476 476 is recommended to use the ``file`` handler instead, as it can handle both
477 477 directories and files.
478 478
479 479 The ``manifest`` template will be rendered for this handler.
480 480 """
481 481 if 'node' in req.form:
482 482 ctx = webutil.changectx(web.repo, req)
483 483 symrev = webutil.symrevorshortnode(req, ctx)
484 484 else:
485 485 ctx = web.repo['tip']
486 486 symrev = 'tip'
487 487 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
488 488 mf = ctx.manifest()
489 489 node = ctx.node()
490 490
491 491 files = {}
492 492 dirs = {}
493 493 parity = paritygen(web.stripecount)
494 494
495 495 if path and path[-1] != "/":
496 496 path += "/"
497 497 l = len(path)
498 498 abspath = "/" + path
499 499
500 500 for full, n in mf.iteritems():
501 501 # the virtual path (working copy path) used for the full
502 502 # (repository) path
503 503 f = decodepath(full)
504 504
505 505 if f[:l] != path:
506 506 continue
507 507 remain = f[l:]
508 508 elements = remain.split('/')
509 509 if len(elements) == 1:
510 510 files[remain] = full
511 511 else:
512 512 h = dirs # need to retain ref to dirs (root)
513 513 for elem in elements[0:-1]:
514 514 if elem not in h:
515 515 h[elem] = {}
516 516 h = h[elem]
517 517 if len(h) > 1:
518 518 break
519 519 h[None] = None # denotes files present
520 520
521 521 if mf and not files and not dirs:
522 522 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
523 523
524 524 def filelist(**map):
525 525 for f in sorted(files):
526 526 full = files[f]
527 527
528 528 fctx = ctx.filectx(full)
529 529 yield {"file": full,
530 530 "parity": next(parity),
531 531 "basename": f,
532 532 "date": fctx.date(),
533 533 "size": fctx.size(),
534 534 "permissions": mf.flags(full)}
535 535
536 536 def dirlist(**map):
537 537 for d in sorted(dirs):
538 538
539 539 emptydirs = []
540 540 h = dirs[d]
541 541 while isinstance(h, dict) and len(h) == 1:
542 542 k, v = h.items()[0]
543 543 if v:
544 544 emptydirs.append(k)
545 545 h = v
546 546
547 547 path = "%s%s" % (abspath, d)
548 548 yield {"parity": next(parity),
549 549 "path": path,
550 550 "emptydirs": "/".join(emptydirs),
551 551 "basename": d}
552 552
553 553 return tmpl("manifest",
554 554 symrev=symrev,
555 555 path=abspath,
556 556 up=webutil.up(abspath),
557 557 upparity=next(parity),
558 558 fentries=filelist,
559 559 dentries=dirlist,
560 560 archives=web.archivelist(hex(node)),
561 561 **webutil.commonentry(web.repo, ctx))
562 562
563 563 @webcommand('tags')
564 564 def tags(web, req, tmpl):
565 565 """
566 566 /tags
567 567 -----
568 568
569 569 Show information about tags.
570 570
571 571 No arguments are accepted.
572 572
573 573 The ``tags`` template is rendered.
574 574 """
575 575 i = list(reversed(web.repo.tagslist()))
576 576 parity = paritygen(web.stripecount)
577 577
578 578 def entries(notip, latestonly, **map):
579 579 t = i
580 580 if notip:
581 581 t = [(k, n) for k, n in i if k != "tip"]
582 582 if latestonly:
583 583 t = t[:1]
584 584 for k, n in t:
585 585 yield {"parity": next(parity),
586 586 "tag": k,
587 587 "date": web.repo[n].date(),
588 588 "node": hex(n)}
589 589
590 590 return tmpl("tags",
591 591 node=hex(web.repo.changelog.tip()),
592 592 entries=lambda **x: entries(False, False, **x),
593 593 entriesnotip=lambda **x: entries(True, False, **x),
594 594 latestentry=lambda **x: entries(True, True, **x))
595 595
596 596 @webcommand('bookmarks')
597 597 def bookmarks(web, req, tmpl):
598 598 """
599 599 /bookmarks
600 600 ----------
601 601
602 602 Show information about bookmarks.
603 603
604 604 No arguments are accepted.
605 605
606 606 The ``bookmarks`` template is rendered.
607 607 """
608 608 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
609 609 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
610 610 i = sorted(i, key=sortkey, reverse=True)
611 611 parity = paritygen(web.stripecount)
612 612
613 613 def entries(latestonly, **map):
614 614 t = i
615 615 if latestonly:
616 616 t = i[:1]
617 617 for k, n in t:
618 618 yield {"parity": next(parity),
619 619 "bookmark": k,
620 620 "date": web.repo[n].date(),
621 621 "node": hex(n)}
622 622
623 623 if i:
624 624 latestrev = i[0][1]
625 625 else:
626 626 latestrev = -1
627 627
628 628 return tmpl("bookmarks",
629 629 node=hex(web.repo.changelog.tip()),
630 630 lastchange=[{"date": web.repo[latestrev].date()}],
631 631 entries=lambda **x: entries(latestonly=False, **x),
632 632 latestentry=lambda **x: entries(latestonly=True, **x))
633 633
634 634 @webcommand('branches')
635 635 def branches(web, req, tmpl):
636 636 """
637 637 /branches
638 638 ---------
639 639
640 640 Show information about branches.
641 641
642 642 All known branches are contained in the output, even closed branches.
643 643
644 644 No arguments are accepted.
645 645
646 646 The ``branches`` template is rendered.
647 647 """
648 648 entries = webutil.branchentries(web.repo, web.stripecount)
649 649 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
650 650 return tmpl('branches', node=hex(web.repo.changelog.tip()),
651 651 entries=entries, latestentry=latestentry)
652 652
653 653 @webcommand('summary')
654 654 def summary(web, req, tmpl):
655 655 """
656 656 /summary
657 657 --------
658 658
659 659 Show a summary of repository state.
660 660
661 661 Information about the latest changesets, bookmarks, tags, and branches
662 662 is captured by this handler.
663 663
664 664 The ``summary`` template is rendered.
665 665 """
666 666 i = reversed(web.repo.tagslist())
667 667
668 668 def tagentries(**map):
669 669 parity = paritygen(web.stripecount)
670 670 count = 0
671 671 for k, n in i:
672 672 if k == "tip": # skip tip
673 673 continue
674 674
675 675 count += 1
676 676 if count > 10: # limit to 10 tags
677 677 break
678 678
679 679 yield tmpl("tagentry",
680 680 parity=next(parity),
681 681 tag=k,
682 682 node=hex(n),
683 683 date=web.repo[n].date())
684 684
685 685 def bookmarks(**map):
686 686 parity = paritygen(web.stripecount)
687 687 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
688 688 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
689 689 marks = sorted(marks, key=sortkey, reverse=True)
690 690 for k, n in marks[:10]: # limit to 10 bookmarks
691 691 yield {'parity': next(parity),
692 692 'bookmark': k,
693 693 'date': web.repo[n].date(),
694 694 'node': hex(n)}
695 695
696 696 def changelist(**map):
697 697 parity = paritygen(web.stripecount, offset=start - end)
698 698 l = [] # build a list in forward order for efficiency
699 699 revs = []
700 700 if start < end:
701 701 revs = web.repo.changelog.revs(start, end - 1)
702 702 for i in revs:
703 703 ctx = web.repo[i]
704 704
705 705 l.append(tmpl(
706 706 'shortlogentry',
707 707 parity=next(parity),
708 708 **webutil.commonentry(web.repo, ctx)))
709 709
710 l.reverse()
711 yield l
710 for entry in reversed(l):
711 yield entry
712 712
713 713 tip = web.repo['tip']
714 714 count = len(web.repo)
715 715 start = max(0, count - web.maxchanges)
716 716 end = min(count, start + web.maxchanges)
717 717
718 718 return tmpl("summary",
719 719 desc=web.config("web", "description", "unknown"),
720 720 owner=get_contact(web.config) or "unknown",
721 721 lastchange=tip.date(),
722 722 tags=tagentries,
723 723 bookmarks=bookmarks,
724 724 branches=webutil.branchentries(web.repo, web.stripecount, 10),
725 725 shortlog=changelist,
726 726 node=tip.hex(),
727 727 symrev='tip',
728 728 archives=web.archivelist("tip"))
729 729
730 730 @webcommand('filediff')
731 731 def filediff(web, req, tmpl):
732 732 """
733 733 /diff/{revision}/{path}
734 734 -----------------------
735 735
736 736 Show how a file changed in a particular commit.
737 737
738 738 The ``filediff`` template is rendered.
739 739
740 740 This handler is registered under both the ``/diff`` and ``/filediff``
741 741 paths. ``/diff`` is used in modern code.
742 742 """
743 743 fctx, ctx = None, None
744 744 try:
745 745 fctx = webutil.filectx(web.repo, req)
746 746 except LookupError:
747 747 ctx = webutil.changectx(web.repo, req)
748 748 path = webutil.cleanpath(web.repo, req.form['file'][0])
749 749 if path not in ctx.files():
750 750 raise
751 751
752 752 if fctx is not None:
753 753 path = fctx.path()
754 754 ctx = fctx.changectx()
755 755
756 756 parity = paritygen(web.stripecount)
757 757 style = web.config('web', 'style', 'paper')
758 758 if 'style' in req.form:
759 759 style = req.form['style'][0]
760 760
761 761 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
762 762 if fctx is not None:
763 763 rename = webutil.renamelink(fctx)
764 764 ctx = fctx
765 765 else:
766 766 rename = []
767 767 ctx = ctx
768 768 return tmpl("filediff",
769 769 file=path,
770 770 symrev=webutil.symrevorshortnode(req, ctx),
771 771 rename=rename,
772 772 diff=diffs,
773 773 **webutil.commonentry(web.repo, ctx))
774 774
775 775 diff = webcommand('diff')(filediff)
776 776
777 777 @webcommand('comparison')
778 778 def comparison(web, req, tmpl):
779 779 """
780 780 /comparison/{revision}/{path}
781 781 -----------------------------
782 782
783 783 Show a comparison between the old and new versions of a file from changes
784 784 made on a particular revision.
785 785
786 786 This is similar to the ``diff`` handler. However, this form features
787 787 a split or side-by-side diff rather than a unified diff.
788 788
789 789 The ``context`` query string argument can be used to control the lines of
790 790 context in the diff.
791 791
792 792 The ``filecomparison`` template is rendered.
793 793 """
794 794 ctx = webutil.changectx(web.repo, req)
795 795 if 'file' not in req.form:
796 796 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
797 797 path = webutil.cleanpath(web.repo, req.form['file'][0])
798 798
799 799 parsecontext = lambda v: v == 'full' and -1 or int(v)
800 800 if 'context' in req.form:
801 801 context = parsecontext(req.form['context'][0])
802 802 else:
803 803 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
804 804
805 805 def filelines(f):
806 806 if util.binary(f.data()):
807 807 mt = mimetypes.guess_type(f.path())[0]
808 808 if not mt:
809 809 mt = 'application/octet-stream'
810 810 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
811 811 return f.data().splitlines()
812 812
813 813 fctx = None
814 814 parent = ctx.p1()
815 815 leftrev = parent.rev()
816 816 leftnode = parent.node()
817 817 rightrev = ctx.rev()
818 818 rightnode = ctx.node()
819 819 if path in ctx:
820 820 fctx = ctx[path]
821 821 rightlines = filelines(fctx)
822 822 if path not in parent:
823 823 leftlines = ()
824 824 else:
825 825 pfctx = parent[path]
826 826 leftlines = filelines(pfctx)
827 827 else:
828 828 rightlines = ()
829 829 pfctx = ctx.parents()[0][path]
830 830 leftlines = filelines(pfctx)
831 831
832 832 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
833 833 if fctx is not None:
834 834 rename = webutil.renamelink(fctx)
835 835 ctx = fctx
836 836 else:
837 837 rename = []
838 838 ctx = ctx
839 839 return tmpl('filecomparison',
840 840 file=path,
841 841 symrev=webutil.symrevorshortnode(req, ctx),
842 842 rename=rename,
843 843 leftrev=leftrev,
844 844 leftnode=hex(leftnode),
845 845 rightrev=rightrev,
846 846 rightnode=hex(rightnode),
847 847 comparison=comparison,
848 848 **webutil.commonentry(web.repo, ctx))
849 849
850 850 @webcommand('annotate')
851 851 def annotate(web, req, tmpl):
852 852 """
853 853 /annotate/{revision}/{path}
854 854 ---------------------------
855 855
856 856 Show changeset information for each line in a file.
857 857
858 858 The ``fileannotate`` template is rendered.
859 859 """
860 860 fctx = webutil.filectx(web.repo, req)
861 861 f = fctx.path()
862 862 parity = paritygen(web.stripecount)
863 863 diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True,
864 864 section='annotate', whitespace=True)
865 865
866 866 def annotate(**map):
867 867 if util.binary(fctx.data()):
868 868 mt = (mimetypes.guess_type(fctx.path())[0]
869 869 or 'application/octet-stream')
870 870 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
871 871 '(binary:%s)' % mt)])
872 872 else:
873 873 lines = enumerate(fctx.annotate(follow=True, linenumber=True,
874 874 diffopts=diffopts))
875 875 for lineno, ((f, targetline), l) in lines:
876 876 yield {"parity": next(parity),
877 877 "node": f.hex(),
878 878 "rev": f.rev(),
879 879 "author": f.user(),
880 880 "desc": f.description(),
881 881 "extra": f.extra(),
882 882 "file": f.path(),
883 883 "targetline": targetline,
884 884 "line": l,
885 885 "lineno": lineno + 1,
886 886 "lineid": "l%d" % (lineno + 1),
887 887 "linenumber": "% 6d" % (lineno + 1),
888 888 "revdate": f.date()}
889 889
890 890 return tmpl("fileannotate",
891 891 file=f,
892 892 annotate=annotate,
893 893 path=webutil.up(f),
894 894 symrev=webutil.symrevorshortnode(req, fctx),
895 895 rename=webutil.renamelink(fctx),
896 896 permissions=fctx.manifest().flags(f),
897 897 **webutil.commonentry(web.repo, fctx))
898 898
899 899 @webcommand('filelog')
900 900 def filelog(web, req, tmpl):
901 901 """
902 902 /filelog/{revision}/{path}
903 903 --------------------------
904 904
905 905 Show information about the history of a file in the repository.
906 906
907 907 The ``revcount`` query string argument can be defined to control the
908 908 maximum number of entries to show.
909 909
910 910 The ``filelog`` template will be rendered.
911 911 """
912 912
913 913 try:
914 914 fctx = webutil.filectx(web.repo, req)
915 915 f = fctx.path()
916 916 fl = fctx.filelog()
917 917 except error.LookupError:
918 918 f = webutil.cleanpath(web.repo, req.form['file'][0])
919 919 fl = web.repo.file(f)
920 920 numrevs = len(fl)
921 921 if not numrevs: # file doesn't exist at all
922 922 raise
923 923 rev = webutil.changectx(web.repo, req).rev()
924 924 first = fl.linkrev(0)
925 925 if rev < first: # current rev is from before file existed
926 926 raise
927 927 frev = numrevs - 1
928 928 while fl.linkrev(frev) > rev:
929 929 frev -= 1
930 930 fctx = web.repo.filectx(f, fl.linkrev(frev))
931 931
932 932 revcount = web.maxshortchanges
933 933 if 'revcount' in req.form:
934 934 try:
935 935 revcount = int(req.form.get('revcount', [revcount])[0])
936 936 revcount = max(revcount, 1)
937 937 tmpl.defaults['sessionvars']['revcount'] = revcount
938 938 except ValueError:
939 939 pass
940 940
941 941 lessvars = copy.copy(tmpl.defaults['sessionvars'])
942 942 lessvars['revcount'] = max(revcount / 2, 1)
943 943 morevars = copy.copy(tmpl.defaults['sessionvars'])
944 944 morevars['revcount'] = revcount * 2
945 945
946 946 count = fctx.filerev() + 1
947 947 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
948 948 end = min(count, start + revcount) # last rev on this page
949 949 parity = paritygen(web.stripecount, offset=start - end)
950 950
951 951 def entries():
952 952 l = []
953 953
954 954 repo = web.repo
955 955 revs = fctx.filelog().revs(start, end - 1)
956 956 for i in revs:
957 957 iterfctx = fctx.filectx(i)
958 958
959 959 l.append(dict(
960 960 parity=next(parity),
961 961 filerev=i,
962 962 file=f,
963 963 rename=webutil.renamelink(iterfctx),
964 964 **webutil.commonentry(repo, iterfctx)))
965 965 for e in reversed(l):
966 966 yield e
967 967
968 968 entries = list(entries())
969 969 latestentry = entries[:1]
970 970
971 971 revnav = webutil.filerevnav(web.repo, fctx.path())
972 972 nav = revnav.gen(end - 1, revcount, count)
973 973 return tmpl("filelog",
974 974 file=f,
975 975 nav=nav,
976 976 symrev=webutil.symrevorshortnode(req, fctx),
977 977 entries=entries,
978 978 latestentry=latestentry,
979 979 revcount=revcount,
980 980 morevars=morevars,
981 981 lessvars=lessvars,
982 982 **webutil.commonentry(web.repo, fctx))
983 983
984 984 @webcommand('archive')
985 985 def archive(web, req, tmpl):
986 986 """
987 987 /archive/{revision}.{format}[/{path}]
988 988 -------------------------------------
989 989
990 990 Obtain an archive of repository content.
991 991
992 992 The content and type of the archive is defined by a URL path parameter.
993 993 ``format`` is the file extension of the archive type to be generated. e.g.
994 994 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
995 995 server configuration.
996 996
997 997 The optional ``path`` URL parameter controls content to include in the
998 998 archive. If omitted, every file in the specified revision is present in the
999 999 archive. If included, only the specified file or contents of the specified
1000 1000 directory will be included in the archive.
1001 1001
1002 1002 No template is used for this handler. Raw, binary content is generated.
1003 1003 """
1004 1004
1005 1005 type_ = req.form.get('type', [None])[0]
1006 1006 allowed = web.configlist("web", "allow_archive")
1007 1007 key = req.form['node'][0]
1008 1008
1009 1009 if type_ not in web.archives:
1010 1010 msg = 'Unsupported archive type: %s' % type_
1011 1011 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1012 1012
1013 1013 if not ((type_ in allowed or
1014 1014 web.configbool("web", "allow" + type_, False))):
1015 1015 msg = 'Archive type not allowed: %s' % type_
1016 1016 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1017 1017
1018 1018 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1019 1019 cnode = web.repo.lookup(key)
1020 1020 arch_version = key
1021 1021 if cnode == key or key == 'tip':
1022 1022 arch_version = short(cnode)
1023 1023 name = "%s-%s" % (reponame, arch_version)
1024 1024
1025 1025 ctx = webutil.changectx(web.repo, req)
1026 1026 pats = []
1027 1027 matchfn = scmutil.match(ctx, [])
1028 1028 file = req.form.get('file', None)
1029 1029 if file:
1030 1030 pats = ['path:' + file[0]]
1031 1031 matchfn = scmutil.match(ctx, pats, default='path')
1032 1032 if pats:
1033 1033 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1034 1034 if not files:
1035 1035 raise ErrorResponse(HTTP_NOT_FOUND,
1036 1036 'file(s) not found: %s' % file[0])
1037 1037
1038 1038 mimetype, artype, extension, encoding = web.archivespecs[type_]
1039 1039 headers = [
1040 1040 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1041 1041 ]
1042 1042 if encoding:
1043 1043 headers.append(('Content-Encoding', encoding))
1044 1044 req.headers.extend(headers)
1045 1045 req.respond(HTTP_OK, mimetype)
1046 1046
1047 1047 archival.archive(web.repo, req, cnode, artype, prefix=name,
1048 1048 matchfn=matchfn,
1049 1049 subrepos=web.configbool("web", "archivesubrepos"))
1050 1050 return []
1051 1051
1052 1052
1053 1053 @webcommand('static')
1054 1054 def static(web, req, tmpl):
1055 1055 fname = req.form['file'][0]
1056 1056 # a repo owner may set web.static in .hg/hgrc to get any file
1057 1057 # readable by the user running the CGI script
1058 1058 static = web.config("web", "static", None, untrusted=False)
1059 1059 if not static:
1060 1060 tp = web.templatepath or templater.templatepaths()
1061 1061 if isinstance(tp, str):
1062 1062 tp = [tp]
1063 1063 static = [os.path.join(p, 'static') for p in tp]
1064 1064 staticfile(static, fname, req)
1065 1065 return []
1066 1066
1067 1067 @webcommand('graph')
1068 1068 def graph(web, req, tmpl):
1069 1069 """
1070 1070 /graph[/{revision}]
1071 1071 -------------------
1072 1072
1073 1073 Show information about the graphical topology of the repository.
1074 1074
1075 1075 Information rendered by this handler can be used to create visual
1076 1076 representations of repository topology.
1077 1077
1078 1078 The ``revision`` URL parameter controls the starting changeset.
1079 1079
1080 1080 The ``revcount`` query string argument can define the number of changesets
1081 1081 to show information for.
1082 1082
1083 1083 This handler will render the ``graph`` template.
1084 1084 """
1085 1085
1086 1086 if 'node' in req.form:
1087 1087 ctx = webutil.changectx(web.repo, req)
1088 1088 symrev = webutil.symrevorshortnode(req, ctx)
1089 1089 else:
1090 1090 ctx = web.repo['tip']
1091 1091 symrev = 'tip'
1092 1092 rev = ctx.rev()
1093 1093
1094 1094 bg_height = 39
1095 1095 revcount = web.maxshortchanges
1096 1096 if 'revcount' in req.form:
1097 1097 try:
1098 1098 revcount = int(req.form.get('revcount', [revcount])[0])
1099 1099 revcount = max(revcount, 1)
1100 1100 tmpl.defaults['sessionvars']['revcount'] = revcount
1101 1101 except ValueError:
1102 1102 pass
1103 1103
1104 1104 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1105 1105 lessvars['revcount'] = max(revcount / 2, 1)
1106 1106 morevars = copy.copy(tmpl.defaults['sessionvars'])
1107 1107 morevars['revcount'] = revcount * 2
1108 1108
1109 1109 count = len(web.repo)
1110 1110 pos = rev
1111 1111
1112 1112 uprev = min(max(0, count - 1), rev + revcount)
1113 1113 downrev = max(0, rev - revcount)
1114 1114 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1115 1115
1116 1116 tree = []
1117 1117 if pos != -1:
1118 1118 allrevs = web.repo.changelog.revs(pos, 0)
1119 1119 revs = []
1120 1120 for i in allrevs:
1121 1121 revs.append(i)
1122 1122 if len(revs) >= revcount:
1123 1123 break
1124 1124
1125 1125 # We have to feed a baseset to dagwalker as it is expecting smartset
1126 1126 # object. This does not have a big impact on hgweb performance itself
1127 1127 # since hgweb graphing code is not itself lazy yet.
1128 1128 dag = graphmod.dagwalker(web.repo, revset.baseset(revs))
1129 1129 # As we said one line above... not lazy.
1130 1130 tree = list(graphmod.colored(dag, web.repo))
1131 1131
1132 1132 def getcolumns(tree):
1133 1133 cols = 0
1134 1134 for (id, type, ctx, vtx, edges) in tree:
1135 1135 if type != graphmod.CHANGESET:
1136 1136 continue
1137 1137 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1138 1138 max([edge[1] for edge in edges] or [0]))
1139 1139 return cols
1140 1140
1141 1141 def graphdata(usetuples, encodestr):
1142 1142 data = []
1143 1143
1144 1144 row = 0
1145 1145 for (id, type, ctx, vtx, edges) in tree:
1146 1146 if type != graphmod.CHANGESET:
1147 1147 continue
1148 1148 node = str(ctx)
1149 1149 age = encodestr(templatefilters.age(ctx.date()))
1150 1150 desc = templatefilters.firstline(encodestr(ctx.description()))
1151 1151 desc = cgi.escape(templatefilters.nonempty(desc))
1152 1152 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1153 1153 branch = cgi.escape(encodestr(ctx.branch()))
1154 1154 try:
1155 1155 branchnode = web.repo.branchtip(branch)
1156 1156 except error.RepoLookupError:
1157 1157 branchnode = None
1158 1158 branch = branch, branchnode == ctx.node()
1159 1159
1160 1160 if usetuples:
1161 1161 data.append((node, vtx, edges, desc, user, age, branch,
1162 1162 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1163 1163 [cgi.escape(encodestr(x))
1164 1164 for x in ctx.bookmarks()]))
1165 1165 else:
1166 1166 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1167 1167 'color': (edge[2] - 1) % 6 + 1,
1168 1168 'width': edge[3], 'bcolor': edge[4]}
1169 1169 for edge in edges]
1170 1170
1171 1171 data.append(
1172 1172 {'node': node,
1173 1173 'col': vtx[0],
1174 1174 'color': (vtx[1] - 1) % 6 + 1,
1175 1175 'edges': edgedata,
1176 1176 'row': row,
1177 1177 'nextrow': row + 1,
1178 1178 'desc': desc,
1179 1179 'user': user,
1180 1180 'age': age,
1181 1181 'bookmarks': webutil.nodebookmarksdict(
1182 1182 web.repo, ctx.node()),
1183 1183 'branches': webutil.nodebranchdict(web.repo, ctx),
1184 1184 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1185 1185 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1186 1186
1187 1187 row += 1
1188 1188
1189 1189 return data
1190 1190
1191 1191 cols = getcolumns(tree)
1192 1192 rows = len(tree)
1193 1193 canvasheight = (rows + 1) * bg_height - 27
1194 1194
1195 1195 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1196 1196 uprev=uprev,
1197 1197 lessvars=lessvars, morevars=morevars, downrev=downrev,
1198 1198 cols=cols, rows=rows,
1199 1199 canvaswidth=(cols + 1) * bg_height,
1200 1200 truecanvasheight=rows * bg_height,
1201 1201 canvasheight=canvasheight, bg_height=bg_height,
1202 1202 # {jsdata} will be passed to |json, so it must be in utf-8
1203 1203 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1204 1204 nodes=lambda **x: graphdata(False, str),
1205 1205 node=ctx.hex(), changenav=changenav)
1206 1206
1207 1207 def _getdoc(e):
1208 1208 doc = e[0].__doc__
1209 1209 if doc:
1210 1210 doc = _(doc).partition('\n')[0]
1211 1211 else:
1212 1212 doc = _('(no help text available)')
1213 1213 return doc
1214 1214
1215 1215 @webcommand('help')
1216 1216 def help(web, req, tmpl):
1217 1217 """
1218 1218 /help[/{topic}]
1219 1219 ---------------
1220 1220
1221 1221 Render help documentation.
1222 1222
1223 1223 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1224 1224 is defined, that help topic will be rendered. If not, an index of
1225 1225 available help topics will be rendered.
1226 1226
1227 1227 The ``help`` template will be rendered when requesting help for a topic.
1228 1228 ``helptopics`` will be rendered for the index of help topics.
1229 1229 """
1230 1230 from .. import commands, help as helpmod # avoid cycle
1231 1231
1232 1232 topicname = req.form.get('node', [None])[0]
1233 1233 if not topicname:
1234 1234 def topics(**map):
1235 1235 for entries, summary, _doc in helpmod.helptable:
1236 1236 yield {'topic': entries[0], 'summary': summary}
1237 1237
1238 1238 early, other = [], []
1239 1239 primary = lambda s: s.partition('|')[0]
1240 1240 for c, e in commands.table.iteritems():
1241 1241 doc = _getdoc(e)
1242 1242 if 'DEPRECATED' in doc or c.startswith('debug'):
1243 1243 continue
1244 1244 cmd = primary(c)
1245 1245 if cmd.startswith('^'):
1246 1246 early.append((cmd[1:], doc))
1247 1247 else:
1248 1248 other.append((cmd, doc))
1249 1249
1250 1250 early.sort()
1251 1251 other.sort()
1252 1252
1253 1253 def earlycommands(**map):
1254 1254 for c, doc in early:
1255 1255 yield {'topic': c, 'summary': doc}
1256 1256
1257 1257 def othercommands(**map):
1258 1258 for c, doc in other:
1259 1259 yield {'topic': c, 'summary': doc}
1260 1260
1261 1261 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1262 1262 othercommands=othercommands, title='Index')
1263 1263
1264 1264 # Render an index of sub-topics.
1265 1265 if topicname in helpmod.subtopics:
1266 1266 topics = []
1267 1267 for entries, summary, _doc in helpmod.subtopics[topicname]:
1268 1268 topics.append({
1269 1269 'topic': '%s.%s' % (topicname, entries[0]),
1270 1270 'basename': entries[0],
1271 1271 'summary': summary,
1272 1272 })
1273 1273
1274 1274 return tmpl('helptopics', topics=topics, title=topicname,
1275 1275 subindex=True)
1276 1276
1277 1277 u = webutil.wsgiui()
1278 1278 u.verbose = True
1279 1279
1280 1280 # Render a page from a sub-topic.
1281 1281 if '.' in topicname:
1282 1282 # TODO implement support for rendering sections, like
1283 1283 # `hg help` works.
1284 1284 topic, subtopic = topicname.split('.', 1)
1285 1285 if topic not in helpmod.subtopics:
1286 1286 raise ErrorResponse(HTTP_NOT_FOUND)
1287 1287 else:
1288 1288 topic = topicname
1289 1289 subtopic = None
1290 1290
1291 1291 try:
1292 1292 doc = helpmod.help_(u, topic, subtopic=subtopic)
1293 1293 except error.UnknownCommand:
1294 1294 raise ErrorResponse(HTTP_NOT_FOUND)
1295 1295 return tmpl('help', topic=topicname, doc=doc)
1296 1296
1297 1297 # tell hggettext to extract docstrings from these functions:
1298 1298 i18nfunctions = commands.values()
@@ -1,204 +1,218 b''
1 1 mimetype = 'application/json'
2 2 filerevision = '\{
3 3 "node": {node|json},
4 4 "path": {file|json},
5 5 "date": {date|json},
6 6 "desc": {desc|utf8|json},
7 7 "branch": {if(branch, branch%changesetbranch, "default"|json)},
8 8 "bookmarks": [{join(bookmarks%changelistentryname, ", ")}],
9 9 "tags": [{join(tags%changelistentryname, ", ")}],
10 10 "user": {author|utf8|json},
11 11 "parents": [{join(parent%changesetparent, ", ")}],
12 12 "phase": {phase|json},
13 13 "lines": [{join(text%lineentry, ", ")}]
14 14 }'
15 15 lineentry = '\{
16 16 "line": {line|json}
17 17 }'
18 18 search = '"not yet implemented"'
19 19 # changelog and shortlog are the same web API but with different
20 20 # number of entries.
21 21 changelog = changelist.tmpl
22 22 shortlog = changelist.tmpl
23 23 changelistentry = '\{
24 24 "node": {node|json},
25 25 "date": {date|json},
26 26 "desc": {desc|utf8|json},
27 27 "branch": {if(branch, branch%changesetbranch, "default"|json)},
28 28 "bookmarks": [{join(bookmarks%changelistentryname, ", ")}],
29 29 "tags": [{join(tags%changelistentryname, ", ")}],
30 30 "user": {author|utf8|json},
31 31 "phase": {phase|json},
32 32 "parents": [{if(allparents, join(allparents%changesetparent, ", "),
33 33 join(parent%changesetparent, ", "))}]
34 34 }'
35 35 changelistentryname = '{name|utf8|json}'
36 36 changeset = '\{
37 37 "node": {node|json},
38 38 "date": {date|json},
39 39 "desc": {desc|utf8|json},
40 40 "branch": {if(branch, branch%changesetbranch, "default"|json)},
41 41 "bookmarks": [{join(changesetbookmark, ", ")}],
42 42 "tags": [{join(changesettag, ", ")}],
43 43 "user": {author|utf8|json},
44 44 "parents": [{join(parent%changesetparent, ", ")}],
45 45 "phase": {phase|json}
46 46 }'
47 47 changesetbranch = '{name|utf8|json}'
48 48 changesetbookmark = '{bookmark|utf8|json}'
49 49 changesettag = '{tag|utf8|json}'
50 50 changesetparent = '{node|json}'
51 51 manifest = '\{
52 52 "node": {node|json},
53 53 "abspath": {path|json},
54 54 "directories": [{join(dentries%direntry, ", ")}],
55 55 "files": [{join(fentries%fileentry, ", ")}],
56 56 "bookmarks": [{join(bookmarks%name, ", ")}],
57 57 "tags": [{join(tags%name, ", ")}]
58 58 }'
59 59 name = '{name|utf8|json}'
60 60 direntry = '\{
61 61 "abspath": {path|json},
62 62 "basename": {basename|json},
63 63 "emptydirs": {emptydirs|json}
64 64 }'
65 65 fileentry = '\{
66 66 "abspath": {file|json},
67 67 "basename": {basename|json},
68 68 "date": {date|json},
69 69 "size": {size|json},
70 70 "flags": {permissions|json}
71 71 }'
72 72 tags = '\{
73 73 "node": {node|json},
74 74 "tags": [{join(entriesnotip%tagentry, ", ")}]
75 75 }'
76 76 tagentry = '\{
77 77 "tag": {tag|utf8|json},
78 78 "node": {node|json},
79 79 "date": {date|json}
80 80 }'
81 81 bookmarks = '\{
82 82 "node": {node|json},
83 83 "bookmarks": [{join(entries%bookmarkentry, ", ")}]
84 84 }'
85 85 bookmarkentry = '\{
86 86 "bookmark": {bookmark|utf8|json},
87 87 "node": {node|json},
88 88 "date": {date|json}
89 89 }'
90 90 branches = '\{
91 91 "branches": [{join(entries%branchentry, ", ")}]
92 92 }'
93 93 branchentry = '\{
94 94 "branch": {branch|utf8|json},
95 95 "node": {node|json},
96 96 "date": {date|json},
97 97 "status": {status|json}
98 98 }'
99 summary = '"not yet implemented"'
99 shortlogentry = '{changelistentry}'
100 summary = '\{
101 "node": {node|json},
102 "lastchange": {lastchange|json},
103 "bookmarks": [{join(bookmarks%bookmarkentry, ", ")}],
104 "branches": [{join(branches%branchentry, ", ")}],
105 "shortlog": [{join(shortlog%shortlogentry, ", ")}],
106 "tags": [{join(tags%tagentry, ", ")}],
107 "archives": [{join(archives%archiveentry, ", ")}]
108 }'
109 archiveentry = '\{
110 "node": {node|json},
111 "extension": {extension|json},
112 "type": {type|json}
113 }'
100 114 filediff = '\{
101 115 "path": {file|json},
102 116 "node": {node|json},
103 117 "date": {date|json},
104 118 "desc": {desc|utf8|json},
105 119 "author": {author|utf8|json},
106 120 "parents": [{join(parent%changesetparent, ", ")}],
107 121 "children": [{join(child%changesetparent, ", ")}],
108 122 "diff": [{join(diff%diffblock, ", ")}]
109 123 }'
110 124 diffblock = '\{
111 125 "blockno": {blockno|json},
112 126 "lines": [{join(lines, ", ")}]
113 127 }'
114 128 difflineplus = '\{
115 129 "t": "+",
116 130 "n": {lineno|json},
117 131 "l": {line|json}
118 132 }'
119 133 difflineminus = '\{
120 134 "t": "-",
121 135 "n": {lineno|json},
122 136 "l": {line|json}
123 137 }'
124 138 difflineat = '\{
125 139 "t": "@",
126 140 "n": {lineno|json},
127 141 "l": {line|json}
128 142 }'
129 143 diffline = '\{
130 144 "t": "",
131 145 "n": {lineno|json},
132 146 "l": {line|json}
133 147 }'
134 148 filecomparison = '\{
135 149 "path": {file|json},
136 150 "node": {node|json},
137 151 "date": {date|json},
138 152 "desc": {desc|utf8|json},
139 153 "author": {author|utf8|json},
140 154 "parents": [{join(parent%changesetparent, ", ")}],
141 155 "children": [{join(child%changesetparent, ", ")}],
142 156 "leftnode": {leftnode|json},
143 157 "rightnode": {rightnode|json},
144 158 "comparison": [{join(comparison, ", ")}]
145 159 }'
146 160 comparisonblock = '\{
147 161 "lines": [{join(lines, ", ")}]
148 162 }'
149 163 comparisonline = '\{
150 164 "t": {type|json},
151 165 "ln": {leftlineno|json},
152 166 "ll": {leftline|json},
153 167 "rn": {rightlineno|json},
154 168 "rl": {rightline|json}
155 169 }'
156 170 fileannotate = '\{
157 171 "abspath": {file|json},
158 172 "node": {node|json},
159 173 "author": {author|utf8|json},
160 174 "date": {date|json},
161 175 "desc": {desc|utf8|json},
162 176 "parents": [{join(parent%changesetparent, ", ")}],
163 177 "children": [{join(child%changesetparent, ", ")}],
164 178 "permissions": {permissions|json},
165 179 "annotate": [{join(annotate%fileannotation, ", ")}]
166 180 }'
167 181 fileannotation = '\{
168 182 "node": {node|json},
169 183 "author": {author|utf8|json},
170 184 "desc": {desc|utf8|json},
171 185 "abspath": {file|json},
172 186 "targetline": {targetline|json},
173 187 "line": {line|json},
174 188 "lineno": {lineno|json},
175 189 "revdate": {revdate|json}
176 190 }'
177 191 filelog = '\{
178 192 "entries": [{join(entries%changelistentry, ", ")}]
179 193 }'
180 194 graph = '"not yet implemented"'
181 195 helptopics = '\{
182 196 "topics": [{join(topics%helptopicentry, ", ")}],
183 197 "earlycommands": [{join(earlycommands%helptopicentry, ", ")}],
184 198 "othercommands": [{join(othercommands%helptopicentry, ", ")}]
185 199 }'
186 200 helptopicentry = '\{
187 201 "topic": {topic|utf8|json},
188 202 "summary": {summary|utf8|json}
189 203 }'
190 204 help = '\{
191 205 "topic": {topic|utf8|json},
192 206 "rawdoc": {doc|utf8|json}
193 207 }'
194 208 filenodelink = ''
195 209 filenolink = ''
196 210 index = '\{
197 211 "entries": [{join(entries%indexentry, ", ")}]
198 212 }'
199 213 indexentry = '\{
200 214 "name": {name|utf8|json},
201 215 "description": {description|utf8|json},
202 216 "contact": {contact|utf8|json},
203 217 "lastchange": {lastchange|json}
204 218 }'
@@ -1,1338 +1,1572 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/78896eb0e102/foo-new
115 115 200 Script output follows
116 116
117 117 {
118 118 "bookmarks": [],
119 119 "branch": "default",
120 120 "date": [
121 121 0.0,
122 122 0
123 123 ],
124 124 "desc": "move foo",
125 125 "lines": [
126 126 {
127 127 "line": "bar\n"
128 128 }
129 129 ],
130 130 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
131 131 "parents": [
132 132 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
133 133 ],
134 134 "path": "foo-new",
135 135 "phase": "public",
136 136 "tags": [
137 137 "tag1"
138 138 ],
139 139 "user": "test"
140 140 }
141 141
142 142 file/{revision} shows root directory info
143 143
144 144 $ request json-file/cc725e08502a
145 145 200 Script output follows
146 146
147 147 {
148 148 "abspath": "/",
149 149 "bookmarks": [],
150 150 "directories": [
151 151 {
152 152 "abspath": "/da",
153 153 "basename": "da",
154 154 "emptydirs": ""
155 155 }
156 156 ],
157 157 "files": [
158 158 {
159 159 "abspath": ".hgtags",
160 160 "basename": ".hgtags",
161 161 "date": [
162 162 0.0,
163 163 0
164 164 ],
165 165 "flags": "",
166 166 "size": 92
167 167 },
168 168 {
169 169 "abspath": "foo-new",
170 170 "basename": "foo-new",
171 171 "date": [
172 172 0.0,
173 173 0
174 174 ],
175 175 "flags": "",
176 176 "size": 4
177 177 }
178 178 ],
179 179 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
180 180 "tags": [
181 181 "tip"
182 182 ]
183 183 }
184 184
185 185 changelog/ shows information about several changesets
186 186
187 187 $ request json-changelog
188 188 200 Script output follows
189 189
190 190 {
191 191 "changeset_count": 10,
192 192 "changesets": [
193 193 {
194 194 "bookmarks": [],
195 195 "branch": "default",
196 196 "date": [
197 197 0.0,
198 198 0
199 199 ],
200 200 "desc": "merge test-branch into default",
201 201 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
202 202 "parents": [
203 203 "ceed296fe500c3fac9541e31dad860cb49c89e45",
204 204 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
205 205 ],
206 206 "phase": "draft",
207 207 "tags": [
208 208 "tip"
209 209 ],
210 210 "user": "test"
211 211 },
212 212 {
213 213 "bookmarks": [],
214 214 "branch": "test-branch",
215 215 "date": [
216 216 0.0,
217 217 0
218 218 ],
219 219 "desc": "another commit in test-branch",
220 220 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
221 221 "parents": [
222 222 "6ab967a8ab3489227a83f80e920faa039a71819f"
223 223 ],
224 224 "phase": "draft",
225 225 "tags": [],
226 226 "user": "test"
227 227 },
228 228 {
229 229 "bookmarks": [],
230 230 "branch": "test-branch",
231 231 "date": [
232 232 0.0,
233 233 0
234 234 ],
235 235 "desc": "create test branch",
236 236 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
237 237 "parents": [
238 238 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
239 239 ],
240 240 "phase": "draft",
241 241 "tags": [],
242 242 "user": "test"
243 243 },
244 244 {
245 245 "bookmarks": [
246 246 "bookmark2"
247 247 ],
248 248 "branch": "default",
249 249 "date": [
250 250 0.0,
251 251 0
252 252 ],
253 253 "desc": "create tag2",
254 254 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45",
255 255 "parents": [
256 256 "f2890a05fea49bfaf9fb27ed5490894eba32da78"
257 257 ],
258 258 "phase": "draft",
259 259 "tags": [],
260 260 "user": "test"
261 261 },
262 262 {
263 263 "bookmarks": [],
264 264 "branch": "default",
265 265 "date": [
266 266 0.0,
267 267 0
268 268 ],
269 269 "desc": "another commit to da/foo",
270 270 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
271 271 "parents": [
272 272 "93a8ce14f89156426b7fa981af8042da53f03aa0"
273 273 ],
274 274 "phase": "draft",
275 275 "tags": [
276 276 "tag2"
277 277 ],
278 278 "user": "test"
279 279 },
280 280 {
281 281 "bookmarks": [],
282 282 "branch": "default",
283 283 "date": [
284 284 0.0,
285 285 0
286 286 ],
287 287 "desc": "create tag",
288 288 "node": "93a8ce14f89156426b7fa981af8042da53f03aa0",
289 289 "parents": [
290 290 "78896eb0e102174ce9278438a95e12543e4367a7"
291 291 ],
292 292 "phase": "public",
293 293 "tags": [],
294 294 "user": "test"
295 295 },
296 296 {
297 297 "bookmarks": [],
298 298 "branch": "default",
299 299 "date": [
300 300 0.0,
301 301 0
302 302 ],
303 303 "desc": "move foo",
304 304 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
305 305 "parents": [
306 306 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
307 307 ],
308 308 "phase": "public",
309 309 "tags": [
310 310 "tag1"
311 311 ],
312 312 "user": "test"
313 313 },
314 314 {
315 315 "bookmarks": [
316 316 "bookmark1"
317 317 ],
318 318 "branch": "default",
319 319 "date": [
320 320 0.0,
321 321 0
322 322 ],
323 323 "desc": "modify da/foo",
324 324 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
325 325 "parents": [
326 326 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
327 327 ],
328 328 "phase": "public",
329 329 "tags": [],
330 330 "user": "test"
331 331 },
332 332 {
333 333 "bookmarks": [],
334 334 "branch": "default",
335 335 "date": [
336 336 0.0,
337 337 0
338 338 ],
339 339 "desc": "modify foo",
340 340 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
341 341 "parents": [
342 342 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
343 343 ],
344 344 "phase": "public",
345 345 "tags": [],
346 346 "user": "test"
347 347 },
348 348 {
349 349 "bookmarks": [],
350 350 "branch": "default",
351 351 "date": [
352 352 0.0,
353 353 0
354 354 ],
355 355 "desc": "initial",
356 356 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
357 357 "parents": [],
358 358 "phase": "public",
359 359 "tags": [],
360 360 "user": "test"
361 361 }
362 362 ],
363 363 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
364 364 }
365 365
366 366 changelog/{revision} shows information starting at a specific changeset
367 367
368 368 $ request json-changelog/f8bbb9024b10
369 369 200 Script output follows
370 370
371 371 {
372 372 "changeset_count": 10,
373 373 "changesets": [
374 374 {
375 375 "bookmarks": [],
376 376 "branch": "default",
377 377 "date": [
378 378 0.0,
379 379 0
380 380 ],
381 381 "desc": "modify foo",
382 382 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
383 383 "parents": [
384 384 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
385 385 ],
386 386 "phase": "public",
387 387 "tags": [],
388 388 "user": "test"
389 389 },
390 390 {
391 391 "bookmarks": [],
392 392 "branch": "default",
393 393 "date": [
394 394 0.0,
395 395 0
396 396 ],
397 397 "desc": "initial",
398 398 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
399 399 "parents": [],
400 400 "phase": "public",
401 401 "tags": [],
402 402 "user": "test"
403 403 }
404 404 ],
405 405 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8"
406 406 }
407 407
408 408 shortlog/ shows information about a set of changesets
409 409
410 410 $ request json-shortlog
411 411 200 Script output follows
412 412
413 413 {
414 414 "changeset_count": 10,
415 415 "changesets": [
416 416 {
417 417 "bookmarks": [],
418 418 "branch": "default",
419 419 "date": [
420 420 0.0,
421 421 0
422 422 ],
423 423 "desc": "merge test-branch into default",
424 424 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
425 425 "parents": [
426 426 "ceed296fe500c3fac9541e31dad860cb49c89e45",
427 427 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
428 428 ],
429 429 "phase": "draft",
430 430 "tags": [
431 431 "tip"
432 432 ],
433 433 "user": "test"
434 434 },
435 435 {
436 436 "bookmarks": [],
437 437 "branch": "test-branch",
438 438 "date": [
439 439 0.0,
440 440 0
441 441 ],
442 442 "desc": "another commit in test-branch",
443 443 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
444 444 "parents": [
445 445 "6ab967a8ab3489227a83f80e920faa039a71819f"
446 446 ],
447 447 "phase": "draft",
448 448 "tags": [],
449 449 "user": "test"
450 450 },
451 451 {
452 452 "bookmarks": [],
453 453 "branch": "test-branch",
454 454 "date": [
455 455 0.0,
456 456 0
457 457 ],
458 458 "desc": "create test branch",
459 459 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
460 460 "parents": [
461 461 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
462 462 ],
463 463 "phase": "draft",
464 464 "tags": [],
465 465 "user": "test"
466 466 },
467 467 {
468 468 "bookmarks": [
469 469 "bookmark2"
470 470 ],
471 471 "branch": "default",
472 472 "date": [
473 473 0.0,
474 474 0
475 475 ],
476 476 "desc": "create tag2",
477 477 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45",
478 478 "parents": [
479 479 "f2890a05fea49bfaf9fb27ed5490894eba32da78"
480 480 ],
481 481 "phase": "draft",
482 482 "tags": [],
483 483 "user": "test"
484 484 },
485 485 {
486 486 "bookmarks": [],
487 487 "branch": "default",
488 488 "date": [
489 489 0.0,
490 490 0
491 491 ],
492 492 "desc": "another commit to da/foo",
493 493 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
494 494 "parents": [
495 495 "93a8ce14f89156426b7fa981af8042da53f03aa0"
496 496 ],
497 497 "phase": "draft",
498 498 "tags": [
499 499 "tag2"
500 500 ],
501 501 "user": "test"
502 502 },
503 503 {
504 504 "bookmarks": [],
505 505 "branch": "default",
506 506 "date": [
507 507 0.0,
508 508 0
509 509 ],
510 510 "desc": "create tag",
511 511 "node": "93a8ce14f89156426b7fa981af8042da53f03aa0",
512 512 "parents": [
513 513 "78896eb0e102174ce9278438a95e12543e4367a7"
514 514 ],
515 515 "phase": "public",
516 516 "tags": [],
517 517 "user": "test"
518 518 },
519 519 {
520 520 "bookmarks": [],
521 521 "branch": "default",
522 522 "date": [
523 523 0.0,
524 524 0
525 525 ],
526 526 "desc": "move foo",
527 527 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
528 528 "parents": [
529 529 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
530 530 ],
531 531 "phase": "public",
532 532 "tags": [
533 533 "tag1"
534 534 ],
535 535 "user": "test"
536 536 },
537 537 {
538 538 "bookmarks": [
539 539 "bookmark1"
540 540 ],
541 541 "branch": "default",
542 542 "date": [
543 543 0.0,
544 544 0
545 545 ],
546 546 "desc": "modify da/foo",
547 547 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
548 548 "parents": [
549 549 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
550 550 ],
551 551 "phase": "public",
552 552 "tags": [],
553 553 "user": "test"
554 554 },
555 555 {
556 556 "bookmarks": [],
557 557 "branch": "default",
558 558 "date": [
559 559 0.0,
560 560 0
561 561 ],
562 562 "desc": "modify foo",
563 563 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
564 564 "parents": [
565 565 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
566 566 ],
567 567 "phase": "public",
568 568 "tags": [],
569 569 "user": "test"
570 570 },
571 571 {
572 572 "bookmarks": [],
573 573 "branch": "default",
574 574 "date": [
575 575 0.0,
576 576 0
577 577 ],
578 578 "desc": "initial",
579 579 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
580 580 "parents": [],
581 581 "phase": "public",
582 582 "tags": [],
583 583 "user": "test"
584 584 }
585 585 ],
586 586 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
587 587 }
588 588
589 589 changeset/ renders the tip changeset
590 590
591 591 $ request json-rev
592 592 200 Script output follows
593 593
594 594 {
595 595 "bookmarks": [],
596 596 "branch": "default",
597 597 "date": [
598 598 0.0,
599 599 0
600 600 ],
601 601 "desc": "merge test-branch into default",
602 602 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
603 603 "parents": [
604 604 "ceed296fe500c3fac9541e31dad860cb49c89e45",
605 605 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
606 606 ],
607 607 "phase": "draft",
608 608 "tags": [
609 609 "tip"
610 610 ],
611 611 "user": "test"
612 612 }
613 613
614 614 changeset/{revision} shows tags
615 615
616 616 $ request json-rev/78896eb0e102
617 617 200 Script output follows
618 618
619 619 {
620 620 "bookmarks": [],
621 621 "branch": "default",
622 622 "date": [
623 623 0.0,
624 624 0
625 625 ],
626 626 "desc": "move foo",
627 627 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
628 628 "parents": [
629 629 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
630 630 ],
631 631 "phase": "public",
632 632 "tags": [
633 633 "tag1"
634 634 ],
635 635 "user": "test"
636 636 }
637 637
638 638 changeset/{revision} shows bookmarks
639 639
640 640 $ request json-rev/8d7c456572ac
641 641 200 Script output follows
642 642
643 643 {
644 644 "bookmarks": [
645 645 "bookmark1"
646 646 ],
647 647 "branch": "default",
648 648 "date": [
649 649 0.0,
650 650 0
651 651 ],
652 652 "desc": "modify da/foo",
653 653 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
654 654 "parents": [
655 655 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
656 656 ],
657 657 "phase": "public",
658 658 "tags": [],
659 659 "user": "test"
660 660 }
661 661
662 662 changeset/{revision} shows branches
663 663
664 664 $ request json-rev/6ab967a8ab34
665 665 200 Script output follows
666 666
667 667 {
668 668 "bookmarks": [],
669 669 "branch": "test-branch",
670 670 "date": [
671 671 0.0,
672 672 0
673 673 ],
674 674 "desc": "create test branch",
675 675 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
676 676 "parents": [
677 677 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
678 678 ],
679 679 "phase": "draft",
680 680 "tags": [],
681 681 "user": "test"
682 682 }
683 683
684 684 manifest/{revision}/{path} shows info about a directory at a revision
685 685
686 686 $ request json-manifest/06e557f3edf6/
687 687 200 Script output follows
688 688
689 689 {
690 690 "abspath": "/",
691 691 "bookmarks": [],
692 692 "directories": [
693 693 {
694 694 "abspath": "/da",
695 695 "basename": "da",
696 696 "emptydirs": ""
697 697 }
698 698 ],
699 699 "files": [
700 700 {
701 701 "abspath": "foo",
702 702 "basename": "foo",
703 703 "date": [
704 704 0.0,
705 705 0
706 706 ],
707 707 "flags": "",
708 708 "size": 4
709 709 }
710 710 ],
711 711 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
712 712 "tags": []
713 713 }
714 714
715 715 tags/ shows tags info
716 716
717 717 $ request json-tags
718 718 200 Script output follows
719 719
720 720 {
721 721 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
722 722 "tags": [
723 723 {
724 724 "date": [
725 725 0.0,
726 726 0
727 727 ],
728 728 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
729 729 "tag": "tag2"
730 730 },
731 731 {
732 732 "date": [
733 733 0.0,
734 734 0
735 735 ],
736 736 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
737 737 "tag": "tag1"
738 738 }
739 739 ]
740 740 }
741 741
742 742 bookmarks/ shows bookmarks info
743 743
744 744 $ request json-bookmarks
745 745 200 Script output follows
746 746
747 747 {
748 748 "bookmarks": [
749 749 {
750 750 "bookmark": "bookmark2",
751 751 "date": [
752 752 0.0,
753 753 0
754 754 ],
755 755 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45"
756 756 },
757 757 {
758 758 "bookmark": "bookmark1",
759 759 "date": [
760 760 0.0,
761 761 0
762 762 ],
763 763 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5"
764 764 }
765 765 ],
766 766 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7"
767 767 }
768 768
769 769 branches/ shows branches info
770 770
771 771 $ request json-branches
772 772 200 Script output follows
773 773
774 774 {
775 775 "branches": [
776 776 {
777 777 "branch": "default",
778 778 "date": [
779 779 0.0,
780 780 0
781 781 ],
782 782 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
783 783 "status": "open"
784 784 },
785 785 {
786 786 "branch": "test-branch",
787 787 "date": [
788 788 0.0,
789 789 0
790 790 ],
791 791 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
792 792 "status": "inactive"
793 793 }
794 794 ]
795 795 }
796 796
797 797 summary/ shows a summary of repository state
798 798
799 799 $ request json-summary
800 800 200 Script output follows
801 801
802 "not yet implemented"
802 {
803 "archives": [],
804 "bookmarks": [
805 {
806 "bookmark": "bookmark2",
807 "date": [
808 0.0,
809 0
810 ],
811 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45"
812 },
813 {
814 "bookmark": "bookmark1",
815 "date": [
816 0.0,
817 0
818 ],
819 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5"
820 }
821 ],
822 "branches": [
823 {
824 "branch": "default",
825 "date": [
826 0.0,
827 0
828 ],
829 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
830 "status": "open"
831 },
832 {
833 "branch": "test-branch",
834 "date": [
835 0.0,
836 0
837 ],
838 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
839 "status": "inactive"
840 }
841 ],
842 "lastchange": [
843 0.0,
844 0
845 ],
846 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
847 "shortlog": [
848 {
849 "bookmarks": [],
850 "branch": "default",
851 "date": [
852 0.0,
853 0
854 ],
855 "desc": "merge test-branch into default",
856 "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
857 "parents": [
858 "ceed296fe500c3fac9541e31dad860cb49c89e45",
859 "ed66c30e87eb65337c05a4229efaa5f1d5285a90"
860 ],
861 "phase": "draft",
862 "tags": [
863 "tip"
864 ],
865 "user": "test"
866 },
867 {
868 "bookmarks": [],
869 "branch": "test-branch",
870 "date": [
871 0.0,
872 0
873 ],
874 "desc": "another commit in test-branch",
875 "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
876 "parents": [
877 "6ab967a8ab3489227a83f80e920faa039a71819f"
878 ],
879 "phase": "draft",
880 "tags": [],
881 "user": "test"
882 },
883 {
884 "bookmarks": [],
885 "branch": "test-branch",
886 "date": [
887 0.0,
888 0
889 ],
890 "desc": "create test branch",
891 "node": "6ab967a8ab3489227a83f80e920faa039a71819f",
892 "parents": [
893 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
894 ],
895 "phase": "draft",
896 "tags": [],
897 "user": "test"
898 },
899 {
900 "bookmarks": [
901 "bookmark2"
902 ],
903 "branch": "default",
904 "date": [
905 0.0,
906 0
907 ],
908 "desc": "create tag2",
909 "node": "ceed296fe500c3fac9541e31dad860cb49c89e45",
910 "parents": [
911 "f2890a05fea49bfaf9fb27ed5490894eba32da78"
912 ],
913 "phase": "draft",
914 "tags": [],
915 "user": "test"
916 },
917 {
918 "bookmarks": [],
919 "branch": "default",
920 "date": [
921 0.0,
922 0
923 ],
924 "desc": "another commit to da/foo",
925 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
926 "parents": [
927 "93a8ce14f89156426b7fa981af8042da53f03aa0"
928 ],
929 "phase": "draft",
930 "tags": [
931 "tag2"
932 ],
933 "user": "test"
934 },
935 {
936 "bookmarks": [],
937 "branch": "default",
938 "date": [
939 0.0,
940 0
941 ],
942 "desc": "create tag",
943 "node": "93a8ce14f89156426b7fa981af8042da53f03aa0",
944 "parents": [
945 "78896eb0e102174ce9278438a95e12543e4367a7"
946 ],
947 "phase": "public",
948 "tags": [],
949 "user": "test"
950 },
951 {
952 "bookmarks": [],
953 "branch": "default",
954 "date": [
955 0.0,
956 0
957 ],
958 "desc": "move foo",
959 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
960 "parents": [
961 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
962 ],
963 "phase": "public",
964 "tags": [
965 "tag1"
966 ],
967 "user": "test"
968 },
969 {
970 "bookmarks": [
971 "bookmark1"
972 ],
973 "branch": "default",
974 "date": [
975 0.0,
976 0
977 ],
978 "desc": "modify da/foo",
979 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
980 "parents": [
981 "f8bbb9024b10f93cdbb8d940337398291d40dea8"
982 ],
983 "phase": "public",
984 "tags": [],
985 "user": "test"
986 },
987 {
988 "bookmarks": [],
989 "branch": "default",
990 "date": [
991 0.0,
992 0
993 ],
994 "desc": "modify foo",
995 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
996 "parents": [
997 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
998 ],
999 "phase": "public",
1000 "tags": [],
1001 "user": "test"
1002 },
1003 {
1004 "bookmarks": [],
1005 "branch": "default",
1006 "date": [
1007 0.0,
1008 0
1009 ],
1010 "desc": "initial",
1011 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
1012 "parents": [],
1013 "phase": "public",
1014 "tags": [],
1015 "user": "test"
1016 }
1017 ],
1018 "tags": [
1019 {
1020 "date": [
1021 0.0,
1022 0
1023 ],
1024 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
1025 "tag": "tag2"
1026 },
1027 {
1028 "date": [
1029 0.0,
1030 0
1031 ],
1032 "node": "78896eb0e102174ce9278438a95e12543e4367a7",
1033 "tag": "tag1"
1034 }
1035 ]
1036 }
803 1037
804 1038 filediff/{revision}/{path} shows changes to a file in a revision
805 1039
806 1040 $ request json-diff/f8bbb9024b10/foo
807 1041 200 Script output follows
808 1042
809 1043 {
810 1044 "author": "test",
811 1045 "children": [],
812 1046 "date": [
813 1047 0.0,
814 1048 0
815 1049 ],
816 1050 "desc": "modify foo",
817 1051 "diff": [
818 1052 {
819 1053 "blockno": 1,
820 1054 "lines": [
821 1055 {
822 1056 "l": "--- a/foo\tThu Jan 01 00:00:00 1970 +0000\n",
823 1057 "n": 1,
824 1058 "t": "-"
825 1059 },
826 1060 {
827 1061 "l": "+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n",
828 1062 "n": 2,
829 1063 "t": "+"
830 1064 },
831 1065 {
832 1066 "l": "@@ -1,1 +1,1 @@\n",
833 1067 "n": 3,
834 1068 "t": "@"
835 1069 },
836 1070 {
837 1071 "l": "-foo\n",
838 1072 "n": 4,
839 1073 "t": "-"
840 1074 },
841 1075 {
842 1076 "l": "+bar\n",
843 1077 "n": 5,
844 1078 "t": "+"
845 1079 }
846 1080 ]
847 1081 }
848 1082 ],
849 1083 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
850 1084 "parents": [
851 1085 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
852 1086 ],
853 1087 "path": "foo"
854 1088 }
855 1089
856 1090 comparison/{revision}/{path} shows information about before and after for a file
857 1091
858 1092 $ request json-comparison/f8bbb9024b10/foo
859 1093 200 Script output follows
860 1094
861 1095 {
862 1096 "author": "test",
863 1097 "children": [],
864 1098 "comparison": [
865 1099 {
866 1100 "lines": [
867 1101 {
868 1102 "ll": "foo",
869 1103 "ln": 1,
870 1104 "rl": "bar",
871 1105 "rn": 1,
872 1106 "t": "replace"
873 1107 }
874 1108 ]
875 1109 }
876 1110 ],
877 1111 "date": [
878 1112 0.0,
879 1113 0
880 1114 ],
881 1115 "desc": "modify foo",
882 1116 "leftnode": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
883 1117 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
884 1118 "parents": [
885 1119 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
886 1120 ],
887 1121 "path": "foo",
888 1122 "rightnode": "f8bbb9024b10f93cdbb8d940337398291d40dea8"
889 1123 }
890 1124
891 1125 annotate/{revision}/{path} shows annotations for each line
892 1126
893 1127 $ request json-annotate/f8bbb9024b10/foo
894 1128 200 Script output follows
895 1129
896 1130 {
897 1131 "abspath": "foo",
898 1132 "annotate": [
899 1133 {
900 1134 "abspath": "foo",
901 1135 "author": "test",
902 1136 "desc": "modify foo",
903 1137 "line": "bar\n",
904 1138 "lineno": 1,
905 1139 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
906 1140 "revdate": [
907 1141 0.0,
908 1142 0
909 1143 ],
910 1144 "targetline": 1
911 1145 }
912 1146 ],
913 1147 "author": "test",
914 1148 "children": [],
915 1149 "date": [
916 1150 0.0,
917 1151 0
918 1152 ],
919 1153 "desc": "modify foo",
920 1154 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
921 1155 "parents": [
922 1156 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
923 1157 ],
924 1158 "permissions": ""
925 1159 }
926 1160
927 1161 filelog/{revision}/{path} shows history of a single file
928 1162
929 1163 $ request json-filelog/f8bbb9024b10/foo
930 1164 200 Script output follows
931 1165
932 1166 {
933 1167 "entries": [
934 1168 {
935 1169 "bookmarks": [],
936 1170 "branch": "default",
937 1171 "date": [
938 1172 0.0,
939 1173 0
940 1174 ],
941 1175 "desc": "modify foo",
942 1176 "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8",
943 1177 "parents": [
944 1178 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
945 1179 ],
946 1180 "phase": "public",
947 1181 "tags": [],
948 1182 "user": "test"
949 1183 },
950 1184 {
951 1185 "bookmarks": [],
952 1186 "branch": "default",
953 1187 "date": [
954 1188 0.0,
955 1189 0
956 1190 ],
957 1191 "desc": "initial",
958 1192 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
959 1193 "parents": [],
960 1194 "phase": "public",
961 1195 "tags": [],
962 1196 "user": "test"
963 1197 }
964 1198 ]
965 1199 }
966 1200
967 1201 $ request json-filelog/cc725e08502a/da/foo
968 1202 200 Script output follows
969 1203
970 1204 {
971 1205 "entries": [
972 1206 {
973 1207 "bookmarks": [],
974 1208 "branch": "default",
975 1209 "date": [
976 1210 0.0,
977 1211 0
978 1212 ],
979 1213 "desc": "another commit to da/foo",
980 1214 "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78",
981 1215 "parents": [
982 1216 "8d7c456572acf3557e8ed8a07286b10c408bcec5"
983 1217 ],
984 1218 "phase": "draft",
985 1219 "tags": [
986 1220 "tag2"
987 1221 ],
988 1222 "user": "test"
989 1223 },
990 1224 {
991 1225 "bookmarks": [
992 1226 "bookmark1"
993 1227 ],
994 1228 "branch": "default",
995 1229 "date": [
996 1230 0.0,
997 1231 0
998 1232 ],
999 1233 "desc": "modify da/foo",
1000 1234 "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5",
1001 1235 "parents": [
1002 1236 "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e"
1003 1237 ],
1004 1238 "phase": "public",
1005 1239 "tags": [],
1006 1240 "user": "test"
1007 1241 },
1008 1242 {
1009 1243 "bookmarks": [],
1010 1244 "branch": "default",
1011 1245 "date": [
1012 1246 0.0,
1013 1247 0
1014 1248 ],
1015 1249 "desc": "initial",
1016 1250 "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e",
1017 1251 "parents": [],
1018 1252 "phase": "public",
1019 1253 "tags": [],
1020 1254 "user": "test"
1021 1255 }
1022 1256 ]
1023 1257 }
1024 1258
1025 1259 (archive/ doesn't use templating, so ignore it)
1026 1260
1027 1261 (static/ doesn't use templating, so ignore it)
1028 1262
1029 1263 graph/ shows information that can be used to render a graph of the DAG
1030 1264
1031 1265 $ request json-graph
1032 1266 200 Script output follows
1033 1267
1034 1268 "not yet implemented"
1035 1269
1036 1270 help/ shows help topics
1037 1271
1038 1272 $ request json-help
1039 1273 200 Script output follows
1040 1274
1041 1275 {
1042 1276 "earlycommands": [
1043 1277 {
1044 1278 "summary": "add the specified files on the next commit",
1045 1279 "topic": "add"
1046 1280 },
1047 1281 {
1048 1282 "summary": "show changeset information by line for each file",
1049 1283 "topic": "annotate"
1050 1284 },
1051 1285 {
1052 1286 "summary": "make a copy of an existing repository",
1053 1287 "topic": "clone"
1054 1288 },
1055 1289 {
1056 1290 "summary": "commit the specified files or all outstanding changes",
1057 1291 "topic": "commit"
1058 1292 },
1059 1293 {
1060 1294 "summary": "diff repository (or selected files)",
1061 1295 "topic": "diff"
1062 1296 },
1063 1297 {
1064 1298 "summary": "dump the header and diffs for one or more changesets",
1065 1299 "topic": "export"
1066 1300 },
1067 1301 {
1068 1302 "summary": "forget the specified files on the next commit",
1069 1303 "topic": "forget"
1070 1304 },
1071 1305 {
1072 1306 "summary": "create a new repository in the given directory",
1073 1307 "topic": "init"
1074 1308 },
1075 1309 {
1076 1310 "summary": "show revision history of entire repository or files",
1077 1311 "topic": "log"
1078 1312 },
1079 1313 {
1080 1314 "summary": "merge another revision into working directory",
1081 1315 "topic": "merge"
1082 1316 },
1083 1317 {
1084 1318 "summary": "pull changes from the specified source",
1085 1319 "topic": "pull"
1086 1320 },
1087 1321 {
1088 1322 "summary": "push changes to the specified destination",
1089 1323 "topic": "push"
1090 1324 },
1091 1325 {
1092 1326 "summary": "remove the specified files on the next commit",
1093 1327 "topic": "remove"
1094 1328 },
1095 1329 {
1096 1330 "summary": "start stand-alone webserver",
1097 1331 "topic": "serve"
1098 1332 },
1099 1333 {
1100 1334 "summary": "show changed files in the working directory",
1101 1335 "topic": "status"
1102 1336 },
1103 1337 {
1104 1338 "summary": "summarize working directory state",
1105 1339 "topic": "summary"
1106 1340 },
1107 1341 {
1108 1342 "summary": "update working directory (or switch revisions)",
1109 1343 "topic": "update"
1110 1344 }
1111 1345 ],
1112 1346 "othercommands": [
1113 1347 {
1114 1348 "summary": "add all new files, delete all missing files",
1115 1349 "topic": "addremove"
1116 1350 },
1117 1351 {
1118 1352 "summary": "create an unversioned archive of a repository revision",
1119 1353 "topic": "archive"
1120 1354 },
1121 1355 {
1122 1356 "summary": "reverse effect of earlier changeset",
1123 1357 "topic": "backout"
1124 1358 },
1125 1359 {
1126 1360 "summary": "subdivision search of changesets",
1127 1361 "topic": "bisect"
1128 1362 },
1129 1363 {
1130 1364 "summary": "create a new bookmark or list existing bookmarks",
1131 1365 "topic": "bookmarks"
1132 1366 },
1133 1367 {
1134 1368 "summary": "set or show the current branch name",
1135 1369 "topic": "branch"
1136 1370 },
1137 1371 {
1138 1372 "summary": "list repository named branches",
1139 1373 "topic": "branches"
1140 1374 },
1141 1375 {
1142 1376 "summary": "create a changegroup file",
1143 1377 "topic": "bundle"
1144 1378 },
1145 1379 {
1146 1380 "summary": "output the current or given revision of files",
1147 1381 "topic": "cat"
1148 1382 },
1149 1383 {
1150 1384 "summary": "show combined config settings from all hgrc files",
1151 1385 "topic": "config"
1152 1386 },
1153 1387 {
1154 1388 "summary": "mark files as copied for the next commit",
1155 1389 "topic": "copy"
1156 1390 },
1157 1391 {
1158 1392 "summary": "list tracked files",
1159 1393 "topic": "files"
1160 1394 },
1161 1395 {
1162 1396 "summary": "copy changes from other branches onto the current branch",
1163 1397 "topic": "graft"
1164 1398 },
1165 1399 {
1166 1400 "summary": "search for a pattern in specified files and revisions",
1167 1401 "topic": "grep"
1168 1402 },
1169 1403 {
1170 1404 "summary": "show branch heads",
1171 1405 "topic": "heads"
1172 1406 },
1173 1407 {
1174 1408 "summary": "show help for a given topic or a help overview",
1175 1409 "topic": "help"
1176 1410 },
1177 1411 {
1178 1412 "summary": "identify the working directory or specified revision",
1179 1413 "topic": "identify"
1180 1414 },
1181 1415 {
1182 1416 "summary": "import an ordered set of patches",
1183 1417 "topic": "import"
1184 1418 },
1185 1419 {
1186 1420 "summary": "show new changesets found in source",
1187 1421 "topic": "incoming"
1188 1422 },
1189 1423 {
1190 1424 "summary": "output the current or given revision of the project manifest",
1191 1425 "topic": "manifest"
1192 1426 },
1193 1427 {
1194 1428 "summary": "show changesets not found in the destination",
1195 1429 "topic": "outgoing"
1196 1430 },
1197 1431 {
1198 1432 "summary": "show aliases for remote repositories",
1199 1433 "topic": "paths"
1200 1434 },
1201 1435 {
1202 1436 "summary": "set or show the current phase name",
1203 1437 "topic": "phase"
1204 1438 },
1205 1439 {
1206 1440 "summary": "roll back an interrupted transaction",
1207 1441 "topic": "recover"
1208 1442 },
1209 1443 {
1210 1444 "summary": "rename files; equivalent of copy + remove",
1211 1445 "topic": "rename"
1212 1446 },
1213 1447 {
1214 1448 "summary": "redo merges or set/view the merge status of files",
1215 1449 "topic": "resolve"
1216 1450 },
1217 1451 {
1218 1452 "summary": "restore files to their checkout state",
1219 1453 "topic": "revert"
1220 1454 },
1221 1455 {
1222 1456 "summary": "print the root (top) of the current working directory",
1223 1457 "topic": "root"
1224 1458 },
1225 1459 {
1226 1460 "summary": "add one or more tags for the current or given revision",
1227 1461 "topic": "tag"
1228 1462 },
1229 1463 {
1230 1464 "summary": "list repository tags",
1231 1465 "topic": "tags"
1232 1466 },
1233 1467 {
1234 1468 "summary": "apply one or more changegroup files",
1235 1469 "topic": "unbundle"
1236 1470 },
1237 1471 {
1238 1472 "summary": "verify the integrity of the repository",
1239 1473 "topic": "verify"
1240 1474 },
1241 1475 {
1242 1476 "summary": "output version and copyright information",
1243 1477 "topic": "version"
1244 1478 }
1245 1479 ],
1246 1480 "topics": [
1247 1481 {
1248 1482 "summary": "Configuration Files",
1249 1483 "topic": "config"
1250 1484 },
1251 1485 {
1252 1486 "summary": "Date Formats",
1253 1487 "topic": "dates"
1254 1488 },
1255 1489 {
1256 1490 "summary": "Diff Formats",
1257 1491 "topic": "diffs"
1258 1492 },
1259 1493 {
1260 1494 "summary": "Environment Variables",
1261 1495 "topic": "environment"
1262 1496 },
1263 1497 {
1264 1498 "summary": "Using Additional Features",
1265 1499 "topic": "extensions"
1266 1500 },
1267 1501 {
1268 1502 "summary": "Specifying File Sets",
1269 1503 "topic": "filesets"
1270 1504 },
1271 1505 {
1272 1506 "summary": "Glossary",
1273 1507 "topic": "glossary"
1274 1508 },
1275 1509 {
1276 1510 "summary": "Syntax for Mercurial Ignore Files",
1277 1511 "topic": "hgignore"
1278 1512 },
1279 1513 {
1280 1514 "summary": "Configuring hgweb",
1281 1515 "topic": "hgweb"
1282 1516 },
1283 1517 {
1284 1518 "summary": "Technical implementation topics",
1285 1519 "topic": "internals"
1286 1520 },
1287 1521 {
1288 1522 "summary": "Merge Tools",
1289 1523 "topic": "merge-tools"
1290 1524 },
1291 1525 {
1292 1526 "summary": "Specifying Multiple Revisions",
1293 1527 "topic": "multirevs"
1294 1528 },
1295 1529 {
1296 1530 "summary": "File Name Patterns",
1297 1531 "topic": "patterns"
1298 1532 },
1299 1533 {
1300 1534 "summary": "Working with Phases",
1301 1535 "topic": "phases"
1302 1536 },
1303 1537 {
1304 1538 "summary": "Specifying Single Revisions",
1305 1539 "topic": "revisions"
1306 1540 },
1307 1541 {
1308 1542 "summary": "Specifying Revision Sets",
1309 1543 "topic": "revsets"
1310 1544 },
1311 1545 {
1312 1546 "summary": "Using Mercurial from scripts and automation",
1313 1547 "topic": "scripting"
1314 1548 },
1315 1549 {
1316 1550 "summary": "Subrepositories",
1317 1551 "topic": "subrepos"
1318 1552 },
1319 1553 {
1320 1554 "summary": "Template Usage",
1321 1555 "topic": "templating"
1322 1556 },
1323 1557 {
1324 1558 "summary": "URL Paths",
1325 1559 "topic": "urls"
1326 1560 }
1327 1561 ]
1328 1562 }
1329 1563
1330 1564 help/{topic} shows an individual help topic
1331 1565
1332 1566 $ request json-help/phases
1333 1567 200 Script output follows
1334 1568
1335 1569 {
1336 1570 "rawdoc": "Working with Phases\n*", (glob)
1337 1571 "topic": "phases"
1338 1572 }
General Comments 0
You need to be logged in to leave comments. Login now