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 |
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 ''' |
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.""" |
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 |
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 |
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) |