comparison mercurial/logcmdutil.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children 90b9a7e06c2c
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
42 ) 42 )
43 43
44 44
45 def getlimit(opts): 45 def getlimit(opts):
46 """get the log limit according to option -l/--limit""" 46 """get the log limit according to option -l/--limit"""
47 limit = opts.get('limit') 47 limit = opts.get(b'limit')
48 if limit: 48 if limit:
49 try: 49 try:
50 limit = int(limit) 50 limit = int(limit)
51 except ValueError: 51 except ValueError:
52 raise error.Abort(_('limit must be a positive integer')) 52 raise error.Abort(_(b'limit must be a positive integer'))
53 if limit <= 0: 53 if limit <= 0:
54 raise error.Abort(_('limit must be positive')) 54 raise error.Abort(_(b'limit must be positive'))
55 else: 55 else:
56 limit = None 56 limit = None
57 return limit 57 return limit
58 58
59 59
66 match, 66 match,
67 changes=None, 67 changes=None,
68 stat=False, 68 stat=False,
69 fp=None, 69 fp=None,
70 graphwidth=0, 70 graphwidth=0,
71 prefix='', 71 prefix=b'',
72 root='', 72 root=b'',
73 listsubrepos=False, 73 listsubrepos=False,
74 hunksfilterfn=None, 74 hunksfilterfn=None,
75 ): 75 ):
76 '''show diff or diffstat.''' 76 '''show diff or diffstat.'''
77 ctx1 = repo[node1] 77 ctx1 = repo[node1]
78 ctx2 = repo[node2] 78 ctx2 = repo[node2]
79 if root: 79 if root:
80 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root) 80 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
81 else: 81 else:
82 relroot = '' 82 relroot = b''
83 copysourcematch = None 83 copysourcematch = None
84 84
85 def compose(f, g): 85 def compose(f, g):
86 return lambda x: f(g(x)) 86 return lambda x: f(g(x))
87 87
88 def pathfn(f): 88 def pathfn(f):
89 return posixpath.join(prefix, f) 89 return posixpath.join(prefix, f)
90 90
91 if relroot != '': 91 if relroot != b'':
92 # XXX relative roots currently don't work if the root is within a 92 # XXX relative roots currently don't work if the root is within a
93 # subrepo 93 # subrepo
94 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) 94 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
95 uirelroot = uipathfn(pathfn(relroot)) 95 uirelroot = uipathfn(pathfn(relroot))
96 relroot += '/' 96 relroot += b'/'
97 for matchroot in match.files(): 97 for matchroot in match.files():
98 if not matchroot.startswith(relroot): 98 if not matchroot.startswith(relroot):
99 ui.warn( 99 ui.warn(
100 _('warning: %s not inside relative root %s\n') 100 _(b'warning: %s not inside relative root %s\n')
101 % (uipathfn(pathfn(matchroot)), uirelroot) 101 % (uipathfn(pathfn(matchroot)), uirelroot)
102 ) 102 )
103 103
104 relrootmatch = scmutil.match(ctx2, pats=[relroot], default='path') 104 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
105 match = matchmod.intersectmatchers(match, relrootmatch) 105 match = matchmod.intersectmatchers(match, relrootmatch)
106 copysourcematch = relrootmatch 106 copysourcematch = relrootmatch
107 107
108 checkroot = repo.ui.configbool( 108 checkroot = repo.ui.configbool(
109 'devel', 'all-warnings' 109 b'devel', b'all-warnings'
110 ) or repo.ui.configbool('devel', 'check-relroot') 110 ) or repo.ui.configbool(b'devel', b'check-relroot')
111 111
112 def relrootpathfn(f): 112 def relrootpathfn(f):
113 if checkroot and not f.startswith(relroot): 113 if checkroot and not f.startswith(relroot):
114 raise AssertionError( 114 raise AssertionError(
115 "file %s doesn't start with relroot %s" % (f, relroot) 115 b"file %s doesn't start with relroot %s" % (f, relroot)
116 ) 116 )
117 return f[len(relroot) :] 117 return f[len(relroot) :]
118 118
119 pathfn = compose(relrootpathfn, pathfn) 119 pathfn = compose(relrootpathfn, pathfn)
120 120
212 hunksfilterfn=self._makehunksfilter(ctx), 212 hunksfilterfn=self._makehunksfilter(ctx),
213 ) 213 )
214 214
215 215
216 def changesetlabels(ctx): 216 def changesetlabels(ctx):
217 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()] 217 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
218 if ctx.obsolete(): 218 if ctx.obsolete():
219 labels.append('changeset.obsolete') 219 labels.append(b'changeset.obsolete')
220 if ctx.isunstable(): 220 if ctx.isunstable():
221 labels.append('changeset.unstable') 221 labels.append(b'changeset.unstable')
222 for instability in ctx.instabilities(): 222 for instability in ctx.instabilities():
223 labels.append('instability.%s' % instability) 223 labels.append(b'instability.%s' % instability)
224 return ' '.join(labels) 224 return b' '.join(labels)
225 225
226 226
227 class changesetprinter(object): 227 class changesetprinter(object):
228 '''show changeset information when templating not requested.''' 228 '''show changeset information when templating not requested.'''
229 229
231 self.ui = ui 231 self.ui = ui
232 self.repo = repo 232 self.repo = repo
233 self.buffered = buffered 233 self.buffered = buffered
234 self._differ = differ or changesetdiffer() 234 self._differ = differ or changesetdiffer()
235 self._diffopts = patch.diffallopts(ui, diffopts) 235 self._diffopts = patch.diffallopts(ui, diffopts)
236 self._includestat = diffopts and diffopts.get('stat') 236 self._includestat = diffopts and diffopts.get(b'stat')
237 self._includediff = diffopts and diffopts.get('patch') 237 self._includediff = diffopts and diffopts.get(b'patch')
238 self.header = {} 238 self.header = {}
239 self.hunk = {} 239 self.hunk = {}
240 self.lastheader = None 240 self.lastheader = None
241 self.footer = None 241 self.footer = None
242 self._columns = templatekw.getlogcolumns() 242 self._columns = templatekw.getlogcolumns()
267 self._show(ctx, copies, props) 267 self._show(ctx, copies, props)
268 268
269 def _show(self, ctx, copies, props): 269 def _show(self, ctx, copies, props):
270 '''show a single changeset or file revision''' 270 '''show a single changeset or file revision'''
271 changenode = ctx.node() 271 changenode = ctx.node()
272 graphwidth = props.get('graphwidth', 0) 272 graphwidth = props.get(b'graphwidth', 0)
273 273
274 if self.ui.quiet: 274 if self.ui.quiet:
275 self.ui.write( 275 self.ui.write(
276 "%s\n" % scmutil.formatchangeid(ctx), label='log.node' 276 b"%s\n" % scmutil.formatchangeid(ctx), label=b'log.node'
277 ) 277 )
278 return 278 return
279 279
280 columns = self._columns 280 columns = self._columns
281 self.ui.write( 281 self.ui.write(
282 columns['changeset'] % scmutil.formatchangeid(ctx), 282 columns[b'changeset'] % scmutil.formatchangeid(ctx),
283 label=changesetlabels(ctx), 283 label=changesetlabels(ctx),
284 ) 284 )
285 285
286 # branches are shown first before any other names due to backwards 286 # branches are shown first before any other names due to backwards
287 # compatibility 287 # compatibility
288 branch = ctx.branch() 288 branch = ctx.branch()
289 # don't show the default branch name 289 # don't show the default branch name
290 if branch != 'default': 290 if branch != b'default':
291 self.ui.write(columns['branch'] % branch, label='log.branch') 291 self.ui.write(columns[b'branch'] % branch, label=b'log.branch')
292 292
293 for nsname, ns in self.repo.names.iteritems(): 293 for nsname, ns in self.repo.names.iteritems():
294 # branches has special logic already handled above, so here we just 294 # branches has special logic already handled above, so here we just
295 # skip it 295 # skip it
296 if nsname == 'branches': 296 if nsname == b'branches':
297 continue 297 continue
298 # we will use the templatename as the color name since those two 298 # we will use the templatename as the color name since those two
299 # should be the same 299 # should be the same
300 for name in ns.names(self.repo, changenode): 300 for name in ns.names(self.repo, changenode):
301 self.ui.write(ns.logfmt % name, label='log.%s' % ns.colorname) 301 self.ui.write(ns.logfmt % name, label=b'log.%s' % ns.colorname)
302 if self.ui.debugflag: 302 if self.ui.debugflag:
303 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase') 303 self.ui.write(
304 columns[b'phase'] % ctx.phasestr(), label=b'log.phase'
305 )
304 for pctx in scmutil.meaningfulparents(self.repo, ctx): 306 for pctx in scmutil.meaningfulparents(self.repo, ctx):
305 label = 'log.parent changeset.%s' % pctx.phasestr() 307 label = b'log.parent changeset.%s' % pctx.phasestr()
306 self.ui.write( 308 self.ui.write(
307 columns['parent'] % scmutil.formatchangeid(pctx), label=label 309 columns[b'parent'] % scmutil.formatchangeid(pctx), label=label
308 ) 310 )
309 311
310 if self.ui.debugflag: 312 if self.ui.debugflag:
311 mnode = ctx.manifestnode() 313 mnode = ctx.manifestnode()
312 if mnode is None: 314 if mnode is None:
313 mnode = wdirid 315 mnode = wdirid
314 mrev = wdirrev 316 mrev = wdirrev
315 else: 317 else:
316 mrev = self.repo.manifestlog.rev(mnode) 318 mrev = self.repo.manifestlog.rev(mnode)
317 self.ui.write( 319 self.ui.write(
318 columns['manifest'] 320 columns[b'manifest']
319 % scmutil.formatrevnode(self.ui, mrev, mnode), 321 % scmutil.formatrevnode(self.ui, mrev, mnode),
320 label='ui.debug log.manifest', 322 label=b'ui.debug log.manifest',
321 ) 323 )
322 self.ui.write(columns['user'] % ctx.user(), label='log.user') 324 self.ui.write(columns[b'user'] % ctx.user(), label=b'log.user')
323 self.ui.write( 325 self.ui.write(
324 columns['date'] % dateutil.datestr(ctx.date()), label='log.date' 326 columns[b'date'] % dateutil.datestr(ctx.date()), label=b'log.date'
325 ) 327 )
326 328
327 if ctx.isunstable(): 329 if ctx.isunstable():
328 instabilities = ctx.instabilities() 330 instabilities = ctx.instabilities()
329 self.ui.write( 331 self.ui.write(
330 columns['instability'] % ', '.join(instabilities), 332 columns[b'instability'] % b', '.join(instabilities),
331 label='log.instability', 333 label=b'log.instability',
332 ) 334 )
333 335
334 elif ctx.obsolete(): 336 elif ctx.obsolete():
335 self._showobsfate(ctx) 337 self._showobsfate(ctx)
336 338
337 self._exthook(ctx) 339 self._exthook(ctx)
338 340
339 if self.ui.debugflag: 341 if self.ui.debugflag:
340 files = ctx.p1().status(ctx)[:3] 342 files = ctx.p1().status(ctx)[:3]
341 for key, value in zip(['files', 'files+', 'files-'], files): 343 for key, value in zip([b'files', b'files+', b'files-'], files):
342 if value: 344 if value:
343 self.ui.write( 345 self.ui.write(
344 columns[key] % " ".join(value), 346 columns[key] % b" ".join(value),
345 label='ui.debug log.files', 347 label=b'ui.debug log.files',
346 ) 348 )
347 elif ctx.files() and self.ui.verbose: 349 elif ctx.files() and self.ui.verbose:
348 self.ui.write( 350 self.ui.write(
349 columns['files'] % " ".join(ctx.files()), 351 columns[b'files'] % b" ".join(ctx.files()),
350 label='ui.note log.files', 352 label=b'ui.note log.files',
351 ) 353 )
352 if copies and self.ui.verbose: 354 if copies and self.ui.verbose:
353 copies = ['%s (%s)' % c for c in copies] 355 copies = [b'%s (%s)' % c for c in copies]
354 self.ui.write( 356 self.ui.write(
355 columns['copies'] % ' '.join(copies), label='ui.note log.copies' 357 columns[b'copies'] % b' '.join(copies),
358 label=b'ui.note log.copies',
356 ) 359 )
357 360
358 extra = ctx.extra() 361 extra = ctx.extra()
359 if extra and self.ui.debugflag: 362 if extra and self.ui.debugflag:
360 for key, value in sorted(extra.items()): 363 for key, value in sorted(extra.items()):
361 self.ui.write( 364 self.ui.write(
362 columns['extra'] % (key, stringutil.escapestr(value)), 365 columns[b'extra'] % (key, stringutil.escapestr(value)),
363 label='ui.debug log.extra', 366 label=b'ui.debug log.extra',
364 ) 367 )
365 368
366 description = ctx.description().strip() 369 description = ctx.description().strip()
367 if description: 370 if description:
368 if self.ui.verbose: 371 if self.ui.verbose:
369 self.ui.write( 372 self.ui.write(
370 _("description:\n"), label='ui.note log.description' 373 _(b"description:\n"), label=b'ui.note log.description'
371 ) 374 )
372 self.ui.write(description, label='ui.note log.description') 375 self.ui.write(description, label=b'ui.note log.description')
373 self.ui.write("\n\n") 376 self.ui.write(b"\n\n")
374 else: 377 else:
375 self.ui.write( 378 self.ui.write(
376 columns['summary'] % description.splitlines()[0], 379 columns[b'summary'] % description.splitlines()[0],
377 label='log.summary', 380 label=b'log.summary',
378 ) 381 )
379 self.ui.write("\n") 382 self.ui.write(b"\n")
380 383
381 self._showpatch(ctx, graphwidth) 384 self._showpatch(ctx, graphwidth)
382 385
383 def _showobsfate(self, ctx): 386 def _showobsfate(self, ctx):
384 # TODO: do not depend on templater 387 # TODO: do not depend on templater
385 tres = formatter.templateresources(self.repo.ui, self.repo) 388 tres = formatter.templateresources(self.repo.ui, self.repo)
386 t = formatter.maketemplater( 389 t = formatter.maketemplater(
387 self.repo.ui, 390 self.repo.ui,
388 '{join(obsfate, "\n")}', 391 b'{join(obsfate, "\n")}',
389 defaults=templatekw.keywords, 392 defaults=templatekw.keywords,
390 resources=tres, 393 resources=tres,
391 ) 394 )
392 obsfate = t.renderdefault({'ctx': ctx}).splitlines() 395 obsfate = t.renderdefault({b'ctx': ctx}).splitlines()
393 396
394 if obsfate: 397 if obsfate:
395 for obsfateline in obsfate: 398 for obsfateline in obsfate:
396 self.ui.write( 399 self.ui.write(
397 self._columns['obsolete'] % obsfateline, label='log.obsfate' 400 self._columns[b'obsolete'] % obsfateline,
401 label=b'log.obsfate',
398 ) 402 )
399 403
400 def _exthook(self, ctx): 404 def _exthook(self, ctx):
401 '''empty method used by extension as a hook point 405 '''empty method used by extension as a hook point
402 ''' 406 '''
405 if self._includestat: 409 if self._includestat:
406 self._differ.showdiff( 410 self._differ.showdiff(
407 self.ui, ctx, self._diffopts, graphwidth, stat=True 411 self.ui, ctx, self._diffopts, graphwidth, stat=True
408 ) 412 )
409 if self._includestat and self._includediff: 413 if self._includestat and self._includediff:
410 self.ui.write("\n") 414 self.ui.write(b"\n")
411 if self._includediff: 415 if self._includediff:
412 self._differ.showdiff( 416 self._differ.showdiff(
413 self.ui, ctx, self._diffopts, graphwidth, stat=False 417 self.ui, ctx, self._diffopts, graphwidth, stat=False
414 ) 418 )
415 if self._includestat or self._includediff: 419 if self._includestat or self._includediff:
416 self.ui.write("\n") 420 self.ui.write(b"\n")
417 421
418 422
419 class changesetformatter(changesetprinter): 423 class changesetformatter(changesetprinter):
420 """Format changeset information by generic formatter""" 424 """Format changeset information by generic formatter"""
421 425
443 branch=ctx.branch(), 447 branch=ctx.branch(),
444 phase=ctx.phasestr(), 448 phase=ctx.phasestr(),
445 user=ctx.user(), 449 user=ctx.user(),
446 date=fm.formatdate(ctx.date()), 450 date=fm.formatdate(ctx.date()),
447 desc=ctx.description(), 451 desc=ctx.description(),
448 bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'), 452 bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'),
449 tags=fm.formatlist(ctx.tags(), name='tag'), 453 tags=fm.formatlist(ctx.tags(), name=b'tag'),
450 parents=fm.formatlist( 454 parents=fm.formatlist(
451 [fm.hexfunc(c.node()) for c in ctx.parents()], name='node' 455 [fm.hexfunc(c.node()) for c in ctx.parents()], name=b'node'
452 ), 456 ),
453 ) 457 )
454 458
455 if self.ui.debugflag: 459 if self.ui.debugflag:
456 fm.data( 460 fm.data(
458 extra=fm.formatdict(ctx.extra()), 462 extra=fm.formatdict(ctx.extra()),
459 ) 463 )
460 464
461 files = ctx.p1().status(ctx) 465 files = ctx.p1().status(ctx)
462 fm.data( 466 fm.data(
463 modified=fm.formatlist(files[0], name='file'), 467 modified=fm.formatlist(files[0], name=b'file'),
464 added=fm.formatlist(files[1], name='file'), 468 added=fm.formatlist(files[1], name=b'file'),
465 removed=fm.formatlist(files[2], name='file'), 469 removed=fm.formatlist(files[2], name=b'file'),
466 ) 470 )
467 471
468 elif self.ui.verbose: 472 elif self.ui.verbose:
469 fm.data(files=fm.formatlist(ctx.files(), name='file')) 473 fm.data(files=fm.formatlist(ctx.files(), name=b'file'))
470 if copies: 474 if copies:
471 fm.data( 475 fm.data(
472 copies=fm.formatdict(copies, key='name', value='source') 476 copies=fm.formatdict(copies, key=b'name', value=b'source')
473 ) 477 )
474 478
475 if self._includestat: 479 if self._includestat:
476 self.ui.pushbuffer() 480 self.ui.pushbuffer()
477 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True) 481 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
508 ) 512 )
509 self._counter = itertools.count() 513 self._counter = itertools.count()
510 514
511 self._tref = tmplspec.ref 515 self._tref = tmplspec.ref
512 self._parts = { 516 self._parts = {
513 'header': '', 517 b'header': b'',
514 'footer': '', 518 b'footer': b'',
515 tmplspec.ref: tmplspec.ref, 519 tmplspec.ref: tmplspec.ref,
516 'docheader': '', 520 b'docheader': b'',
517 'docfooter': '', 521 b'docfooter': b'',
518 'separator': '', 522 b'separator': b'',
519 } 523 }
520 if tmplspec.mapfile: 524 if tmplspec.mapfile:
521 # find correct templates for current mode, for backward 525 # find correct templates for current mode, for backward
522 # compatibility with 'log -v/-q/--debug' using a mapfile 526 # compatibility with 'log -v/-q/--debug' using a mapfile
523 tmplmodes = [ 527 tmplmodes = [
524 (True, ''), 528 (True, b''),
525 (self.ui.verbose, '_verbose'), 529 (self.ui.verbose, b'_verbose'),
526 (self.ui.quiet, '_quiet'), 530 (self.ui.quiet, b'_quiet'),
527 (self.ui.debugflag, '_debug'), 531 (self.ui.debugflag, b'_debug'),
528 ] 532 ]
529 for mode, postfix in tmplmodes: 533 for mode, postfix in tmplmodes:
530 for t in self._parts: 534 for t in self._parts:
531 cur = t + postfix 535 cur = t + postfix
532 if mode and cur in self.t: 536 if mode and cur in self.t:
534 else: 538 else:
535 partnames = [p for p in self._parts.keys() if p != tmplspec.ref] 539 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
536 m = formatter.templatepartsmap(tmplspec, self.t, partnames) 540 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
537 self._parts.update(m) 541 self._parts.update(m)
538 542
539 if self._parts['docheader']: 543 if self._parts[b'docheader']:
540 self.ui.write(self.t.render(self._parts['docheader'], {})) 544 self.ui.write(self.t.render(self._parts[b'docheader'], {}))
541 545
542 def close(self): 546 def close(self):
543 if self._parts['docfooter']: 547 if self._parts[b'docfooter']:
544 if not self.footer: 548 if not self.footer:
545 self.footer = "" 549 self.footer = b""
546 self.footer += self.t.render(self._parts['docfooter'], {}) 550 self.footer += self.t.render(self._parts[b'docfooter'], {})
547 return super(changesettemplater, self).close() 551 return super(changesettemplater, self).close()
548 552
549 def _show(self, ctx, copies, props): 553 def _show(self, ctx, copies, props):
550 '''show a single changeset or file revision''' 554 '''show a single changeset or file revision'''
551 props = props.copy() 555 props = props.copy()
552 props['ctx'] = ctx 556 props[b'ctx'] = ctx
553 props['index'] = index = next(self._counter) 557 props[b'index'] = index = next(self._counter)
554 props['revcache'] = {'copies': copies} 558 props[b'revcache'] = {b'copies': copies}
555 graphwidth = props.get('graphwidth', 0) 559 graphwidth = props.get(b'graphwidth', 0)
556 560
557 # write separator, which wouldn't work well with the header part below 561 # write separator, which wouldn't work well with the header part below
558 # since there's inherently a conflict between header (across items) and 562 # since there's inherently a conflict between header (across items) and
559 # separator (per item) 563 # separator (per item)
560 if self._parts['separator'] and index > 0: 564 if self._parts[b'separator'] and index > 0:
561 self.ui.write(self.t.render(self._parts['separator'], {})) 565 self.ui.write(self.t.render(self._parts[b'separator'], {}))
562 566
563 # write header 567 # write header
564 if self._parts['header']: 568 if self._parts[b'header']:
565 h = self.t.render(self._parts['header'], props) 569 h = self.t.render(self._parts[b'header'], props)
566 if self.buffered: 570 if self.buffered:
567 self.header[ctx.rev()] = h 571 self.header[ctx.rev()] = h
568 else: 572 else:
569 if self.lastheader != h: 573 if self.lastheader != h:
570 self.lastheader = h 574 self.lastheader = h
573 # write changeset metadata, then patch if requested 577 # write changeset metadata, then patch if requested
574 key = self._parts[self._tref] 578 key = self._parts[self._tref]
575 self.ui.write(self.t.render(key, props)) 579 self.ui.write(self.t.render(key, props))
576 self._showpatch(ctx, graphwidth) 580 self._showpatch(ctx, graphwidth)
577 581
578 if self._parts['footer']: 582 if self._parts[b'footer']:
579 if not self.footer: 583 if not self.footer:
580 self.footer = self.t.render(self._parts['footer'], props) 584 self.footer = self.t.render(self._parts[b'footer'], props)
581 585
582 586
583 def templatespec(tmpl, mapfile): 587 def templatespec(tmpl, mapfile):
584 if pycompat.ispy3: 588 if pycompat.ispy3:
585 assert not isinstance(tmpl, str), 'tmpl must not be a str' 589 assert not isinstance(tmpl, str), b'tmpl must not be a str'
586 if mapfile: 590 if mapfile:
587 return formatter.templatespec('changeset', tmpl, mapfile) 591 return formatter.templatespec(b'changeset', tmpl, mapfile)
588 else: 592 else:
589 return formatter.templatespec('', tmpl, None) 593 return formatter.templatespec(b'', tmpl, None)
590 594
591 595
592 def _lookuptemplate(ui, tmpl, style): 596 def _lookuptemplate(ui, tmpl, style):
593 """Find the template matching the given template spec or style 597 """Find the template matching the given template spec or style
594 598
595 See formatter.lookuptemplate() for details. 599 See formatter.lookuptemplate() for details.
596 """ 600 """
597 601
598 # ui settings 602 # ui settings
599 if not tmpl and not style: # template are stronger than style 603 if not tmpl and not style: # template are stronger than style
600 tmpl = ui.config('ui', 'logtemplate') 604 tmpl = ui.config(b'ui', b'logtemplate')
601 if tmpl: 605 if tmpl:
602 return templatespec(templater.unquotestring(tmpl), None) 606 return templatespec(templater.unquotestring(tmpl), None)
603 else: 607 else:
604 style = util.expandpath(ui.config('ui', 'style')) 608 style = util.expandpath(ui.config(b'ui', b'style'))
605 609
606 if not tmpl and style: 610 if not tmpl and style:
607 mapfile = style 611 mapfile = style
608 if not os.path.split(mapfile)[0]: 612 if not os.path.split(mapfile)[0]:
609 mapname = templater.templatepath( 613 mapname = templater.templatepath(
610 'map-cmdline.' + mapfile 614 b'map-cmdline.' + mapfile
611 ) or templater.templatepath(mapfile) 615 ) or templater.templatepath(mapfile)
612 if mapname: 616 if mapname:
613 mapfile = mapname 617 mapfile = mapname
614 return templatespec(None, mapfile) 618 return templatespec(None, mapfile)
615 619
616 if not tmpl: 620 if not tmpl:
617 return templatespec(None, None) 621 return templatespec(None, None)
618 622
619 return formatter.lookuptemplate(ui, 'changeset', tmpl) 623 return formatter.lookuptemplate(ui, b'changeset', tmpl)
620 624
621 625
622 def maketemplater(ui, repo, tmpl, buffered=False): 626 def maketemplater(ui, repo, tmpl, buffered=False):
623 """Create a changesettemplater from a literal template 'tmpl' 627 """Create a changesettemplater from a literal template 'tmpl'
624 byte-string.""" 628 byte-string."""
636 4. [ui] setting 'style' 640 4. [ui] setting 'style'
637 If all of these values are either the unset or the empty string, 641 If all of these values are either the unset or the empty string,
638 regular display via changesetprinter() is done. 642 regular display via changesetprinter() is done.
639 """ 643 """
640 postargs = (differ, opts, buffered) 644 postargs = (differ, opts, buffered)
641 if opts.get('template') in {'cbor', 'json'}: 645 if opts.get(b'template') in {b'cbor', b'json'}:
642 fm = ui.formatter('log', opts) 646 fm = ui.formatter(b'log', opts)
643 return changesetformatter(ui, repo, fm, *postargs) 647 return changesetformatter(ui, repo, fm, *postargs)
644 648
645 spec = _lookuptemplate(ui, opts.get('template'), opts.get('style')) 649 spec = _lookuptemplate(ui, opts.get(b'template'), opts.get(b'style'))
646 650
647 if not spec.ref and not spec.tmpl and not spec.mapfile: 651 if not spec.ref and not spec.tmpl and not spec.mapfile:
648 return changesetprinter(ui, repo, *postargs) 652 return changesetprinter(ui, repo, *postargs)
649 653
650 return changesettemplater(ui, repo, spec, *postargs) 654 return changesettemplater(ui, repo, spec, *postargs)
664 # _matchfiles() revset but walkchangerevs() builds its matcher with 668 # _matchfiles() revset but walkchangerevs() builds its matcher with
665 # scmutil.match(). The difference is input pats are globbed on 669 # scmutil.match(). The difference is input pats are globbed on
666 # platforms without shell expansion (windows). 670 # platforms without shell expansion (windows).
667 wctx = repo[None] 671 wctx = repo[None]
668 match, pats = scmutil.matchandpats(wctx, pats, opts) 672 match, pats = scmutil.matchandpats(wctx, pats, opts)
669 slowpath = match.anypats() or (not match.always() and opts.get('removed')) 673 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
670 if not slowpath: 674 if not slowpath:
671 follow = opts.get('follow') or opts.get('follow_first') 675 follow = opts.get(b'follow') or opts.get(b'follow_first')
672 startctxs = [] 676 startctxs = []
673 if follow and opts.get('rev'): 677 if follow and opts.get(b'rev'):
674 startctxs = [repo[r] for r in revs] 678 startctxs = [repo[r] for r in revs]
675 for f in match.files(): 679 for f in match.files():
676 if follow and startctxs: 680 if follow and startctxs:
677 # No idea if the path was a directory at that revision, so 681 # No idea if the path was a directory at that revision, so
678 # take the slow path. 682 # take the slow path.
685 if os.path.exists(repo.wjoin(f)): 689 if os.path.exists(repo.wjoin(f)):
686 slowpath = True 690 slowpath = True
687 continue 691 continue
688 else: 692 else:
689 raise error.Abort( 693 raise error.Abort(
690 _('cannot follow file not in parent ' 'revision: "%s"') 694 _(
695 b'cannot follow file not in parent '
696 b'revision: "%s"'
697 )
691 % f 698 % f
692 ) 699 )
693 filelog = repo.file(f) 700 filelog = repo.file(f)
694 if not filelog: 701 if not filelog:
695 # A zero count may be a directory or deleted file, so 702 # A zero count may be a directory or deleted file, so
696 # try to find matching entries on the slow path. 703 # try to find matching entries on the slow path.
697 if follow: 704 if follow:
698 raise error.Abort( 705 raise error.Abort(
699 _('cannot follow nonexistent file: "%s"') % f 706 _(b'cannot follow nonexistent file: "%s"') % f
700 ) 707 )
701 slowpath = True 708 slowpath = True
702 709
703 # We decided to fall back to the slowpath because at least one 710 # We decided to fall back to the slowpath because at least one
704 # of the paths was not a file. Check to see if at least one of them 711 # of the paths was not a file. Check to see if at least one of them
705 # existed in history - in that case, we'll continue down the 712 # existed in history - in that case, we'll continue down the
706 # slowpath; otherwise, we can turn off the slowpath 713 # slowpath; otherwise, we can turn off the slowpath
707 if slowpath: 714 if slowpath:
708 for path in match.files(): 715 for path in match.files():
709 if path == '.' or path in repo.store: 716 if path == b'.' or path in repo.store:
710 break 717 break
711 else: 718 else:
712 slowpath = False 719 slowpath = False
713 720
714 return match, pats, slowpath 721 return match, pats, slowpath
742 '''hook for extensions to override the filematcher for non-follow cases''' 749 '''hook for extensions to override the filematcher for non-follow cases'''
743 return None 750 return None
744 751
745 752
746 _opt2logrevset = { 753 _opt2logrevset = {
747 'no_merges': ('not merge()', None), 754 b'no_merges': (b'not merge()', None),
748 'only_merges': ('merge()', None), 755 b'only_merges': (b'merge()', None),
749 '_matchfiles': (None, '_matchfiles(%ps)'), 756 b'_matchfiles': (None, b'_matchfiles(%ps)'),
750 'date': ('date(%s)', None), 757 b'date': (b'date(%s)', None),
751 'branch': ('branch(%s)', '%lr'), 758 b'branch': (b'branch(%s)', b'%lr'),
752 '_patslog': ('filelog(%s)', '%lr'), 759 b'_patslog': (b'filelog(%s)', b'%lr'),
753 'keyword': ('keyword(%s)', '%lr'), 760 b'keyword': (b'keyword(%s)', b'%lr'),
754 'prune': ('ancestors(%s)', 'not %lr'), 761 b'prune': (b'ancestors(%s)', b'not %lr'),
755 'user': ('user(%s)', '%lr'), 762 b'user': (b'user(%s)', b'%lr'),
756 } 763 }
757 764
758 765
759 def _makerevset(repo, match, pats, slowpath, opts): 766 def _makerevset(repo, match, pats, slowpath, opts):
760 """Return a revset string built from log options and file patterns""" 767 """Return a revset string built from log options and file patterns"""
761 opts = dict(opts) 768 opts = dict(opts)
762 # follow or not follow? 769 # follow or not follow?
763 follow = opts.get('follow') or opts.get('follow_first') 770 follow = opts.get(b'follow') or opts.get(b'follow_first')
764 771
765 # branch and only_branch are really aliases and must be handled at 772 # branch and only_branch are really aliases and must be handled at
766 # the same time 773 # the same time
767 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', []) 774 opts[b'branch'] = opts.get(b'branch', []) + opts.get(b'only_branch', [])
768 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']] 775 opts[b'branch'] = [repo.lookupbranch(b) for b in opts[b'branch']]
769 776
770 if slowpath: 777 if slowpath:
771 # See walkchangerevs() slow path. 778 # See walkchangerevs() slow path.
772 # 779 #
773 # pats/include/exclude cannot be represented as separate 780 # pats/include/exclude cannot be represented as separate
774 # revset expressions as their filtering logic applies at file 781 # revset expressions as their filtering logic applies at file
775 # level. For instance "-I a -X b" matches a revision touching 782 # level. For instance "-I a -X b" matches a revision touching
776 # "a" and "b" while "file(a) and not file(b)" does 783 # "a" and "b" while "file(a) and not file(b)" does
777 # not. Besides, filesets are evaluated against the working 784 # not. Besides, filesets are evaluated against the working
778 # directory. 785 # directory.
779 matchargs = ['r:', 'd:relpath'] 786 matchargs = [b'r:', b'd:relpath']
780 for p in pats: 787 for p in pats:
781 matchargs.append('p:' + p) 788 matchargs.append(b'p:' + p)
782 for p in opts.get('include', []): 789 for p in opts.get(b'include', []):
783 matchargs.append('i:' + p) 790 matchargs.append(b'i:' + p)
784 for p in opts.get('exclude', []): 791 for p in opts.get(b'exclude', []):
785 matchargs.append('x:' + p) 792 matchargs.append(b'x:' + p)
786 opts['_matchfiles'] = matchargs 793 opts[b'_matchfiles'] = matchargs
787 elif not follow: 794 elif not follow:
788 opts['_patslog'] = list(pats) 795 opts[b'_patslog'] = list(pats)
789 796
790 expr = [] 797 expr = []
791 for op, val in sorted(opts.iteritems()): 798 for op, val in sorted(opts.iteritems()):
792 if not val: 799 if not val:
793 continue 800 continue
794 if op not in _opt2logrevset: 801 if op not in _opt2logrevset:
795 continue 802 continue
796 revop, listop = _opt2logrevset[op] 803 revop, listop = _opt2logrevset[op]
797 if revop and '%' not in revop: 804 if revop and b'%' not in revop:
798 expr.append(revop) 805 expr.append(revop)
799 elif not listop: 806 elif not listop:
800 expr.append(revsetlang.formatspec(revop, val)) 807 expr.append(revsetlang.formatspec(revop, val))
801 else: 808 else:
802 if revop: 809 if revop:
803 val = [revsetlang.formatspec(revop, v) for v in val] 810 val = [revsetlang.formatspec(revop, v) for v in val]
804 expr.append(revsetlang.formatspec(listop, val)) 811 expr.append(revsetlang.formatspec(listop, val))
805 812
806 if expr: 813 if expr:
807 expr = '(' + ' and '.join(expr) + ')' 814 expr = b'(' + b' and '.join(expr) + b')'
808 else: 815 else:
809 expr = None 816 expr = None
810 return expr 817 return expr
811 818
812 819
813 def _initialrevs(repo, opts): 820 def _initialrevs(repo, opts):
814 """Return the initial set of revisions to be filtered or followed""" 821 """Return the initial set of revisions to be filtered or followed"""
815 follow = opts.get('follow') or opts.get('follow_first') 822 follow = opts.get(b'follow') or opts.get(b'follow_first')
816 if opts.get('rev'): 823 if opts.get(b'rev'):
817 revs = scmutil.revrange(repo, opts['rev']) 824 revs = scmutil.revrange(repo, opts[b'rev'])
818 elif follow and repo.dirstate.p1() == nullid: 825 elif follow and repo.dirstate.p1() == nullid:
819 revs = smartset.baseset() 826 revs = smartset.baseset()
820 elif follow: 827 elif follow:
821 revs = repo.revs('.') 828 revs = repo.revs(b'.')
822 else: 829 else:
823 revs = smartset.spanset(repo) 830 revs = smartset.spanset(repo)
824 revs.reverse() 831 revs.reverse()
825 return revs 832 return revs
826 833
828 def getrevs(repo, pats, opts): 835 def getrevs(repo, pats, opts):
829 """Return (revs, differ) where revs is a smartset 836 """Return (revs, differ) where revs is a smartset
830 837
831 differ is a changesetdiffer with pre-configured file matcher. 838 differ is a changesetdiffer with pre-configured file matcher.
832 """ 839 """
833 follow = opts.get('follow') or opts.get('follow_first') 840 follow = opts.get(b'follow') or opts.get(b'follow_first')
834 followfirst = opts.get('follow_first') 841 followfirst = opts.get(b'follow_first')
835 limit = getlimit(opts) 842 limit = getlimit(opts)
836 revs = _initialrevs(repo, opts) 843 revs = _initialrevs(repo, opts)
837 if not revs: 844 if not revs:
838 return smartset.baseset(), None 845 return smartset.baseset(), None
839 match, pats, slowpath = _makematcher(repo, revs, pats, opts) 846 match, pats, slowpath = _makematcher(repo, revs, pats, opts)
850 857
851 def filematcher(ctx): 858 def filematcher(ctx):
852 return match 859 return match
853 860
854 expr = _makerevset(repo, match, pats, slowpath, opts) 861 expr = _makerevset(repo, match, pats, slowpath, opts)
855 if opts.get('graph'): 862 if opts.get(b'graph'):
856 # User-specified revs might be unsorted, but don't sort before 863 # User-specified revs might be unsorted, but don't sort before
857 # _makerevset because it might depend on the order of revs 864 # _makerevset because it might depend on the order of revs
858 if repo.ui.configbool('experimental', 'log.topo'): 865 if repo.ui.configbool(b'experimental', b'log.topo'):
859 if not revs.istopo(): 866 if not revs.istopo():
860 revs = dagop.toposort(revs, repo.changelog.parentrevs) 867 revs = dagop.toposort(revs, repo.changelog.parentrevs)
861 # TODO: try to iterate the set lazily 868 # TODO: try to iterate the set lazily
862 revs = revset.baseset(list(revs), istopo=True) 869 revs = revset.baseset(list(revs), istopo=True)
863 elif not (revs.isdescending() or revs.istopo()): 870 elif not (revs.isdescending() or revs.istopo()):
876 def _parselinerangeopt(repo, opts): 883 def _parselinerangeopt(repo, opts):
877 """Parse --line-range log option and return a list of tuples (filename, 884 """Parse --line-range log option and return a list of tuples (filename,
878 (fromline, toline)). 885 (fromline, toline)).
879 """ 886 """
880 linerangebyfname = [] 887 linerangebyfname = []
881 for pat in opts.get('line_range', []): 888 for pat in opts.get(b'line_range', []):
882 try: 889 try:
883 pat, linerange = pat.rsplit(',', 1) 890 pat, linerange = pat.rsplit(b',', 1)
884 except ValueError: 891 except ValueError:
885 raise error.Abort(_('malformatted line-range pattern %s') % pat) 892 raise error.Abort(_(b'malformatted line-range pattern %s') % pat)
886 try: 893 try:
887 fromline, toline = map(int, linerange.split(':')) 894 fromline, toline = map(int, linerange.split(b':'))
888 except ValueError: 895 except ValueError:
889 raise error.Abort(_("invalid line range for %s") % pat) 896 raise error.Abort(_(b"invalid line range for %s") % pat)
890 msg = _("line range pattern '%s' must match exactly one file") % pat 897 msg = _(b"line range pattern '%s' must match exactly one file") % pat
891 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg) 898 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
892 linerangebyfname.append( 899 linerangebyfname.append(
893 (fname, util.processlinerange(fromline, toline)) 900 (fname, util.processlinerange(fromline, toline))
894 ) 901 )
895 return linerangebyfname 902 return linerangebyfname
909 # Two-levels map of "rev -> file ctx -> [line range]". 916 # Two-levels map of "rev -> file ctx -> [line range]".
910 linerangesbyrev = {} 917 linerangesbyrev = {}
911 for fname, (fromline, toline) in _parselinerangeopt(repo, opts): 918 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
912 if fname not in wctx: 919 if fname not in wctx:
913 raise error.Abort( 920 raise error.Abort(
914 _('cannot follow file not in parent ' 'revision: "%s"') % fname 921 _(b'cannot follow file not in parent ' b'revision: "%s"')
922 % fname
915 ) 923 )
916 fctx = wctx.filectx(fname) 924 fctx = wctx.filectx(fname)
917 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline): 925 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
918 rev = fctx.introrev() 926 rev = fctx.introrev()
919 if rev not in userrevs: 927 if rev not in userrevs:
956 differ._makehunksfilter = hunksfilter 964 differ._makehunksfilter = hunksfilter
957 return revs, differ 965 return revs, differ
958 966
959 967
960 def _graphnodeformatter(ui, displayer): 968 def _graphnodeformatter(ui, displayer):
961 spec = ui.config('ui', 'graphnodetemplate') 969 spec = ui.config(b'ui', b'graphnodetemplate')
962 if not spec: 970 if not spec:
963 return templatekw.getgraphnode # fast path for "{graphnode}" 971 return templatekw.getgraphnode # fast path for "{graphnode}"
964 972
965 spec = templater.unquotestring(spec) 973 spec = templater.unquotestring(spec)
966 if isinstance(displayer, changesettemplater): 974 if isinstance(displayer, changesettemplater):
971 templ = formatter.maketemplater( 979 templ = formatter.maketemplater(
972 ui, spec, defaults=templatekw.keywords, resources=tres 980 ui, spec, defaults=templatekw.keywords, resources=tres
973 ) 981 )
974 982
975 def formatnode(repo, ctx): 983 def formatnode(repo, ctx):
976 props = {'ctx': ctx, 'repo': repo} 984 props = {b'ctx': ctx, b'repo': repo}
977 return templ.renderdefault(props) 985 return templ.renderdefault(props)
978 986
979 return formatnode 987 return formatnode
980 988
981 989
982 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None): 990 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None):
983 props = props or {} 991 props = props or {}
984 formatnode = _graphnodeformatter(ui, displayer) 992 formatnode = _graphnodeformatter(ui, displayer)
985 state = graphmod.asciistate() 993 state = graphmod.asciistate()
986 styles = state['styles'] 994 styles = state[b'styles']
987 995
988 # only set graph styling if HGPLAIN is not set. 996 # only set graph styling if HGPLAIN is not set.
989 if ui.plain('graph'): 997 if ui.plain(b'graph'):
990 # set all edge styles to |, the default pre-3.8 behaviour 998 # set all edge styles to |, the default pre-3.8 behaviour
991 styles.update(dict.fromkeys(styles, '|')) 999 styles.update(dict.fromkeys(styles, b'|'))
992 else: 1000 else:
993 edgetypes = { 1001 edgetypes = {
994 'parent': graphmod.PARENT, 1002 b'parent': graphmod.PARENT,
995 'grandparent': graphmod.GRANDPARENT, 1003 b'grandparent': graphmod.GRANDPARENT,
996 'missing': graphmod.MISSINGPARENT, 1004 b'missing': graphmod.MISSINGPARENT,
997 } 1005 }
998 for name, key in edgetypes.items(): 1006 for name, key in edgetypes.items():
999 # experimental config: experimental.graphstyle.* 1007 # experimental config: experimental.graphstyle.*
1000 styles[key] = ui.config( 1008 styles[key] = ui.config(
1001 'experimental', 'graphstyle.%s' % name, styles[key] 1009 b'experimental', b'graphstyle.%s' % name, styles[key]
1002 ) 1010 )
1003 if not styles[key]: 1011 if not styles[key]:
1004 styles[key] = None 1012 styles[key] = None
1005 1013
1006 # experimental config: experimental.graphshorten 1014 # experimental config: experimental.graphshorten
1007 state['graphshorten'] = ui.configbool('experimental', 'graphshorten') 1015 state[b'graphshorten'] = ui.configbool(b'experimental', b'graphshorten')
1008 1016
1009 for rev, type, ctx, parents in dag: 1017 for rev, type, ctx, parents in dag:
1010 char = formatnode(repo, ctx) 1018 char = formatnode(repo, ctx)
1011 copies = getcopies(ctx) if getcopies else None 1019 copies = getcopies(ctx) if getcopies else None
1012 edges = edgefn(type, char, state, rev, parents) 1020 edges = edgefn(type, char, state, rev, parents)
1013 firstedge = next(edges) 1021 firstedge = next(edges)
1014 width = firstedge[2] 1022 width = firstedge[2]
1015 displayer.show( 1023 displayer.show(
1016 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props) 1024 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props)
1017 ) 1025 )
1018 lines = displayer.hunk.pop(rev).split('\n') 1026 lines = displayer.hunk.pop(rev).split(b'\n')
1019 if not lines[-1]: 1027 if not lines[-1]:
1020 del lines[-1] 1028 del lines[-1]
1021 displayer.flush(ctx) 1029 displayer.flush(ctx)
1022 for type, char, width, coldata in itertools.chain([firstedge], edges): 1030 for type, char, width, coldata in itertools.chain([firstedge], edges):
1023 graphmod.ascii(ui, state, type, char, lines, coldata) 1031 graphmod.ascii(ui, state, type, char, lines, coldata)
1038 displayer.flush(ctx) 1046 displayer.flush(ctx)
1039 displayer.close() 1047 displayer.close()
1040 1048
1041 1049
1042 def checkunsupportedgraphflags(pats, opts): 1050 def checkunsupportedgraphflags(pats, opts):
1043 for op in ["newest_first"]: 1051 for op in [b"newest_first"]:
1044 if op in opts and opts[op]: 1052 if op in opts and opts[op]:
1045 raise error.Abort( 1053 raise error.Abort(
1046 _("-G/--graph option is incompatible with --%s") 1054 _(b"-G/--graph option is incompatible with --%s")
1047 % op.replace("_", "-") 1055 % op.replace(b"_", b"-")
1048 ) 1056 )
1049 1057
1050 1058
1051 def graphrevs(repo, nodes, opts): 1059 def graphrevs(repo, nodes, opts):
1052 limit = getlimit(opts) 1060 limit = getlimit(opts)