Mercurial > hg
comparison mercurial/logcmdutil.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | a68350a7fc55 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
39 from .utils import ( | 39 from .utils import ( |
40 dateutil, | 40 dateutil, |
41 stringutil, | 41 stringutil, |
42 ) | 42 ) |
43 | 43 |
44 | |
44 def getlimit(opts): | 45 def getlimit(opts): |
45 """get the log limit according to option -l/--limit""" | 46 """get the log limit according to option -l/--limit""" |
46 limit = opts.get('limit') | 47 limit = opts.get('limit') |
47 if limit: | 48 if limit: |
48 try: | 49 try: |
53 raise error.Abort(_('limit must be positive')) | 54 raise error.Abort(_('limit must be positive')) |
54 else: | 55 else: |
55 limit = None | 56 limit = None |
56 return limit | 57 return limit |
57 | 58 |
58 def diffordiffstat(ui, repo, diffopts, node1, node2, match, | 59 |
59 changes=None, stat=False, fp=None, graphwidth=0, | 60 def diffordiffstat( |
60 prefix='', root='', listsubrepos=False, hunksfilterfn=None): | 61 ui, |
62 repo, | |
63 diffopts, | |
64 node1, | |
65 node2, | |
66 match, | |
67 changes=None, | |
68 stat=False, | |
69 fp=None, | |
70 graphwidth=0, | |
71 prefix='', | |
72 root='', | |
73 listsubrepos=False, | |
74 hunksfilterfn=None, | |
75 ): | |
61 '''show diff or diffstat.''' | 76 '''show diff or diffstat.''' |
62 ctx1 = repo[node1] | 77 ctx1 = repo[node1] |
63 ctx2 = repo[node2] | 78 ctx2 = repo[node2] |
64 if root: | 79 if root: |
65 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root) | 80 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root) |
66 else: | 81 else: |
67 relroot = '' | 82 relroot = '' |
68 copysourcematch = None | 83 copysourcematch = None |
84 | |
69 def compose(f, g): | 85 def compose(f, g): |
70 return lambda x: f(g(x)) | 86 return lambda x: f(g(x)) |
87 | |
71 def pathfn(f): | 88 def pathfn(f): |
72 return posixpath.join(prefix, f) | 89 return posixpath.join(prefix, f) |
90 | |
73 if relroot != '': | 91 if relroot != '': |
74 # 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 |
75 # subrepo | 93 # subrepo |
76 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) | 94 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) |
77 uirelroot = uipathfn(pathfn(relroot)) | 95 uirelroot = uipathfn(pathfn(relroot)) |
78 relroot += '/' | 96 relroot += '/' |
79 for matchroot in match.files(): | 97 for matchroot in match.files(): |
80 if not matchroot.startswith(relroot): | 98 if not matchroot.startswith(relroot): |
81 ui.warn(_('warning: %s not inside relative root %s\n') % | 99 ui.warn( |
82 (uipathfn(pathfn(matchroot)), uirelroot)) | 100 _('warning: %s not inside relative root %s\n') |
101 % (uipathfn(pathfn(matchroot)), uirelroot) | |
102 ) | |
83 | 103 |
84 relrootmatch = scmutil.match(ctx2, pats=[relroot], default='path') | 104 relrootmatch = scmutil.match(ctx2, pats=[relroot], default='path') |
85 match = matchmod.intersectmatchers(match, relrootmatch) | 105 match = matchmod.intersectmatchers(match, relrootmatch) |
86 copysourcematch = relrootmatch | 106 copysourcematch = relrootmatch |
87 | 107 |
88 checkroot = (repo.ui.configbool('devel', 'all-warnings') or | 108 checkroot = repo.ui.configbool( |
89 repo.ui.configbool('devel', 'check-relroot')) | 109 'devel', 'all-warnings' |
110 ) or repo.ui.configbool('devel', 'check-relroot') | |
111 | |
90 def relrootpathfn(f): | 112 def relrootpathfn(f): |
91 if checkroot and not f.startswith(relroot): | 113 if checkroot and not f.startswith(relroot): |
92 raise AssertionError( | 114 raise AssertionError( |
93 "file %s doesn't start with relroot %s" % (f, relroot)) | 115 "file %s doesn't start with relroot %s" % (f, relroot) |
94 return f[len(relroot):] | 116 ) |
117 return f[len(relroot) :] | |
118 | |
95 pathfn = compose(relrootpathfn, pathfn) | 119 pathfn = compose(relrootpathfn, pathfn) |
96 | 120 |
97 if stat: | 121 if stat: |
98 diffopts = diffopts.copy(context=0, noprefix=False) | 122 diffopts = diffopts.copy(context=0, noprefix=False) |
99 width = 80 | 123 width = 80 |
101 width = ui.termwidth() - graphwidth | 125 width = ui.termwidth() - graphwidth |
102 # If an explicit --root was given, don't respect ui.relative-paths | 126 # If an explicit --root was given, don't respect ui.relative-paths |
103 if not relroot: | 127 if not relroot: |
104 pathfn = compose(scmutil.getuipathfn(repo), pathfn) | 128 pathfn = compose(scmutil.getuipathfn(repo), pathfn) |
105 | 129 |
106 chunks = ctx2.diff(ctx1, match, changes, opts=diffopts, pathfn=pathfn, | 130 chunks = ctx2.diff( |
107 copysourcematch=copysourcematch, | 131 ctx1, |
108 hunksfilterfn=hunksfilterfn) | 132 match, |
133 changes, | |
134 opts=diffopts, | |
135 pathfn=pathfn, | |
136 copysourcematch=copysourcematch, | |
137 hunksfilterfn=hunksfilterfn, | |
138 ) | |
109 | 139 |
110 if fp is not None or ui.canwritewithoutlabels(): | 140 if fp is not None or ui.canwritewithoutlabels(): |
111 out = fp or ui | 141 out = fp or ui |
112 if stat: | 142 if stat: |
113 chunks = [patch.diffstat(util.iterlines(chunks), width=width)] | 143 chunks = [patch.diffstat(util.iterlines(chunks), width=width)] |
115 out.write(chunk) | 145 out.write(chunk) |
116 else: | 146 else: |
117 if stat: | 147 if stat: |
118 chunks = patch.diffstatui(util.iterlines(chunks), width=width) | 148 chunks = patch.diffstatui(util.iterlines(chunks), width=width) |
119 else: | 149 else: |
120 chunks = patch.difflabel(lambda chunks, **kwargs: chunks, chunks, | 150 chunks = patch.difflabel( |
121 opts=diffopts) | 151 lambda chunks, **kwargs: chunks, chunks, opts=diffopts |
152 ) | |
122 if ui.canbatchlabeledwrites(): | 153 if ui.canbatchlabeledwrites(): |
154 | |
123 def gen(): | 155 def gen(): |
124 for chunk, label in chunks: | 156 for chunk, label in chunks: |
125 yield ui.label(chunk, label=label) | 157 yield ui.label(chunk, label=label) |
158 | |
126 for chunk in util.filechunkiter(util.chunkbuffer(gen())): | 159 for chunk in util.filechunkiter(util.chunkbuffer(gen())): |
127 ui.write(chunk) | 160 ui.write(chunk) |
128 else: | 161 else: |
129 for chunk, label in chunks: | 162 for chunk, label in chunks: |
130 ui.write(chunk, label=label) | 163 ui.write(chunk, label=label) |
140 # subpath. The best we can do is to ignore it. | 173 # subpath. The best we can do is to ignore it. |
141 tempnode2 = None | 174 tempnode2 = None |
142 submatch = matchmod.subdirmatcher(subpath, match) | 175 submatch = matchmod.subdirmatcher(subpath, match) |
143 subprefix = repo.wvfs.reljoin(prefix, subpath) | 176 subprefix = repo.wvfs.reljoin(prefix, subpath) |
144 if listsubrepos or match.exact(subpath) or any(submatch.files()): | 177 if listsubrepos or match.exact(subpath) or any(submatch.files()): |
145 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes, | 178 sub.diff( |
146 stat=stat, fp=fp, prefix=subprefix) | 179 ui, |
180 diffopts, | |
181 tempnode2, | |
182 submatch, | |
183 changes=changes, | |
184 stat=stat, | |
185 fp=fp, | |
186 prefix=subprefix, | |
187 ) | |
188 | |
147 | 189 |
148 class changesetdiffer(object): | 190 class changesetdiffer(object): |
149 """Generate diff of changeset with pre-configured filtering functions""" | 191 """Generate diff of changeset with pre-configured filtering functions""" |
150 | 192 |
151 def _makefilematcher(self, ctx): | 193 def _makefilematcher(self, ctx): |
156 | 198 |
157 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False): | 199 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False): |
158 repo = ctx.repo() | 200 repo = ctx.repo() |
159 node = ctx.node() | 201 node = ctx.node() |
160 prev = ctx.p1().node() | 202 prev = ctx.p1().node() |
161 diffordiffstat(ui, repo, diffopts, prev, node, | 203 diffordiffstat( |
162 match=self._makefilematcher(ctx), stat=stat, | 204 ui, |
163 graphwidth=graphwidth, | 205 repo, |
164 hunksfilterfn=self._makehunksfilter(ctx)) | 206 diffopts, |
207 prev, | |
208 node, | |
209 match=self._makefilematcher(ctx), | |
210 stat=stat, | |
211 graphwidth=graphwidth, | |
212 hunksfilterfn=self._makehunksfilter(ctx), | |
213 ) | |
214 | |
165 | 215 |
166 def changesetlabels(ctx): | 216 def changesetlabels(ctx): |
167 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()] | 217 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()] |
168 if ctx.obsolete(): | 218 if ctx.obsolete(): |
169 labels.append('changeset.obsolete') | 219 labels.append('changeset.obsolete') |
170 if ctx.isunstable(): | 220 if ctx.isunstable(): |
171 labels.append('changeset.unstable') | 221 labels.append('changeset.unstable') |
172 for instability in ctx.instabilities(): | 222 for instability in ctx.instabilities(): |
173 labels.append('instability.%s' % instability) | 223 labels.append('instability.%s' % instability) |
174 return ' '.join(labels) | 224 return ' '.join(labels) |
225 | |
175 | 226 |
176 class changesetprinter(object): | 227 class changesetprinter(object): |
177 '''show changeset information when templating not requested.''' | 228 '''show changeset information when templating not requested.''' |
178 | 229 |
179 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False): | 230 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False): |
219 '''show a single changeset or file revision''' | 270 '''show a single changeset or file revision''' |
220 changenode = ctx.node() | 271 changenode = ctx.node() |
221 graphwidth = props.get('graphwidth', 0) | 272 graphwidth = props.get('graphwidth', 0) |
222 | 273 |
223 if self.ui.quiet: | 274 if self.ui.quiet: |
224 self.ui.write("%s\n" % scmutil.formatchangeid(ctx), | 275 self.ui.write( |
225 label='log.node') | 276 "%s\n" % scmutil.formatchangeid(ctx), label='log.node' |
277 ) | |
226 return | 278 return |
227 | 279 |
228 columns = self._columns | 280 columns = self._columns |
229 self.ui.write(columns['changeset'] % scmutil.formatchangeid(ctx), | 281 self.ui.write( |
230 label=changesetlabels(ctx)) | 282 columns['changeset'] % scmutil.formatchangeid(ctx), |
283 label=changesetlabels(ctx), | |
284 ) | |
231 | 285 |
232 # branches are shown first before any other names due to backwards | 286 # branches are shown first before any other names due to backwards |
233 # compatibility | 287 # compatibility |
234 branch = ctx.branch() | 288 branch = ctx.branch() |
235 # don't show the default branch name | 289 # don't show the default branch name |
242 if nsname == 'branches': | 296 if nsname == 'branches': |
243 continue | 297 continue |
244 # 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 |
245 # should be the same | 299 # should be the same |
246 for name in ns.names(self.repo, changenode): | 300 for name in ns.names(self.repo, changenode): |
247 self.ui.write(ns.logfmt % name, | 301 self.ui.write(ns.logfmt % name, label='log.%s' % ns.colorname) |
248 label='log.%s' % ns.colorname) | |
249 if self.ui.debugflag: | 302 if self.ui.debugflag: |
250 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase') | 303 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase') |
251 for pctx in scmutil.meaningfulparents(self.repo, ctx): | 304 for pctx in scmutil.meaningfulparents(self.repo, ctx): |
252 label = 'log.parent changeset.%s' % pctx.phasestr() | 305 label = 'log.parent changeset.%s' % pctx.phasestr() |
253 self.ui.write(columns['parent'] % scmutil.formatchangeid(pctx), | 306 self.ui.write( |
254 label=label) | 307 columns['parent'] % scmutil.formatchangeid(pctx), label=label |
308 ) | |
255 | 309 |
256 if self.ui.debugflag: | 310 if self.ui.debugflag: |
257 mnode = ctx.manifestnode() | 311 mnode = ctx.manifestnode() |
258 if mnode is None: | 312 if mnode is None: |
259 mnode = wdirid | 313 mnode = wdirid |
260 mrev = wdirrev | 314 mrev = wdirrev |
261 else: | 315 else: |
262 mrev = self.repo.manifestlog.rev(mnode) | 316 mrev = self.repo.manifestlog.rev(mnode) |
263 self.ui.write(columns['manifest'] | 317 self.ui.write( |
264 % scmutil.formatrevnode(self.ui, mrev, mnode), | 318 columns['manifest'] |
265 label='ui.debug log.manifest') | 319 % scmutil.formatrevnode(self.ui, mrev, mnode), |
320 label='ui.debug log.manifest', | |
321 ) | |
266 self.ui.write(columns['user'] % ctx.user(), label='log.user') | 322 self.ui.write(columns['user'] % ctx.user(), label='log.user') |
267 self.ui.write(columns['date'] % dateutil.datestr(ctx.date()), | 323 self.ui.write( |
268 label='log.date') | 324 columns['date'] % dateutil.datestr(ctx.date()), label='log.date' |
325 ) | |
269 | 326 |
270 if ctx.isunstable(): | 327 if ctx.isunstable(): |
271 instabilities = ctx.instabilities() | 328 instabilities = ctx.instabilities() |
272 self.ui.write(columns['instability'] % ', '.join(instabilities), | 329 self.ui.write( |
273 label='log.instability') | 330 columns['instability'] % ', '.join(instabilities), |
331 label='log.instability', | |
332 ) | |
274 | 333 |
275 elif ctx.obsolete(): | 334 elif ctx.obsolete(): |
276 self._showobsfate(ctx) | 335 self._showobsfate(ctx) |
277 | 336 |
278 self._exthook(ctx) | 337 self._exthook(ctx) |
279 | 338 |
280 if self.ui.debugflag: | 339 if self.ui.debugflag: |
281 files = ctx.p1().status(ctx)[:3] | 340 files = ctx.p1().status(ctx)[:3] |
282 for key, value in zip(['files', 'files+', 'files-'], files): | 341 for key, value in zip(['files', 'files+', 'files-'], files): |
283 if value: | 342 if value: |
284 self.ui.write(columns[key] % " ".join(value), | 343 self.ui.write( |
285 label='ui.debug log.files') | 344 columns[key] % " ".join(value), |
345 label='ui.debug log.files', | |
346 ) | |
286 elif ctx.files() and self.ui.verbose: | 347 elif ctx.files() and self.ui.verbose: |
287 self.ui.write(columns['files'] % " ".join(ctx.files()), | 348 self.ui.write( |
288 label='ui.note log.files') | 349 columns['files'] % " ".join(ctx.files()), |
350 label='ui.note log.files', | |
351 ) | |
289 if copies and self.ui.verbose: | 352 if copies and self.ui.verbose: |
290 copies = ['%s (%s)' % c for c in copies] | 353 copies = ['%s (%s)' % c for c in copies] |
291 self.ui.write(columns['copies'] % ' '.join(copies), | 354 self.ui.write( |
292 label='ui.note log.copies') | 355 columns['copies'] % ' '.join(copies), label='ui.note log.copies' |
356 ) | |
293 | 357 |
294 extra = ctx.extra() | 358 extra = ctx.extra() |
295 if extra and self.ui.debugflag: | 359 if extra and self.ui.debugflag: |
296 for key, value in sorted(extra.items()): | 360 for key, value in sorted(extra.items()): |
297 self.ui.write(columns['extra'] | 361 self.ui.write( |
298 % (key, stringutil.escapestr(value)), | 362 columns['extra'] % (key, stringutil.escapestr(value)), |
299 label='ui.debug log.extra') | 363 label='ui.debug log.extra', |
364 ) | |
300 | 365 |
301 description = ctx.description().strip() | 366 description = ctx.description().strip() |
302 if description: | 367 if description: |
303 if self.ui.verbose: | 368 if self.ui.verbose: |
304 self.ui.write(_("description:\n"), | 369 self.ui.write( |
305 label='ui.note log.description') | 370 _("description:\n"), label='ui.note log.description' |
306 self.ui.write(description, | 371 ) |
307 label='ui.note log.description') | 372 self.ui.write(description, label='ui.note log.description') |
308 self.ui.write("\n\n") | 373 self.ui.write("\n\n") |
309 else: | 374 else: |
310 self.ui.write(columns['summary'] % description.splitlines()[0], | 375 self.ui.write( |
311 label='log.summary') | 376 columns['summary'] % description.splitlines()[0], |
377 label='log.summary', | |
378 ) | |
312 self.ui.write("\n") | 379 self.ui.write("\n") |
313 | 380 |
314 self._showpatch(ctx, graphwidth) | 381 self._showpatch(ctx, graphwidth) |
315 | 382 |
316 def _showobsfate(self, ctx): | 383 def _showobsfate(self, ctx): |
317 # TODO: do not depend on templater | 384 # TODO: do not depend on templater |
318 tres = formatter.templateresources(self.repo.ui, self.repo) | 385 tres = formatter.templateresources(self.repo.ui, self.repo) |
319 t = formatter.maketemplater(self.repo.ui, '{join(obsfate, "\n")}', | 386 t = formatter.maketemplater( |
320 defaults=templatekw.keywords, | 387 self.repo.ui, |
321 resources=tres) | 388 '{join(obsfate, "\n")}', |
389 defaults=templatekw.keywords, | |
390 resources=tres, | |
391 ) | |
322 obsfate = t.renderdefault({'ctx': ctx}).splitlines() | 392 obsfate = t.renderdefault({'ctx': ctx}).splitlines() |
323 | 393 |
324 if obsfate: | 394 if obsfate: |
325 for obsfateline in obsfate: | 395 for obsfateline in obsfate: |
326 self.ui.write(self._columns['obsolete'] % obsfateline, | 396 self.ui.write( |
327 label='log.obsfate') | 397 self._columns['obsolete'] % obsfateline, label='log.obsfate' |
398 ) | |
328 | 399 |
329 def _exthook(self, ctx): | 400 def _exthook(self, ctx): |
330 '''empty method used by extension as a hook point | 401 '''empty method used by extension as a hook point |
331 ''' | 402 ''' |
332 | 403 |
333 def _showpatch(self, ctx, graphwidth=0): | 404 def _showpatch(self, ctx, graphwidth=0): |
334 if self._includestat: | 405 if self._includestat: |
335 self._differ.showdiff(self.ui, ctx, self._diffopts, | 406 self._differ.showdiff( |
336 graphwidth, stat=True) | 407 self.ui, ctx, self._diffopts, graphwidth, stat=True |
408 ) | |
337 if self._includestat and self._includediff: | 409 if self._includestat and self._includediff: |
338 self.ui.write("\n") | 410 self.ui.write("\n") |
339 if self._includediff: | 411 if self._includediff: |
340 self._differ.showdiff(self.ui, ctx, self._diffopts, | 412 self._differ.showdiff( |
341 graphwidth, stat=False) | 413 self.ui, ctx, self._diffopts, graphwidth, stat=False |
414 ) | |
342 if self._includestat or self._includediff: | 415 if self._includestat or self._includediff: |
343 self.ui.write("\n") | 416 self.ui.write("\n") |
344 | 417 |
418 | |
345 class changesetformatter(changesetprinter): | 419 class changesetformatter(changesetprinter): |
346 """Format changeset information by generic formatter""" | 420 """Format changeset information by generic formatter""" |
347 | 421 |
348 def __init__(self, ui, repo, fm, differ=None, diffopts=None, | 422 def __init__( |
349 buffered=False): | 423 self, ui, repo, fm, differ=None, diffopts=None, buffered=False |
424 ): | |
350 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered) | 425 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered) |
351 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True) | 426 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True) |
352 self._fm = fm | 427 self._fm = fm |
353 | 428 |
354 def close(self): | 429 def close(self): |
357 def _show(self, ctx, copies, props): | 432 def _show(self, ctx, copies, props): |
358 '''show a single changeset or file revision''' | 433 '''show a single changeset or file revision''' |
359 fm = self._fm | 434 fm = self._fm |
360 fm.startitem() | 435 fm.startitem() |
361 fm.context(ctx=ctx) | 436 fm.context(ctx=ctx) |
362 fm.data(rev=scmutil.intrev(ctx), | 437 fm.data(rev=scmutil.intrev(ctx), node=fm.hexfunc(scmutil.binnode(ctx))) |
363 node=fm.hexfunc(scmutil.binnode(ctx))) | |
364 | 438 |
365 if self.ui.quiet: | 439 if self.ui.quiet: |
366 return | 440 return |
367 | 441 |
368 fm.data(branch=ctx.branch(), | 442 fm.data( |
369 phase=ctx.phasestr(), | 443 branch=ctx.branch(), |
370 user=ctx.user(), | 444 phase=ctx.phasestr(), |
371 date=fm.formatdate(ctx.date()), | 445 user=ctx.user(), |
372 desc=ctx.description(), | 446 date=fm.formatdate(ctx.date()), |
373 bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'), | 447 desc=ctx.description(), |
374 tags=fm.formatlist(ctx.tags(), name='tag'), | 448 bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'), |
375 parents=fm.formatlist([fm.hexfunc(c.node()) | 449 tags=fm.formatlist(ctx.tags(), name='tag'), |
376 for c in ctx.parents()], name='node')) | 450 parents=fm.formatlist( |
451 [fm.hexfunc(c.node()) for c in ctx.parents()], name='node' | |
452 ), | |
453 ) | |
377 | 454 |
378 if self.ui.debugflag: | 455 if self.ui.debugflag: |
379 fm.data(manifest=fm.hexfunc(ctx.manifestnode() or wdirid), | 456 fm.data( |
380 extra=fm.formatdict(ctx.extra())) | 457 manifest=fm.hexfunc(ctx.manifestnode() or wdirid), |
458 extra=fm.formatdict(ctx.extra()), | |
459 ) | |
381 | 460 |
382 files = ctx.p1().status(ctx) | 461 files = ctx.p1().status(ctx) |
383 fm.data(modified=fm.formatlist(files[0], name='file'), | 462 fm.data( |
384 added=fm.formatlist(files[1], name='file'), | 463 modified=fm.formatlist(files[0], name='file'), |
385 removed=fm.formatlist(files[2], name='file')) | 464 added=fm.formatlist(files[1], name='file'), |
465 removed=fm.formatlist(files[2], name='file'), | |
466 ) | |
386 | 467 |
387 elif self.ui.verbose: | 468 elif self.ui.verbose: |
388 fm.data(files=fm.formatlist(ctx.files(), name='file')) | 469 fm.data(files=fm.formatlist(ctx.files(), name='file')) |
389 if copies: | 470 if copies: |
390 fm.data(copies=fm.formatdict(copies, | 471 fm.data( |
391 key='name', value='source')) | 472 copies=fm.formatdict(copies, key='name', value='source') |
473 ) | |
392 | 474 |
393 if self._includestat: | 475 if self._includestat: |
394 self.ui.pushbuffer() | 476 self.ui.pushbuffer() |
395 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True) | 477 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True) |
396 fm.data(diffstat=self.ui.popbuffer()) | 478 fm.data(diffstat=self.ui.popbuffer()) |
397 if self._includediff: | 479 if self._includediff: |
398 self.ui.pushbuffer() | 480 self.ui.pushbuffer() |
399 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False) | 481 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False) |
400 fm.data(diff=self.ui.popbuffer()) | 482 fm.data(diff=self.ui.popbuffer()) |
401 | 483 |
484 | |
402 class changesettemplater(changesetprinter): | 485 class changesettemplater(changesetprinter): |
403 '''format changeset information. | 486 '''format changeset information. |
404 | 487 |
405 Note: there are a variety of convenience functions to build a | 488 Note: there are a variety of convenience functions to build a |
406 changesettemplater for common cases. See functions such as: | 489 changesettemplater for common cases. See functions such as: |
408 functions that use changesest_templater. | 491 functions that use changesest_templater. |
409 ''' | 492 ''' |
410 | 493 |
411 # Arguments before "buffered" used to be positional. Consider not | 494 # Arguments before "buffered" used to be positional. Consider not |
412 # adding/removing arguments before "buffered" to not break callers. | 495 # adding/removing arguments before "buffered" to not break callers. |
413 def __init__(self, ui, repo, tmplspec, differ=None, diffopts=None, | 496 def __init__( |
414 buffered=False): | 497 self, ui, repo, tmplspec, differ=None, diffopts=None, buffered=False |
498 ): | |
415 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered) | 499 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered) |
416 # tres is shared with _graphnodeformatter() | 500 # tres is shared with _graphnodeformatter() |
417 self._tresources = tres = formatter.templateresources(ui, repo) | 501 self._tresources = tres = formatter.templateresources(ui, repo) |
418 self.t = formatter.loadtemplater(ui, tmplspec, | 502 self.t = formatter.loadtemplater( |
419 defaults=templatekw.keywords, | 503 ui, |
420 resources=tres, | 504 tmplspec, |
421 cache=templatekw.defaulttempl) | 505 defaults=templatekw.keywords, |
506 resources=tres, | |
507 cache=templatekw.defaulttempl, | |
508 ) | |
422 self._counter = itertools.count() | 509 self._counter = itertools.count() |
423 | 510 |
424 self._tref = tmplspec.ref | 511 self._tref = tmplspec.ref |
425 self._parts = {'header': '', 'footer': '', | 512 self._parts = { |
426 tmplspec.ref: tmplspec.ref, | 513 'header': '', |
427 'docheader': '', 'docfooter': '', | 514 'footer': '', |
428 'separator': ''} | 515 tmplspec.ref: tmplspec.ref, |
516 'docheader': '', | |
517 'docfooter': '', | |
518 'separator': '', | |
519 } | |
429 if tmplspec.mapfile: | 520 if tmplspec.mapfile: |
430 # find correct templates for current mode, for backward | 521 # find correct templates for current mode, for backward |
431 # compatibility with 'log -v/-q/--debug' using a mapfile | 522 # compatibility with 'log -v/-q/--debug' using a mapfile |
432 tmplmodes = [ | 523 tmplmodes = [ |
433 (True, ''), | 524 (True, ''), |
486 | 577 |
487 if self._parts['footer']: | 578 if self._parts['footer']: |
488 if not self.footer: | 579 if not self.footer: |
489 self.footer = self.t.render(self._parts['footer'], props) | 580 self.footer = self.t.render(self._parts['footer'], props) |
490 | 581 |
582 | |
491 def templatespec(tmpl, mapfile): | 583 def templatespec(tmpl, mapfile): |
492 if pycompat.ispy3: | 584 if pycompat.ispy3: |
493 assert not isinstance(tmpl, str), 'tmpl must not be a str' | 585 assert not isinstance(tmpl, str), 'tmpl must not be a str' |
494 if mapfile: | 586 if mapfile: |
495 return formatter.templatespec('changeset', tmpl, mapfile) | 587 return formatter.templatespec('changeset', tmpl, mapfile) |
496 else: | 588 else: |
497 return formatter.templatespec('', tmpl, None) | 589 return formatter.templatespec('', tmpl, None) |
498 | 590 |
591 | |
499 def _lookuptemplate(ui, tmpl, style): | 592 def _lookuptemplate(ui, tmpl, style): |
500 """Find the template matching the given template spec or style | 593 """Find the template matching the given template spec or style |
501 | 594 |
502 See formatter.lookuptemplate() for details. | 595 See formatter.lookuptemplate() for details. |
503 """ | 596 """ |
504 | 597 |
505 # ui settings | 598 # ui settings |
506 if not tmpl and not style: # template are stronger than style | 599 if not tmpl and not style: # template are stronger than style |
507 tmpl = ui.config('ui', 'logtemplate') | 600 tmpl = ui.config('ui', 'logtemplate') |
508 if tmpl: | 601 if tmpl: |
509 return templatespec(templater.unquotestring(tmpl), None) | 602 return templatespec(templater.unquotestring(tmpl), None) |
510 else: | 603 else: |
511 style = util.expandpath(ui.config('ui', 'style')) | 604 style = util.expandpath(ui.config('ui', 'style')) |
512 | 605 |
513 if not tmpl and style: | 606 if not tmpl and style: |
514 mapfile = style | 607 mapfile = style |
515 if not os.path.split(mapfile)[0]: | 608 if not os.path.split(mapfile)[0]: |
516 mapname = (templater.templatepath('map-cmdline.' + mapfile) | 609 mapname = templater.templatepath( |
517 or templater.templatepath(mapfile)) | 610 'map-cmdline.' + mapfile |
611 ) or templater.templatepath(mapfile) | |
518 if mapname: | 612 if mapname: |
519 mapfile = mapname | 613 mapfile = mapname |
520 return templatespec(None, mapfile) | 614 return templatespec(None, mapfile) |
521 | 615 |
522 if not tmpl: | 616 if not tmpl: |
523 return templatespec(None, None) | 617 return templatespec(None, None) |
524 | 618 |
525 return formatter.lookuptemplate(ui, 'changeset', tmpl) | 619 return formatter.lookuptemplate(ui, 'changeset', tmpl) |
620 | |
526 | 621 |
527 def maketemplater(ui, repo, tmpl, buffered=False): | 622 def maketemplater(ui, repo, tmpl, buffered=False): |
528 """Create a changesettemplater from a literal template 'tmpl' | 623 """Create a changesettemplater from a literal template 'tmpl' |
529 byte-string.""" | 624 byte-string.""" |
530 spec = templatespec(tmpl, None) | 625 spec = templatespec(tmpl, None) |
531 return changesettemplater(ui, repo, spec, buffered=buffered) | 626 return changesettemplater(ui, repo, spec, buffered=buffered) |
627 | |
532 | 628 |
533 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False): | 629 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False): |
534 """show one changeset using template or regular display. | 630 """show one changeset using template or regular display. |
535 | 631 |
536 Display format will be the first non-empty hit of: | 632 Display format will be the first non-empty hit of: |
550 | 646 |
551 if not spec.ref and not spec.tmpl and not spec.mapfile: | 647 if not spec.ref and not spec.tmpl and not spec.mapfile: |
552 return changesetprinter(ui, repo, *postargs) | 648 return changesetprinter(ui, repo, *postargs) |
553 | 649 |
554 return changesettemplater(ui, repo, spec, *postargs) | 650 return changesettemplater(ui, repo, spec, *postargs) |
651 | |
555 | 652 |
556 def _makematcher(repo, revs, pats, opts): | 653 def _makematcher(repo, revs, pats, opts): |
557 """Build matcher and expanded patterns from log options | 654 """Build matcher and expanded patterns from log options |
558 | 655 |
559 If --follow, revs are the revisions to follow from. | 656 If --follow, revs are the revisions to follow from. |
587 # take the slow path. | 684 # take the slow path. |
588 if os.path.exists(repo.wjoin(f)): | 685 if os.path.exists(repo.wjoin(f)): |
589 slowpath = True | 686 slowpath = True |
590 continue | 687 continue |
591 else: | 688 else: |
592 raise error.Abort(_('cannot follow file not in parent ' | 689 raise error.Abort( |
593 'revision: "%s"') % f) | 690 _('cannot follow file not in parent ' 'revision: "%s"') |
691 % f | |
692 ) | |
594 filelog = repo.file(f) | 693 filelog = repo.file(f) |
595 if not filelog: | 694 if not filelog: |
596 # A zero count may be a directory or deleted file, so | 695 # A zero count may be a directory or deleted file, so |
597 # try to find matching entries on the slow path. | 696 # try to find matching entries on the slow path. |
598 if follow: | 697 if follow: |
599 raise error.Abort( | 698 raise error.Abort( |
600 _('cannot follow nonexistent file: "%s"') % f) | 699 _('cannot follow nonexistent file: "%s"') % f |
700 ) | |
601 slowpath = True | 701 slowpath = True |
602 | 702 |
603 # We decided to fall back to the slowpath because at least one | 703 # We decided to fall back to the slowpath because at least one |
604 # of the paths was not a file. Check to see if at least one of them | 704 # of the paths was not a file. Check to see if at least one of them |
605 # existed in history - in that case, we'll continue down the | 705 # existed in history - in that case, we'll continue down the |
611 else: | 711 else: |
612 slowpath = False | 712 slowpath = False |
613 | 713 |
614 return match, pats, slowpath | 714 return match, pats, slowpath |
615 | 715 |
716 | |
616 def _fileancestors(repo, revs, match, followfirst): | 717 def _fileancestors(repo, revs, match, followfirst): |
617 fctxs = [] | 718 fctxs = [] |
618 for r in revs: | 719 for r in revs: |
619 ctx = repo[r] | 720 ctx = repo[r] |
620 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match)) | 721 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match)) |
623 # to know which file of the revision must be diffed. With | 724 # to know which file of the revision must be diffed. With |
624 # --follow, we want the names of the ancestors of FILE in the | 725 # --follow, we want the names of the ancestors of FILE in the |
625 # revision, stored in "fcache". "fcache" is populated as a side effect | 726 # revision, stored in "fcache". "fcache" is populated as a side effect |
626 # of the graph traversal. | 727 # of the graph traversal. |
627 fcache = {} | 728 fcache = {} |
729 | |
628 def filematcher(ctx): | 730 def filematcher(ctx): |
629 return scmutil.matchfiles(repo, fcache.get(ctx.rev(), [])) | 731 return scmutil.matchfiles(repo, fcache.get(ctx.rev(), [])) |
630 | 732 |
631 def revgen(): | 733 def revgen(): |
632 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst): | 734 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst): |
633 fcache[rev] = [c.path() for c in cs] | 735 fcache[rev] = [c.path() for c in cs] |
634 yield rev | 736 yield rev |
737 | |
635 return smartset.generatorset(revgen(), iterasc=False), filematcher | 738 return smartset.generatorset(revgen(), iterasc=False), filematcher |
739 | |
636 | 740 |
637 def _makenofollowfilematcher(repo, pats, opts): | 741 def _makenofollowfilematcher(repo, pats, opts): |
638 '''hook for extensions to override the filematcher for non-follow cases''' | 742 '''hook for extensions to override the filematcher for non-follow cases''' |
639 return None | 743 return None |
640 | 744 |
745 | |
641 _opt2logrevset = { | 746 _opt2logrevset = { |
642 'no_merges': ('not merge()', None), | 747 'no_merges': ('not merge()', None), |
643 'only_merges': ('merge()', None), | 748 'only_merges': ('merge()', None), |
644 '_matchfiles': (None, '_matchfiles(%ps)'), | 749 '_matchfiles': (None, '_matchfiles(%ps)'), |
645 'date': ('date(%s)', None), | 750 'date': ('date(%s)', None), |
646 'branch': ('branch(%s)', '%lr'), | 751 'branch': ('branch(%s)', '%lr'), |
647 '_patslog': ('filelog(%s)', '%lr'), | 752 '_patslog': ('filelog(%s)', '%lr'), |
648 'keyword': ('keyword(%s)', '%lr'), | 753 'keyword': ('keyword(%s)', '%lr'), |
649 'prune': ('ancestors(%s)', 'not %lr'), | 754 'prune': ('ancestors(%s)', 'not %lr'), |
650 'user': ('user(%s)', '%lr'), | 755 'user': ('user(%s)', '%lr'), |
651 } | 756 } |
757 | |
652 | 758 |
653 def _makerevset(repo, match, pats, slowpath, opts): | 759 def _makerevset(repo, match, pats, slowpath, opts): |
654 """Return a revset string built from log options and file patterns""" | 760 """Return a revset string built from log options and file patterns""" |
655 opts = dict(opts) | 761 opts = dict(opts) |
656 # follow or not follow? | 762 # follow or not follow? |
701 expr = '(' + ' and '.join(expr) + ')' | 807 expr = '(' + ' and '.join(expr) + ')' |
702 else: | 808 else: |
703 expr = None | 809 expr = None |
704 return expr | 810 return expr |
705 | 811 |
812 | |
706 def _initialrevs(repo, opts): | 813 def _initialrevs(repo, opts): |
707 """Return the initial set of revisions to be filtered or followed""" | 814 """Return the initial set of revisions to be filtered or followed""" |
708 follow = opts.get('follow') or opts.get('follow_first') | 815 follow = opts.get('follow') or opts.get('follow_first') |
709 if opts.get('rev'): | 816 if opts.get('rev'): |
710 revs = scmutil.revrange(repo, opts['rev']) | 817 revs = scmutil.revrange(repo, opts['rev']) |
714 revs = repo.revs('.') | 821 revs = repo.revs('.') |
715 else: | 822 else: |
716 revs = smartset.spanset(repo) | 823 revs = smartset.spanset(repo) |
717 revs.reverse() | 824 revs.reverse() |
718 return revs | 825 return revs |
826 | |
719 | 827 |
720 def getrevs(repo, pats, opts): | 828 def getrevs(repo, pats, opts): |
721 """Return (revs, differ) where revs is a smartset | 829 """Return (revs, differ) where revs is a smartset |
722 | 830 |
723 differ is a changesetdiffer with pre-configured file matcher. | 831 differ is a changesetdiffer with pre-configured file matcher. |
737 revs, filematcher = _fileancestors(repo, revs, match, followfirst) | 845 revs, filematcher = _fileancestors(repo, revs, match, followfirst) |
738 revs.reverse() | 846 revs.reverse() |
739 if filematcher is None: | 847 if filematcher is None: |
740 filematcher = _makenofollowfilematcher(repo, pats, opts) | 848 filematcher = _makenofollowfilematcher(repo, pats, opts) |
741 if filematcher is None: | 849 if filematcher is None: |
850 | |
742 def filematcher(ctx): | 851 def filematcher(ctx): |
743 return match | 852 return match |
744 | 853 |
745 expr = _makerevset(repo, match, pats, slowpath, opts) | 854 expr = _makerevset(repo, match, pats, slowpath, opts) |
746 if opts.get('graph'): | 855 if opts.get('graph'): |
760 revs = revs.slice(0, limit) | 869 revs = revs.slice(0, limit) |
761 | 870 |
762 differ = changesetdiffer() | 871 differ = changesetdiffer() |
763 differ._makefilematcher = filematcher | 872 differ._makefilematcher = filematcher |
764 return revs, differ | 873 return revs, differ |
874 | |
765 | 875 |
766 def _parselinerangeopt(repo, opts): | 876 def _parselinerangeopt(repo, opts): |
767 """Parse --line-range log option and return a list of tuples (filename, | 877 """Parse --line-range log option and return a list of tuples (filename, |
768 (fromline, toline)). | 878 (fromline, toline)). |
769 """ | 879 """ |
778 except ValueError: | 888 except ValueError: |
779 raise error.Abort(_("invalid line range for %s") % pat) | 889 raise error.Abort(_("invalid line range for %s") % pat) |
780 msg = _("line range pattern '%s' must match exactly one file") % pat | 890 msg = _("line range pattern '%s' must match exactly one file") % pat |
781 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg) | 891 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg) |
782 linerangebyfname.append( | 892 linerangebyfname.append( |
783 (fname, util.processlinerange(fromline, toline))) | 893 (fname, util.processlinerange(fromline, toline)) |
894 ) | |
784 return linerangebyfname | 895 return linerangebyfname |
896 | |
785 | 897 |
786 def getlinerangerevs(repo, userrevs, opts): | 898 def getlinerangerevs(repo, userrevs, opts): |
787 """Return (revs, differ). | 899 """Return (revs, differ). |
788 | 900 |
789 "revs" are revisions obtained by processing "line-range" log options and | 901 "revs" are revisions obtained by processing "line-range" log options and |
796 | 908 |
797 # Two-levels map of "rev -> file ctx -> [line range]". | 909 # Two-levels map of "rev -> file ctx -> [line range]". |
798 linerangesbyrev = {} | 910 linerangesbyrev = {} |
799 for fname, (fromline, toline) in _parselinerangeopt(repo, opts): | 911 for fname, (fromline, toline) in _parselinerangeopt(repo, opts): |
800 if fname not in wctx: | 912 if fname not in wctx: |
801 raise error.Abort(_('cannot follow file not in parent ' | 913 raise error.Abort( |
802 'revision: "%s"') % fname) | 914 _('cannot follow file not in parent ' 'revision: "%s"') % fname |
915 ) | |
803 fctx = wctx.filectx(fname) | 916 fctx = wctx.filectx(fname) |
804 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline): | 917 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline): |
805 rev = fctx.introrev() | 918 rev = fctx.introrev() |
806 if rev not in userrevs: | 919 if rev not in userrevs: |
807 continue | 920 continue |
808 linerangesbyrev.setdefault( | 921 linerangesbyrev.setdefault(rev, {}).setdefault( |
809 rev, {}).setdefault( | 922 fctx.path(), [] |
810 fctx.path(), []).append(linerange) | 923 ).append(linerange) |
811 | 924 |
812 def nofilterhunksfn(fctx, hunks): | 925 def nofilterhunksfn(fctx, hunks): |
813 return hunks | 926 return hunks |
814 | 927 |
815 def hunksfilter(ctx): | 928 def hunksfilter(ctx): |
819 | 932 |
820 def filterfn(fctx, hunks): | 933 def filterfn(fctx, hunks): |
821 lineranges = fctxlineranges.get(fctx.path()) | 934 lineranges = fctxlineranges.get(fctx.path()) |
822 if lineranges is not None: | 935 if lineranges is not None: |
823 for hr, lines in hunks: | 936 for hr, lines in hunks: |
824 if hr is None: # binary | 937 if hr is None: # binary |
825 yield hr, lines | 938 yield hr, lines |
826 continue | 939 continue |
827 if any(mdiff.hunkinrange(hr[2:], lr) | 940 if any(mdiff.hunkinrange(hr[2:], lr) for lr in lineranges): |
828 for lr in lineranges): | |
829 yield hr, lines | 941 yield hr, lines |
830 else: | 942 else: |
831 for hunk in hunks: | 943 for hunk in hunks: |
832 yield hunk | 944 yield hunk |
833 | 945 |
841 | 953 |
842 differ = changesetdiffer() | 954 differ = changesetdiffer() |
843 differ._makefilematcher = filematcher | 955 differ._makefilematcher = filematcher |
844 differ._makehunksfilter = hunksfilter | 956 differ._makehunksfilter = hunksfilter |
845 return revs, differ | 957 return revs, differ |
958 | |
846 | 959 |
847 def _graphnodeformatter(ui, displayer): | 960 def _graphnodeformatter(ui, displayer): |
848 spec = ui.config('ui', 'graphnodetemplate') | 961 spec = ui.config('ui', 'graphnodetemplate') |
849 if not spec: | 962 if not spec: |
850 return templatekw.getgraphnode # fast path for "{graphnode}" | 963 return templatekw.getgraphnode # fast path for "{graphnode}" |
853 if isinstance(displayer, changesettemplater): | 966 if isinstance(displayer, changesettemplater): |
854 # reuse cache of slow templates | 967 # reuse cache of slow templates |
855 tres = displayer._tresources | 968 tres = displayer._tresources |
856 else: | 969 else: |
857 tres = formatter.templateresources(ui) | 970 tres = formatter.templateresources(ui) |
858 templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords, | 971 templ = formatter.maketemplater( |
859 resources=tres) | 972 ui, spec, defaults=templatekw.keywords, resources=tres |
973 ) | |
974 | |
860 def formatnode(repo, ctx): | 975 def formatnode(repo, ctx): |
861 props = {'ctx': ctx, 'repo': repo} | 976 props = {'ctx': ctx, 'repo': repo} |
862 return templ.renderdefault(props) | 977 return templ.renderdefault(props) |
978 | |
863 return formatnode | 979 return formatnode |
980 | |
864 | 981 |
865 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None): | 982 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None): |
866 props = props or {} | 983 props = props or {} |
867 formatnode = _graphnodeformatter(ui, displayer) | 984 formatnode = _graphnodeformatter(ui, displayer) |
868 state = graphmod.asciistate() | 985 state = graphmod.asciistate() |
874 styles.update(dict.fromkeys(styles, '|')) | 991 styles.update(dict.fromkeys(styles, '|')) |
875 else: | 992 else: |
876 edgetypes = { | 993 edgetypes = { |
877 'parent': graphmod.PARENT, | 994 'parent': graphmod.PARENT, |
878 'grandparent': graphmod.GRANDPARENT, | 995 'grandparent': graphmod.GRANDPARENT, |
879 'missing': graphmod.MISSINGPARENT | 996 'missing': graphmod.MISSINGPARENT, |
880 } | 997 } |
881 for name, key in edgetypes.items(): | 998 for name, key in edgetypes.items(): |
882 # experimental config: experimental.graphstyle.* | 999 # experimental config: experimental.graphstyle.* |
883 styles[key] = ui.config('experimental', 'graphstyle.%s' % name, | 1000 styles[key] = ui.config( |
884 styles[key]) | 1001 'experimental', 'graphstyle.%s' % name, styles[key] |
1002 ) | |
885 if not styles[key]: | 1003 if not styles[key]: |
886 styles[key] = None | 1004 styles[key] = None |
887 | 1005 |
888 # experimental config: experimental.graphshorten | 1006 # experimental config: experimental.graphshorten |
889 state['graphshorten'] = ui.configbool('experimental', 'graphshorten') | 1007 state['graphshorten'] = ui.configbool('experimental', 'graphshorten') |
892 char = formatnode(repo, ctx) | 1010 char = formatnode(repo, ctx) |
893 copies = getcopies(ctx) if getcopies else None | 1011 copies = getcopies(ctx) if getcopies else None |
894 edges = edgefn(type, char, state, rev, parents) | 1012 edges = edgefn(type, char, state, rev, parents) |
895 firstedge = next(edges) | 1013 firstedge = next(edges) |
896 width = firstedge[2] | 1014 width = firstedge[2] |
897 displayer.show(ctx, copies=copies, | 1015 displayer.show( |
898 graphwidth=width, **pycompat.strkwargs(props)) | 1016 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props) |
1017 ) | |
899 lines = displayer.hunk.pop(rev).split('\n') | 1018 lines = displayer.hunk.pop(rev).split('\n') |
900 if not lines[-1]: | 1019 if not lines[-1]: |
901 del lines[-1] | 1020 del lines[-1] |
902 displayer.flush(ctx) | 1021 displayer.flush(ctx) |
903 for type, char, width, coldata in itertools.chain([firstedge], edges): | 1022 for type, char, width, coldata in itertools.chain([firstedge], edges): |
904 graphmod.ascii(ui, state, type, char, lines, coldata) | 1023 graphmod.ascii(ui, state, type, char, lines, coldata) |
905 lines = [] | 1024 lines = [] |
906 displayer.close() | 1025 displayer.close() |
907 | 1026 |
1027 | |
908 def displaygraphrevs(ui, repo, revs, displayer, getrenamed): | 1028 def displaygraphrevs(ui, repo, revs, displayer, getrenamed): |
909 revdag = graphmod.dagwalker(repo, revs) | 1029 revdag = graphmod.dagwalker(repo, revs) |
910 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed) | 1030 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed) |
1031 | |
911 | 1032 |
912 def displayrevs(ui, repo, revs, displayer, getcopies): | 1033 def displayrevs(ui, repo, revs, displayer, getcopies): |
913 for rev in revs: | 1034 for rev in revs: |
914 ctx = repo[rev] | 1035 ctx = repo[rev] |
915 copies = getcopies(ctx) if getcopies else None | 1036 copies = getcopies(ctx) if getcopies else None |
916 displayer.show(ctx, copies=copies) | 1037 displayer.show(ctx, copies=copies) |
917 displayer.flush(ctx) | 1038 displayer.flush(ctx) |
918 displayer.close() | 1039 displayer.close() |
919 | 1040 |
1041 | |
920 def checkunsupportedgraphflags(pats, opts): | 1042 def checkunsupportedgraphflags(pats, opts): |
921 for op in ["newest_first"]: | 1043 for op in ["newest_first"]: |
922 if op in opts and opts[op]: | 1044 if op in opts and opts[op]: |
923 raise error.Abort(_("-G/--graph option is incompatible with --%s") | 1045 raise error.Abort( |
924 % op.replace("_", "-")) | 1046 _("-G/--graph option is incompatible with --%s") |
1047 % op.replace("_", "-") | |
1048 ) | |
1049 | |
925 | 1050 |
926 def graphrevs(repo, nodes, opts): | 1051 def graphrevs(repo, nodes, opts): |
927 limit = getlimit(opts) | 1052 limit = getlimit(opts) |
928 nodes.reverse() | 1053 nodes.reverse() |
929 if limit is not None: | 1054 if limit is not None: |