##// END OF EJS Templates
merge with stable
Matt Mackall -
r25695:ce3d4b85 merge default
parent child Browse files
Show More
@@ -1,702 +1,708
1 1 # Patch transplanting extension for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.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 '''command to transplant changesets from another branch
9 9
10 10 This extension allows you to transplant changes to another parent revision,
11 11 possibly in another repository. The transplant is done using 'diff' patches.
12 12
13 13 Transplanted patches are recorded in .hg/transplant/transplants, as a
14 14 map from a changeset hash to its hash in the source repository.
15 15 '''
16 16
17 17 from mercurial.i18n import _
18 18 import os, tempfile
19 19 from mercurial.node import short
20 20 from mercurial import bundlerepo, hg, merge, match
21 21 from mercurial import patch, revlog, scmutil, util, error, cmdutil
22 22 from mercurial import revset, templatekw, exchange
23 23
24 24 class TransplantError(error.Abort):
25 25 pass
26 26
27 27 cmdtable = {}
28 28 command = cmdutil.command(cmdtable)
29 29 # Note for extension authors: ONLY specify testedwith = 'internal' for
30 30 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
31 31 # be specifying the version(s) of Mercurial they are tested with, or
32 32 # leave the attribute unspecified.
33 33 testedwith = 'internal'
34 34
35 35 class transplantentry(object):
36 36 def __init__(self, lnode, rnode):
37 37 self.lnode = lnode
38 38 self.rnode = rnode
39 39
40 40 class transplants(object):
41 41 def __init__(self, path=None, transplantfile=None, opener=None):
42 42 self.path = path
43 43 self.transplantfile = transplantfile
44 44 self.opener = opener
45 45
46 46 if not opener:
47 47 self.opener = scmutil.opener(self.path)
48 48 self.transplants = {}
49 49 self.dirty = False
50 50 self.read()
51 51
52 52 def read(self):
53 53 abspath = os.path.join(self.path, self.transplantfile)
54 54 if self.transplantfile and os.path.exists(abspath):
55 55 for line in self.opener.read(self.transplantfile).splitlines():
56 56 lnode, rnode = map(revlog.bin, line.split(':'))
57 57 list = self.transplants.setdefault(rnode, [])
58 58 list.append(transplantentry(lnode, rnode))
59 59
60 60 def write(self):
61 61 if self.dirty and self.transplantfile:
62 62 if not os.path.isdir(self.path):
63 63 os.mkdir(self.path)
64 64 fp = self.opener(self.transplantfile, 'w')
65 65 for list in self.transplants.itervalues():
66 66 for t in list:
67 67 l, r = map(revlog.hex, (t.lnode, t.rnode))
68 68 fp.write(l + ':' + r + '\n')
69 69 fp.close()
70 70 self.dirty = False
71 71
72 72 def get(self, rnode):
73 73 return self.transplants.get(rnode) or []
74 74
75 75 def set(self, lnode, rnode):
76 76 list = self.transplants.setdefault(rnode, [])
77 77 list.append(transplantentry(lnode, rnode))
78 78 self.dirty = True
79 79
80 80 def remove(self, transplant):
81 81 list = self.transplants.get(transplant.rnode)
82 82 if list:
83 83 del list[list.index(transplant)]
84 84 self.dirty = True
85 85
86 86 class transplanter(object):
87 87 def __init__(self, ui, repo, opts):
88 88 self.ui = ui
89 89 self.path = repo.join('transplant')
90 90 self.opener = scmutil.opener(self.path)
91 91 self.transplants = transplants(self.path, 'transplants',
92 92 opener=self.opener)
93 93 def getcommiteditor():
94 94 editform = cmdutil.mergeeditform(repo[None], 'transplant')
95 95 return cmdutil.getcommiteditor(editform=editform, **opts)
96 96 self.getcommiteditor = getcommiteditor
97 97
98 98 def applied(self, repo, node, parent):
99 99 '''returns True if a node is already an ancestor of parent
100 100 or is parent or has already been transplanted'''
101 101 if hasnode(repo, parent):
102 102 parentrev = repo.changelog.rev(parent)
103 103 if hasnode(repo, node):
104 104 rev = repo.changelog.rev(node)
105 105 reachable = repo.changelog.ancestors([parentrev], rev,
106 106 inclusive=True)
107 107 if rev in reachable:
108 108 return True
109 109 for t in self.transplants.get(node):
110 110 # it might have been stripped
111 111 if not hasnode(repo, t.lnode):
112 112 self.transplants.remove(t)
113 113 return False
114 114 lnoderev = repo.changelog.rev(t.lnode)
115 115 if lnoderev in repo.changelog.ancestors([parentrev], lnoderev,
116 116 inclusive=True):
117 117 return True
118 118 return False
119 119
120 120 def apply(self, repo, source, revmap, merges, opts={}):
121 121 '''apply the revisions in revmap one by one in revision order'''
122 122 revs = sorted(revmap)
123 123 p1, p2 = repo.dirstate.parents()
124 124 pulls = []
125 125 diffopts = patch.difffeatureopts(self.ui, opts)
126 126 diffopts.git = True
127 127
128 128 lock = wlock = tr = None
129 129 try:
130 130 wlock = repo.wlock()
131 131 lock = repo.lock()
132 132 tr = repo.transaction('transplant')
133 133 for rev in revs:
134 134 node = revmap[rev]
135 135 revstr = '%s:%s' % (rev, short(node))
136 136
137 137 if self.applied(repo, node, p1):
138 138 self.ui.warn(_('skipping already applied revision %s\n') %
139 139 revstr)
140 140 continue
141 141
142 142 parents = source.changelog.parents(node)
143 143 if not (opts.get('filter') or opts.get('log')):
144 144 # If the changeset parent is the same as the
145 145 # wdir's parent, just pull it.
146 146 if parents[0] == p1:
147 147 pulls.append(node)
148 148 p1 = node
149 149 continue
150 150 if pulls:
151 151 if source != repo:
152 152 exchange.pull(repo, source.peer(), heads=pulls)
153 153 merge.update(repo, pulls[-1], False, False, None)
154 154 p1, p2 = repo.dirstate.parents()
155 155 pulls = []
156 156
157 157 domerge = False
158 158 if node in merges:
159 159 # pulling all the merge revs at once would mean we
160 160 # couldn't transplant after the latest even if
161 161 # transplants before them fail.
162 162 domerge = True
163 163 if not hasnode(repo, node):
164 164 exchange.pull(repo, source.peer(), heads=[node])
165 165
166 166 skipmerge = False
167 167 if parents[1] != revlog.nullid:
168 168 if not opts.get('parent'):
169 169 self.ui.note(_('skipping merge changeset %s:%s\n')
170 170 % (rev, short(node)))
171 171 skipmerge = True
172 172 else:
173 173 parent = source.lookup(opts['parent'])
174 174 if parent not in parents:
175 175 raise util.Abort(_('%s is not a parent of %s') %
176 176 (short(parent), short(node)))
177 177 else:
178 178 parent = parents[0]
179 179
180 180 if skipmerge:
181 181 patchfile = None
182 182 else:
183 183 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
184 184 fp = os.fdopen(fd, 'w')
185 185 gen = patch.diff(source, parent, node, opts=diffopts)
186 186 for chunk in gen:
187 187 fp.write(chunk)
188 188 fp.close()
189 189
190 190 del revmap[rev]
191 191 if patchfile or domerge:
192 192 try:
193 193 try:
194 194 n = self.applyone(repo, node,
195 195 source.changelog.read(node),
196 196 patchfile, merge=domerge,
197 197 log=opts.get('log'),
198 198 filter=opts.get('filter'))
199 199 except TransplantError:
200 200 # Do not rollback, it is up to the user to
201 201 # fix the merge or cancel everything
202 202 tr.close()
203 203 raise
204 204 if n and domerge:
205 205 self.ui.status(_('%s merged at %s\n') % (revstr,
206 206 short(n)))
207 207 elif n:
208 208 self.ui.status(_('%s transplanted to %s\n')
209 209 % (short(node),
210 210 short(n)))
211 211 finally:
212 212 if patchfile:
213 213 os.unlink(patchfile)
214 214 tr.close()
215 215 if pulls:
216 216 exchange.pull(repo, source.peer(), heads=pulls)
217 217 merge.update(repo, pulls[-1], False, False, None)
218 218 finally:
219 219 self.saveseries(revmap, merges)
220 220 self.transplants.write()
221 221 if tr:
222 222 tr.release()
223 223 lock.release()
224 224 wlock.release()
225 225
226 226 def filter(self, filter, node, changelog, patchfile):
227 227 '''arbitrarily rewrite changeset before applying it'''
228 228
229 229 self.ui.status(_('filtering %s\n') % patchfile)
230 230 user, date, msg = (changelog[1], changelog[2], changelog[4])
231 231 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
232 232 fp = os.fdopen(fd, 'w')
233 233 fp.write("# HG changeset patch\n")
234 234 fp.write("# User %s\n" % user)
235 235 fp.write("# Date %d %d\n" % date)
236 236 fp.write(msg + '\n')
237 237 fp.close()
238 238
239 239 try:
240 240 self.ui.system('%s %s %s' % (filter, util.shellquote(headerfile),
241 241 util.shellquote(patchfile)),
242 242 environ={'HGUSER': changelog[1],
243 243 'HGREVISION': revlog.hex(node),
244 244 },
245 245 onerr=util.Abort, errprefix=_('filter failed'))
246 246 user, date, msg = self.parselog(file(headerfile))[1:4]
247 247 finally:
248 248 os.unlink(headerfile)
249 249
250 250 return (user, date, msg)
251 251
252 252 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
253 253 filter=None):
254 254 '''apply the patch in patchfile to the repository as a transplant'''
255 255 (manifest, user, (time, timezone), files, message) = cl[:5]
256 256 date = "%d %d" % (time, timezone)
257 257 extra = {'transplant_source': node}
258 258 if filter:
259 259 (user, date, message) = self.filter(filter, node, cl, patchfile)
260 260
261 261 if log:
262 262 # we don't translate messages inserted into commits
263 263 message += '\n(transplanted from %s)' % revlog.hex(node)
264 264
265 265 self.ui.status(_('applying %s\n') % short(node))
266 266 self.ui.note('%s %s\n%s\n' % (user, date, message))
267 267
268 268 if not patchfile and not merge:
269 269 raise util.Abort(_('can only omit patchfile if merging'))
270 270 if patchfile:
271 271 try:
272 272 files = set()
273 273 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
274 274 files = list(files)
275 275 except Exception as inst:
276 276 seriespath = os.path.join(self.path, 'series')
277 277 if os.path.exists(seriespath):
278 278 os.unlink(seriespath)
279 279 p1 = repo.dirstate.p1()
280 280 p2 = node
281 281 self.log(user, date, message, p1, p2, merge=merge)
282 282 self.ui.write(str(inst) + '\n')
283 283 raise TransplantError(_('fix up the merge and run '
284 284 'hg transplant --continue'))
285 285 else:
286 286 files = None
287 287 if merge:
288 288 p1, p2 = repo.dirstate.parents()
289 289 repo.setparents(p1, node)
290 290 m = match.always(repo.root, '')
291 291 else:
292 292 m = match.exact(repo.root, '', files)
293 293
294 294 n = repo.commit(message, user, date, extra=extra, match=m,
295 295 editor=self.getcommiteditor())
296 296 if not n:
297 297 self.ui.warn(_('skipping emptied changeset %s\n') % short(node))
298 298 return None
299 299 if not merge:
300 300 self.transplants.set(n, node)
301 301
302 302 return n
303 303
304 304 def resume(self, repo, source, opts):
305 305 '''recover last transaction and apply remaining changesets'''
306 306 if os.path.exists(os.path.join(self.path, 'journal')):
307 307 n, node = self.recover(repo, source, opts)
308 308 if n:
309 309 self.ui.status(_('%s transplanted as %s\n') % (short(node),
310 310 short(n)))
311 311 else:
312 312 self.ui.status(_('%s skipped due to empty diff\n')
313 313 % (short(node),))
314 314 seriespath = os.path.join(self.path, 'series')
315 315 if not os.path.exists(seriespath):
316 316 self.transplants.write()
317 317 return
318 318 nodes, merges = self.readseries()
319 319 revmap = {}
320 320 for n in nodes:
321 321 revmap[source.changelog.rev(n)] = n
322 322 os.unlink(seriespath)
323 323
324 324 self.apply(repo, source, revmap, merges, opts)
325 325
326 326 def recover(self, repo, source, opts):
327 327 '''commit working directory using journal metadata'''
328 328 node, user, date, message, parents = self.readlog()
329 329 merge = False
330 330
331 331 if not user or not date or not message or not parents[0]:
332 332 raise util.Abort(_('transplant log file is corrupt'))
333 333
334 334 parent = parents[0]
335 335 if len(parents) > 1:
336 336 if opts.get('parent'):
337 337 parent = source.lookup(opts['parent'])
338 338 if parent not in parents:
339 339 raise util.Abort(_('%s is not a parent of %s') %
340 340 (short(parent), short(node)))
341 341 else:
342 342 merge = True
343 343
344 344 extra = {'transplant_source': node}
345 345 wlock = repo.wlock()
346 346 try:
347 347 p1, p2 = repo.dirstate.parents()
348 348 if p1 != parent:
349 349 raise util.Abort(_('working directory not at transplant '
350 350 'parent %s') % revlog.hex(parent))
351 351 if merge:
352 352 repo.setparents(p1, parents[1])
353 353 modified, added, removed, deleted = repo.status()[:4]
354 354 if merge or modified or added or removed or deleted:
355 355 n = repo.commit(message, user, date, extra=extra,
356 356 editor=self.getcommiteditor())
357 357 if not n:
358 358 raise util.Abort(_('commit failed'))
359 359 if not merge:
360 360 self.transplants.set(n, node)
361 361 else:
362 362 n = None
363 363 self.unlog()
364 364
365 365 return n, node
366 366 finally:
367 367 wlock.release()
368 368
369 369 def readseries(self):
370 370 nodes = []
371 371 merges = []
372 372 cur = nodes
373 373 for line in self.opener.read('series').splitlines():
374 374 if line.startswith('# Merges'):
375 375 cur = merges
376 376 continue
377 377 cur.append(revlog.bin(line))
378 378
379 379 return (nodes, merges)
380 380
381 381 def saveseries(self, revmap, merges):
382 382 if not revmap:
383 383 return
384 384
385 385 if not os.path.isdir(self.path):
386 386 os.mkdir(self.path)
387 387 series = self.opener('series', 'w')
388 388 for rev in sorted(revmap):
389 389 series.write(revlog.hex(revmap[rev]) + '\n')
390 390 if merges:
391 391 series.write('# Merges\n')
392 392 for m in merges:
393 393 series.write(revlog.hex(m) + '\n')
394 394 series.close()
395 395
396 396 def parselog(self, fp):
397 397 parents = []
398 398 message = []
399 399 node = revlog.nullid
400 400 inmsg = False
401 401 user = None
402 402 date = None
403 403 for line in fp.read().splitlines():
404 404 if inmsg:
405 405 message.append(line)
406 406 elif line.startswith('# User '):
407 407 user = line[7:]
408 408 elif line.startswith('# Date '):
409 409 date = line[7:]
410 410 elif line.startswith('# Node ID '):
411 411 node = revlog.bin(line[10:])
412 412 elif line.startswith('# Parent '):
413 413 parents.append(revlog.bin(line[9:]))
414 414 elif not line.startswith('# '):
415 415 inmsg = True
416 416 message.append(line)
417 417 if None in (user, date):
418 418 raise util.Abort(_("filter corrupted changeset (no user or date)"))
419 419 return (node, user, date, '\n'.join(message), parents)
420 420
421 421 def log(self, user, date, message, p1, p2, merge=False):
422 422 '''journal changelog metadata for later recover'''
423 423
424 424 if not os.path.isdir(self.path):
425 425 os.mkdir(self.path)
426 426 fp = self.opener('journal', 'w')
427 427 fp.write('# User %s\n' % user)
428 428 fp.write('# Date %s\n' % date)
429 429 fp.write('# Node ID %s\n' % revlog.hex(p2))
430 430 fp.write('# Parent ' + revlog.hex(p1) + '\n')
431 431 if merge:
432 432 fp.write('# Parent ' + revlog.hex(p2) + '\n')
433 433 fp.write(message.rstrip() + '\n')
434 434 fp.close()
435 435
436 436 def readlog(self):
437 437 return self.parselog(self.opener('journal'))
438 438
439 439 def unlog(self):
440 440 '''remove changelog journal'''
441 441 absdst = os.path.join(self.path, 'journal')
442 442 if os.path.exists(absdst):
443 443 os.unlink(absdst)
444 444
445 445 def transplantfilter(self, repo, source, root):
446 446 def matchfn(node):
447 447 if self.applied(repo, node, root):
448 448 return False
449 449 if source.changelog.parents(node)[1] != revlog.nullid:
450 450 return False
451 451 extra = source.changelog.read(node)[5]
452 452 cnode = extra.get('transplant_source')
453 453 if cnode and self.applied(repo, cnode, root):
454 454 return False
455 455 return True
456 456
457 457 return matchfn
458 458
459 459 def hasnode(repo, node):
460 460 try:
461 461 return repo.changelog.rev(node) is not None
462 462 except error.RevlogError:
463 463 return False
464 464
465 465 def browserevs(ui, repo, nodes, opts):
466 466 '''interactively transplant changesets'''
467 467 displayer = cmdutil.show_changeset(ui, repo, opts)
468 468 transplants = []
469 469 merges = []
470 470 prompt = _('apply changeset? [ynmpcq?]:'
471 471 '$$ &yes, transplant this changeset'
472 472 '$$ &no, skip this changeset'
473 473 '$$ &merge at this changeset'
474 474 '$$ show &patch'
475 475 '$$ &commit selected changesets'
476 476 '$$ &quit and cancel transplant'
477 477 '$$ &? (show this help)')
478 478 for node in nodes:
479 479 displayer.show(repo[node])
480 480 action = None
481 481 while not action:
482 482 action = 'ynmpcq?'[ui.promptchoice(prompt)]
483 483 if action == '?':
484 484 for c, t in ui.extractchoices(prompt)[1]:
485 485 ui.write('%s: %s\n' % (c, t))
486 486 action = None
487 487 elif action == 'p':
488 488 parent = repo.changelog.parents(node)[0]
489 489 for chunk in patch.diff(repo, parent, node):
490 490 ui.write(chunk)
491 491 action = None
492 492 if action == 'y':
493 493 transplants.append(node)
494 494 elif action == 'm':
495 495 merges.append(node)
496 496 elif action == 'c':
497 497 break
498 498 elif action == 'q':
499 499 transplants = ()
500 500 merges = ()
501 501 break
502 502 displayer.close()
503 503 return (transplants, merges)
504 504
505 505 @command('transplant',
506 506 [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
507 507 ('b', 'branch', [], _('use this source changeset as head'), _('REV')),
508 508 ('a', 'all', None, _('pull all changesets up to the --branch revisions')),
509 509 ('p', 'prune', [], _('skip over REV'), _('REV')),
510 510 ('m', 'merge', [], _('merge at REV'), _('REV')),
511 511 ('', 'parent', '',
512 512 _('parent to choose when transplanting merge'), _('REV')),
513 513 ('e', 'edit', False, _('invoke editor on commit messages')),
514 514 ('', 'log', None, _('append transplant info to log message')),
515 515 ('c', 'continue', None, _('continue last transplant session '
516 516 'after fixing conflicts')),
517 517 ('', 'filter', '',
518 518 _('filter changesets through command'), _('CMD'))],
519 519 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
520 520 '[-m REV] [REV]...'))
521 521 def transplant(ui, repo, *revs, **opts):
522 522 '''transplant changesets from another branch
523 523
524 524 Selected changesets will be applied on top of the current working
525 525 directory with the log of the original changeset. The changesets
526 526 are copied and will thus appear twice in the history with different
527 527 identities.
528 528
529 529 Consider using the graft command if everything is inside the same
530 530 repository - it will use merges and will usually give a better result.
531 531 Use the rebase extension if the changesets are unpublished and you want
532 532 to move them instead of copying them.
533 533
534 534 If --log is specified, log messages will have a comment appended
535 535 of the form::
536 536
537 537 (transplanted from CHANGESETHASH)
538 538
539 539 You can rewrite the changelog message with the --filter option.
540 540 Its argument will be invoked with the current changelog message as
541 541 $1 and the patch as $2.
542 542
543 543 --source/-s specifies another repository to use for selecting changesets,
544 544 just as if it temporarily had been pulled.
545 545 If --branch/-b is specified, these revisions will be used as
546 546 heads when deciding which changesets to transplant, just as if only
547 547 these revisions had been pulled.
548 548 If --all/-a is specified, all the revisions up to the heads specified
549 549 with --branch will be transplanted.
550 550
551 551 Example:
552 552
553 553 - transplant all changes up to REV on top of your current revision::
554 554
555 555 hg transplant --branch REV --all
556 556
557 557 You can optionally mark selected transplanted changesets as merge
558 558 changesets. You will not be prompted to transplant any ancestors
559 559 of a merged transplant, and you can merge descendants of them
560 560 normally instead of transplanting them.
561 561
562 562 Merge changesets may be transplanted directly by specifying the
563 563 proper parent changeset by calling :hg:`transplant --parent`.
564 564
565 565 If no merges or revisions are provided, :hg:`transplant` will
566 566 start an interactive changeset browser.
567 567
568 568 If a changeset application fails, you can fix the merge by hand
569 569 and then resume where you left off by calling :hg:`transplant
570 570 --continue/-c`.
571 571 '''
572 572 def incwalk(repo, csets, match=util.always):
573 573 for node in csets:
574 574 if match(node):
575 575 yield node
576 576
577 577 def transplantwalk(repo, dest, heads, match=util.always):
578 578 '''Yield all nodes that are ancestors of a head but not ancestors
579 579 of dest.
580 580 If no heads are specified, the heads of repo will be used.'''
581 581 if not heads:
582 582 heads = repo.heads()
583 583 ancestors = []
584 584 ctx = repo[dest]
585 585 for head in heads:
586 586 ancestors.append(ctx.ancestor(repo[head]).node())
587 587 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
588 588 if match(node):
589 589 yield node
590 590
591 591 def checkopts(opts, revs):
592 592 if opts.get('continue'):
593 593 if opts.get('branch') or opts.get('all') or opts.get('merge'):
594 594 raise util.Abort(_('--continue is incompatible with '
595 595 '--branch, --all and --merge'))
596 596 return
597 597 if not (opts.get('source') or revs or
598 598 opts.get('merge') or opts.get('branch')):
599 599 raise util.Abort(_('no source URL, branch revision or revision '
600 600 'list provided'))
601 601 if opts.get('all'):
602 602 if not opts.get('branch'):
603 603 raise util.Abort(_('--all requires a branch revision'))
604 604 if revs:
605 605 raise util.Abort(_('--all is incompatible with a '
606 606 'revision list'))
607 607
608 608 checkopts(opts, revs)
609 609
610 610 if not opts.get('log'):
611 611 opts['log'] = ui.config('transplant', 'log')
612 612 if not opts.get('filter'):
613 613 opts['filter'] = ui.config('transplant', 'filter')
614 614
615 615 tp = transplanter(ui, repo, opts)
616 616
617 617 cmdutil.checkunfinished(repo)
618 618 p1, p2 = repo.dirstate.parents()
619 619 if len(repo) > 0 and p1 == revlog.nullid:
620 620 raise util.Abort(_('no revision checked out'))
621 621 if not opts.get('continue'):
622 622 if p2 != revlog.nullid:
623 623 raise util.Abort(_('outstanding uncommitted merges'))
624 624 m, a, r, d = repo.status()[:4]
625 625 if m or a or r or d:
626 626 raise util.Abort(_('outstanding local changes'))
627 627
628 628 sourcerepo = opts.get('source')
629 629 if sourcerepo:
630 630 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
631 631 heads = map(peer.lookup, opts.get('branch', ()))
632 target = set(heads)
633 for r in revs:
634 try:
635 target.add(peer.lookup(r))
636 except error.RepoError:
637 pass
632 638 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
633 onlyheads=heads, force=True)
639 onlyheads=sorted(target), force=True)
634 640 else:
635 641 source = repo
636 642 heads = map(source.lookup, opts.get('branch', ()))
637 643 cleanupfn = None
638 644
639 645 try:
640 646 if opts.get('continue'):
641 647 tp.resume(repo, source, opts)
642 648 return
643 649
644 650 tf = tp.transplantfilter(repo, source, p1)
645 651 if opts.get('prune'):
646 652 prune = set(source.lookup(r)
647 653 for r in scmutil.revrange(source, opts.get('prune')))
648 654 matchfn = lambda x: tf(x) and x not in prune
649 655 else:
650 656 matchfn = tf
651 657 merges = map(source.lookup, opts.get('merge', ()))
652 658 revmap = {}
653 659 if revs:
654 660 for r in scmutil.revrange(source, revs):
655 661 revmap[int(r)] = source.lookup(r)
656 662 elif opts.get('all') or not merges:
657 663 if source != repo:
658 664 alltransplants = incwalk(source, csets, match=matchfn)
659 665 else:
660 666 alltransplants = transplantwalk(source, p1, heads,
661 667 match=matchfn)
662 668 if opts.get('all'):
663 669 revs = alltransplants
664 670 else:
665 671 revs, newmerges = browserevs(ui, source, alltransplants, opts)
666 672 merges.extend(newmerges)
667 673 for r in revs:
668 674 revmap[source.changelog.rev(r)] = r
669 675 for r in merges:
670 676 revmap[source.changelog.rev(r)] = r
671 677
672 678 tp.apply(repo, source, revmap, merges, opts)
673 679 finally:
674 680 if cleanupfn:
675 681 cleanupfn()
676 682
677 683 def revsettransplanted(repo, subset, x):
678 684 """``transplanted([set])``
679 685 Transplanted changesets in set, or all transplanted changesets.
680 686 """
681 687 if x:
682 688 s = revset.getset(repo, subset, x)
683 689 else:
684 690 s = subset
685 691 return revset.baseset([r for r in s if
686 692 repo[r].extra().get('transplant_source')])
687 693
688 694 def kwtransplanted(repo, ctx, **args):
689 695 """:transplanted: String. The node identifier of the transplanted
690 696 changeset if any."""
691 697 n = ctx.extra().get('transplant_source')
692 698 return n and revlog.hex(n) or ''
693 699
694 700 def extsetup(ui):
695 701 revset.symbols['transplanted'] = revsettransplanted
696 702 templatekw.keywords['transplanted'] = kwtransplanted
697 703 cmdutil.unfinishedstates.append(
698 704 ['series', True, False, _('transplant in progress'),
699 705 _("use 'hg transplant --continue' or 'hg update' to abort")])
700 706
701 707 # tell hggettext to extract docstrings from these functions:
702 708 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,337 +1,336
1 1 # archival.py - revision archival for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.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 i18n import _
9 9 import match as matchmod
10 10 import cmdutil
11 11 import scmutil, util, encoding
12 12 import cStringIO, os, tarfile, time, zipfile
13 13 import zlib, gzip
14 14 import struct
15 15 import error
16 16
17 17 # from unzip source code:
18 18 _UNX_IFREG = 0x8000
19 19 _UNX_IFLNK = 0xa000
20 20
21 21 def tidyprefix(dest, kind, prefix):
22 22 '''choose prefix to use for names in archive. make sure prefix is
23 23 safe for consumers.'''
24 24
25 25 if prefix:
26 26 prefix = util.normpath(prefix)
27 27 else:
28 28 if not isinstance(dest, str):
29 29 raise ValueError('dest must be string if no prefix')
30 30 prefix = os.path.basename(dest)
31 31 lower = prefix.lower()
32 32 for sfx in exts.get(kind, []):
33 33 if lower.endswith(sfx):
34 34 prefix = prefix[:-len(sfx)]
35 35 break
36 36 lpfx = os.path.normpath(util.localpath(prefix))
37 37 prefix = util.pconvert(lpfx)
38 38 if not prefix.endswith('/'):
39 39 prefix += '/'
40 40 # Drop the leading '.' path component if present, so Windows can read the
41 41 # zip files (issue4634)
42 42 if prefix.startswith('./'):
43 43 prefix = prefix[2:]
44 44 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
45 45 raise util.Abort(_('archive prefix contains illegal components'))
46 46 return prefix
47 47
48 48 exts = {
49 49 'tar': ['.tar'],
50 50 'tbz2': ['.tbz2', '.tar.bz2'],
51 51 'tgz': ['.tgz', '.tar.gz'],
52 52 'zip': ['.zip'],
53 53 }
54 54
55 55 def guesskind(dest):
56 56 for kind, extensions in exts.iteritems():
57 57 if any(dest.endswith(ext) for ext in extensions):
58 58 return kind
59 59 return None
60 60
61 61 def _rootctx(repo):
62 62 # repo[0] may be hidden
63 63 for rev in repo:
64 64 return repo[rev]
65 65 return repo['null']
66 66
67 67 def buildmetadata(ctx):
68 68 '''build content of .hg_archival.txt'''
69 69 repo = ctx.repo()
70 70 hex = ctx.hex()
71 71 if ctx.rev() is None:
72 72 hex = ctx.p1().hex()
73 73 if ctx.dirty():
74 74 hex += '+'
75 75
76 76 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
77 77 _rootctx(repo).hex(), hex, encoding.fromlocal(ctx.branch()))
78 78
79 79 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
80 80 if repo.tagtype(t) == 'global')
81 81 if not tags:
82 82 repo.ui.pushbuffer()
83 83 opts = {'template': '{latesttag}\n{latesttagdistance}',
84 84 'style': '', 'patch': None, 'git': None}
85 85 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
86 86 ltags, dist = repo.ui.popbuffer().split('\n')
87 87 ltags = ltags.split(':')
88 # XXX: ctx.rev() needs to be handled differently with wdir()
89 88 if ctx.rev() is None:
90 89 changessince = len(repo.revs('only(%d,%s)', ctx.p1(),
91 90 ltags[0])) + 1
92 91 else:
93 92 changessince = len(repo.revs('only(%d,%s)', ctx.rev(), ltags[0]))
94 93 tags = ''.join('latesttag: %s\n' % t for t in ltags)
95 94 tags += 'latesttagdistance: %s\n' % dist
96 95 tags += 'changessincelatesttag: %s\n' % changessince
97 96
98 97 return base + tags
99 98
100 99 class tarit(object):
101 100 '''write archive to tar file or stream. can write uncompressed,
102 101 or compress with gzip or bzip2.'''
103 102
104 103 class GzipFileWithTime(gzip.GzipFile):
105 104
106 105 def __init__(self, *args, **kw):
107 106 timestamp = None
108 107 if 'timestamp' in kw:
109 108 timestamp = kw.pop('timestamp')
110 109 if timestamp is None:
111 110 self.timestamp = time.time()
112 111 else:
113 112 self.timestamp = timestamp
114 113 gzip.GzipFile.__init__(self, *args, **kw)
115 114
116 115 def _write_gzip_header(self):
117 116 self.fileobj.write('\037\213') # magic header
118 117 self.fileobj.write('\010') # compression method
119 118 # Python 2.6 introduced self.name and deprecated self.filename
120 119 try:
121 120 fname = self.name
122 121 except AttributeError:
123 122 fname = self.filename
124 123 if fname and fname.endswith('.gz'):
125 124 fname = fname[:-3]
126 125 flags = 0
127 126 if fname:
128 127 flags = gzip.FNAME
129 128 self.fileobj.write(chr(flags))
130 129 gzip.write32u(self.fileobj, long(self.timestamp))
131 130 self.fileobj.write('\002')
132 131 self.fileobj.write('\377')
133 132 if fname:
134 133 self.fileobj.write(fname + '\000')
135 134
136 135 def __init__(self, dest, mtime, kind=''):
137 136 self.mtime = mtime
138 137 self.fileobj = None
139 138
140 139 def taropen(name, mode, fileobj=None):
141 140 if kind == 'gz':
142 141 mode = mode[0]
143 142 if not fileobj:
144 143 fileobj = open(name, mode + 'b')
145 144 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
146 145 zlib.Z_BEST_COMPRESSION,
147 146 fileobj, timestamp=mtime)
148 147 self.fileobj = gzfileobj
149 148 return tarfile.TarFile.taropen(name, mode, gzfileobj)
150 149 else:
151 150 return tarfile.open(name, mode + kind, fileobj)
152 151
153 152 if isinstance(dest, str):
154 153 self.z = taropen(dest, mode='w:')
155 154 else:
156 155 # Python 2.5-2.5.1 have a regression that requires a name arg
157 156 self.z = taropen(name='', mode='w|', fileobj=dest)
158 157
159 158 def addfile(self, name, mode, islink, data):
160 159 i = tarfile.TarInfo(name)
161 160 i.mtime = self.mtime
162 161 i.size = len(data)
163 162 if islink:
164 163 i.type = tarfile.SYMTYPE
165 164 i.mode = 0o777
166 165 i.linkname = data
167 166 data = None
168 167 i.size = 0
169 168 else:
170 169 i.mode = mode
171 170 data = cStringIO.StringIO(data)
172 171 self.z.addfile(i, data)
173 172
174 173 def done(self):
175 174 self.z.close()
176 175 if self.fileobj:
177 176 self.fileobj.close()
178 177
179 178 class tellable(object):
180 179 '''provide tell method for zipfile.ZipFile when writing to http
181 180 response file object.'''
182 181
183 182 def __init__(self, fp):
184 183 self.fp = fp
185 184 self.offset = 0
186 185
187 186 def __getattr__(self, key):
188 187 return getattr(self.fp, key)
189 188
190 189 def write(self, s):
191 190 self.fp.write(s)
192 191 self.offset += len(s)
193 192
194 193 def tell(self):
195 194 return self.offset
196 195
197 196 class zipit(object):
198 197 '''write archive to zip file or stream. can write uncompressed,
199 198 or compressed with deflate.'''
200 199
201 200 def __init__(self, dest, mtime, compress=True):
202 201 if not isinstance(dest, str):
203 202 try:
204 203 dest.tell()
205 204 except (AttributeError, IOError):
206 205 dest = tellable(dest)
207 206 self.z = zipfile.ZipFile(dest, 'w',
208 207 compress and zipfile.ZIP_DEFLATED or
209 208 zipfile.ZIP_STORED)
210 209
211 210 # Python's zipfile module emits deprecation warnings if we try
212 211 # to store files with a date before 1980.
213 212 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
214 213 if mtime < epoch:
215 214 mtime = epoch
216 215
217 216 self.mtime = mtime
218 217 self.date_time = time.gmtime(mtime)[:6]
219 218
220 219 def addfile(self, name, mode, islink, data):
221 220 i = zipfile.ZipInfo(name, self.date_time)
222 221 i.compress_type = self.z.compression
223 222 # unzip will not honor unix file modes unless file creator is
224 223 # set to unix (id 3).
225 224 i.create_system = 3
226 225 ftype = _UNX_IFREG
227 226 if islink:
228 227 mode = 0o777
229 228 ftype = _UNX_IFLNK
230 229 i.external_attr = (mode | ftype) << 16L
231 230 # add "extended-timestamp" extra block, because zip archives
232 231 # without this will be extracted with unexpected timestamp,
233 232 # if TZ is not configured as GMT
234 233 i.extra += struct.pack('<hhBl',
235 234 0x5455, # block type: "extended-timestamp"
236 235 1 + 4, # size of this block
237 236 1, # "modification time is present"
238 237 int(self.mtime)) # last modification (UTC)
239 238 self.z.writestr(i, data)
240 239
241 240 def done(self):
242 241 self.z.close()
243 242
244 243 class fileit(object):
245 244 '''write archive as files in directory.'''
246 245
247 246 def __init__(self, name, mtime):
248 247 self.basedir = name
249 248 self.opener = scmutil.opener(self.basedir)
250 249
251 250 def addfile(self, name, mode, islink, data):
252 251 if islink:
253 252 self.opener.symlink(data, name)
254 253 return
255 254 f = self.opener(name, "w", atomictemp=True)
256 255 f.write(data)
257 256 f.close()
258 257 destfile = os.path.join(self.basedir, name)
259 258 os.chmod(destfile, mode)
260 259
261 260 def done(self):
262 261 pass
263 262
264 263 archivers = {
265 264 'files': fileit,
266 265 'tar': tarit,
267 266 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
268 267 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
269 268 'uzip': lambda name, mtime: zipit(name, mtime, False),
270 269 'zip': zipit,
271 270 }
272 271
273 272 def archive(repo, dest, node, kind, decode=True, matchfn=None,
274 273 prefix='', mtime=None, subrepos=False):
275 274 '''create archive of repo as it was at node.
276 275
277 276 dest can be name of directory, name of archive file, or file
278 277 object to write archive to.
279 278
280 279 kind is type of archive to create.
281 280
282 281 decode tells whether to put files through decode filters from
283 282 hgrc.
284 283
285 284 matchfn is function to filter names of files to write to archive.
286 285
287 286 prefix is name of path to put before every archive member.'''
288 287
289 288 if kind == 'files':
290 289 if prefix:
291 290 raise util.Abort(_('cannot give prefix when archiving to files'))
292 291 else:
293 292 prefix = tidyprefix(dest, kind, prefix)
294 293
295 294 def write(name, mode, islink, getdata):
296 295 data = getdata()
297 296 if decode:
298 297 data = repo.wwritedata(name, data)
299 298 archiver.addfile(prefix + name, mode, islink, data)
300 299
301 300 if kind not in archivers:
302 301 raise util.Abort(_("unknown archive type '%s'") % kind)
303 302
304 303 ctx = repo[node]
305 304 archiver = archivers[kind](dest, mtime or ctx.date()[0])
306 305
307 306 if repo.ui.configbool("ui", "archivemeta", True):
308 307 name = '.hg_archival.txt'
309 308 if not matchfn or matchfn(name):
310 309 write(name, 0o644, False, lambda: buildmetadata(ctx))
311 310
312 311 if matchfn:
313 312 files = [f for f in ctx.manifest().keys() if matchfn(f)]
314 313 else:
315 314 files = ctx.manifest().keys()
316 315 total = len(files)
317 316 if total:
318 317 files.sort()
319 318 repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total)
320 319 for i, f in enumerate(files):
321 320 ff = ctx.flags(f)
322 321 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, ctx[f].data)
323 322 repo.ui.progress(_('archiving'), i + 1, item=f,
324 323 unit=_('files'), total=total)
325 324 repo.ui.progress(_('archiving'), None)
326 325
327 326 if subrepos:
328 327 for subpath in sorted(ctx.substate):
329 328 sub = ctx.workingsub(subpath)
330 329 submatch = matchmod.narrowmatcher(subpath, matchfn)
331 330 total += sub.archive(archiver, prefix, submatch)
332 331
333 332 if total == 0:
334 333 raise error.Abort(_('no files match the archive pattern'))
335 334
336 335 archiver.done()
337 336 return total
@@ -1,886 +1,888
1 1 # changegroup.py - Mercurial changegroup manipulation functions
2 2 #
3 3 # Copyright 2006 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 import weakref
9 9 from i18n import _
10 10 from node import nullrev, nullid, hex, short
11 11 import mdiff, util, dagutil
12 12 import struct, os, bz2, zlib, tempfile
13 13 import discovery, error, phases, branchmap
14 14
15 15 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
16 16 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
17 17
18 18 def readexactly(stream, n):
19 19 '''read n bytes from stream.read and abort if less was available'''
20 20 s = stream.read(n)
21 21 if len(s) < n:
22 22 raise util.Abort(_("stream ended unexpectedly"
23 23 " (got %d bytes, expected %d)")
24 24 % (len(s), n))
25 25 return s
26 26
27 27 def getchunk(stream):
28 28 """return the next chunk from stream as a string"""
29 29 d = readexactly(stream, 4)
30 30 l = struct.unpack(">l", d)[0]
31 31 if l <= 4:
32 32 if l:
33 33 raise util.Abort(_("invalid chunk length %d") % l)
34 34 return ""
35 35 return readexactly(stream, l - 4)
36 36
37 37 def chunkheader(length):
38 38 """return a changegroup chunk header (string)"""
39 39 return struct.pack(">l", length + 4)
40 40
41 41 def closechunk():
42 42 """return a changegroup chunk header (string) for a zero-length chunk"""
43 43 return struct.pack(">l", 0)
44 44
45 45 def combineresults(results):
46 46 """logic to combine 0 or more addchangegroup results into one"""
47 47 changedheads = 0
48 48 result = 1
49 49 for ret in results:
50 50 # If any changegroup result is 0, return 0
51 51 if ret == 0:
52 52 result = 0
53 53 break
54 54 if ret < -1:
55 55 changedheads += ret + 1
56 56 elif ret > 1:
57 57 changedheads += ret - 1
58 58 if changedheads > 0:
59 59 result = 1 + changedheads
60 60 elif changedheads < 0:
61 61 result = -1 + changedheads
62 62 return result
63 63
64 64 class nocompress(object):
65 65 def compress(self, x):
66 66 return x
67 67 def flush(self):
68 68 return ""
69 69
70 70 bundletypes = {
71 71 "": ("", nocompress), # only when using unbundle on ssh and old http servers
72 72 # since the unification ssh accepts a header but there
73 73 # is no capability signaling it.
74 74 "HG20": (), # special-cased below
75 75 "HG10UN": ("HG10UN", nocompress),
76 76 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
77 77 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
78 78 }
79 79
80 80 # hgweb uses this list to communicate its preferred type
81 81 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
82 82
83 83 def writebundle(ui, cg, filename, bundletype, vfs=None):
84 84 """Write a bundle file and return its filename.
85 85
86 86 Existing files will not be overwritten.
87 87 If no filename is specified, a temporary file is created.
88 88 bz2 compression can be turned off.
89 89 The bundle file will be deleted in case of errors.
90 90 """
91 91
92 92 fh = None
93 93 cleanup = None
94 94 try:
95 95 if filename:
96 96 if vfs:
97 97 fh = vfs.open(filename, "wb")
98 98 else:
99 99 fh = open(filename, "wb")
100 100 else:
101 101 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
102 102 fh = os.fdopen(fd, "wb")
103 103 cleanup = filename
104 104
105 105 if bundletype == "HG20":
106 106 import bundle2
107 107 bundle = bundle2.bundle20(ui)
108 108 part = bundle.newpart('changegroup', data=cg.getchunks())
109 109 part.addparam('version', cg.version)
110 110 z = nocompress()
111 111 chunkiter = bundle.getchunks()
112 112 else:
113 113 if cg.version != '01':
114 114 raise util.Abort(_('old bundle types only supports v1 '
115 115 'changegroups'))
116 116 header, compressor = bundletypes[bundletype]
117 117 fh.write(header)
118 118 z = compressor()
119 119 chunkiter = cg.getchunks()
120 120
121 121 # parse the changegroup data, otherwise we will block
122 122 # in case of sshrepo because we don't know the end of the stream
123 123
124 124 # an empty chunkgroup is the end of the changegroup
125 125 # a changegroup has at least 2 chunkgroups (changelog and manifest).
126 126 # after that, an empty chunkgroup is the end of the changegroup
127 127 for chunk in chunkiter:
128 128 fh.write(z.compress(chunk))
129 129 fh.write(z.flush())
130 130 cleanup = None
131 131 return filename
132 132 finally:
133 133 if fh is not None:
134 134 fh.close()
135 135 if cleanup is not None:
136 136 if filename and vfs:
137 137 vfs.unlink(cleanup)
138 138 else:
139 139 os.unlink(cleanup)
140 140
141 141 def decompressor(fh, alg):
142 142 if alg == 'UN':
143 143 return fh
144 144 elif alg == 'GZ':
145 145 def generator(f):
146 146 zd = zlib.decompressobj()
147 147 for chunk in util.filechunkiter(f):
148 148 yield zd.decompress(chunk)
149 149 elif alg == 'BZ':
150 150 def generator(f):
151 151 zd = bz2.BZ2Decompressor()
152 152 zd.decompress("BZ")
153 153 for chunk in util.filechunkiter(f, 4096):
154 154 yield zd.decompress(chunk)
155 155 else:
156 156 raise util.Abort("unknown bundle compression '%s'" % alg)
157 157 return util.chunkbuffer(generator(fh))
158 158
159 159 class cg1unpacker(object):
160 160 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
161 161 deltaheadersize = struct.calcsize(deltaheader)
162 162 version = '01'
163 163 def __init__(self, fh, alg):
164 164 self._stream = decompressor(fh, alg)
165 165 self._type = alg
166 166 self.callback = None
167 167 def compressed(self):
168 168 return self._type != 'UN'
169 169 def read(self, l):
170 170 return self._stream.read(l)
171 171 def seek(self, pos):
172 172 return self._stream.seek(pos)
173 173 def tell(self):
174 174 return self._stream.tell()
175 175 def close(self):
176 176 return self._stream.close()
177 177
178 178 def chunklength(self):
179 179 d = readexactly(self._stream, 4)
180 180 l = struct.unpack(">l", d)[0]
181 181 if l <= 4:
182 182 if l:
183 183 raise util.Abort(_("invalid chunk length %d") % l)
184 184 return 0
185 185 if self.callback:
186 186 self.callback()
187 187 return l - 4
188 188
189 189 def changelogheader(self):
190 190 """v10 does not have a changelog header chunk"""
191 191 return {}
192 192
193 193 def manifestheader(self):
194 194 """v10 does not have a manifest header chunk"""
195 195 return {}
196 196
197 197 def filelogheader(self):
198 198 """return the header of the filelogs chunk, v10 only has the filename"""
199 199 l = self.chunklength()
200 200 if not l:
201 201 return {}
202 202 fname = readexactly(self._stream, l)
203 203 return {'filename': fname}
204 204
205 205 def _deltaheader(self, headertuple, prevnode):
206 206 node, p1, p2, cs = headertuple
207 207 if prevnode is None:
208 208 deltabase = p1
209 209 else:
210 210 deltabase = prevnode
211 211 return node, p1, p2, deltabase, cs
212 212
213 213 def deltachunk(self, prevnode):
214 214 l = self.chunklength()
215 215 if not l:
216 216 return {}
217 217 headerdata = readexactly(self._stream, self.deltaheadersize)
218 218 header = struct.unpack(self.deltaheader, headerdata)
219 219 delta = readexactly(self._stream, l - self.deltaheadersize)
220 220 node, p1, p2, deltabase, cs = self._deltaheader(header, prevnode)
221 221 return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs,
222 222 'deltabase': deltabase, 'delta': delta}
223 223
224 224 def getchunks(self):
225 225 """returns all the chunks contains in the bundle
226 226
227 227 Used when you need to forward the binary stream to a file or another
228 228 network API. To do so, it parse the changegroup data, otherwise it will
229 229 block in case of sshrepo because it don't know the end of the stream.
230 230 """
231 231 # an empty chunkgroup is the end of the changegroup
232 232 # a changegroup has at least 2 chunkgroups (changelog and manifest).
233 233 # after that, an empty chunkgroup is the end of the changegroup
234 234 empty = False
235 235 count = 0
236 236 while not empty or count <= 2:
237 237 empty = True
238 238 count += 1
239 239 while True:
240 240 chunk = getchunk(self)
241 241 if not chunk:
242 242 break
243 243 empty = False
244 244 yield chunkheader(len(chunk))
245 245 pos = 0
246 246 while pos < len(chunk):
247 247 next = pos + 2**20
248 248 yield chunk[pos:next]
249 249 pos = next
250 250 yield closechunk()
251 251
252 252 class cg2unpacker(cg1unpacker):
253 253 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
254 254 deltaheadersize = struct.calcsize(deltaheader)
255 255 version = '02'
256 256
257 257 def _deltaheader(self, headertuple, prevnode):
258 258 node, p1, p2, deltabase, cs = headertuple
259 259 return node, p1, p2, deltabase, cs
260 260
261 261 class headerlessfixup(object):
262 262 def __init__(self, fh, h):
263 263 self._h = h
264 264 self._fh = fh
265 265 def read(self, n):
266 266 if self._h:
267 267 d, self._h = self._h[:n], self._h[n:]
268 268 if len(d) < n:
269 269 d += readexactly(self._fh, n - len(d))
270 270 return d
271 271 return readexactly(self._fh, n)
272 272
273 273 class cg1packer(object):
274 274 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
275 275 version = '01'
276 276 def __init__(self, repo, bundlecaps=None):
277 277 """Given a source repo, construct a bundler.
278 278
279 279 bundlecaps is optional and can be used to specify the set of
280 280 capabilities which can be used to build the bundle.
281 281 """
282 282 # Set of capabilities we can use to build the bundle.
283 283 if bundlecaps is None:
284 284 bundlecaps = set()
285 285 self._bundlecaps = bundlecaps
286 286 reorder = repo.ui.config('bundle', 'reorder', 'auto')
287 287 if reorder == 'auto':
288 288 reorder = None
289 289 else:
290 290 reorder = util.parsebool(reorder)
291 291 self._repo = repo
292 292 self._reorder = reorder
293 293 self._progress = repo.ui.progress
294 294 if self._repo.ui.verbose and not self._repo.ui.debugflag:
295 295 self._verbosenote = self._repo.ui.note
296 296 else:
297 297 self._verbosenote = lambda s: None
298 298
299 299 def close(self):
300 300 return closechunk()
301 301
302 302 def fileheader(self, fname):
303 303 return chunkheader(len(fname)) + fname
304 304
305 305 def group(self, nodelist, revlog, lookup, units=None):
306 306 """Calculate a delta group, yielding a sequence of changegroup chunks
307 307 (strings).
308 308
309 309 Given a list of changeset revs, return a set of deltas and
310 310 metadata corresponding to nodes. The first delta is
311 311 first parent(nodelist[0]) -> nodelist[0], the receiver is
312 312 guaranteed to have this parent as it has all history before
313 313 these changesets. In the case firstparent is nullrev the
314 314 changegroup starts with a full revision.
315 315
316 316 If units is not None, progress detail will be generated, units specifies
317 317 the type of revlog that is touched (changelog, manifest, etc.).
318 318 """
319 319 # if we don't have any revisions touched by these changesets, bail
320 320 if len(nodelist) == 0:
321 321 yield self.close()
322 322 return
323 323
324 324 # for generaldelta revlogs, we linearize the revs; this will both be
325 325 # much quicker and generate a much smaller bundle
326 326 if (revlog._generaldelta and self._reorder is None) or self._reorder:
327 327 dag = dagutil.revlogdag(revlog)
328 328 revs = set(revlog.rev(n) for n in nodelist)
329 329 revs = dag.linearize(revs)
330 330 else:
331 331 revs = sorted([revlog.rev(n) for n in nodelist])
332 332
333 333 # add the parent of the first rev
334 334 p = revlog.parentrevs(revs[0])[0]
335 335 revs.insert(0, p)
336 336
337 337 # build deltas
338 338 total = len(revs) - 1
339 339 msgbundling = _('bundling')
340 340 for r in xrange(len(revs) - 1):
341 341 if units is not None:
342 342 self._progress(msgbundling, r + 1, unit=units, total=total)
343 343 prev, curr = revs[r], revs[r + 1]
344 344 linknode = lookup(revlog.node(curr))
345 345 for c in self.revchunk(revlog, curr, prev, linknode):
346 346 yield c
347 347
348 348 if units is not None:
349 349 self._progress(msgbundling, None)
350 350 yield self.close()
351 351
352 352 # filter any nodes that claim to be part of the known set
353 353 def prune(self, revlog, missing, commonrevs):
354 354 rr, rl = revlog.rev, revlog.linkrev
355 355 return [n for n in missing if rl(rr(n)) not in commonrevs]
356 356
357 357 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
358 358 '''yield a sequence of changegroup chunks (strings)'''
359 359 repo = self._repo
360 360 cl = repo.changelog
361 361 ml = repo.manifest
362 362
363 363 clrevorder = {}
364 364 mfs = {} # needed manifests
365 365 fnodes = {} # needed file nodes
366 366 changedfiles = set()
367 367
368 368 # Callback for the changelog, used to collect changed files and manifest
369 369 # nodes.
370 370 # Returns the linkrev node (identity in the changelog case).
371 371 def lookupcl(x):
372 372 c = cl.read(x)
373 373 clrevorder[x] = len(clrevorder)
374 374 changedfiles.update(c[3])
375 375 # record the first changeset introducing this manifest version
376 376 mfs.setdefault(c[0], x)
377 377 return x
378 378
379 379 self._verbosenote(_('uncompressed size of bundle content:\n'))
380 380 size = 0
381 381 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
382 382 size += len(chunk)
383 383 yield chunk
384 384 self._verbosenote(_('%8.i (changelog)\n') % size)
385 385
386 386 # We need to make sure that the linkrev in the changegroup refers to
387 387 # the first changeset that introduced the manifest or file revision.
388 388 # The fastpath is usually safer than the slowpath, because the filelogs
389 389 # are walked in revlog order.
390 390 #
391 391 # When taking the slowpath with reorder=None and the manifest revlog
392 392 # uses generaldelta, the manifest may be walked in the "wrong" order.
393 393 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
394 394 # cc0ff93d0c0c).
395 395 #
396 396 # When taking the fastpath, we are only vulnerable to reordering
397 397 # of the changelog itself. The changelog never uses generaldelta, so
398 398 # it is only reordered when reorder=True. To handle this case, we
399 399 # simply take the slowpath, which already has the 'clrevorder' logic.
400 400 # This was also fixed in cc0ff93d0c0c.
401 401 fastpathlinkrev = fastpathlinkrev and not self._reorder
402 402 # Callback for the manifest, used to collect linkrevs for filelog
403 403 # revisions.
404 404 # Returns the linkrev node (collected in lookupcl).
405 405 def lookupmf(x):
406 406 clnode = mfs[x]
407 407 if not fastpathlinkrev:
408 408 mdata = ml.readfast(x)
409 409 for f, n in mdata.iteritems():
410 410 if f in changedfiles:
411 411 # record the first changeset introducing this filelog
412 412 # version
413 413 fclnodes = fnodes.setdefault(f, {})
414 414 fclnode = fclnodes.setdefault(n, clnode)
415 415 if clrevorder[clnode] < clrevorder[fclnode]:
416 416 fclnodes[n] = clnode
417 417 return clnode
418 418
419 419 mfnodes = self.prune(ml, mfs, commonrevs)
420 420 size = 0
421 421 for chunk in self.group(mfnodes, ml, lookupmf, units=_('manifests')):
422 422 size += len(chunk)
423 423 yield chunk
424 424 self._verbosenote(_('%8.i (manifests)\n') % size)
425 425
426 426 mfs.clear()
427 427 clrevs = set(cl.rev(x) for x in clnodes)
428 428
429 429 def linknodes(filerevlog, fname):
430 430 if fastpathlinkrev:
431 431 llr = filerevlog.linkrev
432 432 def genfilenodes():
433 433 for r in filerevlog:
434 434 linkrev = llr(r)
435 435 if linkrev in clrevs:
436 436 yield filerevlog.node(r), cl.node(linkrev)
437 437 return dict(genfilenodes())
438 438 return fnodes.get(fname, {})
439 439
440 440 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
441 441 source):
442 442 yield chunk
443 443
444 444 yield self.close()
445 445
446 446 if clnodes:
447 447 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
448 448
449 449 # The 'source' parameter is useful for extensions
450 450 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
451 451 repo = self._repo
452 452 progress = self._progress
453 453 msgbundling = _('bundling')
454 454
455 455 total = len(changedfiles)
456 456 # for progress output
457 457 msgfiles = _('files')
458 458 for i, fname in enumerate(sorted(changedfiles)):
459 459 filerevlog = repo.file(fname)
460 460 if not filerevlog:
461 461 raise util.Abort(_("empty or missing revlog for %s") % fname)
462 462
463 463 linkrevnodes = linknodes(filerevlog, fname)
464 464 # Lookup for filenodes, we collected the linkrev nodes above in the
465 465 # fastpath case and with lookupmf in the slowpath case.
466 466 def lookupfilelog(x):
467 467 return linkrevnodes[x]
468 468
469 469 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs)
470 470 if filenodes:
471 471 progress(msgbundling, i + 1, item=fname, unit=msgfiles,
472 472 total=total)
473 473 h = self.fileheader(fname)
474 474 size = len(h)
475 475 yield h
476 476 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
477 477 size += len(chunk)
478 478 yield chunk
479 479 self._verbosenote(_('%8.i %s\n') % (size, fname))
480 480 progress(msgbundling, None)
481 481
482 482 def deltaparent(self, revlog, rev, p1, p2, prev):
483 483 return prev
484 484
485 485 def revchunk(self, revlog, rev, prev, linknode):
486 486 node = revlog.node(rev)
487 487 p1, p2 = revlog.parentrevs(rev)
488 488 base = self.deltaparent(revlog, rev, p1, p2, prev)
489 489
490 490 prefix = ''
491 491 if revlog.iscensored(base) or revlog.iscensored(rev):
492 492 try:
493 493 delta = revlog.revision(node)
494 494 except error.CensoredNodeError as e:
495 495 delta = e.tombstone
496 496 if base == nullrev:
497 497 prefix = mdiff.trivialdiffheader(len(delta))
498 498 else:
499 499 baselen = revlog.rawsize(base)
500 500 prefix = mdiff.replacediffheader(baselen, len(delta))
501 501 elif base == nullrev:
502 502 delta = revlog.revision(node)
503 503 prefix = mdiff.trivialdiffheader(len(delta))
504 504 else:
505 505 delta = revlog.revdiff(base, rev)
506 506 p1n, p2n = revlog.parents(node)
507 507 basenode = revlog.node(base)
508 508 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode)
509 509 meta += prefix
510 510 l = len(meta) + len(delta)
511 511 yield chunkheader(l)
512 512 yield meta
513 513 yield delta
514 514 def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
515 515 # do nothing with basenode, it is implicitly the previous one in HG10
516 516 return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
517 517
518 518 class cg2packer(cg1packer):
519 519 version = '02'
520 520 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
521 521
522 522 def __init__(self, repo, bundlecaps=None):
523 523 super(cg2packer, self).__init__(repo, bundlecaps)
524 524 if self._reorder is None:
525 525 # Since generaldelta is directly supported by cg2, reordering
526 526 # generally doesn't help, so we disable it by default (treating
527 527 # bundle.reorder=auto just like bundle.reorder=False).
528 528 self._reorder = False
529 529
530 530 def deltaparent(self, revlog, rev, p1, p2, prev):
531 531 dp = revlog.deltaparent(rev)
532 532 # avoid storing full revisions; pick prev in those cases
533 533 # also pick prev when we can't be sure remote has dp
534 534 if dp == nullrev or (dp != p1 and dp != p2 and dp != prev):
535 535 return prev
536 536 return dp
537 537
538 538 def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
539 539 return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
540 540
541 541 packermap = {'01': (cg1packer, cg1unpacker),
542 542 '02': (cg2packer, cg2unpacker)}
543 543
544 544 def _changegroupinfo(repo, nodes, source):
545 545 if repo.ui.verbose or source == 'bundle':
546 546 repo.ui.status(_("%d changesets found\n") % len(nodes))
547 547 if repo.ui.debugflag:
548 548 repo.ui.debug("list of changesets:\n")
549 549 for node in nodes:
550 550 repo.ui.debug("%s\n" % hex(node))
551 551
552 552 def getsubsetraw(repo, outgoing, bundler, source, fastpath=False):
553 553 repo = repo.unfiltered()
554 554 commonrevs = outgoing.common
555 555 csets = outgoing.missing
556 556 heads = outgoing.missingheads
557 557 # We go through the fast path if we get told to, or if all (unfiltered
558 558 # heads have been requested (since we then know there all linkrevs will
559 559 # be pulled by the client).
560 560 heads.sort()
561 561 fastpathlinkrev = fastpath or (
562 562 repo.filtername is None and heads == sorted(repo.heads()))
563 563
564 564 repo.hook('preoutgoing', throw=True, source=source)
565 565 _changegroupinfo(repo, csets, source)
566 566 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
567 567
568 568 def getsubset(repo, outgoing, bundler, source, fastpath=False, version='01'):
569 569 gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath)
570 570 return packermap[version][1](util.chunkbuffer(gengroup), 'UN')
571 571
572 572 def changegroupsubset(repo, roots, heads, source, version='01'):
573 573 """Compute a changegroup consisting of all the nodes that are
574 574 descendants of any of the roots and ancestors of any of the heads.
575 575 Return a chunkbuffer object whose read() method will return
576 576 successive changegroup chunks.
577 577
578 578 It is fairly complex as determining which filenodes and which
579 579 manifest nodes need to be included for the changeset to be complete
580 580 is non-trivial.
581 581
582 582 Another wrinkle is doing the reverse, figuring out which changeset in
583 583 the changegroup a particular filenode or manifestnode belongs to.
584 584 """
585 585 cl = repo.changelog
586 586 if not roots:
587 587 roots = [nullid]
588 # TODO: remove call to nodesbetween.
589 csets, roots, heads = cl.nodesbetween(roots, heads)
590 588 discbases = []
591 589 for n in roots:
592 590 discbases.extend([p for p in cl.parents(n) if p != nullid])
591 # TODO: remove call to nodesbetween.
592 csets, roots, heads = cl.nodesbetween(roots, heads)
593 included = set(csets)
594 discbases = [n for n in discbases if n not in included]
593 595 outgoing = discovery.outgoing(cl, discbases, heads)
594 596 bundler = packermap[version][0](repo)
595 597 return getsubset(repo, outgoing, bundler, source, version=version)
596 598
597 599 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
598 600 version='01'):
599 601 """Like getbundle, but taking a discovery.outgoing as an argument.
600 602
601 603 This is only implemented for local repos and reuses potentially
602 604 precomputed sets in outgoing. Returns a raw changegroup generator."""
603 605 if not outgoing.missing:
604 606 return None
605 607 bundler = packermap[version][0](repo, bundlecaps)
606 608 return getsubsetraw(repo, outgoing, bundler, source)
607 609
608 610 def getlocalchangegroup(repo, source, outgoing, bundlecaps=None):
609 611 """Like getbundle, but taking a discovery.outgoing as an argument.
610 612
611 613 This is only implemented for local repos and reuses potentially
612 614 precomputed sets in outgoing."""
613 615 if not outgoing.missing:
614 616 return None
615 617 bundler = cg1packer(repo, bundlecaps)
616 618 return getsubset(repo, outgoing, bundler, source)
617 619
618 620 def computeoutgoing(repo, heads, common):
619 621 """Computes which revs are outgoing given a set of common
620 622 and a set of heads.
621 623
622 624 This is a separate function so extensions can have access to
623 625 the logic.
624 626
625 627 Returns a discovery.outgoing object.
626 628 """
627 629 cl = repo.changelog
628 630 if common:
629 631 hasnode = cl.hasnode
630 632 common = [n for n in common if hasnode(n)]
631 633 else:
632 634 common = [nullid]
633 635 if not heads:
634 636 heads = cl.heads()
635 637 return discovery.outgoing(cl, common, heads)
636 638
637 639 def getchangegroup(repo, source, heads=None, common=None, bundlecaps=None):
638 640 """Like changegroupsubset, but returns the set difference between the
639 641 ancestors of heads and the ancestors common.
640 642
641 643 If heads is None, use the local heads. If common is None, use [nullid].
642 644
643 645 The nodes in common might not all be known locally due to the way the
644 646 current discovery protocol works.
645 647 """
646 648 outgoing = computeoutgoing(repo, heads, common)
647 649 return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps)
648 650
649 651 def changegroup(repo, basenodes, source):
650 652 # to avoid a race we use changegroupsubset() (issue1320)
651 653 return changegroupsubset(repo, basenodes, repo.heads(), source)
652 654
653 655 def addchangegroupfiles(repo, source, revmap, trp, pr, needfiles):
654 656 revisions = 0
655 657 files = 0
656 658 while True:
657 659 chunkdata = source.filelogheader()
658 660 if not chunkdata:
659 661 break
660 662 f = chunkdata["filename"]
661 663 repo.ui.debug("adding %s revisions\n" % f)
662 664 pr()
663 665 fl = repo.file(f)
664 666 o = len(fl)
665 667 try:
666 668 if not fl.addgroup(source, revmap, trp):
667 669 raise util.Abort(_("received file revlog group is empty"))
668 670 except error.CensoredBaseError as e:
669 671 raise util.Abort(_("received delta base is censored: %s") % e)
670 672 revisions += len(fl) - o
671 673 files += 1
672 674 if f in needfiles:
673 675 needs = needfiles[f]
674 676 for new in xrange(o, len(fl)):
675 677 n = fl.node(new)
676 678 if n in needs:
677 679 needs.remove(n)
678 680 else:
679 681 raise util.Abort(
680 682 _("received spurious file revlog entry"))
681 683 if not needs:
682 684 del needfiles[f]
683 685 repo.ui.progress(_('files'), None)
684 686
685 687 for f, needs in needfiles.iteritems():
686 688 fl = repo.file(f)
687 689 for n in needs:
688 690 try:
689 691 fl.rev(n)
690 692 except error.LookupError:
691 693 raise util.Abort(
692 694 _('missing file data for %s:%s - run hg verify') %
693 695 (f, hex(n)))
694 696
695 697 return revisions, files
696 698
697 699 def addchangegroup(repo, source, srctype, url, emptyok=False,
698 700 targetphase=phases.draft, expectedtotal=None):
699 701 """Add the changegroup returned by source.read() to this repo.
700 702 srctype is a string like 'push', 'pull', or 'unbundle'. url is
701 703 the URL of the repo where this changegroup is coming from.
702 704
703 705 Return an integer summarizing the change to this repo:
704 706 - nothing changed or no source: 0
705 707 - more heads than before: 1+added heads (2..n)
706 708 - fewer heads than before: -1-removed heads (-2..-n)
707 709 - number of heads stays the same: 1
708 710 """
709 711 repo = repo.unfiltered()
710 712 def csmap(x):
711 713 repo.ui.debug("add changeset %s\n" % short(x))
712 714 return len(cl)
713 715
714 716 def revmap(x):
715 717 return cl.rev(x)
716 718
717 719 if not source:
718 720 return 0
719 721
720 722 changesets = files = revisions = 0
721 723 efiles = set()
722 724
723 725 tr = repo.transaction("\n".join([srctype, util.hidepassword(url)]))
724 726 # The transaction could have been created before and already carries source
725 727 # information. In this case we use the top level data. We overwrite the
726 728 # argument because we need to use the top level value (if they exist) in
727 729 # this function.
728 730 srctype = tr.hookargs.setdefault('source', srctype)
729 731 url = tr.hookargs.setdefault('url', url)
730 732
731 733 # write changelog data to temp files so concurrent readers will not see
732 734 # inconsistent view
733 735 cl = repo.changelog
734 736 cl.delayupdate(tr)
735 737 oldheads = cl.heads()
736 738 try:
737 739 repo.hook('prechangegroup', throw=True, **tr.hookargs)
738 740
739 741 trp = weakref.proxy(tr)
740 742 # pull off the changeset group
741 743 repo.ui.status(_("adding changesets\n"))
742 744 clstart = len(cl)
743 745 class prog(object):
744 746 def __init__(self, step, total):
745 747 self._step = step
746 748 self._total = total
747 749 self._count = 1
748 750 def __call__(self):
749 751 repo.ui.progress(self._step, self._count, unit=_('chunks'),
750 752 total=self._total)
751 753 self._count += 1
752 754 source.callback = prog(_('changesets'), expectedtotal)
753 755
754 756 source.changelogheader()
755 757 srccontent = cl.addgroup(source, csmap, trp)
756 758 if not (srccontent or emptyok):
757 759 raise util.Abort(_("received changelog group is empty"))
758 760 clend = len(cl)
759 761 changesets = clend - clstart
760 762 for c in xrange(clstart, clend):
761 763 efiles.update(repo[c].files())
762 764 efiles = len(efiles)
763 765 repo.ui.progress(_('changesets'), None)
764 766
765 767 # pull off the manifest group
766 768 repo.ui.status(_("adding manifests\n"))
767 769 # manifests <= changesets
768 770 source.callback = prog(_('manifests'), changesets)
769 771 # no need to check for empty manifest group here:
770 772 # if the result of the merge of 1 and 2 is the same in 3 and 4,
771 773 # no new manifest will be created and the manifest group will
772 774 # be empty during the pull
773 775 source.manifestheader()
774 776 repo.manifest.addgroup(source, revmap, trp)
775 777 repo.ui.progress(_('manifests'), None)
776 778
777 779 needfiles = {}
778 780 if repo.ui.configbool('server', 'validate', default=False):
779 781 # validate incoming csets have their manifests
780 782 for cset in xrange(clstart, clend):
781 783 mfnode = repo.changelog.read(repo.changelog.node(cset))[0]
782 784 mfest = repo.manifest.readdelta(mfnode)
783 785 # store file nodes we must see
784 786 for f, n in mfest.iteritems():
785 787 needfiles.setdefault(f, set()).add(n)
786 788
787 789 # process the files
788 790 repo.ui.status(_("adding file changes\n"))
789 791 source.callback = None
790 792 pr = prog(_('files'), efiles)
791 793 newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr,
792 794 needfiles)
793 795 revisions += newrevs
794 796 files += newfiles
795 797
796 798 dh = 0
797 799 if oldheads:
798 800 heads = cl.heads()
799 801 dh = len(heads) - len(oldheads)
800 802 for h in heads:
801 803 if h not in oldheads and repo[h].closesbranch():
802 804 dh -= 1
803 805 htext = ""
804 806 if dh:
805 807 htext = _(" (%+d heads)") % dh
806 808
807 809 repo.ui.status(_("added %d changesets"
808 810 " with %d changes to %d files%s\n")
809 811 % (changesets, revisions, files, htext))
810 812 repo.invalidatevolatilesets()
811 813
812 814 if changesets > 0:
813 815 p = lambda: tr.writepending() and repo.root or ""
814 816 if 'node' not in tr.hookargs:
815 817 tr.hookargs['node'] = hex(cl.node(clstart))
816 818 hookargs = dict(tr.hookargs)
817 819 else:
818 820 hookargs = dict(tr.hookargs)
819 821 hookargs['node'] = hex(cl.node(clstart))
820 822 repo.hook('pretxnchangegroup', throw=True, pending=p, **hookargs)
821 823
822 824 added = [cl.node(r) for r in xrange(clstart, clend)]
823 825 publishing = repo.publishing()
824 826 if srctype in ('push', 'serve'):
825 827 # Old servers can not push the boundary themselves.
826 828 # New servers won't push the boundary if changeset already
827 829 # exists locally as secret
828 830 #
829 831 # We should not use added here but the list of all change in
830 832 # the bundle
831 833 if publishing:
832 834 phases.advanceboundary(repo, tr, phases.public, srccontent)
833 835 else:
834 836 # Those changesets have been pushed from the outside, their
835 837 # phases are going to be pushed alongside. Therefor
836 838 # `targetphase` is ignored.
837 839 phases.advanceboundary(repo, tr, phases.draft, srccontent)
838 840 phases.retractboundary(repo, tr, phases.draft, added)
839 841 elif srctype != 'strip':
840 842 # publishing only alter behavior during push
841 843 #
842 844 # strip should not touch boundary at all
843 845 phases.retractboundary(repo, tr, targetphase, added)
844 846
845 847 if changesets > 0:
846 848 if srctype != 'strip':
847 849 # During strip, branchcache is invalid but coming call to
848 850 # `destroyed` will repair it.
849 851 # In other case we can safely update cache on disk.
850 852 branchmap.updatecache(repo.filtered('served'))
851 853
852 854 def runhooks():
853 855 # These hooks run when the lock releases, not when the
854 856 # transaction closes. So it's possible for the changelog
855 857 # to have changed since we last saw it.
856 858 if clstart >= len(repo):
857 859 return
858 860
859 861 # forcefully update the on-disk branch cache
860 862 repo.ui.debug("updating the branch cache\n")
861 863 repo.hook("changegroup", **hookargs)
862 864
863 865 for n in added:
864 866 args = hookargs.copy()
865 867 args['node'] = hex(n)
866 868 repo.hook("incoming", **args)
867 869
868 870 newheads = [h for h in repo.heads() if h not in oldheads]
869 871 repo.ui.log("incoming",
870 872 "%s incoming changes - new heads: %s\n",
871 873 len(added),
872 874 ', '.join([hex(c[:6]) for c in newheads]))
873 875
874 876 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
875 877 lambda tr: repo._afterlock(runhooks))
876 878
877 879 tr.close()
878 880
879 881 finally:
880 882 tr.release()
881 883 repo.ui.flush()
882 884 # never return 0 here:
883 885 if dh < 0:
884 886 return dh - 1
885 887 else:
886 888 return dh + 1
@@ -1,862 +1,897
1 1 # templater.py - template expansion for output
2 2 #
3 3 # Copyright 2005, 2006 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 i18n import _
9 9 import os, re
10 10 import util, config, templatefilters, templatekw, parser, error
11 11 import revset as revsetmod
12 12 import types
13 13 import minirst
14 14
15 15 # template parsing
16 16
17 17 elements = {
18 18 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
19 19 ",": (2, None, ("list", 2)),
20 20 "|": (5, None, ("|", 5)),
21 21 "%": (6, None, ("%", 6)),
22 22 ")": (0, None, None),
23 23 "integer": (0, ("integer",), None),
24 24 "symbol": (0, ("symbol",), None),
25 25 "string": (0, ("template",), None),
26 26 "rawstring": (0, ("rawstring",), None),
27 27 "end": (0, None, None),
28 28 }
29 29
30 30 def tokenize(program, start, end):
31 31 pos = start
32 32 while pos < end:
33 33 c = program[pos]
34 34 if c.isspace(): # skip inter-token whitespace
35 35 pass
36 36 elif c in "(,)%|": # handle simple operators
37 37 yield (c, None, pos)
38 38 elif (c in '"\'' or c == 'r' and
39 39 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
40 40 if c == 'r':
41 41 pos += 1
42 42 c = program[pos]
43 43 decode = False
44 44 else:
45 45 decode = True
46 46 pos += 1
47 47 s = pos
48 48 while pos < end: # find closing quote
49 49 d = program[pos]
50 50 if d == '\\': # skip over escaped characters
51 51 pos += 2
52 52 continue
53 53 if d == c:
54 54 if not decode:
55 55 yield ('rawstring', program[s:pos], s)
56 56 break
57 57 yield ('string', program[s:pos], s)
58 58 break
59 59 pos += 1
60 60 else:
61 61 raise error.ParseError(_("unterminated string"), s)
62 62 elif c.isdigit() or c == '-':
63 63 s = pos
64 64 if c == '-': # simply take negate operator as part of integer
65 65 pos += 1
66 66 if pos >= end or not program[pos].isdigit():
67 67 raise error.ParseError(_("integer literal without digits"), s)
68 68 pos += 1
69 69 while pos < end:
70 70 d = program[pos]
71 71 if not d.isdigit():
72 72 break
73 73 pos += 1
74 74 yield ('integer', program[s:pos], s)
75 75 pos -= 1
76 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
77 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
78 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
79 # where some of nested templates were preprocessed as strings and
80 # then compiled. therefore, \"...\" was allowed. (issue4733)
81 #
82 # processing flow of _evalifliteral() at 5ab28a2e9962:
83 # outer template string -> stringify() -> compiletemplate()
84 # ------------------------ ------------ ------------------
85 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
86 # ~~~~~~~~
87 # escaped quoted string
88 if c == 'r':
89 pos += 1
90 token = 'rawstring'
91 else:
92 token = 'string'
93 quote = program[pos:pos + 2]
94 s = pos = pos + 2
95 while pos < end: # find closing escaped quote
96 if program.startswith('\\\\\\', pos, end):
97 pos += 4 # skip over double escaped characters
98 continue
99 if program.startswith(quote, pos, end):
100 try:
101 # interpret as if it were a part of an outer string
102 data = program[s:pos].decode('string-escape')
103 except ValueError: # unbalanced escapes
104 raise error.ParseError(_("syntax error"), s)
105 yield (token, data, s)
106 pos += 1
107 break
108 pos += 1
109 else:
110 raise error.ParseError(_("unterminated string"), s)
76 111 elif c.isalnum() or c in '_':
77 112 s = pos
78 113 pos += 1
79 114 while pos < end: # find end of symbol
80 115 d = program[pos]
81 116 if not (d.isalnum() or d == "_"):
82 117 break
83 118 pos += 1
84 119 sym = program[s:pos]
85 120 yield ('symbol', sym, s)
86 121 pos -= 1
87 122 elif c == '}':
88 123 pos += 1
89 124 break
90 125 else:
91 126 raise error.ParseError(_("syntax error"), pos)
92 127 pos += 1
93 128 yield ('end', None, pos)
94 129
95 130 def compiletemplate(tmpl, context):
96 131 parsed = []
97 132 pos, stop = 0, len(tmpl)
98 133 p = parser.parser(elements)
99 134 while pos < stop:
100 135 n = tmpl.find('{', pos)
101 136 if n < 0:
102 137 parsed.append(('string', tmpl[pos:]))
103 138 break
104 139 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
105 140 if bs % 2 == 1:
106 141 # escaped (e.g. '\{', '\\\{', but not '\\{')
107 142 parsed.append(('string', (tmpl[pos:n - 1] + "{")))
108 143 pos = n + 1
109 144 continue
110 145 if n > pos:
111 146 parsed.append(('string', tmpl[pos:n]))
112 147
113 148 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop))
114 149 parsed.append(parseres)
115 150
116 151 return [compileexp(e, context, methods) for e in parsed]
117 152
118 153 def compileexp(exp, context, curmethods):
119 154 t = exp[0]
120 155 if t in curmethods:
121 156 return curmethods[t](exp, context)
122 157 raise error.ParseError(_("unknown method '%s'") % t)
123 158
124 159 # template evaluation
125 160
126 161 def getsymbol(exp):
127 162 if exp[0] == 'symbol':
128 163 return exp[1]
129 164 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
130 165
131 166 def getlist(x):
132 167 if not x:
133 168 return []
134 169 if x[0] == 'list':
135 170 return getlist(x[1]) + [x[2]]
136 171 return [x]
137 172
138 173 def getfilter(exp, context):
139 174 f = getsymbol(exp)
140 175 if f not in context._filters:
141 176 raise error.ParseError(_("unknown function '%s'") % f)
142 177 return context._filters[f]
143 178
144 179 def gettemplate(exp, context):
145 180 if exp[0] == 'template':
146 181 return compiletemplate(exp[1], context)
147 182 if exp[0] == 'symbol':
148 183 # unlike runsymbol(), here 'symbol' is always taken as template name
149 184 # even if it exists in mapping. this allows us to override mapping
150 185 # by web templates, e.g. 'changelogtag' is redefined in map file.
151 186 return context._load(exp[1])
152 187 raise error.ParseError(_("expected template specifier"))
153 188
154 189 def runinteger(context, mapping, data):
155 190 return int(data)
156 191
157 192 def runstring(context, mapping, data):
158 193 return data.decode("string-escape")
159 194
160 195 def runrawstring(context, mapping, data):
161 196 return data
162 197
163 198 def runsymbol(context, mapping, key):
164 199 v = mapping.get(key)
165 200 if v is None:
166 201 v = context._defaults.get(key)
167 202 if v is None:
168 203 try:
169 204 v = context.process(key, mapping)
170 205 except TemplateNotFound:
171 206 v = ''
172 207 if callable(v):
173 208 return v(**mapping)
174 209 if isinstance(v, types.GeneratorType):
175 210 v = list(v)
176 211 return v
177 212
178 213 def buildtemplate(exp, context):
179 214 ctmpl = compiletemplate(exp[1], context)
180 215 if len(ctmpl) == 1:
181 216 return ctmpl[0] # fast path for string with no template fragment
182 217 return (runtemplate, ctmpl)
183 218
184 219 def runtemplate(context, mapping, template):
185 220 for func, data in template:
186 221 yield func(context, mapping, data)
187 222
188 223 def buildfilter(exp, context):
189 224 func, data = compileexp(exp[1], context, methods)
190 225 filt = getfilter(exp[2], context)
191 226 return (runfilter, (func, data, filt))
192 227
193 228 def runfilter(context, mapping, data):
194 229 func, data, filt = data
195 230 # func() may return string, generator of strings or arbitrary object such
196 231 # as date tuple, but filter does not want generator.
197 232 thing = func(context, mapping, data)
198 233 if isinstance(thing, types.GeneratorType):
199 234 thing = stringify(thing)
200 235 try:
201 236 return filt(thing)
202 237 except (ValueError, AttributeError, TypeError):
203 238 if isinstance(data, tuple):
204 239 dt = data[1]
205 240 else:
206 241 dt = data
207 242 raise util.Abort(_("template filter '%s' is not compatible with "
208 243 "keyword '%s'") % (filt.func_name, dt))
209 244
210 245 def buildmap(exp, context):
211 246 func, data = compileexp(exp[1], context, methods)
212 247 ctmpl = gettemplate(exp[2], context)
213 248 return (runmap, (func, data, ctmpl))
214 249
215 250 def runmap(context, mapping, data):
216 251 func, data, ctmpl = data
217 252 d = func(context, mapping, data)
218 253 if callable(d):
219 254 d = d()
220 255
221 256 lm = mapping.copy()
222 257
223 258 for i in d:
224 259 if isinstance(i, dict):
225 260 lm.update(i)
226 261 lm['originalnode'] = mapping.get('node')
227 262 yield runtemplate(context, lm, ctmpl)
228 263 else:
229 264 # v is not an iterable of dicts, this happen when 'key'
230 265 # has been fully expanded already and format is useless.
231 266 # If so, return the expanded value.
232 267 yield i
233 268
234 269 def buildfunc(exp, context):
235 270 n = getsymbol(exp[1])
236 271 args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])]
237 272 if n in funcs:
238 273 f = funcs[n]
239 274 return (f, args)
240 275 if n in context._filters:
241 276 if len(args) != 1:
242 277 raise error.ParseError(_("filter %s expects one argument") % n)
243 278 f = context._filters[n]
244 279 return (runfilter, (args[0][0], args[0][1], f))
245 280 raise error.ParseError(_("unknown function '%s'") % n)
246 281
247 282 def date(context, mapping, args):
248 283 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
249 284 strings."""
250 285 if not (1 <= len(args) <= 2):
251 286 # i18n: "date" is a keyword
252 287 raise error.ParseError(_("date expects one or two arguments"))
253 288
254 289 date = args[0][0](context, mapping, args[0][1])
255 290 fmt = None
256 291 if len(args) == 2:
257 292 fmt = stringify(args[1][0](context, mapping, args[1][1]))
258 293 try:
259 294 if fmt is None:
260 295 return util.datestr(date)
261 296 else:
262 297 return util.datestr(date, fmt)
263 298 except (TypeError, ValueError):
264 299 # i18n: "date" is a keyword
265 300 raise error.ParseError(_("date expects a date information"))
266 301
267 302 def diff(context, mapping, args):
268 303 """:diff([includepattern [, excludepattern]]): Show a diff, optionally
269 304 specifying files to include or exclude."""
270 305 if len(args) > 2:
271 306 # i18n: "diff" is a keyword
272 307 raise error.ParseError(_("diff expects one, two or no arguments"))
273 308
274 309 def getpatterns(i):
275 310 if i < len(args):
276 311 s = stringify(args[i][0](context, mapping, args[i][1])).strip()
277 312 if s:
278 313 return [s]
279 314 return []
280 315
281 316 ctx = mapping['ctx']
282 317 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
283 318
284 319 return ''.join(chunks)
285 320
286 321 def fill(context, mapping, args):
287 322 """:fill(text[, width[, initialident[, hangindent]]]): Fill many
288 323 paragraphs with optional indentation. See the "fill" filter."""
289 324 if not (1 <= len(args) <= 4):
290 325 # i18n: "fill" is a keyword
291 326 raise error.ParseError(_("fill expects one to four arguments"))
292 327
293 328 text = stringify(args[0][0](context, mapping, args[0][1]))
294 329 width = 76
295 330 initindent = ''
296 331 hangindent = ''
297 332 if 2 <= len(args) <= 4:
298 333 try:
299 334 width = int(stringify(args[1][0](context, mapping, args[1][1])))
300 335 except ValueError:
301 336 # i18n: "fill" is a keyword
302 337 raise error.ParseError(_("fill expects an integer width"))
303 338 try:
304 339 initindent = stringify(args[2][0](context, mapping, args[2][1]))
305 340 hangindent = stringify(args[3][0](context, mapping, args[3][1]))
306 341 except IndexError:
307 342 pass
308 343
309 344 return templatefilters.fill(text, width, initindent, hangindent)
310 345
311 346 def pad(context, mapping, args):
312 347 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a
313 348 fill character."""
314 349 if not (2 <= len(args) <= 4):
315 350 # i18n: "pad" is a keyword
316 351 raise error.ParseError(_("pad() expects two to four arguments"))
317 352
318 353 width = int(args[1][1])
319 354
320 355 text = stringify(args[0][0](context, mapping, args[0][1]))
321 356
322 357 right = False
323 358 fillchar = ' '
324 359 if len(args) > 2:
325 360 fillchar = stringify(args[2][0](context, mapping, args[2][1]))
326 361 if len(args) > 3:
327 362 right = util.parsebool(args[3][1])
328 363
329 364 if right:
330 365 return text.rjust(width, fillchar)
331 366 else:
332 367 return text.ljust(width, fillchar)
333 368
334 369 def indent(context, mapping, args):
335 370 """:indent(text, indentchars[, firstline]): Indents all non-empty lines
336 371 with the characters given in the indentchars string. An optional
337 372 third parameter will override the indent for the first line only
338 373 if present."""
339 374 if not (2 <= len(args) <= 3):
340 375 # i18n: "indent" is a keyword
341 376 raise error.ParseError(_("indent() expects two or three arguments"))
342 377
343 378 text = stringify(args[0][0](context, mapping, args[0][1]))
344 379 indent = stringify(args[1][0](context, mapping, args[1][1]))
345 380
346 381 if len(args) == 3:
347 382 firstline = stringify(args[2][0](context, mapping, args[2][1]))
348 383 else:
349 384 firstline = indent
350 385
351 386 # the indent function doesn't indent the first line, so we do it here
352 387 return templatefilters.indent(firstline + text, indent)
353 388
354 389 def get(context, mapping, args):
355 390 """:get(dict, key): Get an attribute/key from an object. Some keywords
356 391 are complex types. This function allows you to obtain the value of an
357 392 attribute on these type."""
358 393 if len(args) != 2:
359 394 # i18n: "get" is a keyword
360 395 raise error.ParseError(_("get() expects two arguments"))
361 396
362 397 dictarg = args[0][0](context, mapping, args[0][1])
363 398 if not util.safehasattr(dictarg, 'get'):
364 399 # i18n: "get" is a keyword
365 400 raise error.ParseError(_("get() expects a dict as first argument"))
366 401
367 402 key = args[1][0](context, mapping, args[1][1])
368 403 yield dictarg.get(key)
369 404
370 405 def if_(context, mapping, args):
371 406 """:if(expr, then[, else]): Conditionally execute based on the result of
372 407 an expression."""
373 408 if not (2 <= len(args) <= 3):
374 409 # i18n: "if" is a keyword
375 410 raise error.ParseError(_("if expects two or three arguments"))
376 411
377 412 test = stringify(args[0][0](context, mapping, args[0][1]))
378 413 if test:
379 414 yield args[1][0](context, mapping, args[1][1])
380 415 elif len(args) == 3:
381 416 yield args[2][0](context, mapping, args[2][1])
382 417
383 418 def ifcontains(context, mapping, args):
384 419 """:ifcontains(search, thing, then[, else]): Conditionally execute based
385 420 on whether the item "search" is in "thing"."""
386 421 if not (3 <= len(args) <= 4):
387 422 # i18n: "ifcontains" is a keyword
388 423 raise error.ParseError(_("ifcontains expects three or four arguments"))
389 424
390 425 item = stringify(args[0][0](context, mapping, args[0][1]))
391 426 items = args[1][0](context, mapping, args[1][1])
392 427
393 428 if item in items:
394 429 yield args[2][0](context, mapping, args[2][1])
395 430 elif len(args) == 4:
396 431 yield args[3][0](context, mapping, args[3][1])
397 432
398 433 def ifeq(context, mapping, args):
399 434 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on
400 435 whether 2 items are equivalent."""
401 436 if not (3 <= len(args) <= 4):
402 437 # i18n: "ifeq" is a keyword
403 438 raise error.ParseError(_("ifeq expects three or four arguments"))
404 439
405 440 test = stringify(args[0][0](context, mapping, args[0][1]))
406 441 match = stringify(args[1][0](context, mapping, args[1][1]))
407 442 if test == match:
408 443 yield args[2][0](context, mapping, args[2][1])
409 444 elif len(args) == 4:
410 445 yield args[3][0](context, mapping, args[3][1])
411 446
412 447 def join(context, mapping, args):
413 448 """:join(list, sep): Join items in a list with a delimiter."""
414 449 if not (1 <= len(args) <= 2):
415 450 # i18n: "join" is a keyword
416 451 raise error.ParseError(_("join expects one or two arguments"))
417 452
418 453 joinset = args[0][0](context, mapping, args[0][1])
419 454 if callable(joinset):
420 455 jf = joinset.joinfmt
421 456 joinset = [jf(x) for x in joinset()]
422 457
423 458 joiner = " "
424 459 if len(args) > 1:
425 460 joiner = stringify(args[1][0](context, mapping, args[1][1]))
426 461
427 462 first = True
428 463 for x in joinset:
429 464 if first:
430 465 first = False
431 466 else:
432 467 yield joiner
433 468 yield x
434 469
435 470 def label(context, mapping, args):
436 471 """:label(label, expr): Apply a label to generated content. Content with
437 472 a label applied can result in additional post-processing, such as
438 473 automatic colorization."""
439 474 if len(args) != 2:
440 475 # i18n: "label" is a keyword
441 476 raise error.ParseError(_("label expects two arguments"))
442 477
443 478 # ignore args[0] (the label string) since this is supposed to be a a no-op
444 479 yield args[1][0](context, mapping, args[1][1])
445 480
446 481 def revset(context, mapping, args):
447 482 """:revset(query[, formatargs...]): Execute a revision set query. See
448 483 :hg:`help revset`."""
449 484 if not len(args) > 0:
450 485 # i18n: "revset" is a keyword
451 486 raise error.ParseError(_("revset expects one or more arguments"))
452 487
453 488 raw = stringify(args[0][0](context, mapping, args[0][1]))
454 489 ctx = mapping['ctx']
455 490 repo = ctx.repo()
456 491
457 492 def query(expr):
458 493 m = revsetmod.match(repo.ui, expr)
459 494 return m(repo)
460 495
461 496 if len(args) > 1:
462 497 formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
463 498 revs = query(revsetmod.formatspec(raw, *formatargs))
464 499 revs = list([str(r) for r in revs])
465 500 else:
466 501 revsetcache = mapping['cache'].setdefault("revsetcache", {})
467 502 if raw in revsetcache:
468 503 revs = revsetcache[raw]
469 504 else:
470 505 revs = query(raw)
471 506 revs = list([str(r) for r in revs])
472 507 revsetcache[raw] = revs
473 508
474 509 return templatekw.showlist("revision", revs, **mapping)
475 510
476 511 def rstdoc(context, mapping, args):
477 512 """:rstdoc(text, style): Format ReStructuredText."""
478 513 if len(args) != 2:
479 514 # i18n: "rstdoc" is a keyword
480 515 raise error.ParseError(_("rstdoc expects two arguments"))
481 516
482 517 text = stringify(args[0][0](context, mapping, args[0][1]))
483 518 style = stringify(args[1][0](context, mapping, args[1][1]))
484 519
485 520 return minirst.format(text, style=style, keep=['verbose'])
486 521
487 522 def shortest(context, mapping, args):
488 523 """:shortest(node, minlength=4): Obtain the shortest representation of
489 524 a node."""
490 525 if not (1 <= len(args) <= 2):
491 526 # i18n: "shortest" is a keyword
492 527 raise error.ParseError(_("shortest() expects one or two arguments"))
493 528
494 529 node = stringify(args[0][0](context, mapping, args[0][1]))
495 530
496 531 minlength = 4
497 532 if len(args) > 1:
498 533 minlength = int(args[1][1])
499 534
500 535 cl = mapping['ctx']._repo.changelog
501 536 def isvalid(test):
502 537 try:
503 538 try:
504 539 cl.index.partialmatch(test)
505 540 except AttributeError:
506 541 # Pure mercurial doesn't support partialmatch on the index.
507 542 # Fallback to the slow way.
508 543 if cl._partialmatch(test) is None:
509 544 return False
510 545
511 546 try:
512 547 i = int(test)
513 548 # if we are a pure int, then starting with zero will not be
514 549 # confused as a rev; or, obviously, if the int is larger than
515 550 # the value of the tip rev
516 551 if test[0] == '0' or i > len(cl):
517 552 return True
518 553 return False
519 554 except ValueError:
520 555 return True
521 556 except error.RevlogError:
522 557 return False
523 558
524 559 shortest = node
525 560 startlength = max(6, minlength)
526 561 length = startlength
527 562 while True:
528 563 test = node[:length]
529 564 if isvalid(test):
530 565 shortest = test
531 566 if length == minlength or length > startlength:
532 567 return shortest
533 568 length -= 1
534 569 else:
535 570 length += 1
536 571 if len(shortest) <= length:
537 572 return shortest
538 573
539 574 def strip(context, mapping, args):
540 575 """:strip(text[, chars]): Strip characters from a string."""
541 576 if not (1 <= len(args) <= 2):
542 577 # i18n: "strip" is a keyword
543 578 raise error.ParseError(_("strip expects one or two arguments"))
544 579
545 580 text = stringify(args[0][0](context, mapping, args[0][1]))
546 581 if len(args) == 2:
547 582 chars = stringify(args[1][0](context, mapping, args[1][1]))
548 583 return text.strip(chars)
549 584 return text.strip()
550 585
551 586 def sub(context, mapping, args):
552 587 """:sub(pattern, replacement, expression): Perform text substitution
553 588 using regular expressions."""
554 589 if len(args) != 3:
555 590 # i18n: "sub" is a keyword
556 591 raise error.ParseError(_("sub expects three arguments"))
557 592
558 593 pat = stringify(args[0][0](context, mapping, args[0][1]))
559 594 rpl = stringify(args[1][0](context, mapping, args[1][1]))
560 595 src = stringify(args[2][0](context, mapping, args[2][1]))
561 596 yield re.sub(pat, rpl, src)
562 597
563 598 def startswith(context, mapping, args):
564 599 """:startswith(pattern, text): Returns the value from the "text" argument
565 600 if it begins with the content from the "pattern" argument."""
566 601 if len(args) != 2:
567 602 # i18n: "startswith" is a keyword
568 603 raise error.ParseError(_("startswith expects two arguments"))
569 604
570 605 patn = stringify(args[0][0](context, mapping, args[0][1]))
571 606 text = stringify(args[1][0](context, mapping, args[1][1]))
572 607 if text.startswith(patn):
573 608 return text
574 609 return ''
575 610
576 611
577 612 def word(context, mapping, args):
578 613 """:word(number, text[, separator]): Return the nth word from a string."""
579 614 if not (2 <= len(args) <= 3):
580 615 # i18n: "word" is a keyword
581 616 raise error.ParseError(_("word expects two or three arguments, got %d")
582 617 % len(args))
583 618
584 619 try:
585 620 num = int(stringify(args[0][0](context, mapping, args[0][1])))
586 621 except ValueError:
587 622 # i18n: "word" is a keyword
588 623 raise error.ParseError(_("word expects an integer index"))
589 624 text = stringify(args[1][0](context, mapping, args[1][1]))
590 625 if len(args) == 3:
591 626 splitter = stringify(args[2][0](context, mapping, args[2][1]))
592 627 else:
593 628 splitter = None
594 629
595 630 tokens = text.split(splitter)
596 631 if num >= len(tokens):
597 632 return ''
598 633 else:
599 634 return tokens[num]
600 635
601 636 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
602 637 exprmethods = {
603 638 "integer": lambda e, c: (runinteger, e[1]),
604 639 "string": lambda e, c: (runstring, e[1]),
605 640 "rawstring": lambda e, c: (runrawstring, e[1]),
606 641 "symbol": lambda e, c: (runsymbol, e[1]),
607 642 "template": buildtemplate,
608 643 "group": lambda e, c: compileexp(e[1], c, exprmethods),
609 644 # ".": buildmember,
610 645 "|": buildfilter,
611 646 "%": buildmap,
612 647 "func": buildfunc,
613 648 }
614 649
615 650 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
616 651 methods = exprmethods.copy()
617 652 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
618 653
619 654 funcs = {
620 655 "date": date,
621 656 "diff": diff,
622 657 "fill": fill,
623 658 "get": get,
624 659 "if": if_,
625 660 "ifcontains": ifcontains,
626 661 "ifeq": ifeq,
627 662 "indent": indent,
628 663 "join": join,
629 664 "label": label,
630 665 "pad": pad,
631 666 "revset": revset,
632 667 "rstdoc": rstdoc,
633 668 "shortest": shortest,
634 669 "startswith": startswith,
635 670 "strip": strip,
636 671 "sub": sub,
637 672 "word": word,
638 673 }
639 674
640 675 # template engine
641 676
642 677 stringify = templatefilters.stringify
643 678
644 679 def _flatten(thing):
645 680 '''yield a single stream from a possibly nested set of iterators'''
646 681 if isinstance(thing, str):
647 682 yield thing
648 683 elif not util.safehasattr(thing, '__iter__'):
649 684 if thing is not None:
650 685 yield str(thing)
651 686 else:
652 687 for i in thing:
653 688 if isinstance(i, str):
654 689 yield i
655 690 elif not util.safehasattr(i, '__iter__'):
656 691 if i is not None:
657 692 yield str(i)
658 693 elif i is not None:
659 694 for j in _flatten(i):
660 695 yield j
661 696
662 697 def unquotestring(s):
663 698 '''unwrap quotes'''
664 699 if len(s) < 2 or s[0] != s[-1]:
665 700 raise SyntaxError(_('unmatched quotes'))
666 701 # de-backslash-ify only <\">. it is invalid syntax in non-string part of
667 702 # template, but we are likely to escape <"> in quoted string and it was
668 703 # accepted before, thanks to issue4290. <\\"> is unmodified because it
669 704 # is ambiguous and it was processed as such before 2.8.1.
670 705 #
671 706 # template result
672 707 # --------- ------------------------
673 708 # {\"\"} parse error
674 709 # "{""}" {""} -> <>
675 710 # "{\"\"}" {""} -> <>
676 711 # {"\""} {"\""} -> <">
677 712 # '{"\""}' {"\""} -> <">
678 713 # "{"\""}" parse error (don't care)
679 714 q = s[0]
680 715 return s[1:-1].replace('\\\\' + q, '\\\\\\' + q).replace('\\' + q, q)
681 716
682 717 class engine(object):
683 718 '''template expansion engine.
684 719
685 720 template expansion works like this. a map file contains key=value
686 721 pairs. if value is quoted, it is treated as string. otherwise, it
687 722 is treated as name of template file.
688 723
689 724 templater is asked to expand a key in map. it looks up key, and
690 725 looks for strings like this: {foo}. it expands {foo} by looking up
691 726 foo in map, and substituting it. expansion is recursive: it stops
692 727 when there is no more {foo} to replace.
693 728
694 729 expansion also allows formatting and filtering.
695 730
696 731 format uses key to expand each item in list. syntax is
697 732 {key%format}.
698 733
699 734 filter uses function to transform value. syntax is
700 735 {key|filter1|filter2|...}.'''
701 736
702 737 def __init__(self, loader, filters={}, defaults={}):
703 738 self._loader = loader
704 739 self._filters = filters
705 740 self._defaults = defaults
706 741 self._cache = {}
707 742
708 743 def _load(self, t):
709 744 '''load, parse, and cache a template'''
710 745 if t not in self._cache:
711 746 self._cache[t] = compiletemplate(self._loader(t), self)
712 747 return self._cache[t]
713 748
714 749 def process(self, t, mapping):
715 750 '''Perform expansion. t is name of map element to expand.
716 751 mapping contains added elements for use during expansion. Is a
717 752 generator.'''
718 753 return _flatten(runtemplate(self, mapping, self._load(t)))
719 754
720 755 engines = {'default': engine}
721 756
722 757 def stylelist():
723 758 paths = templatepaths()
724 759 if not paths:
725 760 return _('no templates found, try `hg debuginstall` for more info')
726 761 dirlist = os.listdir(paths[0])
727 762 stylelist = []
728 763 for file in dirlist:
729 764 split = file.split(".")
730 765 if split[0] == "map-cmdline":
731 766 stylelist.append(split[1])
732 767 return ", ".join(sorted(stylelist))
733 768
734 769 class TemplateNotFound(util.Abort):
735 770 pass
736 771
737 772 class templater(object):
738 773
739 774 def __init__(self, mapfile, filters={}, defaults={}, cache={},
740 775 minchunk=1024, maxchunk=65536):
741 776 '''set up template engine.
742 777 mapfile is name of file to read map definitions from.
743 778 filters is dict of functions. each transforms a value into another.
744 779 defaults is dict of default map definitions.'''
745 780 self.mapfile = mapfile or 'template'
746 781 self.cache = cache.copy()
747 782 self.map = {}
748 783 if mapfile:
749 784 self.base = os.path.dirname(mapfile)
750 785 else:
751 786 self.base = ''
752 787 self.filters = templatefilters.filters.copy()
753 788 self.filters.update(filters)
754 789 self.defaults = defaults
755 790 self.minchunk, self.maxchunk = minchunk, maxchunk
756 791 self.ecache = {}
757 792
758 793 if not mapfile:
759 794 return
760 795 if not os.path.exists(mapfile):
761 796 raise util.Abort(_("style '%s' not found") % mapfile,
762 797 hint=_("available styles: %s") % stylelist())
763 798
764 799 conf = config.config(includepaths=templatepaths())
765 800 conf.read(mapfile)
766 801
767 802 for key, val in conf[''].items():
768 803 if not val:
769 804 raise SyntaxError(_('%s: missing value') % conf.source('', key))
770 805 if val[0] in "'\"":
771 806 try:
772 807 self.cache[key] = unquotestring(val)
773 808 except SyntaxError as inst:
774 809 raise SyntaxError('%s: %s' %
775 810 (conf.source('', key), inst.args[0]))
776 811 else:
777 812 val = 'default', val
778 813 if ':' in val[1]:
779 814 val = val[1].split(':', 1)
780 815 self.map[key] = val[0], os.path.join(self.base, val[1])
781 816
782 817 def __contains__(self, key):
783 818 return key in self.cache or key in self.map
784 819
785 820 def load(self, t):
786 821 '''Get the template for the given template name. Use a local cache.'''
787 822 if t not in self.cache:
788 823 try:
789 824 self.cache[t] = util.readfile(self.map[t][1])
790 825 except KeyError as inst:
791 826 raise TemplateNotFound(_('"%s" not in template map') %
792 827 inst.args[0])
793 828 except IOError as inst:
794 829 raise IOError(inst.args[0], _('template file %s: %s') %
795 830 (self.map[t][1], inst.args[1]))
796 831 return self.cache[t]
797 832
798 833 def __call__(self, t, **mapping):
799 834 ttype = t in self.map and self.map[t][0] or 'default'
800 835 if ttype not in self.ecache:
801 836 self.ecache[ttype] = engines[ttype](self.load,
802 837 self.filters, self.defaults)
803 838 proc = self.ecache[ttype]
804 839
805 840 stream = proc.process(t, mapping)
806 841 if self.minchunk:
807 842 stream = util.increasingchunks(stream, min=self.minchunk,
808 843 max=self.maxchunk)
809 844 return stream
810 845
811 846 def templatepaths():
812 847 '''return locations used for template files.'''
813 848 pathsrel = ['templates']
814 849 paths = [os.path.normpath(os.path.join(util.datapath, f))
815 850 for f in pathsrel]
816 851 return [p for p in paths if os.path.isdir(p)]
817 852
818 853 def templatepath(name):
819 854 '''return location of template file. returns None if not found.'''
820 855 for p in templatepaths():
821 856 f = os.path.join(p, name)
822 857 if os.path.exists(f):
823 858 return f
824 859 return None
825 860
826 861 def stylemap(styles, paths=None):
827 862 """Return path to mapfile for a given style.
828 863
829 864 Searches mapfile in the following locations:
830 865 1. templatepath/style/map
831 866 2. templatepath/map-style
832 867 3. templatepath/map
833 868 """
834 869
835 870 if paths is None:
836 871 paths = templatepaths()
837 872 elif isinstance(paths, str):
838 873 paths = [paths]
839 874
840 875 if isinstance(styles, str):
841 876 styles = [styles]
842 877
843 878 for style in styles:
844 879 # only plain name is allowed to honor template paths
845 880 if (not style
846 881 or style in (os.curdir, os.pardir)
847 882 or os.sep in style
848 883 or os.altsep and os.altsep in style):
849 884 continue
850 885 locations = [os.path.join(style, 'map'), 'map-' + style]
851 886 locations.append('map')
852 887
853 888 for path in paths:
854 889 for location in locations:
855 890 mapfile = os.path.join(path, location)
856 891 if os.path.isfile(mapfile):
857 892 return style, mapfile
858 893
859 894 raise RuntimeError("No hgweb templates found in %r" % paths)
860 895
861 896 # tell hggettext to extract docstrings from these functions:
862 897 i18nfunctions = funcs.values()
@@ -1,3325 +1,3376
1 1 $ hg init a
2 2 $ cd a
3 3 $ echo a > a
4 4 $ hg add a
5 5 $ echo line 1 > b
6 6 $ echo line 2 >> b
7 7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8 8
9 9 $ hg add b
10 10 $ echo other 1 > c
11 11 $ echo other 2 >> c
12 12 $ echo >> c
13 13 $ echo other 3 >> c
14 14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15 15
16 16 $ hg add c
17 17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 18 $ echo c >> c
19 19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20 20
21 21 $ echo foo > .hg/branch
22 22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23 23
24 24 $ hg co -q 3
25 25 $ echo other 4 >> d
26 26 $ hg add d
27 27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28 28
29 29 $ hg merge -q foo
30 30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31 31
32 32 Second branch starting at nullrev:
33 33
34 34 $ hg update null
35 35 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
36 36 $ echo second > second
37 37 $ hg add second
38 38 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
39 39 created new head
40 40
41 41 $ echo third > third
42 42 $ hg add third
43 43 $ hg mv second fourth
44 44 $ hg commit -m third -d "2020-01-01 10:01"
45 45
46 46 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
47 47 fourth (second)
48 48 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
49 49 second -> fourth
50 50 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
51 51 8 t
52 52 7 f
53 53
54 54 Quoting for ui.logtemplate
55 55
56 56 $ hg tip --config "ui.logtemplate={rev}\n"
57 57 8
58 58 $ hg tip --config "ui.logtemplate='{rev}\n'"
59 59 8
60 60 $ hg tip --config 'ui.logtemplate="{rev}\n"'
61 61 8
62 62
63 63 Make sure user/global hgrc does not affect tests
64 64
65 65 $ echo '[ui]' > .hg/hgrc
66 66 $ echo 'logtemplate =' >> .hg/hgrc
67 67 $ echo 'style =' >> .hg/hgrc
68 68
69 69 Add some simple styles to settings
70 70
71 71 $ echo '[templates]' >> .hg/hgrc
72 72 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
73 73 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
74 74
75 75 $ hg log -l1 -Tsimple
76 76 8
77 77 $ hg log -l1 -Tsimple2
78 78 8
79 79
80 80 Test templates and style maps in files:
81 81
82 82 $ echo "{rev}" > tmpl
83 83 $ hg log -l1 -T./tmpl
84 84 8
85 85 $ hg log -l1 -Tblah/blah
86 86 blah/blah (no-eol)
87 87
88 88 $ printf 'changeset = "{rev}\\n"\n' > map-simple
89 89 $ hg log -l1 -T./map-simple
90 90 8
91 91
92 92 Template should precede style option
93 93
94 94 $ hg log -l1 --style default -T '{rev}\n'
95 95 8
96 96
97 97 Add a commit with empty description, to ensure that the templates
98 98 below will omit the description line.
99 99
100 100 $ echo c >> c
101 101 $ hg add c
102 102 $ hg commit -qm ' '
103 103
104 104 Default style is like normal output. Phases style should be the same
105 105 as default style, except for extra phase lines.
106 106
107 107 $ hg log > log.out
108 108 $ hg log --style default > style.out
109 109 $ cmp log.out style.out || diff -u log.out style.out
110 110 $ hg log -T phases > phases.out
111 111 $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
112 112 @@ -2,0 +3 @@
113 113 +phase: draft
114 114 @@ -6,0 +8 @@
115 115 +phase: draft
116 116 @@ -11,0 +14 @@
117 117 +phase: draft
118 118 @@ -17,0 +21 @@
119 119 +phase: draft
120 120 @@ -24,0 +29 @@
121 121 +phase: draft
122 122 @@ -31,0 +37 @@
123 123 +phase: draft
124 124 @@ -36,0 +43 @@
125 125 +phase: draft
126 126 @@ -41,0 +49 @@
127 127 +phase: draft
128 128 @@ -46,0 +55 @@
129 129 +phase: draft
130 130 @@ -51,0 +61 @@
131 131 +phase: draft
132 132
133 133 $ hg log -v > log.out
134 134 $ hg log -v --style default > style.out
135 135 $ cmp log.out style.out || diff -u log.out style.out
136 136 $ hg log -v -T phases > phases.out
137 137 $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
138 138 @@ -2,0 +3 @@
139 139 +phase: draft
140 140 @@ -7,0 +9 @@
141 141 +phase: draft
142 142 @@ -15,0 +18 @@
143 143 +phase: draft
144 144 @@ -24,0 +28 @@
145 145 +phase: draft
146 146 @@ -33,0 +38 @@
147 147 +phase: draft
148 148 @@ -43,0 +49 @@
149 149 +phase: draft
150 150 @@ -50,0 +57 @@
151 151 +phase: draft
152 152 @@ -58,0 +66 @@
153 153 +phase: draft
154 154 @@ -66,0 +75 @@
155 155 +phase: draft
156 156 @@ -77,0 +87 @@
157 157 +phase: draft
158 158
159 159 $ hg log -q > log.out
160 160 $ hg log -q --style default > style.out
161 161 $ cmp log.out style.out || diff -u log.out style.out
162 162 $ hg log -q -T phases > phases.out
163 163 $ cmp log.out phases.out || diff -u log.out phases.out
164 164
165 165 $ hg log --debug > log.out
166 166 $ hg log --debug --style default > style.out
167 167 $ cmp log.out style.out || diff -u log.out style.out
168 168 $ hg log --debug -T phases > phases.out
169 169 $ cmp log.out phases.out || diff -u log.out phases.out
170 170
171 171 Default style should also preserve color information (issue2866):
172 172
173 173 $ cp $HGRCPATH $HGRCPATH-bak
174 174 $ cat <<EOF >> $HGRCPATH
175 175 > [extensions]
176 176 > color=
177 177 > EOF
178 178
179 179 $ hg --color=debug log > log.out
180 180 $ hg --color=debug log --style default > style.out
181 181 $ cmp log.out style.out || diff -u log.out style.out
182 182 $ hg --color=debug log -T phases > phases.out
183 183 $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
184 184 @@ -2,0 +3 @@
185 185 +[log.phase|phase: draft]
186 186 @@ -6,0 +8 @@
187 187 +[log.phase|phase: draft]
188 188 @@ -11,0 +14 @@
189 189 +[log.phase|phase: draft]
190 190 @@ -17,0 +21 @@
191 191 +[log.phase|phase: draft]
192 192 @@ -24,0 +29 @@
193 193 +[log.phase|phase: draft]
194 194 @@ -31,0 +37 @@
195 195 +[log.phase|phase: draft]
196 196 @@ -36,0 +43 @@
197 197 +[log.phase|phase: draft]
198 198 @@ -41,0 +49 @@
199 199 +[log.phase|phase: draft]
200 200 @@ -46,0 +55 @@
201 201 +[log.phase|phase: draft]
202 202 @@ -51,0 +61 @@
203 203 +[log.phase|phase: draft]
204 204
205 205 $ hg --color=debug -v log > log.out
206 206 $ hg --color=debug -v log --style default > style.out
207 207 $ cmp log.out style.out || diff -u log.out style.out
208 208 $ hg --color=debug -v log -T phases > phases.out
209 209 $ diff -U 0 log.out phases.out | grep -v '^---\|^+++'
210 210 @@ -2,0 +3 @@
211 211 +[log.phase|phase: draft]
212 212 @@ -7,0 +9 @@
213 213 +[log.phase|phase: draft]
214 214 @@ -15,0 +18 @@
215 215 +[log.phase|phase: draft]
216 216 @@ -24,0 +28 @@
217 217 +[log.phase|phase: draft]
218 218 @@ -33,0 +38 @@
219 219 +[log.phase|phase: draft]
220 220 @@ -43,0 +49 @@
221 221 +[log.phase|phase: draft]
222 222 @@ -50,0 +57 @@
223 223 +[log.phase|phase: draft]
224 224 @@ -58,0 +66 @@
225 225 +[log.phase|phase: draft]
226 226 @@ -66,0 +75 @@
227 227 +[log.phase|phase: draft]
228 228 @@ -77,0 +87 @@
229 229 +[log.phase|phase: draft]
230 230
231 231 $ hg --color=debug -q log > log.out
232 232 $ hg --color=debug -q log --style default > style.out
233 233 $ cmp log.out style.out || diff -u log.out style.out
234 234 $ hg --color=debug -q log -T phases > phases.out
235 235 $ cmp log.out phases.out || diff -u log.out phases.out
236 236
237 237 $ hg --color=debug --debug log > log.out
238 238 $ hg --color=debug --debug log --style default > style.out
239 239 $ cmp log.out style.out || diff -u log.out style.out
240 240 $ hg --color=debug --debug log -T phases > phases.out
241 241 $ cmp log.out phases.out || diff -u log.out phases.out
242 242
243 243 $ mv $HGRCPATH-bak $HGRCPATH
244 244
245 245 Remove commit with empty commit message, so as to not pollute further
246 246 tests.
247 247
248 248 $ hg --config extensions.strip= strip -q .
249 249
250 250 Revision with no copies (used to print a traceback):
251 251
252 252 $ hg tip -v --template '\n'
253 253
254 254
255 255 Compact style works:
256 256
257 257 $ hg log -Tcompact
258 258 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
259 259 third
260 260
261 261 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
262 262 second
263 263
264 264 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
265 265 merge
266 266
267 267 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
268 268 new head
269 269
270 270 4 bbe44766e73d 1970-01-17 04:53 +0000 person
271 271 new branch
272 272
273 273 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
274 274 no user, no domain
275 275
276 276 2 97054abb4ab8 1970-01-14 21:20 +0000 other
277 277 no person
278 278
279 279 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
280 280 other 1
281 281
282 282 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
283 283 line 1
284 284
285 285
286 286 $ hg log -v --style compact
287 287 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
288 288 third
289 289
290 290 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
291 291 second
292 292
293 293 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
294 294 merge
295 295
296 296 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
297 297 new head
298 298
299 299 4 bbe44766e73d 1970-01-17 04:53 +0000 person
300 300 new branch
301 301
302 302 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
303 303 no user, no domain
304 304
305 305 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
306 306 no person
307 307
308 308 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
309 309 other 1
310 310 other 2
311 311
312 312 other 3
313 313
314 314 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
315 315 line 1
316 316 line 2
317 317
318 318
319 319 $ hg log --debug --style compact
320 320 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
321 321 third
322 322
323 323 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
324 324 second
325 325
326 326 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
327 327 merge
328 328
329 329 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
330 330 new head
331 331
332 332 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
333 333 new branch
334 334
335 335 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
336 336 no user, no domain
337 337
338 338 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
339 339 no person
340 340
341 341 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
342 342 other 1
343 343 other 2
344 344
345 345 other 3
346 346
347 347 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
348 348 line 1
349 349 line 2
350 350
351 351
352 352 Test xml styles:
353 353
354 354 $ hg log --style xml
355 355 <?xml version="1.0"?>
356 356 <log>
357 357 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
358 358 <tag>tip</tag>
359 359 <author email="test">test</author>
360 360 <date>2020-01-01T10:01:00+00:00</date>
361 361 <msg xml:space="preserve">third</msg>
362 362 </logentry>
363 363 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
364 364 <parent revision="-1" node="0000000000000000000000000000000000000000" />
365 365 <author email="user@hostname">User Name</author>
366 366 <date>1970-01-12T13:46:40+00:00</date>
367 367 <msg xml:space="preserve">second</msg>
368 368 </logentry>
369 369 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
370 370 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
371 371 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
372 372 <author email="person">person</author>
373 373 <date>1970-01-18T08:40:01+00:00</date>
374 374 <msg xml:space="preserve">merge</msg>
375 375 </logentry>
376 376 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
377 377 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
378 378 <author email="person">person</author>
379 379 <date>1970-01-18T08:40:00+00:00</date>
380 380 <msg xml:space="preserve">new head</msg>
381 381 </logentry>
382 382 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
383 383 <branch>foo</branch>
384 384 <author email="person">person</author>
385 385 <date>1970-01-17T04:53:20+00:00</date>
386 386 <msg xml:space="preserve">new branch</msg>
387 387 </logentry>
388 388 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
389 389 <author email="person">person</author>
390 390 <date>1970-01-16T01:06:40+00:00</date>
391 391 <msg xml:space="preserve">no user, no domain</msg>
392 392 </logentry>
393 393 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
394 394 <author email="other@place">other</author>
395 395 <date>1970-01-14T21:20:00+00:00</date>
396 396 <msg xml:space="preserve">no person</msg>
397 397 </logentry>
398 398 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
399 399 <author email="other@place">A. N. Other</author>
400 400 <date>1970-01-13T17:33:20+00:00</date>
401 401 <msg xml:space="preserve">other 1
402 402 other 2
403 403
404 404 other 3</msg>
405 405 </logentry>
406 406 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
407 407 <author email="user@hostname">User Name</author>
408 408 <date>1970-01-12T13:46:40+00:00</date>
409 409 <msg xml:space="preserve">line 1
410 410 line 2</msg>
411 411 </logentry>
412 412 </log>
413 413
414 414 $ hg log -v --style xml
415 415 <?xml version="1.0"?>
416 416 <log>
417 417 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
418 418 <tag>tip</tag>
419 419 <author email="test">test</author>
420 420 <date>2020-01-01T10:01:00+00:00</date>
421 421 <msg xml:space="preserve">third</msg>
422 422 <paths>
423 423 <path action="A">fourth</path>
424 424 <path action="A">third</path>
425 425 <path action="R">second</path>
426 426 </paths>
427 427 <copies>
428 428 <copy source="second">fourth</copy>
429 429 </copies>
430 430 </logentry>
431 431 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
432 432 <parent revision="-1" node="0000000000000000000000000000000000000000" />
433 433 <author email="user@hostname">User Name</author>
434 434 <date>1970-01-12T13:46:40+00:00</date>
435 435 <msg xml:space="preserve">second</msg>
436 436 <paths>
437 437 <path action="A">second</path>
438 438 </paths>
439 439 </logentry>
440 440 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
441 441 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
442 442 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
443 443 <author email="person">person</author>
444 444 <date>1970-01-18T08:40:01+00:00</date>
445 445 <msg xml:space="preserve">merge</msg>
446 446 <paths>
447 447 </paths>
448 448 </logentry>
449 449 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
450 450 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
451 451 <author email="person">person</author>
452 452 <date>1970-01-18T08:40:00+00:00</date>
453 453 <msg xml:space="preserve">new head</msg>
454 454 <paths>
455 455 <path action="A">d</path>
456 456 </paths>
457 457 </logentry>
458 458 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
459 459 <branch>foo</branch>
460 460 <author email="person">person</author>
461 461 <date>1970-01-17T04:53:20+00:00</date>
462 462 <msg xml:space="preserve">new branch</msg>
463 463 <paths>
464 464 </paths>
465 465 </logentry>
466 466 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
467 467 <author email="person">person</author>
468 468 <date>1970-01-16T01:06:40+00:00</date>
469 469 <msg xml:space="preserve">no user, no domain</msg>
470 470 <paths>
471 471 <path action="M">c</path>
472 472 </paths>
473 473 </logentry>
474 474 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
475 475 <author email="other@place">other</author>
476 476 <date>1970-01-14T21:20:00+00:00</date>
477 477 <msg xml:space="preserve">no person</msg>
478 478 <paths>
479 479 <path action="A">c</path>
480 480 </paths>
481 481 </logentry>
482 482 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
483 483 <author email="other@place">A. N. Other</author>
484 484 <date>1970-01-13T17:33:20+00:00</date>
485 485 <msg xml:space="preserve">other 1
486 486 other 2
487 487
488 488 other 3</msg>
489 489 <paths>
490 490 <path action="A">b</path>
491 491 </paths>
492 492 </logentry>
493 493 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
494 494 <author email="user@hostname">User Name</author>
495 495 <date>1970-01-12T13:46:40+00:00</date>
496 496 <msg xml:space="preserve">line 1
497 497 line 2</msg>
498 498 <paths>
499 499 <path action="A">a</path>
500 500 </paths>
501 501 </logentry>
502 502 </log>
503 503
504 504 $ hg log --debug --style xml
505 505 <?xml version="1.0"?>
506 506 <log>
507 507 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
508 508 <tag>tip</tag>
509 509 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
510 510 <parent revision="-1" node="0000000000000000000000000000000000000000" />
511 511 <author email="test">test</author>
512 512 <date>2020-01-01T10:01:00+00:00</date>
513 513 <msg xml:space="preserve">third</msg>
514 514 <paths>
515 515 <path action="A">fourth</path>
516 516 <path action="A">third</path>
517 517 <path action="R">second</path>
518 518 </paths>
519 519 <copies>
520 520 <copy source="second">fourth</copy>
521 521 </copies>
522 522 <extra key="branch">default</extra>
523 523 </logentry>
524 524 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
525 525 <parent revision="-1" node="0000000000000000000000000000000000000000" />
526 526 <parent revision="-1" node="0000000000000000000000000000000000000000" />
527 527 <author email="user@hostname">User Name</author>
528 528 <date>1970-01-12T13:46:40+00:00</date>
529 529 <msg xml:space="preserve">second</msg>
530 530 <paths>
531 531 <path action="A">second</path>
532 532 </paths>
533 533 <extra key="branch">default</extra>
534 534 </logentry>
535 535 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
536 536 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
537 537 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
538 538 <author email="person">person</author>
539 539 <date>1970-01-18T08:40:01+00:00</date>
540 540 <msg xml:space="preserve">merge</msg>
541 541 <paths>
542 542 </paths>
543 543 <extra key="branch">default</extra>
544 544 </logentry>
545 545 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
546 546 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
547 547 <parent revision="-1" node="0000000000000000000000000000000000000000" />
548 548 <author email="person">person</author>
549 549 <date>1970-01-18T08:40:00+00:00</date>
550 550 <msg xml:space="preserve">new head</msg>
551 551 <paths>
552 552 <path action="A">d</path>
553 553 </paths>
554 554 <extra key="branch">default</extra>
555 555 </logentry>
556 556 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
557 557 <branch>foo</branch>
558 558 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
559 559 <parent revision="-1" node="0000000000000000000000000000000000000000" />
560 560 <author email="person">person</author>
561 561 <date>1970-01-17T04:53:20+00:00</date>
562 562 <msg xml:space="preserve">new branch</msg>
563 563 <paths>
564 564 </paths>
565 565 <extra key="branch">foo</extra>
566 566 </logentry>
567 567 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
568 568 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
569 569 <parent revision="-1" node="0000000000000000000000000000000000000000" />
570 570 <author email="person">person</author>
571 571 <date>1970-01-16T01:06:40+00:00</date>
572 572 <msg xml:space="preserve">no user, no domain</msg>
573 573 <paths>
574 574 <path action="M">c</path>
575 575 </paths>
576 576 <extra key="branch">default</extra>
577 577 </logentry>
578 578 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
579 579 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
580 580 <parent revision="-1" node="0000000000000000000000000000000000000000" />
581 581 <author email="other@place">other</author>
582 582 <date>1970-01-14T21:20:00+00:00</date>
583 583 <msg xml:space="preserve">no person</msg>
584 584 <paths>
585 585 <path action="A">c</path>
586 586 </paths>
587 587 <extra key="branch">default</extra>
588 588 </logentry>
589 589 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
590 590 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
591 591 <parent revision="-1" node="0000000000000000000000000000000000000000" />
592 592 <author email="other@place">A. N. Other</author>
593 593 <date>1970-01-13T17:33:20+00:00</date>
594 594 <msg xml:space="preserve">other 1
595 595 other 2
596 596
597 597 other 3</msg>
598 598 <paths>
599 599 <path action="A">b</path>
600 600 </paths>
601 601 <extra key="branch">default</extra>
602 602 </logentry>
603 603 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
604 604 <parent revision="-1" node="0000000000000000000000000000000000000000" />
605 605 <parent revision="-1" node="0000000000000000000000000000000000000000" />
606 606 <author email="user@hostname">User Name</author>
607 607 <date>1970-01-12T13:46:40+00:00</date>
608 608 <msg xml:space="preserve">line 1
609 609 line 2</msg>
610 610 <paths>
611 611 <path action="A">a</path>
612 612 </paths>
613 613 <extra key="branch">default</extra>
614 614 </logentry>
615 615 </log>
616 616
617 617
618 618 Test JSON style:
619 619
620 620 $ hg log -k nosuch -Tjson
621 621 []
622 622
623 623 $ hg log -qr . -Tjson
624 624 [
625 625 {
626 626 "rev": 8,
627 627 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
628 628 }
629 629 ]
630 630
631 631 $ hg log -vpr . -Tjson --stat
632 632 [
633 633 {
634 634 "rev": 8,
635 635 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
636 636 "branch": "default",
637 637 "phase": "draft",
638 638 "user": "test",
639 639 "date": [1577872860, 0],
640 640 "desc": "third",
641 641 "bookmarks": [],
642 642 "tags": ["tip"],
643 643 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
644 644 "files": ["fourth", "second", "third"],
645 645 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
646 646 "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n"
647 647 }
648 648 ]
649 649
650 650 honor --git but not format-breaking diffopts
651 651 $ hg --config diff.noprefix=True log --git -vpr . -Tjson
652 652 [
653 653 {
654 654 "rev": 8,
655 655 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
656 656 "branch": "default",
657 657 "phase": "draft",
658 658 "user": "test",
659 659 "date": [1577872860, 0],
660 660 "desc": "third",
661 661 "bookmarks": [],
662 662 "tags": ["tip"],
663 663 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
664 664 "files": ["fourth", "second", "third"],
665 665 "diff": "diff --git a/second b/fourth\nrename from second\nrename to fourth\ndiff --git a/third b/third\nnew file mode 100644\n--- /dev/null\n+++ b/third\n@@ -0,0 +1,1 @@\n+third\n"
666 666 }
667 667 ]
668 668
669 669 $ hg log -T json
670 670 [
671 671 {
672 672 "rev": 8,
673 673 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
674 674 "branch": "default",
675 675 "phase": "draft",
676 676 "user": "test",
677 677 "date": [1577872860, 0],
678 678 "desc": "third",
679 679 "bookmarks": [],
680 680 "tags": ["tip"],
681 681 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
682 682 },
683 683 {
684 684 "rev": 7,
685 685 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
686 686 "branch": "default",
687 687 "phase": "draft",
688 688 "user": "User Name <user@hostname>",
689 689 "date": [1000000, 0],
690 690 "desc": "second",
691 691 "bookmarks": [],
692 692 "tags": [],
693 693 "parents": ["0000000000000000000000000000000000000000"]
694 694 },
695 695 {
696 696 "rev": 6,
697 697 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
698 698 "branch": "default",
699 699 "phase": "draft",
700 700 "user": "person",
701 701 "date": [1500001, 0],
702 702 "desc": "merge",
703 703 "bookmarks": [],
704 704 "tags": [],
705 705 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
706 706 },
707 707 {
708 708 "rev": 5,
709 709 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
710 710 "branch": "default",
711 711 "phase": "draft",
712 712 "user": "person",
713 713 "date": [1500000, 0],
714 714 "desc": "new head",
715 715 "bookmarks": [],
716 716 "tags": [],
717 717 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
718 718 },
719 719 {
720 720 "rev": 4,
721 721 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
722 722 "branch": "foo",
723 723 "phase": "draft",
724 724 "user": "person",
725 725 "date": [1400000, 0],
726 726 "desc": "new branch",
727 727 "bookmarks": [],
728 728 "tags": [],
729 729 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
730 730 },
731 731 {
732 732 "rev": 3,
733 733 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
734 734 "branch": "default",
735 735 "phase": "draft",
736 736 "user": "person",
737 737 "date": [1300000, 0],
738 738 "desc": "no user, no domain",
739 739 "bookmarks": [],
740 740 "tags": [],
741 741 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
742 742 },
743 743 {
744 744 "rev": 2,
745 745 "node": "97054abb4ab824450e9164180baf491ae0078465",
746 746 "branch": "default",
747 747 "phase": "draft",
748 748 "user": "other@place",
749 749 "date": [1200000, 0],
750 750 "desc": "no person",
751 751 "bookmarks": [],
752 752 "tags": [],
753 753 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
754 754 },
755 755 {
756 756 "rev": 1,
757 757 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
758 758 "branch": "default",
759 759 "phase": "draft",
760 760 "user": "A. N. Other <other@place>",
761 761 "date": [1100000, 0],
762 762 "desc": "other 1\nother 2\n\nother 3",
763 763 "bookmarks": [],
764 764 "tags": [],
765 765 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
766 766 },
767 767 {
768 768 "rev": 0,
769 769 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
770 770 "branch": "default",
771 771 "phase": "draft",
772 772 "user": "User Name <user@hostname>",
773 773 "date": [1000000, 0],
774 774 "desc": "line 1\nline 2",
775 775 "bookmarks": [],
776 776 "tags": [],
777 777 "parents": ["0000000000000000000000000000000000000000"]
778 778 }
779 779 ]
780 780
781 781 $ hg heads -v -Tjson
782 782 [
783 783 {
784 784 "rev": 8,
785 785 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
786 786 "branch": "default",
787 787 "phase": "draft",
788 788 "user": "test",
789 789 "date": [1577872860, 0],
790 790 "desc": "third",
791 791 "bookmarks": [],
792 792 "tags": ["tip"],
793 793 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
794 794 "files": ["fourth", "second", "third"]
795 795 },
796 796 {
797 797 "rev": 6,
798 798 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
799 799 "branch": "default",
800 800 "phase": "draft",
801 801 "user": "person",
802 802 "date": [1500001, 0],
803 803 "desc": "merge",
804 804 "bookmarks": [],
805 805 "tags": [],
806 806 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
807 807 "files": []
808 808 },
809 809 {
810 810 "rev": 4,
811 811 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
812 812 "branch": "foo",
813 813 "phase": "draft",
814 814 "user": "person",
815 815 "date": [1400000, 0],
816 816 "desc": "new branch",
817 817 "bookmarks": [],
818 818 "tags": [],
819 819 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
820 820 "files": []
821 821 }
822 822 ]
823 823
824 824 $ hg log --debug -Tjson
825 825 [
826 826 {
827 827 "rev": 8,
828 828 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
829 829 "branch": "default",
830 830 "phase": "draft",
831 831 "user": "test",
832 832 "date": [1577872860, 0],
833 833 "desc": "third",
834 834 "bookmarks": [],
835 835 "tags": ["tip"],
836 836 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
837 837 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
838 838 "extra": {"branch": "default"},
839 839 "modified": [],
840 840 "added": ["fourth", "third"],
841 841 "removed": ["second"]
842 842 },
843 843 {
844 844 "rev": 7,
845 845 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
846 846 "branch": "default",
847 847 "phase": "draft",
848 848 "user": "User Name <user@hostname>",
849 849 "date": [1000000, 0],
850 850 "desc": "second",
851 851 "bookmarks": [],
852 852 "tags": [],
853 853 "parents": ["0000000000000000000000000000000000000000"],
854 854 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
855 855 "extra": {"branch": "default"},
856 856 "modified": [],
857 857 "added": ["second"],
858 858 "removed": []
859 859 },
860 860 {
861 861 "rev": 6,
862 862 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
863 863 "branch": "default",
864 864 "phase": "draft",
865 865 "user": "person",
866 866 "date": [1500001, 0],
867 867 "desc": "merge",
868 868 "bookmarks": [],
869 869 "tags": [],
870 870 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
871 871 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
872 872 "extra": {"branch": "default"},
873 873 "modified": [],
874 874 "added": [],
875 875 "removed": []
876 876 },
877 877 {
878 878 "rev": 5,
879 879 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
880 880 "branch": "default",
881 881 "phase": "draft",
882 882 "user": "person",
883 883 "date": [1500000, 0],
884 884 "desc": "new head",
885 885 "bookmarks": [],
886 886 "tags": [],
887 887 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
888 888 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
889 889 "extra": {"branch": "default"},
890 890 "modified": [],
891 891 "added": ["d"],
892 892 "removed": []
893 893 },
894 894 {
895 895 "rev": 4,
896 896 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
897 897 "branch": "foo",
898 898 "phase": "draft",
899 899 "user": "person",
900 900 "date": [1400000, 0],
901 901 "desc": "new branch",
902 902 "bookmarks": [],
903 903 "tags": [],
904 904 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
905 905 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
906 906 "extra": {"branch": "foo"},
907 907 "modified": [],
908 908 "added": [],
909 909 "removed": []
910 910 },
911 911 {
912 912 "rev": 3,
913 913 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
914 914 "branch": "default",
915 915 "phase": "draft",
916 916 "user": "person",
917 917 "date": [1300000, 0],
918 918 "desc": "no user, no domain",
919 919 "bookmarks": [],
920 920 "tags": [],
921 921 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
922 922 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
923 923 "extra": {"branch": "default"},
924 924 "modified": ["c"],
925 925 "added": [],
926 926 "removed": []
927 927 },
928 928 {
929 929 "rev": 2,
930 930 "node": "97054abb4ab824450e9164180baf491ae0078465",
931 931 "branch": "default",
932 932 "phase": "draft",
933 933 "user": "other@place",
934 934 "date": [1200000, 0],
935 935 "desc": "no person",
936 936 "bookmarks": [],
937 937 "tags": [],
938 938 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
939 939 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
940 940 "extra": {"branch": "default"},
941 941 "modified": [],
942 942 "added": ["c"],
943 943 "removed": []
944 944 },
945 945 {
946 946 "rev": 1,
947 947 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
948 948 "branch": "default",
949 949 "phase": "draft",
950 950 "user": "A. N. Other <other@place>",
951 951 "date": [1100000, 0],
952 952 "desc": "other 1\nother 2\n\nother 3",
953 953 "bookmarks": [],
954 954 "tags": [],
955 955 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
956 956 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
957 957 "extra": {"branch": "default"},
958 958 "modified": [],
959 959 "added": ["b"],
960 960 "removed": []
961 961 },
962 962 {
963 963 "rev": 0,
964 964 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
965 965 "branch": "default",
966 966 "phase": "draft",
967 967 "user": "User Name <user@hostname>",
968 968 "date": [1000000, 0],
969 969 "desc": "line 1\nline 2",
970 970 "bookmarks": [],
971 971 "tags": [],
972 972 "parents": ["0000000000000000000000000000000000000000"],
973 973 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
974 974 "extra": {"branch": "default"},
975 975 "modified": [],
976 976 "added": ["a"],
977 977 "removed": []
978 978 }
979 979 ]
980 980
981 981 Error if style not readable:
982 982
983 983 #if unix-permissions no-root
984 984 $ touch q
985 985 $ chmod 0 q
986 986 $ hg log --style ./q
987 987 abort: Permission denied: ./q
988 988 [255]
989 989 #endif
990 990
991 991 Error if no style:
992 992
993 993 $ hg log --style notexist
994 994 abort: style 'notexist' not found
995 995 (available styles: bisect, changelog, compact, default, phases, status, xml)
996 996 [255]
997 997
998 998 $ hg log -T list
999 999 available styles: bisect, changelog, compact, default, phases, status, xml
1000 1000 abort: specify a template
1001 1001 [255]
1002 1002
1003 1003 Error if style missing key:
1004 1004
1005 1005 $ echo 'q = q' > t
1006 1006 $ hg log --style ./t
1007 1007 abort: "changeset" not in template map
1008 1008 [255]
1009 1009
1010 1010 Error if style missing value:
1011 1011
1012 1012 $ echo 'changeset =' > t
1013 1013 $ hg log --style t
1014 1014 abort: t:1: missing value
1015 1015 [255]
1016 1016
1017 1017 Error if include fails:
1018 1018
1019 1019 $ echo 'changeset = q' >> t
1020 1020 #if unix-permissions no-root
1021 1021 $ hg log --style ./t
1022 1022 abort: template file ./q: Permission denied
1023 1023 [255]
1024 1024 $ rm q
1025 1025 #endif
1026 1026
1027 1027 Include works:
1028 1028
1029 1029 $ echo '{rev}' > q
1030 1030 $ hg log --style ./t
1031 1031 8
1032 1032 7
1033 1033 6
1034 1034 5
1035 1035 4
1036 1036 3
1037 1037 2
1038 1038 1
1039 1039 0
1040 1040
1041 1041 Check that {phase} works correctly on parents:
1042 1042
1043 1043 $ cat << EOF > parentphase
1044 1044 > changeset_debug = '{rev} ({phase}):{parents}\n'
1045 1045 > parent = ' {rev} ({phase})'
1046 1046 > EOF
1047 1047 $ hg phase -r 5 --public
1048 1048 $ hg phase -r 7 --secret --force
1049 1049 $ hg log --debug -G --style ./parentphase
1050 1050 @ 8 (secret): 7 (secret) -1 (public)
1051 1051 |
1052 1052 o 7 (secret): -1 (public) -1 (public)
1053 1053
1054 1054 o 6 (draft): 5 (public) 4 (draft)
1055 1055 |\
1056 1056 | o 5 (public): 3 (public) -1 (public)
1057 1057 | |
1058 1058 o | 4 (draft): 3 (public) -1 (public)
1059 1059 |/
1060 1060 o 3 (public): 2 (public) -1 (public)
1061 1061 |
1062 1062 o 2 (public): 1 (public) -1 (public)
1063 1063 |
1064 1064 o 1 (public): 0 (public) -1 (public)
1065 1065 |
1066 1066 o 0 (public): -1 (public) -1 (public)
1067 1067
1068 1068
1069 1069 Missing non-standard names give no error (backward compatibility):
1070 1070
1071 1071 $ echo "changeset = '{c}'" > t
1072 1072 $ hg log --style ./t
1073 1073
1074 1074 Defining non-standard name works:
1075 1075
1076 1076 $ cat <<EOF > t
1077 1077 > changeset = '{c}'
1078 1078 > c = q
1079 1079 > EOF
1080 1080 $ hg log --style ./t
1081 1081 8
1082 1082 7
1083 1083 6
1084 1084 5
1085 1085 4
1086 1086 3
1087 1087 2
1088 1088 1
1089 1089 0
1090 1090
1091 1091 ui.style works:
1092 1092
1093 1093 $ echo '[ui]' > .hg/hgrc
1094 1094 $ echo 'style = t' >> .hg/hgrc
1095 1095 $ hg log
1096 1096 8
1097 1097 7
1098 1098 6
1099 1099 5
1100 1100 4
1101 1101 3
1102 1102 2
1103 1103 1
1104 1104 0
1105 1105
1106 1106
1107 1107 Issue338:
1108 1108
1109 1109 $ hg log --style=changelog > changelog
1110 1110
1111 1111 $ cat changelog
1112 1112 2020-01-01 test <test>
1113 1113
1114 1114 * fourth, second, third:
1115 1115 third
1116 1116 [95c24699272e] [tip]
1117 1117
1118 1118 1970-01-12 User Name <user@hostname>
1119 1119
1120 1120 * second:
1121 1121 second
1122 1122 [29114dbae42b]
1123 1123
1124 1124 1970-01-18 person <person>
1125 1125
1126 1126 * merge
1127 1127 [d41e714fe50d]
1128 1128
1129 1129 * d:
1130 1130 new head
1131 1131 [13207e5a10d9]
1132 1132
1133 1133 1970-01-17 person <person>
1134 1134
1135 1135 * new branch
1136 1136 [bbe44766e73d] <foo>
1137 1137
1138 1138 1970-01-16 person <person>
1139 1139
1140 1140 * c:
1141 1141 no user, no domain
1142 1142 [10e46f2dcbf4]
1143 1143
1144 1144 1970-01-14 other <other@place>
1145 1145
1146 1146 * c:
1147 1147 no person
1148 1148 [97054abb4ab8]
1149 1149
1150 1150 1970-01-13 A. N. Other <other@place>
1151 1151
1152 1152 * b:
1153 1153 other 1 other 2
1154 1154
1155 1155 other 3
1156 1156 [b608e9d1a3f0]
1157 1157
1158 1158 1970-01-12 User Name <user@hostname>
1159 1159
1160 1160 * a:
1161 1161 line 1 line 2
1162 1162 [1e4e1b8f71e0]
1163 1163
1164 1164
1165 1165 Issue2130: xml output for 'hg heads' is malformed
1166 1166
1167 1167 $ hg heads --style changelog
1168 1168 2020-01-01 test <test>
1169 1169
1170 1170 * fourth, second, third:
1171 1171 third
1172 1172 [95c24699272e] [tip]
1173 1173
1174 1174 1970-01-18 person <person>
1175 1175
1176 1176 * merge
1177 1177 [d41e714fe50d]
1178 1178
1179 1179 1970-01-17 person <person>
1180 1180
1181 1181 * new branch
1182 1182 [bbe44766e73d] <foo>
1183 1183
1184 1184
1185 1185 Keys work:
1186 1186
1187 1187 $ for key in author branch branches date desc file_adds file_dels file_mods \
1188 1188 > file_copies file_copies_switch files \
1189 1189 > manifest node parents rev tags diffstat extras \
1190 1190 > p1rev p2rev p1node p2node; do
1191 1191 > for mode in '' --verbose --debug; do
1192 1192 > hg log $mode --template "$key$mode: {$key}\n"
1193 1193 > done
1194 1194 > done
1195 1195 author: test
1196 1196 author: User Name <user@hostname>
1197 1197 author: person
1198 1198 author: person
1199 1199 author: person
1200 1200 author: person
1201 1201 author: other@place
1202 1202 author: A. N. Other <other@place>
1203 1203 author: User Name <user@hostname>
1204 1204 author--verbose: test
1205 1205 author--verbose: User Name <user@hostname>
1206 1206 author--verbose: person
1207 1207 author--verbose: person
1208 1208 author--verbose: person
1209 1209 author--verbose: person
1210 1210 author--verbose: other@place
1211 1211 author--verbose: A. N. Other <other@place>
1212 1212 author--verbose: User Name <user@hostname>
1213 1213 author--debug: test
1214 1214 author--debug: User Name <user@hostname>
1215 1215 author--debug: person
1216 1216 author--debug: person
1217 1217 author--debug: person
1218 1218 author--debug: person
1219 1219 author--debug: other@place
1220 1220 author--debug: A. N. Other <other@place>
1221 1221 author--debug: User Name <user@hostname>
1222 1222 branch: default
1223 1223 branch: default
1224 1224 branch: default
1225 1225 branch: default
1226 1226 branch: foo
1227 1227 branch: default
1228 1228 branch: default
1229 1229 branch: default
1230 1230 branch: default
1231 1231 branch--verbose: default
1232 1232 branch--verbose: default
1233 1233 branch--verbose: default
1234 1234 branch--verbose: default
1235 1235 branch--verbose: foo
1236 1236 branch--verbose: default
1237 1237 branch--verbose: default
1238 1238 branch--verbose: default
1239 1239 branch--verbose: default
1240 1240 branch--debug: default
1241 1241 branch--debug: default
1242 1242 branch--debug: default
1243 1243 branch--debug: default
1244 1244 branch--debug: foo
1245 1245 branch--debug: default
1246 1246 branch--debug: default
1247 1247 branch--debug: default
1248 1248 branch--debug: default
1249 1249 branches:
1250 1250 branches:
1251 1251 branches:
1252 1252 branches:
1253 1253 branches: foo
1254 1254 branches:
1255 1255 branches:
1256 1256 branches:
1257 1257 branches:
1258 1258 branches--verbose:
1259 1259 branches--verbose:
1260 1260 branches--verbose:
1261 1261 branches--verbose:
1262 1262 branches--verbose: foo
1263 1263 branches--verbose:
1264 1264 branches--verbose:
1265 1265 branches--verbose:
1266 1266 branches--verbose:
1267 1267 branches--debug:
1268 1268 branches--debug:
1269 1269 branches--debug:
1270 1270 branches--debug:
1271 1271 branches--debug: foo
1272 1272 branches--debug:
1273 1273 branches--debug:
1274 1274 branches--debug:
1275 1275 branches--debug:
1276 1276 date: 1577872860.00
1277 1277 date: 1000000.00
1278 1278 date: 1500001.00
1279 1279 date: 1500000.00
1280 1280 date: 1400000.00
1281 1281 date: 1300000.00
1282 1282 date: 1200000.00
1283 1283 date: 1100000.00
1284 1284 date: 1000000.00
1285 1285 date--verbose: 1577872860.00
1286 1286 date--verbose: 1000000.00
1287 1287 date--verbose: 1500001.00
1288 1288 date--verbose: 1500000.00
1289 1289 date--verbose: 1400000.00
1290 1290 date--verbose: 1300000.00
1291 1291 date--verbose: 1200000.00
1292 1292 date--verbose: 1100000.00
1293 1293 date--verbose: 1000000.00
1294 1294 date--debug: 1577872860.00
1295 1295 date--debug: 1000000.00
1296 1296 date--debug: 1500001.00
1297 1297 date--debug: 1500000.00
1298 1298 date--debug: 1400000.00
1299 1299 date--debug: 1300000.00
1300 1300 date--debug: 1200000.00
1301 1301 date--debug: 1100000.00
1302 1302 date--debug: 1000000.00
1303 1303 desc: third
1304 1304 desc: second
1305 1305 desc: merge
1306 1306 desc: new head
1307 1307 desc: new branch
1308 1308 desc: no user, no domain
1309 1309 desc: no person
1310 1310 desc: other 1
1311 1311 other 2
1312 1312
1313 1313 other 3
1314 1314 desc: line 1
1315 1315 line 2
1316 1316 desc--verbose: third
1317 1317 desc--verbose: second
1318 1318 desc--verbose: merge
1319 1319 desc--verbose: new head
1320 1320 desc--verbose: new branch
1321 1321 desc--verbose: no user, no domain
1322 1322 desc--verbose: no person
1323 1323 desc--verbose: other 1
1324 1324 other 2
1325 1325
1326 1326 other 3
1327 1327 desc--verbose: line 1
1328 1328 line 2
1329 1329 desc--debug: third
1330 1330 desc--debug: second
1331 1331 desc--debug: merge
1332 1332 desc--debug: new head
1333 1333 desc--debug: new branch
1334 1334 desc--debug: no user, no domain
1335 1335 desc--debug: no person
1336 1336 desc--debug: other 1
1337 1337 other 2
1338 1338
1339 1339 other 3
1340 1340 desc--debug: line 1
1341 1341 line 2
1342 1342 file_adds: fourth third
1343 1343 file_adds: second
1344 1344 file_adds:
1345 1345 file_adds: d
1346 1346 file_adds:
1347 1347 file_adds:
1348 1348 file_adds: c
1349 1349 file_adds: b
1350 1350 file_adds: a
1351 1351 file_adds--verbose: fourth third
1352 1352 file_adds--verbose: second
1353 1353 file_adds--verbose:
1354 1354 file_adds--verbose: d
1355 1355 file_adds--verbose:
1356 1356 file_adds--verbose:
1357 1357 file_adds--verbose: c
1358 1358 file_adds--verbose: b
1359 1359 file_adds--verbose: a
1360 1360 file_adds--debug: fourth third
1361 1361 file_adds--debug: second
1362 1362 file_adds--debug:
1363 1363 file_adds--debug: d
1364 1364 file_adds--debug:
1365 1365 file_adds--debug:
1366 1366 file_adds--debug: c
1367 1367 file_adds--debug: b
1368 1368 file_adds--debug: a
1369 1369 file_dels: second
1370 1370 file_dels:
1371 1371 file_dels:
1372 1372 file_dels:
1373 1373 file_dels:
1374 1374 file_dels:
1375 1375 file_dels:
1376 1376 file_dels:
1377 1377 file_dels:
1378 1378 file_dels--verbose: second
1379 1379 file_dels--verbose:
1380 1380 file_dels--verbose:
1381 1381 file_dels--verbose:
1382 1382 file_dels--verbose:
1383 1383 file_dels--verbose:
1384 1384 file_dels--verbose:
1385 1385 file_dels--verbose:
1386 1386 file_dels--verbose:
1387 1387 file_dels--debug: second
1388 1388 file_dels--debug:
1389 1389 file_dels--debug:
1390 1390 file_dels--debug:
1391 1391 file_dels--debug:
1392 1392 file_dels--debug:
1393 1393 file_dels--debug:
1394 1394 file_dels--debug:
1395 1395 file_dels--debug:
1396 1396 file_mods:
1397 1397 file_mods:
1398 1398 file_mods:
1399 1399 file_mods:
1400 1400 file_mods:
1401 1401 file_mods: c
1402 1402 file_mods:
1403 1403 file_mods:
1404 1404 file_mods:
1405 1405 file_mods--verbose:
1406 1406 file_mods--verbose:
1407 1407 file_mods--verbose:
1408 1408 file_mods--verbose:
1409 1409 file_mods--verbose:
1410 1410 file_mods--verbose: c
1411 1411 file_mods--verbose:
1412 1412 file_mods--verbose:
1413 1413 file_mods--verbose:
1414 1414 file_mods--debug:
1415 1415 file_mods--debug:
1416 1416 file_mods--debug:
1417 1417 file_mods--debug:
1418 1418 file_mods--debug:
1419 1419 file_mods--debug: c
1420 1420 file_mods--debug:
1421 1421 file_mods--debug:
1422 1422 file_mods--debug:
1423 1423 file_copies: fourth (second)
1424 1424 file_copies:
1425 1425 file_copies:
1426 1426 file_copies:
1427 1427 file_copies:
1428 1428 file_copies:
1429 1429 file_copies:
1430 1430 file_copies:
1431 1431 file_copies:
1432 1432 file_copies--verbose: fourth (second)
1433 1433 file_copies--verbose:
1434 1434 file_copies--verbose:
1435 1435 file_copies--verbose:
1436 1436 file_copies--verbose:
1437 1437 file_copies--verbose:
1438 1438 file_copies--verbose:
1439 1439 file_copies--verbose:
1440 1440 file_copies--verbose:
1441 1441 file_copies--debug: fourth (second)
1442 1442 file_copies--debug:
1443 1443 file_copies--debug:
1444 1444 file_copies--debug:
1445 1445 file_copies--debug:
1446 1446 file_copies--debug:
1447 1447 file_copies--debug:
1448 1448 file_copies--debug:
1449 1449 file_copies--debug:
1450 1450 file_copies_switch:
1451 1451 file_copies_switch:
1452 1452 file_copies_switch:
1453 1453 file_copies_switch:
1454 1454 file_copies_switch:
1455 1455 file_copies_switch:
1456 1456 file_copies_switch:
1457 1457 file_copies_switch:
1458 1458 file_copies_switch:
1459 1459 file_copies_switch--verbose:
1460 1460 file_copies_switch--verbose:
1461 1461 file_copies_switch--verbose:
1462 1462 file_copies_switch--verbose:
1463 1463 file_copies_switch--verbose:
1464 1464 file_copies_switch--verbose:
1465 1465 file_copies_switch--verbose:
1466 1466 file_copies_switch--verbose:
1467 1467 file_copies_switch--verbose:
1468 1468 file_copies_switch--debug:
1469 1469 file_copies_switch--debug:
1470 1470 file_copies_switch--debug:
1471 1471 file_copies_switch--debug:
1472 1472 file_copies_switch--debug:
1473 1473 file_copies_switch--debug:
1474 1474 file_copies_switch--debug:
1475 1475 file_copies_switch--debug:
1476 1476 file_copies_switch--debug:
1477 1477 files: fourth second third
1478 1478 files: second
1479 1479 files:
1480 1480 files: d
1481 1481 files:
1482 1482 files: c
1483 1483 files: c
1484 1484 files: b
1485 1485 files: a
1486 1486 files--verbose: fourth second third
1487 1487 files--verbose: second
1488 1488 files--verbose:
1489 1489 files--verbose: d
1490 1490 files--verbose:
1491 1491 files--verbose: c
1492 1492 files--verbose: c
1493 1493 files--verbose: b
1494 1494 files--verbose: a
1495 1495 files--debug: fourth second third
1496 1496 files--debug: second
1497 1497 files--debug:
1498 1498 files--debug: d
1499 1499 files--debug:
1500 1500 files--debug: c
1501 1501 files--debug: c
1502 1502 files--debug: b
1503 1503 files--debug: a
1504 1504 manifest: 6:94961b75a2da
1505 1505 manifest: 5:f2dbc354b94e
1506 1506 manifest: 4:4dc3def4f9b4
1507 1507 manifest: 4:4dc3def4f9b4
1508 1508 manifest: 3:cb5a1327723b
1509 1509 manifest: 3:cb5a1327723b
1510 1510 manifest: 2:6e0e82995c35
1511 1511 manifest: 1:4e8d705b1e53
1512 1512 manifest: 0:a0c8bcbbb45c
1513 1513 manifest--verbose: 6:94961b75a2da
1514 1514 manifest--verbose: 5:f2dbc354b94e
1515 1515 manifest--verbose: 4:4dc3def4f9b4
1516 1516 manifest--verbose: 4:4dc3def4f9b4
1517 1517 manifest--verbose: 3:cb5a1327723b
1518 1518 manifest--verbose: 3:cb5a1327723b
1519 1519 manifest--verbose: 2:6e0e82995c35
1520 1520 manifest--verbose: 1:4e8d705b1e53
1521 1521 manifest--verbose: 0:a0c8bcbbb45c
1522 1522 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1523 1523 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1524 1524 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1525 1525 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1526 1526 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1527 1527 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1528 1528 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1529 1529 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1530 1530 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1531 1531 node: 95c24699272ef57d062b8bccc32c878bf841784a
1532 1532 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1533 1533 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1534 1534 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1535 1535 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1536 1536 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1537 1537 node: 97054abb4ab824450e9164180baf491ae0078465
1538 1538 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1539 1539 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1540 1540 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1541 1541 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1542 1542 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1543 1543 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1544 1544 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1545 1545 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1546 1546 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1547 1547 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1548 1548 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1549 1549 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1550 1550 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1551 1551 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1552 1552 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1553 1553 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1554 1554 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1555 1555 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1556 1556 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1557 1557 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1558 1558 parents:
1559 1559 parents: -1:000000000000
1560 1560 parents: 5:13207e5a10d9 4:bbe44766e73d
1561 1561 parents: 3:10e46f2dcbf4
1562 1562 parents:
1563 1563 parents:
1564 1564 parents:
1565 1565 parents:
1566 1566 parents:
1567 1567 parents--verbose:
1568 1568 parents--verbose: -1:000000000000
1569 1569 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1570 1570 parents--verbose: 3:10e46f2dcbf4
1571 1571 parents--verbose:
1572 1572 parents--verbose:
1573 1573 parents--verbose:
1574 1574 parents--verbose:
1575 1575 parents--verbose:
1576 1576 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1577 1577 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1578 1578 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1579 1579 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1580 1580 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1581 1581 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1582 1582 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1583 1583 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1584 1584 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1585 1585 rev: 8
1586 1586 rev: 7
1587 1587 rev: 6
1588 1588 rev: 5
1589 1589 rev: 4
1590 1590 rev: 3
1591 1591 rev: 2
1592 1592 rev: 1
1593 1593 rev: 0
1594 1594 rev--verbose: 8
1595 1595 rev--verbose: 7
1596 1596 rev--verbose: 6
1597 1597 rev--verbose: 5
1598 1598 rev--verbose: 4
1599 1599 rev--verbose: 3
1600 1600 rev--verbose: 2
1601 1601 rev--verbose: 1
1602 1602 rev--verbose: 0
1603 1603 rev--debug: 8
1604 1604 rev--debug: 7
1605 1605 rev--debug: 6
1606 1606 rev--debug: 5
1607 1607 rev--debug: 4
1608 1608 rev--debug: 3
1609 1609 rev--debug: 2
1610 1610 rev--debug: 1
1611 1611 rev--debug: 0
1612 1612 tags: tip
1613 1613 tags:
1614 1614 tags:
1615 1615 tags:
1616 1616 tags:
1617 1617 tags:
1618 1618 tags:
1619 1619 tags:
1620 1620 tags:
1621 1621 tags--verbose: tip
1622 1622 tags--verbose:
1623 1623 tags--verbose:
1624 1624 tags--verbose:
1625 1625 tags--verbose:
1626 1626 tags--verbose:
1627 1627 tags--verbose:
1628 1628 tags--verbose:
1629 1629 tags--verbose:
1630 1630 tags--debug: tip
1631 1631 tags--debug:
1632 1632 tags--debug:
1633 1633 tags--debug:
1634 1634 tags--debug:
1635 1635 tags--debug:
1636 1636 tags--debug:
1637 1637 tags--debug:
1638 1638 tags--debug:
1639 1639 diffstat: 3: +2/-1
1640 1640 diffstat: 1: +1/-0
1641 1641 diffstat: 0: +0/-0
1642 1642 diffstat: 1: +1/-0
1643 1643 diffstat: 0: +0/-0
1644 1644 diffstat: 1: +1/-0
1645 1645 diffstat: 1: +4/-0
1646 1646 diffstat: 1: +2/-0
1647 1647 diffstat: 1: +1/-0
1648 1648 diffstat--verbose: 3: +2/-1
1649 1649 diffstat--verbose: 1: +1/-0
1650 1650 diffstat--verbose: 0: +0/-0
1651 1651 diffstat--verbose: 1: +1/-0
1652 1652 diffstat--verbose: 0: +0/-0
1653 1653 diffstat--verbose: 1: +1/-0
1654 1654 diffstat--verbose: 1: +4/-0
1655 1655 diffstat--verbose: 1: +2/-0
1656 1656 diffstat--verbose: 1: +1/-0
1657 1657 diffstat--debug: 3: +2/-1
1658 1658 diffstat--debug: 1: +1/-0
1659 1659 diffstat--debug: 0: +0/-0
1660 1660 diffstat--debug: 1: +1/-0
1661 1661 diffstat--debug: 0: +0/-0
1662 1662 diffstat--debug: 1: +1/-0
1663 1663 diffstat--debug: 1: +4/-0
1664 1664 diffstat--debug: 1: +2/-0
1665 1665 diffstat--debug: 1: +1/-0
1666 1666 extras: branch=default
1667 1667 extras: branch=default
1668 1668 extras: branch=default
1669 1669 extras: branch=default
1670 1670 extras: branch=foo
1671 1671 extras: branch=default
1672 1672 extras: branch=default
1673 1673 extras: branch=default
1674 1674 extras: branch=default
1675 1675 extras--verbose: branch=default
1676 1676 extras--verbose: branch=default
1677 1677 extras--verbose: branch=default
1678 1678 extras--verbose: branch=default
1679 1679 extras--verbose: branch=foo
1680 1680 extras--verbose: branch=default
1681 1681 extras--verbose: branch=default
1682 1682 extras--verbose: branch=default
1683 1683 extras--verbose: branch=default
1684 1684 extras--debug: branch=default
1685 1685 extras--debug: branch=default
1686 1686 extras--debug: branch=default
1687 1687 extras--debug: branch=default
1688 1688 extras--debug: branch=foo
1689 1689 extras--debug: branch=default
1690 1690 extras--debug: branch=default
1691 1691 extras--debug: branch=default
1692 1692 extras--debug: branch=default
1693 1693 p1rev: 7
1694 1694 p1rev: -1
1695 1695 p1rev: 5
1696 1696 p1rev: 3
1697 1697 p1rev: 3
1698 1698 p1rev: 2
1699 1699 p1rev: 1
1700 1700 p1rev: 0
1701 1701 p1rev: -1
1702 1702 p1rev--verbose: 7
1703 1703 p1rev--verbose: -1
1704 1704 p1rev--verbose: 5
1705 1705 p1rev--verbose: 3
1706 1706 p1rev--verbose: 3
1707 1707 p1rev--verbose: 2
1708 1708 p1rev--verbose: 1
1709 1709 p1rev--verbose: 0
1710 1710 p1rev--verbose: -1
1711 1711 p1rev--debug: 7
1712 1712 p1rev--debug: -1
1713 1713 p1rev--debug: 5
1714 1714 p1rev--debug: 3
1715 1715 p1rev--debug: 3
1716 1716 p1rev--debug: 2
1717 1717 p1rev--debug: 1
1718 1718 p1rev--debug: 0
1719 1719 p1rev--debug: -1
1720 1720 p2rev: -1
1721 1721 p2rev: -1
1722 1722 p2rev: 4
1723 1723 p2rev: -1
1724 1724 p2rev: -1
1725 1725 p2rev: -1
1726 1726 p2rev: -1
1727 1727 p2rev: -1
1728 1728 p2rev: -1
1729 1729 p2rev--verbose: -1
1730 1730 p2rev--verbose: -1
1731 1731 p2rev--verbose: 4
1732 1732 p2rev--verbose: -1
1733 1733 p2rev--verbose: -1
1734 1734 p2rev--verbose: -1
1735 1735 p2rev--verbose: -1
1736 1736 p2rev--verbose: -1
1737 1737 p2rev--verbose: -1
1738 1738 p2rev--debug: -1
1739 1739 p2rev--debug: -1
1740 1740 p2rev--debug: 4
1741 1741 p2rev--debug: -1
1742 1742 p2rev--debug: -1
1743 1743 p2rev--debug: -1
1744 1744 p2rev--debug: -1
1745 1745 p2rev--debug: -1
1746 1746 p2rev--debug: -1
1747 1747 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1748 1748 p1node: 0000000000000000000000000000000000000000
1749 1749 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1750 1750 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1751 1751 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1752 1752 p1node: 97054abb4ab824450e9164180baf491ae0078465
1753 1753 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1754 1754 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1755 1755 p1node: 0000000000000000000000000000000000000000
1756 1756 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1757 1757 p1node--verbose: 0000000000000000000000000000000000000000
1758 1758 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1759 1759 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1760 1760 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1761 1761 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1762 1762 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1763 1763 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1764 1764 p1node--verbose: 0000000000000000000000000000000000000000
1765 1765 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1766 1766 p1node--debug: 0000000000000000000000000000000000000000
1767 1767 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1768 1768 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1769 1769 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1770 1770 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1771 1771 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1772 1772 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1773 1773 p1node--debug: 0000000000000000000000000000000000000000
1774 1774 p2node: 0000000000000000000000000000000000000000
1775 1775 p2node: 0000000000000000000000000000000000000000
1776 1776 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1777 1777 p2node: 0000000000000000000000000000000000000000
1778 1778 p2node: 0000000000000000000000000000000000000000
1779 1779 p2node: 0000000000000000000000000000000000000000
1780 1780 p2node: 0000000000000000000000000000000000000000
1781 1781 p2node: 0000000000000000000000000000000000000000
1782 1782 p2node: 0000000000000000000000000000000000000000
1783 1783 p2node--verbose: 0000000000000000000000000000000000000000
1784 1784 p2node--verbose: 0000000000000000000000000000000000000000
1785 1785 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1786 1786 p2node--verbose: 0000000000000000000000000000000000000000
1787 1787 p2node--verbose: 0000000000000000000000000000000000000000
1788 1788 p2node--verbose: 0000000000000000000000000000000000000000
1789 1789 p2node--verbose: 0000000000000000000000000000000000000000
1790 1790 p2node--verbose: 0000000000000000000000000000000000000000
1791 1791 p2node--verbose: 0000000000000000000000000000000000000000
1792 1792 p2node--debug: 0000000000000000000000000000000000000000
1793 1793 p2node--debug: 0000000000000000000000000000000000000000
1794 1794 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1795 1795 p2node--debug: 0000000000000000000000000000000000000000
1796 1796 p2node--debug: 0000000000000000000000000000000000000000
1797 1797 p2node--debug: 0000000000000000000000000000000000000000
1798 1798 p2node--debug: 0000000000000000000000000000000000000000
1799 1799 p2node--debug: 0000000000000000000000000000000000000000
1800 1800 p2node--debug: 0000000000000000000000000000000000000000
1801 1801
1802 1802 Filters work:
1803 1803
1804 1804 $ hg log --template '{author|domain}\n'
1805 1805
1806 1806 hostname
1807 1807
1808 1808
1809 1809
1810 1810
1811 1811 place
1812 1812 place
1813 1813 hostname
1814 1814
1815 1815 $ hg log --template '{author|person}\n'
1816 1816 test
1817 1817 User Name
1818 1818 person
1819 1819 person
1820 1820 person
1821 1821 person
1822 1822 other
1823 1823 A. N. Other
1824 1824 User Name
1825 1825
1826 1826 $ hg log --template '{author|user}\n'
1827 1827 test
1828 1828 user
1829 1829 person
1830 1830 person
1831 1831 person
1832 1832 person
1833 1833 other
1834 1834 other
1835 1835 user
1836 1836
1837 1837 $ hg log --template '{date|date}\n'
1838 1838 Wed Jan 01 10:01:00 2020 +0000
1839 1839 Mon Jan 12 13:46:40 1970 +0000
1840 1840 Sun Jan 18 08:40:01 1970 +0000
1841 1841 Sun Jan 18 08:40:00 1970 +0000
1842 1842 Sat Jan 17 04:53:20 1970 +0000
1843 1843 Fri Jan 16 01:06:40 1970 +0000
1844 1844 Wed Jan 14 21:20:00 1970 +0000
1845 1845 Tue Jan 13 17:33:20 1970 +0000
1846 1846 Mon Jan 12 13:46:40 1970 +0000
1847 1847
1848 1848 $ hg log --template '{date|isodate}\n'
1849 1849 2020-01-01 10:01 +0000
1850 1850 1970-01-12 13:46 +0000
1851 1851 1970-01-18 08:40 +0000
1852 1852 1970-01-18 08:40 +0000
1853 1853 1970-01-17 04:53 +0000
1854 1854 1970-01-16 01:06 +0000
1855 1855 1970-01-14 21:20 +0000
1856 1856 1970-01-13 17:33 +0000
1857 1857 1970-01-12 13:46 +0000
1858 1858
1859 1859 $ hg log --template '{date|isodatesec}\n'
1860 1860 2020-01-01 10:01:00 +0000
1861 1861 1970-01-12 13:46:40 +0000
1862 1862 1970-01-18 08:40:01 +0000
1863 1863 1970-01-18 08:40:00 +0000
1864 1864 1970-01-17 04:53:20 +0000
1865 1865 1970-01-16 01:06:40 +0000
1866 1866 1970-01-14 21:20:00 +0000
1867 1867 1970-01-13 17:33:20 +0000
1868 1868 1970-01-12 13:46:40 +0000
1869 1869
1870 1870 $ hg log --template '{date|rfc822date}\n'
1871 1871 Wed, 01 Jan 2020 10:01:00 +0000
1872 1872 Mon, 12 Jan 1970 13:46:40 +0000
1873 1873 Sun, 18 Jan 1970 08:40:01 +0000
1874 1874 Sun, 18 Jan 1970 08:40:00 +0000
1875 1875 Sat, 17 Jan 1970 04:53:20 +0000
1876 1876 Fri, 16 Jan 1970 01:06:40 +0000
1877 1877 Wed, 14 Jan 1970 21:20:00 +0000
1878 1878 Tue, 13 Jan 1970 17:33:20 +0000
1879 1879 Mon, 12 Jan 1970 13:46:40 +0000
1880 1880
1881 1881 $ hg log --template '{desc|firstline}\n'
1882 1882 third
1883 1883 second
1884 1884 merge
1885 1885 new head
1886 1886 new branch
1887 1887 no user, no domain
1888 1888 no person
1889 1889 other 1
1890 1890 line 1
1891 1891
1892 1892 $ hg log --template '{node|short}\n'
1893 1893 95c24699272e
1894 1894 29114dbae42b
1895 1895 d41e714fe50d
1896 1896 13207e5a10d9
1897 1897 bbe44766e73d
1898 1898 10e46f2dcbf4
1899 1899 97054abb4ab8
1900 1900 b608e9d1a3f0
1901 1901 1e4e1b8f71e0
1902 1902
1903 1903 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1904 1904 <changeset author="test"/>
1905 1905 <changeset author="User Name &lt;user@hostname&gt;"/>
1906 1906 <changeset author="person"/>
1907 1907 <changeset author="person"/>
1908 1908 <changeset author="person"/>
1909 1909 <changeset author="person"/>
1910 1910 <changeset author="other@place"/>
1911 1911 <changeset author="A. N. Other &lt;other@place&gt;"/>
1912 1912 <changeset author="User Name &lt;user@hostname&gt;"/>
1913 1913
1914 1914 $ hg log --template '{rev}: {children}\n'
1915 1915 8:
1916 1916 7: 8:95c24699272e
1917 1917 6:
1918 1918 5: 6:d41e714fe50d
1919 1919 4: 6:d41e714fe50d
1920 1920 3: 4:bbe44766e73d 5:13207e5a10d9
1921 1921 2: 3:10e46f2dcbf4
1922 1922 1: 2:97054abb4ab8
1923 1923 0: 1:b608e9d1a3f0
1924 1924
1925 1925 Formatnode filter works:
1926 1926
1927 1927 $ hg -q log -r 0 --template '{node|formatnode}\n'
1928 1928 1e4e1b8f71e0
1929 1929
1930 1930 $ hg log -r 0 --template '{node|formatnode}\n'
1931 1931 1e4e1b8f71e0
1932 1932
1933 1933 $ hg -v log -r 0 --template '{node|formatnode}\n'
1934 1934 1e4e1b8f71e0
1935 1935
1936 1936 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1937 1937 1e4e1b8f71e05681d422154f5421e385fec3454f
1938 1938
1939 1939 Age filter:
1940 1940
1941 1941 $ hg init unstable-hash
1942 1942 $ cd unstable-hash
1943 1943 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1944 1944
1945 1945 >>> from datetime import datetime, timedelta
1946 1946 >>> fp = open('a', 'w')
1947 1947 >>> n = datetime.now() + timedelta(366 * 7)
1948 1948 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
1949 1949 >>> fp.close()
1950 1950 $ hg add a
1951 1951 $ hg commit -m future -d "`cat a`"
1952 1952
1953 1953 $ hg log -l1 --template '{date|age}\n'
1954 1954 7 years from now
1955 1955
1956 1956 $ cd ..
1957 1957 $ rm -rf unstable-hash
1958 1958
1959 1959 Add a dummy commit to make up for the instability of the above:
1960 1960
1961 1961 $ echo a > a
1962 1962 $ hg add a
1963 1963 $ hg ci -m future
1964 1964
1965 1965 Count filter:
1966 1966
1967 1967 $ hg log -l1 --template '{node|count} {node|short|count}\n'
1968 1968 40 12
1969 1969
1970 1970 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
1971 1971 0 1 4
1972 1972
1973 1973 $ hg log -G --template '{rev}: children: {children|count}, \
1974 1974 > tags: {tags|count}, file_adds: {file_adds|count}, \
1975 1975 > ancestors: {revset("ancestors(%s)", rev)|count}'
1976 1976 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
1977 1977 |
1978 1978 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
1979 1979 |
1980 1980 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
1981 1981
1982 1982 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
1983 1983 |\
1984 1984 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
1985 1985 | |
1986 1986 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
1987 1987 |/
1988 1988 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
1989 1989 |
1990 1990 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
1991 1991 |
1992 1992 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
1993 1993 |
1994 1994 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
1995 1995
1996 1996
1997 1997 Upper/lower filters:
1998 1998
1999 1999 $ hg log -r0 --template '{branch|upper}\n'
2000 2000 DEFAULT
2001 2001 $ hg log -r0 --template '{author|lower}\n'
2002 2002 user name <user@hostname>
2003 2003 $ hg log -r0 --template '{date|upper}\n'
2004 2004 abort: template filter 'upper' is not compatible with keyword 'date'
2005 2005 [255]
2006 2006
2007 2007 Add a commit that does all possible modifications at once
2008 2008
2009 2009 $ echo modify >> third
2010 2010 $ touch b
2011 2011 $ hg add b
2012 2012 $ hg mv fourth fifth
2013 2013 $ hg rm a
2014 2014 $ hg ci -m "Modify, add, remove, rename"
2015 2015
2016 2016 Check the status template
2017 2017
2018 2018 $ cat <<EOF >> $HGRCPATH
2019 2019 > [extensions]
2020 2020 > color=
2021 2021 > EOF
2022 2022
2023 2023 $ hg log -T status -r 10
2024 2024 changeset: 10:0f9759ec227a
2025 2025 tag: tip
2026 2026 user: test
2027 2027 date: Thu Jan 01 00:00:00 1970 +0000
2028 2028 summary: Modify, add, remove, rename
2029 2029 files:
2030 2030 M third
2031 2031 A b
2032 2032 A fifth
2033 2033 R a
2034 2034 R fourth
2035 2035
2036 2036 $ hg log -T status -C -r 10
2037 2037 changeset: 10:0f9759ec227a
2038 2038 tag: tip
2039 2039 user: test
2040 2040 date: Thu Jan 01 00:00:00 1970 +0000
2041 2041 summary: Modify, add, remove, rename
2042 2042 files:
2043 2043 M third
2044 2044 A b
2045 2045 A fifth
2046 2046 fourth
2047 2047 R a
2048 2048 R fourth
2049 2049
2050 2050 $ hg log -T status -C -r 10 -v
2051 2051 changeset: 10:0f9759ec227a
2052 2052 tag: tip
2053 2053 user: test
2054 2054 date: Thu Jan 01 00:00:00 1970 +0000
2055 2055 description:
2056 2056 Modify, add, remove, rename
2057 2057
2058 2058 files:
2059 2059 M third
2060 2060 A b
2061 2061 A fifth
2062 2062 fourth
2063 2063 R a
2064 2064 R fourth
2065 2065
2066 2066 $ hg log -T status -C -r 10 --debug
2067 2067 changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c
2068 2068 tag: tip
2069 2069 phase: secret
2070 2070 parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066
2071 2071 parent: -1:0000000000000000000000000000000000000000
2072 2072 manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567
2073 2073 user: test
2074 2074 date: Thu Jan 01 00:00:00 1970 +0000
2075 2075 extra: branch=default
2076 2076 description:
2077 2077 Modify, add, remove, rename
2078 2078
2079 2079 files:
2080 2080 M third
2081 2081 A b
2082 2082 A fifth
2083 2083 fourth
2084 2084 R a
2085 2085 R fourth
2086 2086
2087 2087 $ hg log -T status -C -r 10 --quiet
2088 2088 10:0f9759ec227a
2089 2089 $ hg --color=debug log -T status -r 10
2090 2090 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2091 2091 [log.tag|tag: tip]
2092 2092 [log.user|user: test]
2093 2093 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2094 2094 [log.summary|summary: Modify, add, remove, rename]
2095 2095 [ui.note log.files|files:]
2096 2096 [status.modified|M third]
2097 2097 [status.added|A b]
2098 2098 [status.added|A fifth]
2099 2099 [status.removed|R a]
2100 2100 [status.removed|R fourth]
2101 2101
2102 2102 $ hg --color=debug log -T status -C -r 10
2103 2103 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2104 2104 [log.tag|tag: tip]
2105 2105 [log.user|user: test]
2106 2106 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2107 2107 [log.summary|summary: Modify, add, remove, rename]
2108 2108 [ui.note log.files|files:]
2109 2109 [status.modified|M third]
2110 2110 [status.added|A b]
2111 2111 [status.added|A fifth]
2112 2112 [status.copied| fourth]
2113 2113 [status.removed|R a]
2114 2114 [status.removed|R fourth]
2115 2115
2116 2116 $ hg --color=debug log -T status -C -r 10 -v
2117 2117 [log.changeset changeset.secret|changeset: 10:0f9759ec227a]
2118 2118 [log.tag|tag: tip]
2119 2119 [log.user|user: test]
2120 2120 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2121 2121 [ui.note log.description|description:]
2122 2122 [ui.note log.description|Modify, add, remove, rename]
2123 2123
2124 2124 [ui.note log.files|files:]
2125 2125 [status.modified|M third]
2126 2126 [status.added|A b]
2127 2127 [status.added|A fifth]
2128 2128 [status.copied| fourth]
2129 2129 [status.removed|R a]
2130 2130 [status.removed|R fourth]
2131 2131
2132 2132 $ hg --color=debug log -T status -C -r 10 --debug
2133 2133 [log.changeset changeset.secret|changeset: 10:0f9759ec227a4859c2014a345cd8a859022b7c6c]
2134 2134 [log.tag|tag: tip]
2135 2135 [log.phase|phase: secret]
2136 2136 [log.parent changeset.secret|parent: 9:bf9dfba36635106d6a73ccc01e28b762da60e066]
2137 2137 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2138 2138 [ui.debug log.manifest|manifest: 8:89dd546f2de0a9d6d664f58d86097eb97baba567]
2139 2139 [log.user|user: test]
2140 2140 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
2141 2141 [ui.debug log.extra|extra: branch=default]
2142 2142 [ui.note log.description|description:]
2143 2143 [ui.note log.description|Modify, add, remove, rename]
2144 2144
2145 2145 [ui.note log.files|files:]
2146 2146 [status.modified|M third]
2147 2147 [status.added|A b]
2148 2148 [status.added|A fifth]
2149 2149 [status.copied| fourth]
2150 2150 [status.removed|R a]
2151 2151 [status.removed|R fourth]
2152 2152
2153 2153 $ hg --color=debug log -T status -C -r 10 --quiet
2154 2154 [log.node|10:0f9759ec227a]
2155 2155
2156 2156 Check the bisect template
2157 2157
2158 2158 $ hg bisect -g 1
2159 2159 $ hg bisect -b 3 --noupdate
2160 2160 Testing changeset 2:97054abb4ab8 (2 changesets remaining, ~1 tests)
2161 2161 $ hg log -T bisect -r 0:4
2162 2162 changeset: 0:1e4e1b8f71e0
2163 2163 bisect: good (implicit)
2164 2164 user: User Name <user@hostname>
2165 2165 date: Mon Jan 12 13:46:40 1970 +0000
2166 2166 summary: line 1
2167 2167
2168 2168 changeset: 1:b608e9d1a3f0
2169 2169 bisect: good
2170 2170 user: A. N. Other <other@place>
2171 2171 date: Tue Jan 13 17:33:20 1970 +0000
2172 2172 summary: other 1
2173 2173
2174 2174 changeset: 2:97054abb4ab8
2175 2175 bisect: untested
2176 2176 user: other@place
2177 2177 date: Wed Jan 14 21:20:00 1970 +0000
2178 2178 summary: no person
2179 2179
2180 2180 changeset: 3:10e46f2dcbf4
2181 2181 bisect: bad
2182 2182 user: person
2183 2183 date: Fri Jan 16 01:06:40 1970 +0000
2184 2184 summary: no user, no domain
2185 2185
2186 2186 changeset: 4:bbe44766e73d
2187 2187 bisect: bad (implicit)
2188 2188 branch: foo
2189 2189 user: person
2190 2190 date: Sat Jan 17 04:53:20 1970 +0000
2191 2191 summary: new branch
2192 2192
2193 2193 $ hg log --debug -T bisect -r 0:4
2194 2194 changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2195 2195 bisect: good (implicit)
2196 2196 phase: public
2197 2197 parent: -1:0000000000000000000000000000000000000000
2198 2198 parent: -1:0000000000000000000000000000000000000000
2199 2199 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
2200 2200 user: User Name <user@hostname>
2201 2201 date: Mon Jan 12 13:46:40 1970 +0000
2202 2202 files+: a
2203 2203 extra: branch=default
2204 2204 description:
2205 2205 line 1
2206 2206 line 2
2207 2207
2208 2208
2209 2209 changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2210 2210 bisect: good
2211 2211 phase: public
2212 2212 parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f
2213 2213 parent: -1:0000000000000000000000000000000000000000
2214 2214 manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
2215 2215 user: A. N. Other <other@place>
2216 2216 date: Tue Jan 13 17:33:20 1970 +0000
2217 2217 files+: b
2218 2218 extra: branch=default
2219 2219 description:
2220 2220 other 1
2221 2221 other 2
2222 2222
2223 2223 other 3
2224 2224
2225 2225
2226 2226 changeset: 2:97054abb4ab824450e9164180baf491ae0078465
2227 2227 bisect: untested
2228 2228 phase: public
2229 2229 parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965
2230 2230 parent: -1:0000000000000000000000000000000000000000
2231 2231 manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
2232 2232 user: other@place
2233 2233 date: Wed Jan 14 21:20:00 1970 +0000
2234 2234 files+: c
2235 2235 extra: branch=default
2236 2236 description:
2237 2237 no person
2238 2238
2239 2239
2240 2240 changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2241 2241 bisect: bad
2242 2242 phase: public
2243 2243 parent: 2:97054abb4ab824450e9164180baf491ae0078465
2244 2244 parent: -1:0000000000000000000000000000000000000000
2245 2245 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2246 2246 user: person
2247 2247 date: Fri Jan 16 01:06:40 1970 +0000
2248 2248 files: c
2249 2249 extra: branch=default
2250 2250 description:
2251 2251 no user, no domain
2252 2252
2253 2253
2254 2254 changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
2255 2255 bisect: bad (implicit)
2256 2256 branch: foo
2257 2257 phase: draft
2258 2258 parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47
2259 2259 parent: -1:0000000000000000000000000000000000000000
2260 2260 manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
2261 2261 user: person
2262 2262 date: Sat Jan 17 04:53:20 1970 +0000
2263 2263 extra: branch=foo
2264 2264 description:
2265 2265 new branch
2266 2266
2267 2267
2268 2268 $ hg log -v -T bisect -r 0:4
2269 2269 changeset: 0:1e4e1b8f71e0
2270 2270 bisect: good (implicit)
2271 2271 user: User Name <user@hostname>
2272 2272 date: Mon Jan 12 13:46:40 1970 +0000
2273 2273 files: a
2274 2274 description:
2275 2275 line 1
2276 2276 line 2
2277 2277
2278 2278
2279 2279 changeset: 1:b608e9d1a3f0
2280 2280 bisect: good
2281 2281 user: A. N. Other <other@place>
2282 2282 date: Tue Jan 13 17:33:20 1970 +0000
2283 2283 files: b
2284 2284 description:
2285 2285 other 1
2286 2286 other 2
2287 2287
2288 2288 other 3
2289 2289
2290 2290
2291 2291 changeset: 2:97054abb4ab8
2292 2292 bisect: untested
2293 2293 user: other@place
2294 2294 date: Wed Jan 14 21:20:00 1970 +0000
2295 2295 files: c
2296 2296 description:
2297 2297 no person
2298 2298
2299 2299
2300 2300 changeset: 3:10e46f2dcbf4
2301 2301 bisect: bad
2302 2302 user: person
2303 2303 date: Fri Jan 16 01:06:40 1970 +0000
2304 2304 files: c
2305 2305 description:
2306 2306 no user, no domain
2307 2307
2308 2308
2309 2309 changeset: 4:bbe44766e73d
2310 2310 bisect: bad (implicit)
2311 2311 branch: foo
2312 2312 user: person
2313 2313 date: Sat Jan 17 04:53:20 1970 +0000
2314 2314 description:
2315 2315 new branch
2316 2316
2317 2317
2318 2318 $ hg --color=debug log -T bisect -r 0:4
2319 2319 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2320 2320 [log.bisect bisect.good|bisect: good (implicit)]
2321 2321 [log.user|user: User Name <user@hostname>]
2322 2322 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2323 2323 [log.summary|summary: line 1]
2324 2324
2325 2325 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2326 2326 [log.bisect bisect.good|bisect: good]
2327 2327 [log.user|user: A. N. Other <other@place>]
2328 2328 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2329 2329 [log.summary|summary: other 1]
2330 2330
2331 2331 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2332 2332 [log.bisect bisect.untested|bisect: untested]
2333 2333 [log.user|user: other@place]
2334 2334 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2335 2335 [log.summary|summary: no person]
2336 2336
2337 2337 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2338 2338 [log.bisect bisect.bad|bisect: bad]
2339 2339 [log.user|user: person]
2340 2340 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2341 2341 [log.summary|summary: no user, no domain]
2342 2342
2343 2343 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2344 2344 [log.bisect bisect.bad|bisect: bad (implicit)]
2345 2345 [log.branch|branch: foo]
2346 2346 [log.user|user: person]
2347 2347 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2348 2348 [log.summary|summary: new branch]
2349 2349
2350 2350 $ hg --color=debug log --debug -T bisect -r 0:4
2351 2351 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2352 2352 [log.bisect bisect.good|bisect: good (implicit)]
2353 2353 [log.phase|phase: public]
2354 2354 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2355 2355 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2356 2356 [ui.debug log.manifest|manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0]
2357 2357 [log.user|user: User Name <user@hostname>]
2358 2358 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2359 2359 [ui.debug log.files|files+: a]
2360 2360 [ui.debug log.extra|extra: branch=default]
2361 2361 [ui.note log.description|description:]
2362 2362 [ui.note log.description|line 1
2363 2363 line 2]
2364 2364
2365 2365
2366 2366 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2367 2367 [log.bisect bisect.good|bisect: good]
2368 2368 [log.phase|phase: public]
2369 2369 [log.parent changeset.public|parent: 0:1e4e1b8f71e05681d422154f5421e385fec3454f]
2370 2370 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2371 2371 [ui.debug log.manifest|manifest: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55]
2372 2372 [log.user|user: A. N. Other <other@place>]
2373 2373 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2374 2374 [ui.debug log.files|files+: b]
2375 2375 [ui.debug log.extra|extra: branch=default]
2376 2376 [ui.note log.description|description:]
2377 2377 [ui.note log.description|other 1
2378 2378 other 2
2379 2379
2380 2380 other 3]
2381 2381
2382 2382
2383 2383 [log.changeset changeset.public|changeset: 2:97054abb4ab824450e9164180baf491ae0078465]
2384 2384 [log.bisect bisect.untested|bisect: untested]
2385 2385 [log.phase|phase: public]
2386 2386 [log.parent changeset.public|parent: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965]
2387 2387 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2388 2388 [ui.debug log.manifest|manifest: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1]
2389 2389 [log.user|user: other@place]
2390 2390 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2391 2391 [ui.debug log.files|files+: c]
2392 2392 [ui.debug log.extra|extra: branch=default]
2393 2393 [ui.note log.description|description:]
2394 2394 [ui.note log.description|no person]
2395 2395
2396 2396
2397 2397 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2398 2398 [log.bisect bisect.bad|bisect: bad]
2399 2399 [log.phase|phase: public]
2400 2400 [log.parent changeset.public|parent: 2:97054abb4ab824450e9164180baf491ae0078465]
2401 2401 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2402 2402 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2403 2403 [log.user|user: person]
2404 2404 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2405 2405 [ui.debug log.files|files: c]
2406 2406 [ui.debug log.extra|extra: branch=default]
2407 2407 [ui.note log.description|description:]
2408 2408 [ui.note log.description|no user, no domain]
2409 2409
2410 2410
2411 2411 [log.changeset changeset.draft|changeset: 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74]
2412 2412 [log.bisect bisect.bad|bisect: bad (implicit)]
2413 2413 [log.branch|branch: foo]
2414 2414 [log.phase|phase: draft]
2415 2415 [log.parent changeset.public|parent: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47]
2416 2416 [log.parent changeset.public|parent: -1:0000000000000000000000000000000000000000]
2417 2417 [ui.debug log.manifest|manifest: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc]
2418 2418 [log.user|user: person]
2419 2419 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2420 2420 [ui.debug log.extra|extra: branch=foo]
2421 2421 [ui.note log.description|description:]
2422 2422 [ui.note log.description|new branch]
2423 2423
2424 2424
2425 2425 $ hg --color=debug log -v -T bisect -r 0:4
2426 2426 [log.changeset changeset.public|changeset: 0:1e4e1b8f71e0]
2427 2427 [log.bisect bisect.good|bisect: good (implicit)]
2428 2428 [log.user|user: User Name <user@hostname>]
2429 2429 [log.date|date: Mon Jan 12 13:46:40 1970 +0000]
2430 2430 [ui.note log.files|files: a]
2431 2431 [ui.note log.description|description:]
2432 2432 [ui.note log.description|line 1
2433 2433 line 2]
2434 2434
2435 2435
2436 2436 [log.changeset changeset.public|changeset: 1:b608e9d1a3f0]
2437 2437 [log.bisect bisect.good|bisect: good]
2438 2438 [log.user|user: A. N. Other <other@place>]
2439 2439 [log.date|date: Tue Jan 13 17:33:20 1970 +0000]
2440 2440 [ui.note log.files|files: b]
2441 2441 [ui.note log.description|description:]
2442 2442 [ui.note log.description|other 1
2443 2443 other 2
2444 2444
2445 2445 other 3]
2446 2446
2447 2447
2448 2448 [log.changeset changeset.public|changeset: 2:97054abb4ab8]
2449 2449 [log.bisect bisect.untested|bisect: untested]
2450 2450 [log.user|user: other@place]
2451 2451 [log.date|date: Wed Jan 14 21:20:00 1970 +0000]
2452 2452 [ui.note log.files|files: c]
2453 2453 [ui.note log.description|description:]
2454 2454 [ui.note log.description|no person]
2455 2455
2456 2456
2457 2457 [log.changeset changeset.public|changeset: 3:10e46f2dcbf4]
2458 2458 [log.bisect bisect.bad|bisect: bad]
2459 2459 [log.user|user: person]
2460 2460 [log.date|date: Fri Jan 16 01:06:40 1970 +0000]
2461 2461 [ui.note log.files|files: c]
2462 2462 [ui.note log.description|description:]
2463 2463 [ui.note log.description|no user, no domain]
2464 2464
2465 2465
2466 2466 [log.changeset changeset.draft|changeset: 4:bbe44766e73d]
2467 2467 [log.bisect bisect.bad|bisect: bad (implicit)]
2468 2468 [log.branch|branch: foo]
2469 2469 [log.user|user: person]
2470 2470 [log.date|date: Sat Jan 17 04:53:20 1970 +0000]
2471 2471 [ui.note log.description|description:]
2472 2472 [ui.note log.description|new branch]
2473 2473
2474 2474
2475 2475 $ hg bisect --reset
2476 2476
2477 2477 Error on syntax:
2478 2478
2479 2479 $ echo 'x = "f' >> t
2480 2480 $ hg log
2481 2481 abort: t:3: unmatched quotes
2482 2482 [255]
2483 2483
2484 2484 Behind the scenes, this will throw TypeError
2485 2485
2486 2486 $ hg log -l 3 --template '{date|obfuscate}\n'
2487 2487 abort: template filter 'obfuscate' is not compatible with keyword 'date'
2488 2488 [255]
2489 2489
2490 2490 Behind the scenes, this will throw a ValueError
2491 2491
2492 2492 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
2493 2493 abort: template filter 'shortdate' is not compatible with keyword 'desc'
2494 2494 [255]
2495 2495
2496 2496 Behind the scenes, this will throw AttributeError
2497 2497
2498 2498 $ hg log -l 3 --template 'line: {date|escape}\n'
2499 2499 abort: template filter 'escape' is not compatible with keyword 'date'
2500 2500 [255]
2501 2501
2502 2502 Behind the scenes, this will throw ValueError
2503 2503
2504 2504 $ hg tip --template '{author|email|date}\n'
2505 2505 abort: template filter 'datefilter' is not compatible with keyword 'author'
2506 2506 [255]
2507 2507
2508 2508 Thrown an error if a template function doesn't exist
2509 2509
2510 2510 $ hg tip --template '{foo()}\n'
2511 2511 hg: parse error: unknown function 'foo'
2512 2512 [255]
2513 2513
2514 2514 Pass generator object created by template function to filter
2515 2515
2516 2516 $ hg log -l 1 --template '{if(author, author)|user}\n'
2517 2517 test
2518 2518
2519 2519 Test diff function:
2520 2520
2521 2521 $ hg diff -c 8
2522 2522 diff -r 29114dbae42b -r 95c24699272e fourth
2523 2523 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2524 2524 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2525 2525 @@ -0,0 +1,1 @@
2526 2526 +second
2527 2527 diff -r 29114dbae42b -r 95c24699272e second
2528 2528 --- a/second Mon Jan 12 13:46:40 1970 +0000
2529 2529 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2530 2530 @@ -1,1 +0,0 @@
2531 2531 -second
2532 2532 diff -r 29114dbae42b -r 95c24699272e third
2533 2533 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2534 2534 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2535 2535 @@ -0,0 +1,1 @@
2536 2536 +third
2537 2537
2538 2538 $ hg log -r 8 -T "{diff()}"
2539 2539 diff -r 29114dbae42b -r 95c24699272e fourth
2540 2540 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2541 2541 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2542 2542 @@ -0,0 +1,1 @@
2543 2543 +second
2544 2544 diff -r 29114dbae42b -r 95c24699272e second
2545 2545 --- a/second Mon Jan 12 13:46:40 1970 +0000
2546 2546 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2547 2547 @@ -1,1 +0,0 @@
2548 2548 -second
2549 2549 diff -r 29114dbae42b -r 95c24699272e third
2550 2550 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2551 2551 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2552 2552 @@ -0,0 +1,1 @@
2553 2553 +third
2554 2554
2555 2555 $ hg log -r 8 -T "{diff('glob:f*')}"
2556 2556 diff -r 29114dbae42b -r 95c24699272e fourth
2557 2557 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2558 2558 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2559 2559 @@ -0,0 +1,1 @@
2560 2560 +second
2561 2561
2562 2562 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
2563 2563 diff -r 29114dbae42b -r 95c24699272e second
2564 2564 --- a/second Mon Jan 12 13:46:40 1970 +0000
2565 2565 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2566 2566 @@ -1,1 +0,0 @@
2567 2567 -second
2568 2568 diff -r 29114dbae42b -r 95c24699272e third
2569 2569 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2570 2570 +++ b/third Wed Jan 01 10:01:00 2020 +0000
2571 2571 @@ -0,0 +1,1 @@
2572 2572 +third
2573 2573
2574 2574 $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
2575 2575 diff -r 29114dbae42b -r 95c24699272e fourth
2576 2576 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2577 2577 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
2578 2578 @@ -0,0 +1,1 @@
2579 2579 +second
2580 2580
2581 2581 $ cd ..
2582 2582
2583 2583
2584 2584 latesttag:
2585 2585
2586 2586 $ hg init latesttag
2587 2587 $ cd latesttag
2588 2588
2589 2589 $ echo a > file
2590 2590 $ hg ci -Am a -d '0 0'
2591 2591 adding file
2592 2592
2593 2593 $ echo b >> file
2594 2594 $ hg ci -m b -d '1 0'
2595 2595
2596 2596 $ echo c >> head1
2597 2597 $ hg ci -Am h1c -d '2 0'
2598 2598 adding head1
2599 2599
2600 2600 $ hg update -q 1
2601 2601 $ echo d >> head2
2602 2602 $ hg ci -Am h2d -d '3 0'
2603 2603 adding head2
2604 2604 created new head
2605 2605
2606 2606 $ echo e >> head2
2607 2607 $ hg ci -m h2e -d '4 0'
2608 2608
2609 2609 $ hg merge -q
2610 2610 $ hg ci -m merge -d '5 -3600'
2611 2611
2612 2612 No tag set:
2613 2613
2614 2614 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2615 2615 5: null+5
2616 2616 4: null+4
2617 2617 3: null+3
2618 2618 2: null+3
2619 2619 1: null+2
2620 2620 0: null+1
2621 2621
2622 2622 One common tag: longest path wins:
2623 2623
2624 2624 $ hg tag -r 1 -m t1 -d '6 0' t1
2625 2625 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2626 2626 6: t1+4
2627 2627 5: t1+3
2628 2628 4: t1+2
2629 2629 3: t1+1
2630 2630 2: t1+1
2631 2631 1: t1+0
2632 2632 0: null+1
2633 2633
2634 2634 One ancestor tag: more recent wins:
2635 2635
2636 2636 $ hg tag -r 2 -m t2 -d '7 0' t2
2637 2637 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2638 2638 7: t2+3
2639 2639 6: t2+2
2640 2640 5: t2+1
2641 2641 4: t1+2
2642 2642 3: t1+1
2643 2643 2: t2+0
2644 2644 1: t1+0
2645 2645 0: null+1
2646 2646
2647 2647 Two branch tags: more recent wins:
2648 2648
2649 2649 $ hg tag -r 3 -m t3 -d '8 0' t3
2650 2650 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2651 2651 8: t3+5
2652 2652 7: t3+4
2653 2653 6: t3+3
2654 2654 5: t3+2
2655 2655 4: t3+1
2656 2656 3: t3+0
2657 2657 2: t2+0
2658 2658 1: t1+0
2659 2659 0: null+1
2660 2660
2661 2661 Merged tag overrides:
2662 2662
2663 2663 $ hg tag -r 5 -m t5 -d '9 0' t5
2664 2664 $ hg tag -r 3 -m at3 -d '10 0' at3
2665 2665 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2666 2666 10: t5+5
2667 2667 9: t5+4
2668 2668 8: t5+3
2669 2669 7: t5+2
2670 2670 6: t5+1
2671 2671 5: t5+0
2672 2672 4: at3:t3+1
2673 2673 3: at3:t3+0
2674 2674 2: t2+0
2675 2675 1: t1+0
2676 2676 0: null+1
2677 2677
2678 2678 $ cd ..
2679 2679
2680 2680
2681 2681 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2682 2682 if it is a relative path
2683 2683
2684 2684 $ mkdir -p home/styles
2685 2685
2686 2686 $ cat > home/styles/teststyle <<EOF
2687 2687 > changeset = 'test {rev}:{node|short}\n'
2688 2688 > EOF
2689 2689
2690 2690 $ HOME=`pwd`/home; export HOME
2691 2691
2692 2692 $ cat > latesttag/.hg/hgrc <<EOF
2693 2693 > [ui]
2694 2694 > style = ~/styles/teststyle
2695 2695 > EOF
2696 2696
2697 2697 $ hg -R latesttag tip
2698 2698 test 10:9b4a630e5f5f
2699 2699
2700 2700 Test recursive showlist template (issue1989):
2701 2701
2702 2702 $ cat > style1989 <<EOF
2703 2703 > changeset = '{file_mods}{manifest}{extras}'
2704 2704 > file_mod = 'M|{author|person}\n'
2705 2705 > manifest = '{rev},{author}\n'
2706 2706 > extra = '{key}: {author}\n'
2707 2707 > EOF
2708 2708
2709 2709 $ hg -R latesttag log -r tip --style=style1989
2710 2710 M|test
2711 2711 10,test
2712 2712 branch: test
2713 2713
2714 2714 Test new-style inline templating:
2715 2715
2716 2716 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2717 2717 modified files: .hgtags
2718 2718
2719 2719 Test the sub function of templating for expansion:
2720 2720
2721 2721 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2722 2722 xx
2723 2723
2724 2724 Test the strip function with chars specified:
2725 2725
2726 2726 $ hg log -R latesttag --template '{desc}\n'
2727 2727 at3
2728 2728 t5
2729 2729 t3
2730 2730 t2
2731 2731 t1
2732 2732 merge
2733 2733 h2e
2734 2734 h2d
2735 2735 h1c
2736 2736 b
2737 2737 a
2738 2738
2739 2739 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2740 2740 at3
2741 2741 5
2742 2742 3
2743 2743 2
2744 2744 1
2745 2745 merg
2746 2746 h2
2747 2747 h2d
2748 2748 h1c
2749 2749 b
2750 2750 a
2751 2751
2752 2752 Test date format:
2753 2753
2754 2754 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
2755 2755 date: 70 01 01 10 +0000
2756 2756 date: 70 01 01 09 +0000
2757 2757 date: 70 01 01 08 +0000
2758 2758 date: 70 01 01 07 +0000
2759 2759 date: 70 01 01 06 +0000
2760 2760 date: 70 01 01 05 +0100
2761 2761 date: 70 01 01 04 +0000
2762 2762 date: 70 01 01 03 +0000
2763 2763 date: 70 01 01 02 +0000
2764 2764 date: 70 01 01 01 +0000
2765 2765 date: 70 01 01 00 +0000
2766 2766
2767 2767 Test invalid date:
2768 2768
2769 2769 $ hg log -R latesttag -T '{date(rev)}\n'
2770 2770 hg: parse error: date expects a date information
2771 2771 [255]
2772 2772
2773 2773 Test integer literal:
2774 2774
2775 2775 $ hg log -Ra -r0 -T '{(0)}\n'
2776 2776 0
2777 2777 $ hg log -Ra -r0 -T '{(123)}\n'
2778 2778 123
2779 2779 $ hg log -Ra -r0 -T '{(-4)}\n'
2780 2780 -4
2781 2781 $ hg log -Ra -r0 -T '{(-)}\n'
2782 2782 hg: parse error at 2: integer literal without digits
2783 2783 [255]
2784 2784 $ hg log -Ra -r0 -T '{(-a)}\n'
2785 2785 hg: parse error at 2: integer literal without digits
2786 2786 [255]
2787 2787
2788 2788 top-level integer literal is interpreted as symbol (i.e. variable name):
2789 2789
2790 2790 $ hg log -Ra -r0 -T '{1}\n'
2791 2791
2792 2792 $ hg log -Ra -r0 -T '{if("t", "{1}")}\n'
2793 2793
2794 2794 $ hg log -Ra -r0 -T '{1|stringify}\n'
2795 2795
2796 2796
2797 2797 unless explicit symbol is expected:
2798 2798
2799 2799 $ hg log -Ra -r0 -T '{desc|1}\n'
2800 2800 hg: parse error: expected a symbol, got 'integer'
2801 2801 [255]
2802 2802 $ hg log -Ra -r0 -T '{1()}\n'
2803 2803 hg: parse error: expected a symbol, got 'integer'
2804 2804 [255]
2805 2805
2806 2806 Test string literal:
2807 2807
2808 2808 $ hg log -Ra -r0 -T '{"string with no template fragment"}\n'
2809 2809 string with no template fragment
2810 2810 $ hg log -Ra -r0 -T '{"template: {rev}"}\n'
2811 2811 template: 0
2812 2812 $ hg log -Ra -r0 -T '{r"rawstring: {rev}"}\n'
2813 2813 rawstring: {rev}
2814 2814
2815 2815 because map operation requires template, raw string can't be used
2816 2816
2817 2817 $ hg log -Ra -r0 -T '{files % r"rawstring"}\n'
2818 2818 hg: parse error: expected template specifier
2819 2819 [255]
2820 2820
2821 2821 Test string escaping:
2822 2822
2823 2823 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2824 2824 >
2825 2825 <>\n<[>
2826 2826 <>\n<]>
2827 2827 <>\n<
2828 2828
2829 2829 $ hg log -R latesttag -r 0 \
2830 2830 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2831 2831 >
2832 2832 <>\n<[>
2833 2833 <>\n<]>
2834 2834 <>\n<
2835 2835
2836 2836 $ hg log -R latesttag -r 0 -T esc \
2837 2837 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2838 2838 >
2839 2839 <>\n<[>
2840 2840 <>\n<]>
2841 2841 <>\n<
2842 2842
2843 2843 $ cat <<'EOF' > esctmpl
2844 2844 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2845 2845 > EOF
2846 2846 $ hg log -R latesttag -r 0 --style ./esctmpl
2847 2847 >
2848 2848 <>\n<[>
2849 2849 <>\n<]>
2850 2850 <>\n<
2851 2851
2852 2852 Test string escaping of quotes:
2853 2853
2854 2854 $ hg log -Ra -r0 -T '{"\""}\n'
2855 2855 "
2856 2856 $ hg log -Ra -r0 -T '{"\\\""}\n'
2857 2857 \"
2858 2858 $ hg log -Ra -r0 -T '{r"\""}\n'
2859 2859 \"
2860 2860 $ hg log -Ra -r0 -T '{r"\\\""}\n'
2861 2861 \\\"
2862 2862
2863
2864 $ hg log -Ra -r0 -T '{"\""}\n'
2865 "
2866 $ hg log -Ra -r0 -T '{"\\\""}\n'
2867 \"
2868 $ hg log -Ra -r0 -T '{r"\""}\n'
2869 \"
2870 $ hg log -Ra -r0 -T '{r"\\\""}\n'
2871 \\\"
2872
2863 2873 Test exception in quoted template. single backslash before quotation mark is
2864 2874 stripped before parsing:
2865 2875
2866 2876 $ cat <<'EOF' > escquotetmpl
2867 2877 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
2868 2878 > EOF
2869 2879 $ cd latesttag
2870 2880 $ hg log -r 2 --style ../escquotetmpl
2871 2881 " \" \" \\" head1
2872 2882
2873 2883 $ hg log -r 2 -T esc --config templates.esc='{\"invalid\"}\n'
2874 2884 hg: parse error at 1: syntax error
2875 2885 [255]
2876 2886 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
2877 2887 valid
2878 2888 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
2879 2889 valid
2890
2891 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
2892 _evalifliteral() templates (issue4733):
2893
2894 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
2895 "2
2896 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
2897 "2
2898 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
2899 "2
2900
2901 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
2902 \"
2903 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
2904 \"
2905 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
2906 \"
2907
2908 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
2909 \\\"
2910 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
2911 \\\"
2912 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
2913 \\\"
2914
2915 escaped single quotes and errors:
2916
2917 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
2918 foo
2919 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
2920 foo
2921 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
2922 hg: parse error at 11: unterminated string
2923 [255]
2924 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
2925 hg: parse error at 11: syntax error
2926 [255]
2927 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
2928 hg: parse error at 12: syntax error
2929 [255]
2930
2880 2931 $ cd ..
2881 2932
2882 2933 Test leading backslashes:
2883 2934
2884 2935 $ cd latesttag
2885 2936 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
2886 2937 {rev} {file}
2887 2938 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
2888 2939 \2 \head1
2889 2940 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
2890 2941 \{rev} \{file}
2891 2942 $ cd ..
2892 2943
2893 2944 Test leading backslashes in "if" expression (issue4714):
2894 2945
2895 2946 $ cd latesttag
2896 2947 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
2897 2948 {rev} \{rev}
2898 2949 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
2899 2950 \2 \\{rev}
2900 2951 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
2901 2952 \{rev} \\\{rev}
2902 2953 $ cd ..
2903 2954
2904 2955 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
2905 2956
2906 2957 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
2907 2958 \x6e
2908 2959 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
2909 2960 \x5c\x786e
2910 2961 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
2911 2962 \x6e
2912 2963 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
2913 2964 \x5c\x786e
2914 2965
2915 2966 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
2916 2967 \x6e
2917 2968 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
2918 2969 \x5c\x786e
2919 2970 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
2920 2971 \x6e
2921 2972 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
2922 2973 \x5c\x786e
2923 2974
2924 2975 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
2925 2976 fourth
2926 2977 second
2927 2978 third
2928 2979 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
2929 2980 fourth\nsecond\nthird
2930 2981
2931 2982 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
2932 2983 <p>
2933 2984 1st
2934 2985 </p>
2935 2986 <p>
2936 2987 2nd
2937 2988 </p>
2938 2989 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
2939 2990 <p>
2940 2991 1st\n\n2nd
2941 2992 </p>
2942 2993 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
2943 2994 1st
2944 2995
2945 2996 2nd
2946 2997
2947 2998 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
2948 2999 o perso
2949 3000 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
2950 3001 no person
2951 3002 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
2952 3003 o perso
2953 3004 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
2954 3005 no perso
2955 3006
2956 3007 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
2957 3008 -o perso-
2958 3009 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
2959 3010 no person
2960 3011 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
2961 3012 \x2do perso\x2d
2962 3013 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
2963 3014 -o perso-
2964 3015 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
2965 3016 \x2do perso\x6e
2966 3017
2967 3018 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
2968 3019 fourth
2969 3020 second
2970 3021 third
2971 3022
2972 3023 Test string escaping in nested expression:
2973 3024
2974 3025 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
2975 3026 fourth\x6esecond\x6ethird
2976 3027 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
2977 3028 fourth\x6esecond\x6ethird
2978 3029
2979 3030 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
2980 3031 fourth\x6esecond\x6ethird
2981 3032 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
2982 3033 fourth\x5c\x786esecond\x5c\x786ethird
2983 3034
2984 3035 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
2985 3036 3:\x6eo user, \x6eo domai\x6e
2986 3037 4:\x5c\x786eew bra\x5c\x786ech
2987 3038
2988 3039 Test recursive evaluation:
2989 3040
2990 3041 $ hg init r
2991 3042 $ cd r
2992 3043 $ echo a > a
2993 3044 $ hg ci -Am '{rev}'
2994 3045 adding a
2995 3046 $ hg log -r 0 --template '{if(rev, desc)}\n'
2996 3047 {rev}
2997 3048 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
2998 3049 test 0
2999 3050
3000 3051 $ hg branch -q 'text.{rev}'
3001 3052 $ echo aa >> aa
3002 3053 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
3003 3054
3004 3055 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
3005 3056 {node|short}desc to
3006 3057 text.{rev}be wrapped
3007 3058 text.{rev}desc to be
3008 3059 text.{rev}wrapped (no-eol)
3009 3060 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
3010 3061 bcc7ff960b8e:desc to
3011 3062 text.1:be wrapped
3012 3063 text.1:desc to be
3013 3064 text.1:wrapped (no-eol)
3014 3065
3015 3066 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
3016 3067 {node|short} (no-eol)
3017 3068 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
3018 3069 bcc-ff---b-e (no-eol)
3019 3070
3020 3071 $ cat >> .hg/hgrc <<EOF
3021 3072 > [extensions]
3022 3073 > color=
3023 3074 > [color]
3024 3075 > mode=ansi
3025 3076 > text.{rev} = red
3026 3077 > text.1 = green
3027 3078 > EOF
3028 3079 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
3029 3080 \x1b[0;31mtext\x1b[0m (esc)
3030 3081 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
3031 3082 \x1b[0;32mtext\x1b[0m (esc)
3032 3083
3033 3084 Test branches inside if statement:
3034 3085
3035 3086 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
3036 3087 no
3037 3088
3038 3089 Test get function:
3039 3090
3040 3091 $ hg log -r 0 --template '{get(extras, "branch")}\n'
3041 3092 default
3042 3093 $ hg log -r 0 --template '{get(files, "should_fail")}\n'
3043 3094 hg: parse error: get() expects a dict as first argument
3044 3095 [255]
3045 3096
3046 3097 Test shortest(node) function:
3047 3098
3048 3099 $ echo b > b
3049 3100 $ hg ci -qAm b
3050 3101 $ hg log --template '{shortest(node)}\n'
3051 3102 e777
3052 3103 bcc7
3053 3104 f776
3054 3105 $ hg log --template '{shortest(node, 10)}\n'
3055 3106 e777603221
3056 3107 bcc7ff960b
3057 3108 f7769ec2ab
3058 3109
3059 3110 Test pad function
3060 3111
3061 3112 $ hg log --template '{pad(rev, 20)} {author|user}\n'
3062 3113 2 test
3063 3114 1 {node|short}
3064 3115 0 test
3065 3116
3066 3117 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
3067 3118 2 test
3068 3119 1 {node|short}
3069 3120 0 test
3070 3121
3071 3122 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
3072 3123 2------------------- test
3073 3124 1------------------- {node|short}
3074 3125 0------------------- test
3075 3126
3076 3127 Test template string in pad function
3077 3128
3078 3129 $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
3079 3130 {0} test
3080 3131
3081 3132 $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
3082 3133 \{rev} test
3083 3134
3084 3135 Test ifcontains function
3085 3136
3086 3137 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
3087 3138 2 is in the string
3088 3139 1 is not
3089 3140 0 is in the string
3090 3141
3091 3142 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
3092 3143 2 did not add a
3093 3144 1 did not add a
3094 3145 0 added a
3095 3146
3096 3147 Test revset function
3097 3148
3098 3149 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
3099 3150 2 current rev
3100 3151 1 not current rev
3101 3152 0 not current rev
3102 3153
3103 3154 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
3104 3155 2 match rev
3105 3156 1 match rev
3106 3157 0 not match rev
3107 3158
3108 3159 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
3109 3160 2 Parents: 1
3110 3161 1 Parents: 0
3111 3162 0 Parents:
3112 3163
3113 3164 $ cat >> .hg/hgrc <<EOF
3114 3165 > [revsetalias]
3115 3166 > myparents(\$1) = parents(\$1)
3116 3167 > EOF
3117 3168 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
3118 3169 2 Parents: 1
3119 3170 1 Parents: 0
3120 3171 0 Parents:
3121 3172
3122 3173 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
3123 3174 Rev: 2
3124 3175 Ancestor: 0
3125 3176 Ancestor: 1
3126 3177 Ancestor: 2
3127 3178
3128 3179 Rev: 1
3129 3180 Ancestor: 0
3130 3181 Ancestor: 1
3131 3182
3132 3183 Rev: 0
3133 3184 Ancestor: 0
3134 3185
3135 3186 $ hg log --template '{revset("TIP"|lower)}\n' -l1
3136 3187 2
3137 3188
3138 3189 Test active bookmark templating
3139 3190
3140 3191 $ hg book foo
3141 3192 $ hg book bar
3142 3193 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, active, \"*\")} '}\n"
3143 3194 2 bar* foo
3144 3195 1
3145 3196 0
3146 3197 $ hg log --template "{rev} {activebookmark}\n"
3147 3198 2 bar
3148 3199 1
3149 3200 0
3150 3201 $ hg bookmarks --inactive bar
3151 3202 $ hg log --template "{rev} {activebookmark}\n"
3152 3203 2
3153 3204 1
3154 3205 0
3155 3206 $ hg book -r1 baz
3156 3207 $ hg log --template "{rev} {join(bookmarks, ' ')}\n"
3157 3208 2 bar foo
3158 3209 1 baz
3159 3210 0
3160 3211 $ hg log --template "{rev} {ifcontains('foo', bookmarks, 't', 'f')}\n"
3161 3212 2 t
3162 3213 1 f
3163 3214 0 f
3164 3215
3165 3216 Test stringify on sub expressions
3166 3217
3167 3218 $ cd ..
3168 3219 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
3169 3220 fourth, second, third
3170 3221 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
3171 3222 abc
3172 3223
3173 3224 Test splitlines
3174 3225
3175 3226 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
3176 3227 @ foo Modify, add, remove, rename
3177 3228 |
3178 3229 o foo future
3179 3230 |
3180 3231 o foo third
3181 3232 |
3182 3233 o foo second
3183 3234
3184 3235 o foo merge
3185 3236 |\
3186 3237 | o foo new head
3187 3238 | |
3188 3239 o | foo new branch
3189 3240 |/
3190 3241 o foo no user, no domain
3191 3242 |
3192 3243 o foo no person
3193 3244 |
3194 3245 o foo other 1
3195 3246 | foo other 2
3196 3247 | foo
3197 3248 | foo other 3
3198 3249 o foo line 1
3199 3250 foo line 2
3200 3251
3201 3252 Test startswith
3202 3253 $ hg log -Gv -R a --template "{startswith(desc)}"
3203 3254 hg: parse error: startswith expects two arguments
3204 3255 [255]
3205 3256
3206 3257 $ hg log -Gv -R a --template "{startswith('line', desc)}"
3207 3258 @
3208 3259 |
3209 3260 o
3210 3261 |
3211 3262 o
3212 3263 |
3213 3264 o
3214 3265
3215 3266 o
3216 3267 |\
3217 3268 | o
3218 3269 | |
3219 3270 o |
3220 3271 |/
3221 3272 o
3222 3273 |
3223 3274 o
3224 3275 |
3225 3276 o
3226 3277 |
3227 3278 o line 1
3228 3279 line 2
3229 3280
3230 3281 Test bad template with better error message
3231 3282
3232 3283 $ hg log -Gv -R a --template '{desc|user()}'
3233 3284 hg: parse error: expected a symbol, got 'func'
3234 3285 [255]
3235 3286
3236 3287 Test word function (including index out of bounds graceful failure)
3237 3288
3238 3289 $ hg log -Gv -R a --template "{word('1', desc)}"
3239 3290 @ add,
3240 3291 |
3241 3292 o
3242 3293 |
3243 3294 o
3244 3295 |
3245 3296 o
3246 3297
3247 3298 o
3248 3299 |\
3249 3300 | o head
3250 3301 | |
3251 3302 o | branch
3252 3303 |/
3253 3304 o user,
3254 3305 |
3255 3306 o person
3256 3307 |
3257 3308 o 1
3258 3309 |
3259 3310 o 1
3260 3311
3261 3312
3262 3313 Test word third parameter used as splitter
3263 3314
3264 3315 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
3265 3316 @ M
3266 3317 |
3267 3318 o future
3268 3319 |
3269 3320 o third
3270 3321 |
3271 3322 o sec
3272 3323
3273 3324 o merge
3274 3325 |\
3275 3326 | o new head
3276 3327 | |
3277 3328 o | new branch
3278 3329 |/
3279 3330 o n
3280 3331 |
3281 3332 o n
3282 3333 |
3283 3334 o
3284 3335 |
3285 3336 o line 1
3286 3337 line 2
3287 3338
3288 3339 Test word error messages for not enough and too many arguments
3289 3340
3290 3341 $ hg log -Gv -R a --template "{word('0')}"
3291 3342 hg: parse error: word expects two or three arguments, got 1
3292 3343 [255]
3293 3344
3294 3345 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
3295 3346 hg: parse error: word expects two or three arguments, got 7
3296 3347 [255]
3297 3348
3298 3349 Test word for integer literal
3299 3350
3300 3351 $ hg log -R a --template "{word(2, desc)}\n" -r0
3301 3352 line
3302 3353
3303 3354 Test word for invalid numbers
3304 3355
3305 3356 $ hg log -Gv -R a --template "{word('a', desc)}"
3306 3357 hg: parse error: word expects an integer index
3307 3358 [255]
3308 3359
3309 3360 Test indent and not adding to empty lines
3310 3361
3311 3362 $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
3312 3363 -----
3313 3364 > line 1
3314 3365 >> line 2
3315 3366 -----
3316 3367 > other 1
3317 3368 >> other 2
3318 3369
3319 3370 >> other 3
3320 3371
3321 3372 Test with non-strings like dates
3322 3373
3323 3374 $ hg log -T "{indent(date, ' ')}\n" -r 2:3 -R a
3324 3375 1200000.00
3325 3376 1300000.00
@@ -1,298 +1,297
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 >
5 5 > [phases]
6 6 > publish=False
7 7 >
8 8 > [alias]
9 9 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
10 10 > EOF
11 11
12 12 $ hg init a
13 13 $ cd a
14 14 $ echo c1 >common
15 15 $ hg add common
16 16 $ hg ci -m C1
17 17
18 18 $ echo c2 >>common
19 19 $ hg ci -m C2
20 20
21 21 $ echo c3 >>common
22 22 $ hg ci -m C3
23 23
24 24 $ hg up -q -C 1
25 25
26 26 $ echo l1 >>extra
27 27 $ hg add extra
28 28 $ hg ci -m L1
29 29 created new head
30 30
31 31 $ sed -e 's/c2/l2/' common > common.new
32 32 $ mv common.new common
33 33 $ hg ci -m L2
34 34
35 35 $ echo l3 >> extra2
36 36 $ hg add extra2
37 37 $ hg ci -m L3
38 38 $ hg bookmark mybook
39 39
40 40 $ hg phase --force --secret 4
41 41
42 42 $ hg tglog
43 43 @ 5:secret 'L3' mybook
44 44 |
45 45 o 4:secret 'L2'
46 46 |
47 47 o 3:draft 'L1'
48 48 |
49 49 | o 2:draft 'C3'
50 50 |/
51 51 o 1:draft 'C2'
52 52 |
53 53 o 0:draft 'C1'
54 54
55 55 Try to call --continue:
56 56
57 57 $ hg rebase --continue
58 58 abort: no rebase in progress
59 59 [255]
60 60
61 61 Conflicting rebase:
62 62
63 63 $ hg rebase -s 3 -d 2
64 64 rebasing 3:3163e20567cc "L1"
65 65 rebasing 4:46f0b057b5c0 "L2"
66 66 merging common
67 67 warning: conflicts during merge.
68 68 merging common incomplete! (edit conflicts, then use 'hg resolve --mark')
69 69 unresolved conflicts (see hg resolve, then hg rebase --continue)
70 70 [1]
71 71
72 72 Try to continue without solving the conflict:
73 73
74 74 $ hg rebase --continue
75 75 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
76 76 rebasing 4:46f0b057b5c0 "L2"
77 77 abort: unresolved merge conflicts (see "hg help resolve")
78 78 [255]
79 79
80 80 Conclude rebase:
81 81
82 82 $ echo 'resolved merge' >common
83 83 $ hg resolve -m common
84 84 (no more unresolved files)
85 85 $ hg rebase --continue
86 86 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
87 87 rebasing 4:46f0b057b5c0 "L2"
88 88 rebasing 5:8029388f38dc "L3" (mybook)
89 89 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-backup.hg (glob)
90 90
91 91 $ hg tglog
92 92 @ 5:secret 'L3' mybook
93 93 |
94 94 o 4:secret 'L2'
95 95 |
96 96 o 3:draft 'L1'
97 97 |
98 98 o 2:draft 'C3'
99 99 |
100 100 o 1:draft 'C2'
101 101 |
102 102 o 0:draft 'C1'
103 103
104 104 Check correctness:
105 105
106 106 $ hg cat -r 0 common
107 107 c1
108 108
109 109 $ hg cat -r 1 common
110 110 c1
111 111 c2
112 112
113 113 $ hg cat -r 2 common
114 114 c1
115 115 c2
116 116 c3
117 117
118 118 $ hg cat -r 3 common
119 119 c1
120 120 c2
121 121 c3
122 122
123 123 $ hg cat -r 4 common
124 124 resolved merge
125 125
126 126 $ hg cat -r 5 common
127 127 resolved merge
128 128
129 129 Bookmark stays active after --continue
130 130 $ hg bookmarks
131 131 * mybook 5:d67b21408fc0
132 132
133 133 $ cd ..
134 134
135 135 Check that the right ancestors is used while rebasing a merge (issue4041)
136 136
137 137 $ hg clone "$TESTDIR/bundles/issue4041.hg" issue4041
138 138 requesting all changes
139 139 adding changesets
140 140 adding manifests
141 141 adding file changes
142 142 added 11 changesets with 8 changes to 3 files (+1 heads)
143 143 updating to branch default
144 144 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 145 $ cd issue4041
146 146 $ hg log -G
147 147 o changeset: 10:2f2496ddf49d
148 148 |\ branch: f1
149 149 | | tag: tip
150 150 | | parent: 7:4c9fbe56a16f
151 151 | | parent: 9:e31216eec445
152 152 | | user: szhang
153 153 | | date: Thu Sep 05 12:59:39 2013 -0400
154 154 | | summary: merge
155 155 | |
156 156 | o changeset: 9:e31216eec445
157 157 | | branch: f1
158 158 | | user: szhang
159 159 | | date: Thu Sep 05 12:59:10 2013 -0400
160 160 | | summary: more changes to f1
161 161 | |
162 162 | o changeset: 8:8e4e2c1a07ae
163 163 | |\ branch: f1
164 164 | | | parent: 2:4bc80088dc6b
165 165 | | | parent: 6:400110238667
166 166 | | | user: szhang
167 167 | | | date: Thu Sep 05 12:57:59 2013 -0400
168 168 | | | summary: bad merge
169 169 | | |
170 170 o | | changeset: 7:4c9fbe56a16f
171 171 |/ / branch: f1
172 172 | | parent: 2:4bc80088dc6b
173 173 | | user: szhang
174 174 | | date: Thu Sep 05 12:54:00 2013 -0400
175 175 | | summary: changed f1
176 176 | |
177 177 | o changeset: 6:400110238667
178 178 | | branch: f2
179 179 | | parent: 4:12e8ec6bb010
180 180 | | user: szhang
181 181 | | date: Tue Sep 03 13:58:02 2013 -0400
182 182 | | summary: changed f2 on f2
183 183 | |
184 184 | | @ changeset: 5:d79e2059b5c0
185 185 | | | parent: 3:8a951942e016
186 186 | | | user: szhang
187 187 | | | date: Tue Sep 03 13:57:39 2013 -0400
188 188 | | | summary: changed f2 on default
189 189 | | |
190 190 | o | changeset: 4:12e8ec6bb010
191 191 | |/ branch: f2
192 192 | | user: szhang
193 193 | | date: Tue Sep 03 13:57:18 2013 -0400
194 194 | | summary: created f2 branch
195 195 | |
196 196 | o changeset: 3:8a951942e016
197 197 | | parent: 0:24797d4f68de
198 198 | | user: szhang
199 199 | | date: Tue Sep 03 13:57:11 2013 -0400
200 200 | | summary: added f2.txt
201 201 | |
202 202 o | changeset: 2:4bc80088dc6b
203 203 | | branch: f1
204 204 | | user: szhang
205 205 | | date: Tue Sep 03 13:56:20 2013 -0400
206 206 | | summary: added f1.txt
207 207 | |
208 208 o | changeset: 1:ef53c9e6b608
209 209 |/ branch: f1
210 210 | user: szhang
211 211 | date: Tue Sep 03 13:55:26 2013 -0400
212 212 | summary: created f1 branch
213 213 |
214 214 o changeset: 0:24797d4f68de
215 215 user: szhang
216 216 date: Tue Sep 03 13:55:08 2013 -0400
217 217 summary: added default.txt
218 218
219 219 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
220 220 rebase onto 2 starting from e31216eec445
221 221 ignoring null merge rebase of 3
222 222 ignoring null merge rebase of 4
223 223 ignoring null merge rebase of 6
224 224 ignoring null merge rebase of 8
225 225 rebasing 9:e31216eec445 "more changes to f1"
226 226 future parents are 2 and -1
227 227 rebase status stored
228 228 update to 2:4bc80088dc6b
229 229 resolving manifests
230 230 branchmerge: False, force: True, partial: False
231 231 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
232 232 f2.txt: other deleted -> r
233 233 removing f2.txt
234 234 f1.txt: remote created -> g
235 235 getting f1.txt
236 236 merge against 9:e31216eec445
237 237 detach base 8:8e4e2c1a07ae
238 238 searching for copies back to rev 3
239 239 resolving manifests
240 240 branchmerge: True, force: True, partial: False
241 241 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
242 242 f1.txt: remote is newer -> g
243 243 getting f1.txt
244 244 committing files:
245 245 f1.txt
246 246 committing manifest
247 247 committing changelog
248 248 rebased as 19c888675e13
249 249 rebasing 10:2f2496ddf49d "merge" (tip)
250 250 future parents are 11 and 7
251 251 rebase status stored
252 252 already in target
253 253 merge against 10:2f2496ddf49d
254 254 detach base 9:e31216eec445
255 255 searching for copies back to rev 3
256 256 resolving manifests
257 257 branchmerge: True, force: True, partial: False
258 258 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
259 259 f1.txt: remote is newer -> g
260 260 getting f1.txt
261 261 committing files:
262 262 f1.txt
263 263 committing manifest
264 264 committing changelog
265 265 rebased as 2a7f09cac94c
266 266 rebase merging completed
267 267 update back to initial working directory parent
268 268 resolving manifests
269 269 branchmerge: False, force: False, partial: False
270 270 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
271 271 f1.txt: other deleted -> r
272 272 removing f1.txt
273 273 f2.txt: remote created -> g
274 274 getting f2.txt
275 3 changesets found
275 2 changesets found
276 276 list of changesets:
277 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
278 277 e31216eec445e44352c5f01588856059466a24c9
279 278 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
280 279 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-backup.hg (glob)
281 280 3 changesets found
282 281 list of changesets:
283 282 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
284 283 19c888675e133ab5dff84516926a65672eaf04d9
285 284 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
286 285 adding branch
287 286 adding changesets
288 287 add changeset 4c9fbe56a16f
289 288 add changeset 19c888675e13
290 289 add changeset 2a7f09cac94c
291 290 adding manifests
292 291 adding file changes
293 292 adding f1.txt revisions
294 293 added 2 changesets with 2 changes to 1 files
295 294 invalid branchheads cache (served): tip differs
296 295 rebase completed
297 296 updating the branch cache
298 297 truncating cache/rbc-revs-v1 to 72
@@ -1,694 +1,828
1 1 $ echo "[extensions]" >> $HGRCPATH
2 2 $ echo "strip=" >> $HGRCPATH
3 3
4 4 $ restore() {
5 5 > hg unbundle -q .hg/strip-backup/*
6 6 > rm .hg/strip-backup/*
7 7 > }
8 8 $ teststrip() {
9 9 > hg up -C $1
10 10 > echo % before update $1, strip $2
11 11 > hg parents
12 12 > hg --traceback strip $2
13 13 > echo % after update $1, strip $2
14 14 > hg parents
15 15 > restore
16 16 > }
17 17
18 18 $ hg init test
19 19 $ cd test
20 20
21 21 $ echo foo > bar
22 22 $ hg ci -Ama
23 23 adding bar
24 24
25 25 $ echo more >> bar
26 26 $ hg ci -Amb
27 27
28 28 $ echo blah >> bar
29 29 $ hg ci -Amc
30 30
31 31 $ hg up 1
32 32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 33 $ echo blah >> bar
34 34 $ hg ci -Amd
35 35 created new head
36 36
37 37 $ echo final >> bar
38 38 $ hg ci -Ame
39 39
40 40 $ hg log
41 41 changeset: 4:443431ffac4f
42 42 tag: tip
43 43 user: test
44 44 date: Thu Jan 01 00:00:00 1970 +0000
45 45 summary: e
46 46
47 47 changeset: 3:65bd5f99a4a3
48 48 parent: 1:ef3a871183d7
49 49 user: test
50 50 date: Thu Jan 01 00:00:00 1970 +0000
51 51 summary: d
52 52
53 53 changeset: 2:264128213d29
54 54 user: test
55 55 date: Thu Jan 01 00:00:00 1970 +0000
56 56 summary: c
57 57
58 58 changeset: 1:ef3a871183d7
59 59 user: test
60 60 date: Thu Jan 01 00:00:00 1970 +0000
61 61 summary: b
62 62
63 63 changeset: 0:9ab35a2d17cb
64 64 user: test
65 65 date: Thu Jan 01 00:00:00 1970 +0000
66 66 summary: a
67 67
68 68
69 69 $ teststrip 4 4
70 70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 71 % before update 4, strip 4
72 72 changeset: 4:443431ffac4f
73 73 tag: tip
74 74 user: test
75 75 date: Thu Jan 01 00:00:00 1970 +0000
76 76 summary: e
77 77
78 78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 79 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
80 80 % after update 4, strip 4
81 81 changeset: 3:65bd5f99a4a3
82 82 tag: tip
83 83 parent: 1:ef3a871183d7
84 84 user: test
85 85 date: Thu Jan 01 00:00:00 1970 +0000
86 86 summary: d
87 87
88 88 $ teststrip 4 3
89 89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 % before update 4, strip 3
91 91 changeset: 4:443431ffac4f
92 92 tag: tip
93 93 user: test
94 94 date: Thu Jan 01 00:00:00 1970 +0000
95 95 summary: e
96 96
97 97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
99 99 % after update 4, strip 3
100 100 changeset: 1:ef3a871183d7
101 101 user: test
102 102 date: Thu Jan 01 00:00:00 1970 +0000
103 103 summary: b
104 104
105 105 $ teststrip 1 4
106 106 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 107 % before update 1, strip 4
108 108 changeset: 1:ef3a871183d7
109 109 user: test
110 110 date: Thu Jan 01 00:00:00 1970 +0000
111 111 summary: b
112 112
113 113 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
114 114 % after update 1, strip 4
115 115 changeset: 1:ef3a871183d7
116 116 user: test
117 117 date: Thu Jan 01 00:00:00 1970 +0000
118 118 summary: b
119 119
120 120 $ teststrip 4 2
121 121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 % before update 4, strip 2
123 123 changeset: 4:443431ffac4f
124 124 tag: tip
125 125 user: test
126 126 date: Thu Jan 01 00:00:00 1970 +0000
127 127 summary: e
128 128
129 129 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
130 130 % after update 4, strip 2
131 131 changeset: 3:443431ffac4f
132 132 tag: tip
133 133 user: test
134 134 date: Thu Jan 01 00:00:00 1970 +0000
135 135 summary: e
136 136
137 137 $ teststrip 4 1
138 138 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 139 % before update 4, strip 1
140 140 changeset: 4:264128213d29
141 141 tag: tip
142 142 parent: 1:ef3a871183d7
143 143 user: test
144 144 date: Thu Jan 01 00:00:00 1970 +0000
145 145 summary: c
146 146
147 147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 148 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
149 149 % after update 4, strip 1
150 150 changeset: 0:9ab35a2d17cb
151 151 tag: tip
152 152 user: test
153 153 date: Thu Jan 01 00:00:00 1970 +0000
154 154 summary: a
155 155
156 156 $ teststrip null 4
157 157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
158 158 % before update null, strip 4
159 159 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
160 160 % after update null, strip 4
161 161
162 162 $ hg log
163 163 changeset: 4:264128213d29
164 164 tag: tip
165 165 parent: 1:ef3a871183d7
166 166 user: test
167 167 date: Thu Jan 01 00:00:00 1970 +0000
168 168 summary: c
169 169
170 170 changeset: 3:443431ffac4f
171 171 user: test
172 172 date: Thu Jan 01 00:00:00 1970 +0000
173 173 summary: e
174 174
175 175 changeset: 2:65bd5f99a4a3
176 176 user: test
177 177 date: Thu Jan 01 00:00:00 1970 +0000
178 178 summary: d
179 179
180 180 changeset: 1:ef3a871183d7
181 181 user: test
182 182 date: Thu Jan 01 00:00:00 1970 +0000
183 183 summary: b
184 184
185 185 changeset: 0:9ab35a2d17cb
186 186 user: test
187 187 date: Thu Jan 01 00:00:00 1970 +0000
188 188 summary: a
189 189
190 190 $ hg up -C 4
191 191 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 192 $ hg parents
193 193 changeset: 4:264128213d29
194 194 tag: tip
195 195 parent: 1:ef3a871183d7
196 196 user: test
197 197 date: Thu Jan 01 00:00:00 1970 +0000
198 198 summary: c
199 199
200 200 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=INVALID strip 4
201 201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 202 unknown strip-bundle2-version value 'INVALID'; should be one of ['01', '02']
203 203 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
204 204 $ hg debugbundle .hg/strip-backup/*
205 205 264128213d290d868c54642d13aeaa3675551a78
206 206 $ restore
207 207
208 208 $ hg up -C 4
209 209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 210 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4
211 211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 212 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
213 213 $ hg parents
214 214 changeset: 1:ef3a871183d7
215 215 user: test
216 216 date: Thu Jan 01 00:00:00 1970 +0000
217 217 summary: b
218 218
219 219 $ hg debugbundle .hg/strip-backup/*
220 220 Stream params: {}
221 221 changegroup -- "{'version': '02'}"
222 222 264128213d290d868c54642d13aeaa3675551a78
223 223 $ hg incoming .hg/strip-backup/*
224 224 comparing with .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
225 225 searching for changes
226 226 changeset: 4:264128213d29
227 227 tag: tip
228 228 parent: 1:ef3a871183d7
229 229 user: test
230 230 date: Thu Jan 01 00:00:00 1970 +0000
231 231 summary: c
232 232
233 233 $ restore
234 234 $ hg up -C 4
235 235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
236 236 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4
237 237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 238 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
239 239 $ hg parents
240 240 changeset: 1:ef3a871183d7
241 241 user: test
242 242 date: Thu Jan 01 00:00:00 1970 +0000
243 243 summary: b
244 244
245 245 $ hg debugbundle .hg/strip-backup/*
246 246 Stream params: {}
247 247 changegroup -- "{'version': '02'}"
248 248 264128213d290d868c54642d13aeaa3675551a78
249 249 $ hg pull .hg/strip-backup/*
250 250 pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
251 251 searching for changes
252 252 adding changesets
253 253 adding manifests
254 254 adding file changes
255 255 added 1 changesets with 0 changes to 0 files (+1 heads)
256 256 (run 'hg heads' to see heads, 'hg merge' to merge)
257 257 $ rm .hg/strip-backup/*
258 258 $ hg log --graph
259 259 o changeset: 4:264128213d29
260 260 | tag: tip
261 261 | parent: 1:ef3a871183d7
262 262 | user: test
263 263 | date: Thu Jan 01 00:00:00 1970 +0000
264 264 | summary: c
265 265 |
266 266 | o changeset: 3:443431ffac4f
267 267 | | user: test
268 268 | | date: Thu Jan 01 00:00:00 1970 +0000
269 269 | | summary: e
270 270 | |
271 271 | o changeset: 2:65bd5f99a4a3
272 272 |/ user: test
273 273 | date: Thu Jan 01 00:00:00 1970 +0000
274 274 | summary: d
275 275 |
276 276 @ changeset: 1:ef3a871183d7
277 277 | user: test
278 278 | date: Thu Jan 01 00:00:00 1970 +0000
279 279 | summary: b
280 280 |
281 281 o changeset: 0:9ab35a2d17cb
282 282 user: test
283 283 date: Thu Jan 01 00:00:00 1970 +0000
284 284 summary: a
285 285
286 286 $ hg up -C 2
287 287 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 288 $ hg merge 4
289 289 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 290 (branch merge, don't forget to commit)
291 291
292 292 before strip of merge parent
293 293
294 294 $ hg parents
295 295 changeset: 2:65bd5f99a4a3
296 296 user: test
297 297 date: Thu Jan 01 00:00:00 1970 +0000
298 298 summary: d
299 299
300 300 changeset: 4:264128213d29
301 301 tag: tip
302 302 parent: 1:ef3a871183d7
303 303 user: test
304 304 date: Thu Jan 01 00:00:00 1970 +0000
305 305 summary: c
306 306
307 307 $ hg strip 4
308 308 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
309 309 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
310 310
311 311 after strip of merge parent
312 312
313 313 $ hg parents
314 314 changeset: 1:ef3a871183d7
315 315 user: test
316 316 date: Thu Jan 01 00:00:00 1970 +0000
317 317 summary: b
318 318
319 319 $ restore
320 320
321 321 $ hg up
322 322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
323 323 $ hg log -G
324 324 @ changeset: 4:264128213d29
325 325 | tag: tip
326 326 | parent: 1:ef3a871183d7
327 327 | user: test
328 328 | date: Thu Jan 01 00:00:00 1970 +0000
329 329 | summary: c
330 330 |
331 331 | o changeset: 3:443431ffac4f
332 332 | | user: test
333 333 | | date: Thu Jan 01 00:00:00 1970 +0000
334 334 | | summary: e
335 335 | |
336 336 | o changeset: 2:65bd5f99a4a3
337 337 |/ user: test
338 338 | date: Thu Jan 01 00:00:00 1970 +0000
339 339 | summary: d
340 340 |
341 341 o changeset: 1:ef3a871183d7
342 342 | user: test
343 343 | date: Thu Jan 01 00:00:00 1970 +0000
344 344 | summary: b
345 345 |
346 346 o changeset: 0:9ab35a2d17cb
347 347 user: test
348 348 date: Thu Jan 01 00:00:00 1970 +0000
349 349 summary: a
350 350
351 351
352 352 2 is parent of 3, only one strip should happen
353 353
354 354 $ hg strip "roots(2)" 3
355 355 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
356 356 $ hg log -G
357 357 @ changeset: 2:264128213d29
358 358 | tag: tip
359 359 | user: test
360 360 | date: Thu Jan 01 00:00:00 1970 +0000
361 361 | summary: c
362 362 |
363 363 o changeset: 1:ef3a871183d7
364 364 | user: test
365 365 | date: Thu Jan 01 00:00:00 1970 +0000
366 366 | summary: b
367 367 |
368 368 o changeset: 0:9ab35a2d17cb
369 369 user: test
370 370 date: Thu Jan 01 00:00:00 1970 +0000
371 371 summary: a
372 372
373 373 $ restore
374 374 $ hg log -G
375 375 o changeset: 4:443431ffac4f
376 376 | tag: tip
377 377 | user: test
378 378 | date: Thu Jan 01 00:00:00 1970 +0000
379 379 | summary: e
380 380 |
381 381 o changeset: 3:65bd5f99a4a3
382 382 | parent: 1:ef3a871183d7
383 383 | user: test
384 384 | date: Thu Jan 01 00:00:00 1970 +0000
385 385 | summary: d
386 386 |
387 387 | @ changeset: 2:264128213d29
388 388 |/ user: test
389 389 | date: Thu Jan 01 00:00:00 1970 +0000
390 390 | summary: c
391 391 |
392 392 o changeset: 1:ef3a871183d7
393 393 | user: test
394 394 | date: Thu Jan 01 00:00:00 1970 +0000
395 395 | summary: b
396 396 |
397 397 o changeset: 0:9ab35a2d17cb
398 398 user: test
399 399 date: Thu Jan 01 00:00:00 1970 +0000
400 400 summary: a
401 401
402 402
403 403 2 different branches: 2 strips
404 404
405 405 $ hg strip 2 4
406 406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 407 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
408 408 $ hg log -G
409 409 o changeset: 2:65bd5f99a4a3
410 410 | tag: tip
411 411 | user: test
412 412 | date: Thu Jan 01 00:00:00 1970 +0000
413 413 | summary: d
414 414 |
415 415 @ changeset: 1:ef3a871183d7
416 416 | user: test
417 417 | date: Thu Jan 01 00:00:00 1970 +0000
418 418 | summary: b
419 419 |
420 420 o changeset: 0:9ab35a2d17cb
421 421 user: test
422 422 date: Thu Jan 01 00:00:00 1970 +0000
423 423 summary: a
424 424
425 425 $ restore
426 426
427 427 2 different branches and a common ancestor: 1 strip
428 428
429 429 $ hg strip 1 "2|4"
430 430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
431 431 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
432 432 $ restore
433 433
434 434 verify fncache is kept up-to-date
435 435
436 436 $ touch a
437 437 $ hg ci -qAm a
438 438 $ cat .hg/store/fncache | sort
439 439 data/a.i
440 440 data/bar.i
441 441 $ hg strip tip
442 442 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
443 443 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
444 444 $ cat .hg/store/fncache
445 445 data/bar.i
446 446
447 447 stripping an empty revset
448 448
449 449 $ hg strip "1 and not 1"
450 450 abort: empty revision set
451 451 [255]
452 452
453 453 remove branchy history for qimport tests
454 454
455 455 $ hg strip 3
456 456 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
457 457
458 458
459 459 strip of applied mq should cleanup status file
460 460
461 461 $ echo "mq=" >> $HGRCPATH
462 462 $ hg up -C 3
463 463 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
464 464 $ echo fooagain >> bar
465 465 $ hg ci -mf
466 466 $ hg qimport -r tip:2
467 467
468 468 applied patches before strip
469 469
470 470 $ hg qapplied
471 471 2.diff
472 472 3.diff
473 473 4.diff
474 474
475 475 stripping revision in queue
476 476
477 477 $ hg strip 3
478 478 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
479 479 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
480 480
481 481 applied patches after stripping rev in queue
482 482
483 483 $ hg qapplied
484 484 2.diff
485 485
486 486 stripping ancestor of queue
487 487
488 488 $ hg strip 1
489 489 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
490 490 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
491 491
492 492 applied patches after stripping ancestor of queue
493 493
494 494 $ hg qapplied
495 495
496 496 Verify strip protects against stripping wc parent when there are uncommitted mods
497 497
498 498 $ echo b > b
499 499 $ echo bb > bar
500 500 $ hg add b
501 501 $ hg ci -m 'b'
502 502 $ hg log --graph
503 503 @ changeset: 1:76dcf9fab855
504 504 | tag: tip
505 505 | user: test
506 506 | date: Thu Jan 01 00:00:00 1970 +0000
507 507 | summary: b
508 508 |
509 509 o changeset: 0:9ab35a2d17cb
510 510 user: test
511 511 date: Thu Jan 01 00:00:00 1970 +0000
512 512 summary: a
513 513
514 514 $ hg up 0
515 515 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
516 516 $ echo c > bar
517 517 $ hg up -t false
518 518 merging bar
519 519 merging bar failed!
520 520 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
521 521 use 'hg resolve' to retry unresolved file merges
522 522 [1]
523 523 $ hg sum
524 524 parent: 1:76dcf9fab855 tip
525 525 b
526 526 branch: default
527 527 commit: 1 modified, 1 unknown, 1 unresolved
528 528 update: (current)
529 529 phases: 2 draft
530 530 mq: 3 unapplied
531 531
532 532 $ echo c > b
533 533 $ hg strip tip
534 534 abort: local changes found
535 535 [255]
536 536 $ hg strip tip --keep
537 537 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
538 538 $ hg log --graph
539 539 @ changeset: 0:9ab35a2d17cb
540 540 tag: tip
541 541 user: test
542 542 date: Thu Jan 01 00:00:00 1970 +0000
543 543 summary: a
544 544
545 545 $ hg status
546 546 M bar
547 547 ? b
548 548 ? bar.orig
549 549
550 550 $ rm bar.orig
551 551 $ hg sum
552 552 parent: 0:9ab35a2d17cb tip
553 553 a
554 554 branch: default
555 555 commit: 1 modified, 1 unknown
556 556 update: (current)
557 557 phases: 1 draft
558 558 mq: 3 unapplied
559 559
560 560 Strip adds, removes, modifies with --keep
561 561
562 562 $ touch b
563 563 $ hg add b
564 564 $ hg commit -mb
565 565 $ touch c
566 566
567 567 ... with a clean working dir
568 568
569 569 $ hg add c
570 570 $ hg rm bar
571 571 $ hg commit -mc
572 572 $ hg status
573 573 $ hg strip --keep tip
574 574 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
575 575 $ hg status
576 576 ! bar
577 577 ? c
578 578
579 579 ... with a dirty working dir
580 580
581 581 $ hg add c
582 582 $ hg rm bar
583 583 $ hg commit -mc
584 584 $ hg status
585 585 $ echo b > b
586 586 $ echo d > d
587 587 $ hg strip --keep tip
588 588 saved backup bundle to $TESTTMP/test/.hg/strip-backup/57e364c8a475-4cfed93c-backup.hg (glob)
589 589 $ hg status
590 590 M b
591 591 ! bar
592 592 ? c
593 593 ? d
594 594 $ cd ..
595 595
596 596 stripping many nodes on a complex graph (issue3299)
597 597
598 598 $ hg init issue3299
599 599 $ cd issue3299
600 600 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
601 601 $ hg strip 'not ancestors(x)'
602 602 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
603 603
604 604 test hg strip -B bookmark
605 605
606 606 $ cd ..
607 607 $ hg init bookmarks
608 608 $ cd bookmarks
609 609 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b'
610 610 $ hg bookmark -r 'a' 'todelete'
611 611 $ hg bookmark -r 'b' 'B'
612 612 $ hg bookmark -r 'b' 'nostrip'
613 613 $ hg bookmark -r 'c' 'delete'
614 614 $ hg up -C todelete
615 615 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
616 616 (activating bookmark todelete)
617 617 $ hg strip -B nostrip
618 618 bookmark 'nostrip' deleted
619 619 abort: empty revision set
620 620 [255]
621 621 $ hg strip -B todelete
622 622 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
623 623 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
624 624 bookmark 'todelete' deleted
625 625 $ hg id -ir dcbb326fdec2
626 626 abort: unknown revision 'dcbb326fdec2'!
627 627 [255]
628 628 $ hg id -ir d62d843c9a01
629 629 d62d843c9a01
630 630 $ hg bookmarks
631 631 B 9:ff43616e5d0f
632 632 delete 6:2702dd0c91e7
633 633 $ hg strip -B delete
634 634 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
635 635 bookmark 'delete' deleted
636 636 $ hg id -ir 6:2702dd0c91e7
637 637 abort: unknown revision '2702dd0c91e7'!
638 638 [255]
639 639 $ hg update B
640 640 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
641 641 (activating bookmark B)
642 642 $ echo a > a
643 643 $ hg add a
644 644 $ hg strip -B B
645 645 abort: local changes found
646 646 [255]
647 647 $ hg bookmarks
648 648 * B 6:ff43616e5d0f
649 649
650 650 Make sure no one adds back a -b option:
651 651
652 652 $ hg strip -b tip
653 653 hg strip: option -b not recognized
654 654 hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...
655 655
656 656 strip changesets and all their descendants from the repository
657 657
658 658 (use "hg help -e strip" to show help for the strip extension)
659 659
660 660 options ([+] can be repeated):
661 661
662 662 -r --rev REV [+] strip specified revision (optional, can specify revisions
663 663 without this option)
664 664 -f --force force removal of changesets, discard uncommitted changes
665 665 (no backup)
666 666 --no-backup no backups
667 667 -k --keep do not modify working directory during strip
668 668 -B --bookmark VALUE remove revs only reachable from given bookmark
669 669 --mq operate on patch repository
670 670
671 671 (use "hg strip -h" to show more help)
672 672 [255]
673 673
674 674 $ cd ..
675 675
676 676 Verify bundles don't get overwritten:
677 677
678 678 $ hg init doublebundle
679 679 $ cd doublebundle
680 680 $ touch a
681 681 $ hg commit -Aqm a
682 682 $ touch b
683 683 $ hg commit -Aqm b
684 684 $ hg strip -r 0
685 685 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
686 686 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg (glob)
687 687 $ ls .hg/strip-backup
688 688 3903775176ed-e68910bd-backup.hg
689 689 $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg
690 690 $ hg strip -r 0
691 691 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg (glob)
692 692 $ ls .hg/strip-backup
693 693 3903775176ed-54390173-backup.hg
694 694 3903775176ed-e68910bd-backup.hg
695 $ cd ..
696
697 Test that we only bundle the stripped changesets (issue4736)
698 ------------------------------------------------------------
699
700 initialisation (previous repo is empty anyway)
701
702 $ hg init issue4736
703 $ cd issue4736
704 $ echo a > a
705 $ hg add a
706 $ hg commit -m commitA
707 $ echo b > b
708 $ hg add b
709 $ hg commit -m commitB
710 $ echo c > c
711 $ hg add c
712 $ hg commit -m commitC
713 $ hg up 'desc(commitB)'
714 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
715 $ echo d > d
716 $ hg add d
717 $ hg commit -m commitD
718 created new head
719 $ hg up 'desc(commitC)'
720 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
721 $ hg merge 'desc(commitD)'
722 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
723 (branch merge, don't forget to commit)
724 $ hg ci -m 'mergeCD'
725 $ hg log -G
726 @ changeset: 4:d8db9d137221
727 |\ tag: tip
728 | | parent: 2:5c51d8d6557d
729 | | parent: 3:6625a5168474
730 | | user: test
731 | | date: Thu Jan 01 00:00:00 1970 +0000
732 | | summary: mergeCD
733 | |
734 | o changeset: 3:6625a5168474
735 | | parent: 1:eca11cf91c71
736 | | user: test
737 | | date: Thu Jan 01 00:00:00 1970 +0000
738 | | summary: commitD
739 | |
740 o | changeset: 2:5c51d8d6557d
741 |/ user: test
742 | date: Thu Jan 01 00:00:00 1970 +0000
743 | summary: commitC
744 |
745 o changeset: 1:eca11cf91c71
746 | user: test
747 | date: Thu Jan 01 00:00:00 1970 +0000
748 | summary: commitB
749 |
750 o changeset: 0:105141ef12d0
751 user: test
752 date: Thu Jan 01 00:00:00 1970 +0000
753 summary: commitA
754
755
756 Check bundle behavior:
757
758 $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg
759 2 changesets found
760 $ hg log -r 'bundle()' -R ../issue4736.hg
761 changeset: 3:6625a5168474
762 parent: 1:eca11cf91c71
763 user: test
764 date: Thu Jan 01 00:00:00 1970 +0000
765 summary: commitD
766
767 changeset: 4:d8db9d137221
768 tag: tip
769 parent: 2:5c51d8d6557d
770 parent: 3:6625a5168474
771 user: test
772 date: Thu Jan 01 00:00:00 1970 +0000
773 summary: mergeCD
774
775
776 check strip behavior
777
778 $ hg --config extensions.strip= strip 'desc(commitD)' --debug
779 resolving manifests
780 branchmerge: False, force: True, partial: False
781 ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
782 c: other deleted -> r
783 removing c
784 d: other deleted -> r
785 removing d
786 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
787 2 changesets found
788 list of changesets:
789 6625a516847449b6f0fa3737b9ba56e9f0f3032c
790 d8db9d1372214336d2b5570f20ee468d2c72fa8b
791 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob)
792 invalid branchheads cache (served): tip differs
793 truncating cache/rbc-revs-v1 to 24
794 $ hg log -G
795 o changeset: 2:5c51d8d6557d
796 | tag: tip
797 | user: test
798 | date: Thu Jan 01 00:00:00 1970 +0000
799 | summary: commitC
800 |
801 @ changeset: 1:eca11cf91c71
802 | user: test
803 | date: Thu Jan 01 00:00:00 1970 +0000
804 | summary: commitB
805 |
806 o changeset: 0:105141ef12d0
807 user: test
808 date: Thu Jan 01 00:00:00 1970 +0000
809 summary: commitA
810
811
812 strip backup content
813
814 $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg
815 changeset: 3:6625a5168474
816 parent: 1:eca11cf91c71
817 user: test
818 date: Thu Jan 01 00:00:00 1970 +0000
819 summary: commitD
820
821 changeset: 4:d8db9d137221
822 tag: tip
823 parent: 2:5c51d8d6557d
824 parent: 3:6625a5168474
825 user: test
826 date: Thu Jan 01 00:00:00 1970 +0000
827 summary: mergeCD
828
@@ -1,792 +1,793
1 1 #require killdaemons
2 2
3 3 $ cat <<EOF >> $HGRCPATH
4 4 > [extensions]
5 5 > transplant=
6 6 > EOF
7 7
8 8 $ hg init t
9 9 $ cd t
10 10 $ echo r1 > r1
11 11 $ hg ci -Amr1 -d'0 0'
12 12 adding r1
13 13 $ echo r2 > r2
14 14 $ hg ci -Amr2 -d'1 0'
15 15 adding r2
16 16 $ hg up 0
17 17 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
18 18
19 19 $ echo b1 > b1
20 20 $ hg ci -Amb1 -d '0 0'
21 21 adding b1
22 22 created new head
23 23 $ echo b2 > b2
24 24 $ hg ci -Amb2 -d '1 0'
25 25 adding b2
26 26 $ echo b3 > b3
27 27 $ hg ci -Amb3 -d '2 0'
28 28 adding b3
29 29
30 30 $ hg log --template '{rev} {parents} {desc}\n'
31 31 4 b3
32 32 3 b2
33 33 2 0:17ab29e464c6 b1
34 34 1 r2
35 35 0 r1
36 36
37 37 $ hg clone . ../rebase
38 38 updating to branch default
39 39 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 40 $ cd ../rebase
41 41
42 42 $ hg up -C 1
43 43 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
44 44
45 45 rebase b onto r1
46 46 (this also tests that editor is not invoked if '--edit' is not specified)
47 47
48 48 $ HGEDITOR=cat hg transplant -a -b tip
49 49 applying 37a1297eb21b
50 50 37a1297eb21b transplanted to e234d668f844
51 51 applying 722f4667af76
52 52 722f4667af76 transplanted to 539f377d78df
53 53 applying a53251cdf717
54 54 a53251cdf717 transplanted to ffd6818a3975
55 55 $ hg log --template '{rev} {parents} {desc}\n'
56 56 7 b3
57 57 6 b2
58 58 5 1:d11e3596cc1a b1
59 59 4 b3
60 60 3 b2
61 61 2 0:17ab29e464c6 b1
62 62 1 r2
63 63 0 r1
64 64
65 65 test transplanted revset
66 66
67 67 $ hg log -r 'transplanted()' --template '{rev} {parents} {desc}\n'
68 68 5 1:d11e3596cc1a b1
69 69 6 b2
70 70 7 b3
71 71 $ hg help revsets | grep transplanted
72 72 "transplanted([set])"
73 73 Transplanted changesets in set, or all transplanted changesets.
74 74
75 75 test transplanted keyword
76 76
77 77 $ hg log --template '{rev} {transplanted}\n'
78 78 7 a53251cdf717679d1907b289f991534be05c997a
79 79 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
80 80 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
81 81 4
82 82 3
83 83 2
84 84 1
85 85 0
86 86
87 87 test destination() revset predicate with a transplant of a transplant; new
88 88 clone so subsequent rollback isn't affected
89 89 (this also tests that editor is invoked if '--edit' is specified)
90 90
91 91 $ hg clone -q . ../destination
92 92 $ cd ../destination
93 93 $ hg up -Cq 0
94 94 $ hg branch -q b4
95 95 $ hg ci -qm "b4"
96 96 $ hg status --rev "7^1" --rev 7
97 97 A b3
98 98 $ cat > $TESTTMP/checkeditform.sh <<EOF
99 99 > env | grep HGEDITFORM
100 100 > true
101 101 > EOF
102 102 $ cat > $TESTTMP/checkeditform-n-cat.sh <<EOF
103 103 > env | grep HGEDITFORM
104 104 > cat \$*
105 105 > EOF
106 106 $ HGEDITOR="sh $TESTTMP/checkeditform-n-cat.sh" hg transplant --edit 7
107 107 applying ffd6818a3975
108 108 HGEDITFORM=transplant.normal
109 109 b3
110 110
111 111
112 112 HG: Enter commit message. Lines beginning with 'HG:' are removed.
113 113 HG: Leave message empty to abort commit.
114 114 HG: --
115 115 HG: user: test
116 116 HG: branch 'b4'
117 117 HG: added b3
118 118 ffd6818a3975 transplanted to 502236fa76bb
119 119
120 120
121 121 $ hg log -r 'destination()'
122 122 changeset: 5:e234d668f844
123 123 parent: 1:d11e3596cc1a
124 124 user: test
125 125 date: Thu Jan 01 00:00:00 1970 +0000
126 126 summary: b1
127 127
128 128 changeset: 6:539f377d78df
129 129 user: test
130 130 date: Thu Jan 01 00:00:01 1970 +0000
131 131 summary: b2
132 132
133 133 changeset: 7:ffd6818a3975
134 134 user: test
135 135 date: Thu Jan 01 00:00:02 1970 +0000
136 136 summary: b3
137 137
138 138 changeset: 9:502236fa76bb
139 139 branch: b4
140 140 tag: tip
141 141 user: test
142 142 date: Thu Jan 01 00:00:02 1970 +0000
143 143 summary: b3
144 144
145 145 $ hg log -r 'destination(a53251cdf717)'
146 146 changeset: 7:ffd6818a3975
147 147 user: test
148 148 date: Thu Jan 01 00:00:02 1970 +0000
149 149 summary: b3
150 150
151 151 changeset: 9:502236fa76bb
152 152 branch: b4
153 153 tag: tip
154 154 user: test
155 155 date: Thu Jan 01 00:00:02 1970 +0000
156 156 summary: b3
157 157
158 158
159 159 test subset parameter in reverse order
160 160 $ hg log -r 'reverse(all()) and destination(a53251cdf717)'
161 161 changeset: 9:502236fa76bb
162 162 branch: b4
163 163 tag: tip
164 164 user: test
165 165 date: Thu Jan 01 00:00:02 1970 +0000
166 166 summary: b3
167 167
168 168 changeset: 7:ffd6818a3975
169 169 user: test
170 170 date: Thu Jan 01 00:00:02 1970 +0000
171 171 summary: b3
172 172
173 173
174 174 back to the original dir
175 175 $ cd ../rebase
176 176
177 177 rollback the transplant
178 178 $ hg rollback
179 179 repository tip rolled back to revision 4 (undo transplant)
180 180 working directory now based on revision 1
181 181 $ hg tip -q
182 182 4:a53251cdf717
183 183 $ hg parents -q
184 184 1:d11e3596cc1a
185 185 $ hg status
186 186 ? b1
187 187 ? b2
188 188 ? b3
189 189
190 190 $ hg clone ../t ../prune
191 191 updating to branch default
192 192 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 193 $ cd ../prune
194 194
195 195 $ hg up -C 1
196 196 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
197 197
198 198 rebase b onto r1, skipping b2
199 199
200 200 $ hg transplant -a -b tip -p 3
201 201 applying 37a1297eb21b
202 202 37a1297eb21b transplanted to e234d668f844
203 203 applying a53251cdf717
204 204 a53251cdf717 transplanted to 7275fda4d04f
205 205 $ hg log --template '{rev} {parents} {desc}\n'
206 206 6 b3
207 207 5 1:d11e3596cc1a b1
208 208 4 b3
209 209 3 b2
210 210 2 0:17ab29e464c6 b1
211 211 1 r2
212 212 0 r1
213 213
214 214 test same-parent transplant with --log
215 215
216 216 $ hg clone -r 1 ../t ../sameparent
217 217 adding changesets
218 218 adding manifests
219 219 adding file changes
220 220 added 2 changesets with 2 changes to 2 files
221 221 updating to branch default
222 222 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 223 $ cd ../sameparent
224 224 $ hg transplant --log -s ../prune 5
225 225 searching for changes
226 226 applying e234d668f844
227 227 e234d668f844 transplanted to e07aea8ecf9c
228 228 $ hg log --template '{rev} {parents} {desc}\n'
229 229 2 b1
230 230 (transplanted from e234d668f844e1b1a765f01db83a32c0c7bfa170)
231 231 1 r2
232 232 0 r1
233 233 remote transplant, and also test that transplant doesn't break with
234 234 format-breaking diffopts
235 235
236 236 $ hg clone -r 1 ../t ../remote
237 237 adding changesets
238 238 adding manifests
239 239 adding file changes
240 240 added 2 changesets with 2 changes to 2 files
241 241 updating to branch default
242 242 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 243 $ cd ../remote
244 244 $ hg --config diff.noprefix=True transplant --log -s ../t 2 4
245 245 searching for changes
246 246 applying 37a1297eb21b
247 247 37a1297eb21b transplanted to c19cf0ccb069
248 248 applying a53251cdf717
249 249 a53251cdf717 transplanted to f7fe5bf98525
250 250 $ hg log --template '{rev} {parents} {desc}\n'
251 251 3 b3
252 252 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
253 253 2 b1
254 254 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
255 255 1 r2
256 256 0 r1
257 257
258 258 skip previous transplants
259 259
260 260 $ hg transplant -s ../t -a -b 4
261 261 searching for changes
262 262 applying 722f4667af76
263 263 722f4667af76 transplanted to 47156cd86c0b
264 264 $ hg log --template '{rev} {parents} {desc}\n'
265 265 4 b2
266 266 3 b3
267 267 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
268 268 2 b1
269 269 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
270 270 1 r2
271 271 0 r1
272 272
273 273 skip local changes transplanted to the source
274 274
275 275 $ echo b4 > b4
276 276 $ hg ci -Amb4 -d '3 0'
277 277 adding b4
278 278 $ hg clone ../t ../pullback
279 279 updating to branch default
280 280 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
281 281 $ cd ../pullback
282 282 $ hg transplant -s ../remote -a -b tip
283 283 searching for changes
284 284 applying 4333daefcb15
285 285 4333daefcb15 transplanted to 5f42c04e07cc
286 286
287 287
288 288 remote transplant with pull
289 289
290 290 $ hg -R ../t serve -p $HGPORT -d --pid-file=../t.pid
291 291 $ cat ../t.pid >> $DAEMON_PIDS
292 292
293 293 $ hg clone -r 0 ../t ../rp
294 294 adding changesets
295 295 adding manifests
296 296 adding file changes
297 297 added 1 changesets with 1 changes to 1 files
298 298 updating to branch default
299 299 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 300 $ cd ../rp
301 $ hg transplant -s http://localhost:$HGPORT/ 2 4
301 $ hg transplant -s http://localhost:$HGPORT/ 37a1297eb21b a53251cdf717
302 302 searching for changes
303 303 searching for changes
304 304 adding changesets
305 305 adding manifests
306 306 adding file changes
307 307 added 1 changesets with 1 changes to 1 files
308 308 applying a53251cdf717
309 309 a53251cdf717 transplanted to 8d9279348abb
310 310 $ hg log --template '{rev} {parents} {desc}\n'
311 311 2 b3
312 312 1 b1
313 313 0 r1
314 314
315 315 remote transplant without pull
316 (It was using "2" and "4" (as the previous transplant used to) which referenced
317 revision different from one run to another)
316 318
317 319 $ hg pull -q http://localhost:$HGPORT/
318 $ hg transplant -s http://localhost:$HGPORT/ 2 4
319 searching for changes
320 $ hg transplant -s http://localhost:$HGPORT/ 8d9279348abb 722f4667af76
320 321 skipping already applied revision 2:8d9279348abb
321 322 applying 722f4667af76
322 323 722f4667af76 transplanted to 76e321915884
323 324
324 325 transplant --continue
325 326
326 327 $ hg init ../tc
327 328 $ cd ../tc
328 329 $ cat <<EOF > foo
329 330 > foo
330 331 > bar
331 332 > baz
332 333 > EOF
333 334 $ echo toremove > toremove
334 335 $ echo baz > baz
335 336 $ hg ci -Amfoo
336 337 adding baz
337 338 adding foo
338 339 adding toremove
339 340 $ cat <<EOF > foo
340 341 > foo2
341 342 > bar2
342 343 > baz2
343 344 > EOF
344 345 $ rm toremove
345 346 $ echo added > added
346 347 $ hg ci -Amfoo2
347 348 adding added
348 349 removing toremove
349 350 $ echo bar > bar
350 351 $ cat > baz <<EOF
351 352 > before baz
352 353 > baz
353 354 > after baz
354 355 > EOF
355 356 $ hg ci -Ambar
356 357 adding bar
357 358 $ echo bar2 >> bar
358 359 $ hg ci -mbar2
359 360 $ hg up 0
360 361 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
361 362 $ echo foobar > foo
362 363 $ hg ci -mfoobar
363 364 created new head
364 365 $ hg transplant 1:3
365 366 applying 46ae92138f3c
366 367 patching file foo
367 368 Hunk #1 FAILED at 0
368 369 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
369 370 patch failed to apply
370 371 abort: fix up the merge and run hg transplant --continue
371 372 [255]
372 373
373 374 transplant -c shouldn't use an old changeset
374 375
375 376 $ hg up -C
376 377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 378 $ rm added
378 379 $ hg transplant 1
379 380 applying 46ae92138f3c
380 381 patching file foo
381 382 Hunk #1 FAILED at 0
382 383 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
383 384 patch failed to apply
384 385 abort: fix up the merge and run hg transplant --continue
385 386 [255]
386 387 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
387 388 HGEDITFORM=transplant.normal
388 389 46ae92138f3c transplanted as 9159dada197d
389 390 $ hg transplant 1:3
390 391 skipping already applied revision 1:46ae92138f3c
391 392 applying 9d6d6b5a8275
392 393 9d6d6b5a8275 transplanted to 2d17a10c922f
393 394 applying 1dab759070cf
394 395 1dab759070cf transplanted to e06a69927eb0
395 396 $ hg locate
396 397 added
397 398 bar
398 399 baz
399 400 foo
400 401
401 402 test multiple revisions and --continue
402 403
403 404 $ hg up -qC 0
404 405 $ echo bazbaz > baz
405 406 $ hg ci -Am anotherbaz baz
406 407 created new head
407 408 $ hg transplant 1:3
408 409 applying 46ae92138f3c
409 410 46ae92138f3c transplanted to 1024233ea0ba
410 411 applying 9d6d6b5a8275
411 412 patching file baz
412 413 Hunk #1 FAILED at 0
413 414 1 out of 1 hunks FAILED -- saving rejects to file baz.rej
414 415 patch failed to apply
415 416 abort: fix up the merge and run hg transplant --continue
416 417 [255]
417 418 $ echo fixed > baz
418 419 $ hg transplant --continue
419 420 9d6d6b5a8275 transplanted as d80c49962290
420 421 applying 1dab759070cf
421 422 1dab759070cf transplanted to aa0ffe6bd5ae
422 423
423 424 $ cd ..
424 425
425 426 Issue1111: Test transplant --merge
426 427
427 428 $ hg init t1111
428 429 $ cd t1111
429 430 $ echo a > a
430 431 $ hg ci -Am adda
431 432 adding a
432 433 $ echo b >> a
433 434 $ hg ci -m appendb
434 435 $ echo c >> a
435 436 $ hg ci -m appendc
436 437 $ hg up -C 0
437 438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 439 $ echo d >> a
439 440 $ hg ci -m appendd
440 441 created new head
441 442
442 443 transplant
443 444
444 445 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant -m 1 -e
445 446 applying 42dc4432fd35
446 447 HGEDITFORM=transplant.merge
447 448 1:42dc4432fd35 merged at a9f4acbac129
448 449 $ hg update -q -C 2
449 450 $ cat > a <<EOF
450 451 > x
451 452 > y
452 453 > z
453 454 > EOF
454 455 $ hg commit -m replace
455 456 $ hg update -q -C 4
456 457 $ hg transplant -m 5
457 458 applying 600a3cdcb41d
458 459 patching file a
459 460 Hunk #1 FAILED at 0
460 461 1 out of 1 hunks FAILED -- saving rejects to file a.rej
461 462 patch failed to apply
462 463 abort: fix up the merge and run hg transplant --continue
463 464 [255]
464 465 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
465 466 HGEDITFORM=transplant.merge
466 467 600a3cdcb41d transplanted as a3f88be652e0
467 468
468 469 $ cd ..
469 470
470 471 test transplant into empty repository
471 472
472 473 $ hg init empty
473 474 $ cd empty
474 475 $ hg transplant -s ../t -b tip -a
475 476 adding changesets
476 477 adding manifests
477 478 adding file changes
478 479 added 4 changesets with 4 changes to 4 files
479 480
480 481 test "--merge" causing pull from source repository on local host
481 482
482 483 $ hg --config extensions.mq= -q strip 2
483 484 $ hg transplant -s ../t --merge tip
484 485 searching for changes
485 486 searching for changes
486 487 adding changesets
487 488 adding manifests
488 489 adding file changes
489 490 added 2 changesets with 2 changes to 2 files
490 491 applying a53251cdf717
491 492 4:a53251cdf717 merged at 4831f4dc831a
492 493
493 494 test interactive transplant
494 495
495 496 $ hg --config extensions.strip= -q strip 0
496 497 $ hg -R ../t log -G --template "{rev}:{node|short}"
497 498 @ 4:a53251cdf717
498 499 |
499 500 o 3:722f4667af76
500 501 |
501 502 o 2:37a1297eb21b
502 503 |
503 504 | o 1:d11e3596cc1a
504 505 |/
505 506 o 0:17ab29e464c6
506 507
507 508 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
508 509 > p
509 510 > y
510 511 > n
511 512 > n
512 513 > m
513 514 > c
514 515 > EOF
515 516 0:17ab29e464c6
516 517 apply changeset? [ynmpcq?]: p
517 518 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
518 519 +++ b/r1 Thu Jan 01 00:00:00 1970 +0000
519 520 @@ -0,0 +1,1 @@
520 521 +r1
521 522 apply changeset? [ynmpcq?]: y
522 523 1:d11e3596cc1a
523 524 apply changeset? [ynmpcq?]: n
524 525 2:37a1297eb21b
525 526 apply changeset? [ynmpcq?]: n
526 527 3:722f4667af76
527 528 apply changeset? [ynmpcq?]: m
528 529 4:a53251cdf717
529 530 apply changeset? [ynmpcq?]: c
530 531 $ hg log -G --template "{node|short}"
531 532 @ 88be5dde5260
532 533 |\
533 534 | o 722f4667af76
534 535 | |
535 536 | o 37a1297eb21b
536 537 |/
537 538 o 17ab29e464c6
538 539
539 540 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
540 541 > x
541 542 > ?
542 543 > y
543 544 > q
544 545 > EOF
545 546 1:d11e3596cc1a
546 547 apply changeset? [ynmpcq?]: x
547 548 unrecognized response
548 549 apply changeset? [ynmpcq?]: ?
549 550 y: yes, transplant this changeset
550 551 n: no, skip this changeset
551 552 m: merge at this changeset
552 553 p: show patch
553 554 c: commit selected changesets
554 555 q: quit and cancel transplant
555 556 ?: ? (show this help)
556 557 apply changeset? [ynmpcq?]: y
557 558 4:a53251cdf717
558 559 apply changeset? [ynmpcq?]: q
559 560 $ hg heads --template "{node|short}\n"
560 561 88be5dde5260
561 562
562 563 $ cd ..
563 564
564 565
565 566 #if unix-permissions system-sh
566 567
567 568 test filter
568 569
569 570 $ hg init filter
570 571 $ cd filter
571 572 $ cat <<'EOF' >test-filter
572 573 > #!/bin/sh
573 574 > sed 's/r1/r2/' $1 > $1.new
574 575 > mv $1.new $1
575 576 > EOF
576 577 $ chmod +x test-filter
577 578 $ hg transplant -s ../t -b tip -a --filter ./test-filter
578 579 filtering * (glob)
579 580 applying 17ab29e464c6
580 581 17ab29e464c6 transplanted to e9ffc54ea104
581 582 filtering * (glob)
582 583 applying 37a1297eb21b
583 584 37a1297eb21b transplanted to 348b36d0b6a5
584 585 filtering * (glob)
585 586 applying 722f4667af76
586 587 722f4667af76 transplanted to 0aa6979afb95
587 588 filtering * (glob)
588 589 applying a53251cdf717
589 590 a53251cdf717 transplanted to 14f8512272b5
590 591 $ hg log --template '{rev} {parents} {desc}\n'
591 592 3 b3
592 593 2 b2
593 594 1 b1
594 595 0 r2
595 596 $ cd ..
596 597
597 598
598 599 test filter with failed patch
599 600
600 601 $ cd filter
601 602 $ hg up 0
602 603 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
603 604 $ echo foo > b1
604 605 $ hg ci -Am foo
605 606 adding b1
606 607 adding test-filter
607 608 created new head
608 609 $ hg transplant 1 --filter ./test-filter
609 610 filtering * (glob)
610 611 applying 348b36d0b6a5
611 612 file b1 already exists
612 613 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
613 614 patch failed to apply
614 615 abort: fix up the merge and run hg transplant --continue
615 616 [255]
616 617 $ cd ..
617 618
618 619 test environment passed to filter
619 620
620 621 $ hg init filter-environment
621 622 $ cd filter-environment
622 623 $ cat <<'EOF' >test-filter-environment
623 624 > #!/bin/sh
624 625 > echo "Transplant by $HGUSER" >> $1
625 626 > echo "Transplant from rev $HGREVISION" >> $1
626 627 > EOF
627 628 $ chmod +x test-filter-environment
628 629 $ hg transplant -s ../t --filter ./test-filter-environment 0
629 630 filtering * (glob)
630 631 applying 17ab29e464c6
631 632 17ab29e464c6 transplanted to 5190e68026a0
632 633
633 634 $ hg log --template '{rev} {parents} {desc}\n'
634 635 0 r1
635 636 Transplant by test
636 637 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
637 638 $ cd ..
638 639
639 640 test transplant with filter handles invalid changelog
640 641
641 642 $ hg init filter-invalid-log
642 643 $ cd filter-invalid-log
643 644 $ cat <<'EOF' >test-filter-invalid-log
644 645 > #!/bin/sh
645 646 > echo "" > $1
646 647 > EOF
647 648 $ chmod +x test-filter-invalid-log
648 649 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
649 650 filtering * (glob)
650 651 abort: filter corrupted changeset (no user or date)
651 652 [255]
652 653 $ cd ..
653 654
654 655 #endif
655 656
656 657
657 658 test with a win32ext like setup (differing EOLs)
658 659
659 660 $ hg init twin1
660 661 $ cd twin1
661 662 $ echo a > a
662 663 $ echo b > b
663 664 $ echo b >> b
664 665 $ hg ci -Am t
665 666 adding a
666 667 adding b
667 668 $ echo a > b
668 669 $ echo b >> b
669 670 $ hg ci -m changeb
670 671 $ cd ..
671 672
672 673 $ hg init twin2
673 674 $ cd twin2
674 675 $ echo '[patch]' >> .hg/hgrc
675 676 $ echo 'eol = crlf' >> .hg/hgrc
676 677 $ $PYTHON -c "file('b', 'wb').write('b\r\nb\r\n')"
677 678 $ hg ci -Am addb
678 679 adding b
679 680 $ hg transplant -s ../twin1 tip
680 681 searching for changes
681 682 warning: repository is unrelated
682 683 applying 2e849d776c17
683 684 2e849d776c17 transplanted to 8e65bebc063e
684 685 $ cat b
685 686 a\r (esc)
686 687 b\r (esc)
687 688 $ cd ..
688 689
689 690 test transplant with merge changeset is skipped
690 691
691 692 $ hg init merge1a
692 693 $ cd merge1a
693 694 $ echo a > a
694 695 $ hg ci -Am a
695 696 adding a
696 697 $ hg branch b
697 698 marked working directory as branch b
698 699 (branches are permanent and global, did you want a bookmark?)
699 700 $ hg ci -m branchb
700 701 $ echo b > b
701 702 $ hg ci -Am b
702 703 adding b
703 704 $ hg update default
704 705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
705 706 $ hg merge b
706 707 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
707 708 (branch merge, don't forget to commit)
708 709 $ hg ci -m mergeb
709 710 $ cd ..
710 711
711 712 $ hg init merge1b
712 713 $ cd merge1b
713 714 $ hg transplant -s ../merge1a tip
714 715 $ cd ..
715 716
716 717 test transplant with merge changeset accepts --parent
717 718
718 719 $ hg init merge2a
719 720 $ cd merge2a
720 721 $ echo a > a
721 722 $ hg ci -Am a
722 723 adding a
723 724 $ hg branch b
724 725 marked working directory as branch b
725 726 (branches are permanent and global, did you want a bookmark?)
726 727 $ hg ci -m branchb
727 728 $ echo b > b
728 729 $ hg ci -Am b
729 730 adding b
730 731 $ hg update default
731 732 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
732 733 $ hg merge b
733 734 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
734 735 (branch merge, don't forget to commit)
735 736 $ hg ci -m mergeb
736 737 $ cd ..
737 738
738 739 $ hg init merge2b
739 740 $ cd merge2b
740 741 $ hg transplant -s ../merge2a --parent 0 tip
741 742 applying be9f9b39483f
742 743 be9f9b39483f transplanted to 9959e51f94d1
743 744 $ cd ..
744 745
745 746 test transplanting a patch turning into a no-op
746 747
747 748 $ hg init binarysource
748 749 $ cd binarysource
749 750 $ echo a > a
750 751 $ hg ci -Am adda a
751 752 >>> file('b', 'wb').write('\0b1')
752 753 $ hg ci -Am addb b
753 754 >>> file('b', 'wb').write('\0b2')
754 755 $ hg ci -m changeb b
755 756 $ cd ..
756 757
757 758 $ hg clone -r0 binarysource binarydest
758 759 adding changesets
759 760 adding manifests
760 761 adding file changes
761 762 added 1 changesets with 1 changes to 1 files
762 763 updating to branch default
763 764 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
764 765 $ cd binarydest
765 766 $ cp ../binarysource/b b
766 767 $ hg ci -Am addb2 b
767 768 $ hg transplant -s ../binarysource 2
768 769 searching for changes
769 770 applying 7a7d57e15850
770 771 skipping emptied changeset 7a7d57e15850
771 772
772 773 Test empty result in --continue
773 774
774 775 $ hg transplant -s ../binarysource 1
775 776 searching for changes
776 777 applying 645035761929
777 778 file b already exists
778 779 1 out of 1 hunks FAILED -- saving rejects to file b.rej
779 780 patch failed to apply
780 781 abort: fix up the merge and run hg transplant --continue
781 782 [255]
782 783 $ hg status
783 784 ? b.rej
784 785 $ hg transplant --continue
785 786 645035761929 skipped due to empty diff
786 787
787 788 $ cd ..
788 789
789 790 Explicitly kill daemons to let the test exit on Windows
790 791
791 792 $ killdaemons.py
792 793
General Comments 0
You need to be logged in to leave comments. Login now