Mercurial > hg
comparison mercurial/logcmdutil.py @ 36007:29b83c08afe0
log: pack filematcher and hunksfilter into changesetdiffer object
This is just a way of getting rid of clumsy makefilematcher/makehunksfilter
arguments. There might be a better abstraction, but I don't think this is bad.
This makes filematcher and hunksfilter available by default, but that should
be fine.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sun, 21 Jan 2018 15:54:18 +0900 |
parents | dd77e36eabb6 |
children | 006ff7268c5c |
comparison
equal
deleted
inserted
replaced
36006:f113ac0750f3 | 36007:29b83c08afe0 |
---|---|
120 tempnode2 = None | 120 tempnode2 = None |
121 submatch = matchmod.subdirmatcher(subpath, match) | 121 submatch = matchmod.subdirmatcher(subpath, match) |
122 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes, | 122 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes, |
123 stat=stat, fp=fp, prefix=prefix) | 123 stat=stat, fp=fp, prefix=prefix) |
124 | 124 |
125 class changesetdiffer(object): | |
126 """Generate diff of changeset with pre-configured filtering functions""" | |
127 | |
128 def _makefilematcher(self, ctx): | |
129 return scmutil.matchall(ctx.repo()) | |
130 | |
131 def _makehunksfilter(self, ctx): | |
132 return None | |
133 | |
134 def showdiff(self, ui, ctx, diffopts, stat=False): | |
135 repo = ctx.repo() | |
136 node = ctx.node() | |
137 prev = ctx.p1().node() | |
138 diffordiffstat(ui, repo, diffopts, prev, node, | |
139 match=self._makefilematcher(ctx), stat=stat, | |
140 hunksfilterfn=self._makehunksfilter(ctx)) | |
141 | |
125 def changesetlabels(ctx): | 142 def changesetlabels(ctx): |
126 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()] | 143 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()] |
127 if ctx.obsolete(): | 144 if ctx.obsolete(): |
128 labels.append('changeset.obsolete') | 145 labels.append('changeset.obsolete') |
129 if ctx.isunstable(): | 146 if ctx.isunstable(): |
133 return ' '.join(labels) | 150 return ' '.join(labels) |
134 | 151 |
135 class changesetprinter(object): | 152 class changesetprinter(object): |
136 '''show changeset information when templating not requested.''' | 153 '''show changeset information when templating not requested.''' |
137 | 154 |
138 def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None, | 155 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False): |
139 diffopts=None, buffered=False): | |
140 self.ui = ui | 156 self.ui = ui |
141 self.repo = repo | 157 self.repo = repo |
142 self.buffered = buffered | 158 self.buffered = buffered |
143 self._makefilematcher = makefilematcher or (lambda ctx: None) | 159 self._differ = differ or changesetdiffer() |
144 self._makehunksfilter = makehunksfilter or (lambda ctx: None) | |
145 self.diffopts = diffopts or {} | 160 self.diffopts = diffopts or {} |
146 self.header = {} | 161 self.header = {} |
147 self.hunk = {} | 162 self.hunk = {} |
148 self.lastheader = None | 163 self.lastheader = None |
149 self.footer = None | 164 self.footer = None |
278 def _exthook(self, ctx): | 293 def _exthook(self, ctx): |
279 '''empty method used by extension as a hook point | 294 '''empty method used by extension as a hook point |
280 ''' | 295 ''' |
281 | 296 |
282 def _showpatch(self, ctx): | 297 def _showpatch(self, ctx): |
283 matchfn = self._makefilematcher(ctx) | |
284 hunksfilterfn = self._makehunksfilter(ctx) | |
285 if not matchfn: | |
286 return | |
287 stat = self.diffopts.get('stat') | 298 stat = self.diffopts.get('stat') |
288 diff = self.diffopts.get('patch') | 299 diff = self.diffopts.get('patch') |
289 diffopts = patch.diffallopts(self.ui, self.diffopts) | 300 diffopts = patch.diffallopts(self.ui, self.diffopts) |
290 node = ctx.node() | |
291 prev = ctx.p1().node() | |
292 if stat: | 301 if stat: |
293 diffordiffstat(self.ui, self.repo, diffopts, prev, node, | 302 self._differ.showdiff(self.ui, ctx, diffopts, stat=True) |
294 match=matchfn, stat=True, | |
295 hunksfilterfn=hunksfilterfn) | |
296 if stat and diff: | 303 if stat and diff: |
297 self.ui.write("\n") | 304 self.ui.write("\n") |
298 if diff: | 305 if diff: |
299 diffordiffstat(self.ui, self.repo, diffopts, prev, node, | 306 self._differ.showdiff(self.ui, ctx, diffopts, stat=False) |
300 match=matchfn, stat=False, | |
301 hunksfilterfn=hunksfilterfn) | |
302 if stat or diff: | 307 if stat or diff: |
303 self.ui.write("\n") | 308 self.ui.write("\n") |
304 | 309 |
305 class jsonchangeset(changesetprinter): | 310 class jsonchangeset(changesetprinter): |
306 '''format changeset information.''' | 311 '''format changeset information.''' |
307 | 312 |
308 def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None, | 313 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False): |
309 diffopts=None, buffered=False): | 314 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered) |
310 changesetprinter.__init__(self, ui, repo, makefilematcher, | |
311 makehunksfilter, diffopts, buffered) | |
312 self.cache = {} | 315 self.cache = {} |
313 self._first = True | 316 self._first = True |
314 | 317 |
315 def close(self): | 318 def close(self): |
316 if not self._first: | 319 if not self._first: |
381 if copies: | 384 if copies: |
382 self.ui.write((',\n "copies": {%s}') % | 385 self.ui.write((',\n "copies": {%s}') % |
383 ", ".join('"%s": "%s"' % (j(k), j(v)) | 386 ", ".join('"%s": "%s"' % (j(k), j(v)) |
384 for k, v in copies)) | 387 for k, v in copies)) |
385 | 388 |
386 matchfn = self._makefilematcher(ctx) | |
387 stat = self.diffopts.get('stat') | 389 stat = self.diffopts.get('stat') |
388 diff = self.diffopts.get('patch') | 390 diff = self.diffopts.get('patch') |
389 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True) | 391 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True) |
390 node, prev = ctx.node(), ctx.p1().node() | 392 if stat: |
391 if matchfn and stat: | |
392 self.ui.pushbuffer() | 393 self.ui.pushbuffer() |
393 diffordiffstat(self.ui, self.repo, diffopts, prev, node, | 394 self._differ.showdiff(self.ui, ctx, diffopts, stat=True) |
394 match=matchfn, stat=True) | |
395 self.ui.write((',\n "diffstat": "%s"') | 395 self.ui.write((',\n "diffstat": "%s"') |
396 % j(self.ui.popbuffer())) | 396 % j(self.ui.popbuffer())) |
397 if matchfn and diff: | 397 if diff: |
398 self.ui.pushbuffer() | 398 self.ui.pushbuffer() |
399 diffordiffstat(self.ui, self.repo, diffopts, prev, node, | 399 self._differ.showdiff(self.ui, ctx, diffopts, stat=False) |
400 match=matchfn, stat=False) | |
401 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer())) | 400 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer())) |
402 | 401 |
403 self.ui.write("\n }") | 402 self.ui.write("\n }") |
404 | 403 |
405 class changesettemplater(changesetprinter): | 404 class changesettemplater(changesetprinter): |
411 functions that use changesest_templater. | 410 functions that use changesest_templater. |
412 ''' | 411 ''' |
413 | 412 |
414 # Arguments before "buffered" used to be positional. Consider not | 413 # Arguments before "buffered" used to be positional. Consider not |
415 # adding/removing arguments before "buffered" to not break callers. | 414 # adding/removing arguments before "buffered" to not break callers. |
416 def __init__(self, ui, repo, tmplspec, makefilematcher=None, | 415 def __init__(self, ui, repo, tmplspec, differ=None, diffopts=None, |
417 makehunksfilter=None, diffopts=None, buffered=False): | 416 buffered=False): |
418 changesetprinter.__init__(self, ui, repo, makefilematcher, | 417 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered) |
419 makehunksfilter, diffopts, buffered) | |
420 tres = formatter.templateresources(ui, repo) | 418 tres = formatter.templateresources(ui, repo) |
421 self.t = formatter.loadtemplater(ui, tmplspec, | 419 self.t = formatter.loadtemplater(ui, tmplspec, |
422 defaults=templatekw.keywords, | 420 defaults=templatekw.keywords, |
423 resources=tres, | 421 resources=tres, |
424 cache=templatekw.defaulttempl) | 422 cache=templatekw.defaulttempl) |
531 """Create a changesettemplater from a literal template 'tmpl' | 529 """Create a changesettemplater from a literal template 'tmpl' |
532 byte-string.""" | 530 byte-string.""" |
533 spec = templatespec(tmpl, None) | 531 spec = templatespec(tmpl, None) |
534 return changesettemplater(ui, repo, spec, buffered=buffered) | 532 return changesettemplater(ui, repo, spec, buffered=buffered) |
535 | 533 |
536 def changesetdisplayer(ui, repo, opts, makefilematcher=None, | 534 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False): |
537 makehunksfilter=None, buffered=False): | |
538 """show one changeset using template or regular display. | 535 """show one changeset using template or regular display. |
539 | 536 |
540 Display format will be the first non-empty hit of: | 537 Display format will be the first non-empty hit of: |
541 1. option 'template' | 538 1. option 'template' |
542 2. option 'style' | 539 2. option 'style' |
543 3. [ui] setting 'logtemplate' | 540 3. [ui] setting 'logtemplate' |
544 4. [ui] setting 'style' | 541 4. [ui] setting 'style' |
545 If all of these values are either the unset or the empty string, | 542 If all of these values are either the unset or the empty string, |
546 regular display via changesetprinter() is done. | 543 regular display via changesetprinter() is done. |
547 """ | 544 """ |
548 # options | 545 postargs = (differ, opts, buffered) |
549 if not makefilematcher and (opts.get('patch') or opts.get('stat')): | |
550 def makefilematcher(ctx): | |
551 return scmutil.matchall(repo) | |
552 | |
553 postargs = (makefilematcher, makehunksfilter, opts, buffered) | |
554 if opts.get('template') == 'json': | 546 if opts.get('template') == 'json': |
555 return jsonchangeset(ui, repo, *postargs) | 547 return jsonchangeset(ui, repo, *postargs) |
556 | 548 |
557 spec = _lookuptemplate(ui, opts.get('template'), opts.get('style')) | 549 spec = _lookuptemplate(ui, opts.get('template'), opts.get('style')) |
558 | 550 |
724 revs = smartset.spanset(repo) | 716 revs = smartset.spanset(repo) |
725 revs.reverse() | 717 revs.reverse() |
726 return revs | 718 return revs |
727 | 719 |
728 def getrevs(repo, pats, opts): | 720 def getrevs(repo, pats, opts): |
729 """Return (revs, filematcher) where revs is a smartset | 721 """Return (revs, differ) where revs is a smartset |
730 | 722 |
731 filematcher is a callable taking a changectx and returning a match | 723 differ is a changesetdiffer with pre-configured file matcher. |
732 objects filtering the files to be detailed when displaying the revision. | |
733 """ | 724 """ |
734 follow = opts.get('follow') or opts.get('follow_first') | 725 follow = opts.get('follow') or opts.get('follow_first') |
735 followfirst = opts.get('follow_first') | 726 followfirst = opts.get('follow_first') |
736 limit = getlimit(opts) | 727 limit = getlimit(opts) |
737 revs = _initialrevs(repo, opts) | 728 revs = _initialrevs(repo, opts) |
760 if expr: | 751 if expr: |
761 matcher = revset.match(None, expr) | 752 matcher = revset.match(None, expr) |
762 revs = matcher(repo, revs) | 753 revs = matcher(repo, revs) |
763 if limit is not None: | 754 if limit is not None: |
764 revs = revs.slice(0, limit) | 755 revs = revs.slice(0, limit) |
765 return revs, filematcher | 756 |
757 differ = changesetdiffer() | |
758 differ._makefilematcher = filematcher | |
759 return revs, differ | |
766 | 760 |
767 def _parselinerangeopt(repo, opts): | 761 def _parselinerangeopt(repo, opts): |
768 """Parse --line-range log option and return a list of tuples (filename, | 762 """Parse --line-range log option and return a list of tuples (filename, |
769 (fromline, toline)). | 763 (fromline, toline)). |
770 """ | 764 """ |
783 linerangebyfname.append( | 777 linerangebyfname.append( |
784 (fname, util.processlinerange(fromline, toline))) | 778 (fname, util.processlinerange(fromline, toline))) |
785 return linerangebyfname | 779 return linerangebyfname |
786 | 780 |
787 def getlinerangerevs(repo, userrevs, opts): | 781 def getlinerangerevs(repo, userrevs, opts): |
788 """Return (revs, filematcher, hunksfilter). | 782 """Return (revs, differ). |
789 | 783 |
790 "revs" are revisions obtained by processing "line-range" log options and | 784 "revs" are revisions obtained by processing "line-range" log options and |
791 walking block ancestors of each specified file/line-range. | 785 walking block ancestors of each specified file/line-range. |
792 | 786 |
793 "filematcher(ctx) -> match" is a factory function returning a match object | 787 "differ" is a changesetdiffer with pre-configured file matcher and hunks |
794 for a given revision for file patterns specified in --line-range option. | 788 filter. |
795 | |
796 "hunksfilter(ctx) -> filterfn(fctx, hunks)" is a factory function | |
797 returning a hunks filtering function. | |
798 """ | 789 """ |
799 wctx = repo[None] | 790 wctx = repo[None] |
800 | 791 |
801 # Two-levels map of "rev -> file ctx -> [line range]". | 792 # Two-levels map of "rev -> file ctx -> [line range]". |
802 linerangesbyrev = {} | 793 linerangesbyrev = {} |
841 files = list(linerangesbyrev.get(ctx.rev(), [])) | 832 files = list(linerangesbyrev.get(ctx.rev(), [])) |
842 return scmutil.matchfiles(repo, files) | 833 return scmutil.matchfiles(repo, files) |
843 | 834 |
844 revs = sorted(linerangesbyrev, reverse=True) | 835 revs = sorted(linerangesbyrev, reverse=True) |
845 | 836 |
846 return revs, filematcher, hunksfilter | 837 differ = changesetdiffer() |
838 differ._makefilematcher = filematcher | |
839 differ._makehunksfilter = hunksfilter | |
840 return revs, differ | |
847 | 841 |
848 def _graphnodeformatter(ui, displayer): | 842 def _graphnodeformatter(ui, displayer): |
849 spec = ui.config('ui', 'graphnodetemplate') | 843 spec = ui.config('ui', 'graphnodetemplate') |
850 if not spec: | 844 if not spec: |
851 return templatekw.showgraphnode # fast path for "{graphnode}" | 845 return templatekw.showgraphnode # fast path for "{graphnode}" |
908 for type, char, width, coldata in itertools.chain([firstedge], edges): | 902 for type, char, width, coldata in itertools.chain([firstedge], edges): |
909 graphmod.ascii(ui, state, type, char, lines, coldata) | 903 graphmod.ascii(ui, state, type, char, lines, coldata) |
910 lines = [] | 904 lines = [] |
911 displayer.close() | 905 displayer.close() |
912 | 906 |
913 def graphlog(ui, repo, revs, filematcher, opts): | 907 def graphlog(ui, repo, revs, differ, opts): |
914 # Parameters are identical to log command ones | 908 # Parameters are identical to log command ones |
915 revdag = graphmod.dagwalker(repo, revs) | 909 revdag = graphmod.dagwalker(repo, revs) |
916 | 910 |
917 getrenamed = None | 911 getrenamed = None |
918 if opts.get('copies'): | 912 if opts.get('copies'): |
920 if opts.get('rev'): | 914 if opts.get('rev'): |
921 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1 | 915 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1 |
922 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) | 916 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) |
923 | 917 |
924 ui.pager('log') | 918 ui.pager('log') |
925 displayer = changesetdisplayer(ui, repo, opts, makefilematcher=filematcher, | 919 displayer = changesetdisplayer(ui, repo, opts, differ, buffered=True) |
926 buffered=True) | |
927 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed) | 920 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed) |
928 | 921 |
929 def checkunsupportedgraphflags(pats, opts): | 922 def checkunsupportedgraphflags(pats, opts): |
930 for op in ["newest_first"]: | 923 for op in ["newest_first"]: |
931 if op in opts and opts[op]: | 924 if op in opts and opts[op]: |