comparison hgext/extdiff.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 9f70512ae2cf
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
116 116
117 configtable = {} 117 configtable = {}
118 configitem = registrar.configitem(configtable) 118 configitem = registrar.configitem(configtable)
119 119
120 configitem( 120 configitem(
121 'extdiff', br'opts\..*', default='', generic=True, 121 b'extdiff', br'opts\..*', default=b'', generic=True,
122 ) 122 )
123 123
124 configitem( 124 configitem(
125 'extdiff', br'gui\..*', generic=True, 125 b'extdiff', br'gui\..*', generic=True,
126 ) 126 )
127 127
128 configitem( 128 configitem(
129 'diff-tools', br'.*\.diffargs$', default=None, generic=True, 129 b'diff-tools', br'.*\.diffargs$', default=None, generic=True,
130 ) 130 )
131 131
132 configitem( 132 configitem(
133 'diff-tools', br'.*\.gui$', generic=True, 133 b'diff-tools', br'.*\.gui$', generic=True,
134 ) 134 )
135 135
136 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for 136 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
137 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should 137 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
138 # be specifying the version(s) of Mercurial they are tested with, or 138 # be specifying the version(s) of Mercurial they are tested with, or
139 # leave the attribute unspecified. 139 # leave the attribute unspecified.
140 testedwith = 'ships-with-hg-core' 140 testedwith = b'ships-with-hg-core'
141 141
142 142
143 def snapshot(ui, repo, files, node, tmproot, listsubrepos): 143 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
144 '''snapshot files as of some revision 144 '''snapshot files as of some revision
145 if not using snapshot, -I/-X does not work and recursive diff 145 if not using snapshot, -I/-X does not work and recursive diff
146 in tools like kdiff3 and meld displays too many files.''' 146 in tools like kdiff3 and meld displays too many files.'''
147 dirname = os.path.basename(repo.root) 147 dirname = os.path.basename(repo.root)
148 if dirname == "": 148 if dirname == b"":
149 dirname = "root" 149 dirname = b"root"
150 if node is not None: 150 if node is not None:
151 dirname = '%s.%s' % (dirname, short(node)) 151 dirname = b'%s.%s' % (dirname, short(node))
152 base = os.path.join(tmproot, dirname) 152 base = os.path.join(tmproot, dirname)
153 os.mkdir(base) 153 os.mkdir(base)
154 fnsandstat = [] 154 fnsandstat = []
155 155
156 if node is not None: 156 if node is not None:
157 ui.note( 157 ui.note(
158 _('making snapshot of %d files from rev %s\n') 158 _(b'making snapshot of %d files from rev %s\n')
159 % (len(files), short(node)) 159 % (len(files), short(node))
160 ) 160 )
161 else: 161 else:
162 ui.note( 162 ui.note(
163 _('making snapshot of %d files from working directory\n') 163 _(b'making snapshot of %d files from working directory\n')
164 % (len(files)) 164 % (len(files))
165 ) 165 )
166 166
167 if files: 167 if files:
168 repo.ui.setconfig("ui", "archivemeta", False) 168 repo.ui.setconfig(b"ui", b"archivemeta", False)
169 169
170 archival.archive( 170 archival.archive(
171 repo, 171 repo,
172 base, 172 base,
173 node, 173 node,
174 'files', 174 b'files',
175 match=scmutil.matchfiles(repo, files), 175 match=scmutil.matchfiles(repo, files),
176 subrepos=listsubrepos, 176 subrepos=listsubrepos,
177 ) 177 )
178 178
179 for fn in sorted(files): 179 for fn in sorted(files):
180 wfn = util.pconvert(fn) 180 wfn = util.pconvert(fn)
181 ui.note(' %s\n' % wfn) 181 ui.note(b' %s\n' % wfn)
182 182
183 if node is None: 183 if node is None:
184 dest = os.path.join(base, wfn) 184 dest = os.path.join(base, wfn)
185 185
186 fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest))) 186 fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest)))
200 ): 200 ):
201 # Function to quote file/dir names in the argument string. 201 # Function to quote file/dir names in the argument string.
202 # When not operating in 3-way mode, an empty string is 202 # When not operating in 3-way mode, an empty string is
203 # returned for parent2 203 # returned for parent2
204 replace = { 204 replace = {
205 'parent': parent1, 205 b'parent': parent1,
206 'parent1': parent1, 206 b'parent1': parent1,
207 'parent2': parent2, 207 b'parent2': parent2,
208 'plabel1': plabel1, 208 b'plabel1': plabel1,
209 'plabel2': plabel2, 209 b'plabel2': plabel2,
210 'child': child, 210 b'child': child,
211 'clabel': clabel, 211 b'clabel': clabel,
212 'root': repo_root, 212 b'root': repo_root,
213 } 213 }
214 214
215 def quote(match): 215 def quote(match):
216 pre = match.group(2) 216 pre = match.group(2)
217 key = match.group(3) 217 key = match.group(3)
218 if not do3way and key == 'parent2': 218 if not do3way and key == b'parent2':
219 return pre 219 return pre
220 return pre + procutil.shellquote(replace[key]) 220 return pre + procutil.shellquote(replace[key])
221 221
222 # Match parent2 first, so 'parent1?' will match both parent1 and parent 222 # Match parent2 first, so 'parent1?' will match both parent1 and parent
223 regex = ( 223 regex = (
224 br'''(['"]?)([^\s'"$]*)''' 224 br'''(['"]?)([^\s'"$]*)'''
225 br'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1' 225 br'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1'
226 ) 226 )
227 if not do3way and not re.search(regex, cmdline): 227 if not do3way and not re.search(regex, cmdline):
228 cmdline += ' $parent1 $child' 228 cmdline += b' $parent1 $child'
229 return re.sub(regex, quote, cmdline) 229 return re.sub(regex, quote, cmdline)
230 230
231 231
232 def _systembackground(cmd, environ=None, cwd=None): 232 def _systembackground(cmd, environ=None, cwd=None):
233 ''' like 'procutil.system', but returns the Popen object directly 233 ''' like 'procutil.system', but returns the Popen object directly
271 path1a = os.path.join(tmproot, dir1a, commonfile) 271 path1a = os.path.join(tmproot, dir1a, commonfile)
272 label1a = commonfile + rev1a 272 label1a = commonfile + rev1a
273 if not os.path.isfile(path1a): 273 if not os.path.isfile(path1a):
274 path1a = os.devnull 274 path1a = os.devnull
275 275
276 path1b = '' 276 path1b = b''
277 label1b = '' 277 label1b = b''
278 if do3way: 278 if do3way:
279 path1b = os.path.join(tmproot, dir1b, commonfile) 279 path1b = os.path.join(tmproot, dir1b, commonfile)
280 label1b = commonfile + rev1b 280 label1b = commonfile + rev1b
281 if not os.path.isfile(path1b): 281 if not os.path.isfile(path1b):
282 path1b = os.devnull 282 path1b = os.devnull
284 path2 = os.path.join(dir2root, dir2, commonfile) 284 path2 = os.path.join(dir2root, dir2, commonfile)
285 label2 = commonfile + rev2 285 label2 = commonfile + rev2
286 286
287 if confirm: 287 if confirm:
288 # Prompt before showing this diff 288 # Prompt before showing this diff
289 difffiles = _('diff %s (%d of %d)') % ( 289 difffiles = _(b'diff %s (%d of %d)') % (
290 commonfile, 290 commonfile,
291 idx + 1, 291 idx + 1,
292 totalfiles, 292 totalfiles,
293 ) 293 )
294 responses = _( 294 responses = _(
295 '[Yns?]' 295 b'[Yns?]'
296 '$$ &Yes, show diff' 296 b'$$ &Yes, show diff'
297 '$$ &No, skip this diff' 297 b'$$ &No, skip this diff'
298 '$$ &Skip remaining diffs' 298 b'$$ &Skip remaining diffs'
299 '$$ &? (display help)' 299 b'$$ &? (display help)'
300 ) 300 )
301 r = ui.promptchoice('%s %s' % (difffiles, responses)) 301 r = ui.promptchoice(b'%s %s' % (difffiles, responses))
302 if r == 3: # ? 302 if r == 3: # ?
303 while r == 3: 303 while r == 3:
304 for c, t in ui.extractchoices(responses)[1]: 304 for c, t in ui.extractchoices(responses)[1]:
305 ui.write('%s - %s\n' % (c, encoding.lower(t))) 305 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
306 r = ui.promptchoice('%s %s' % (difffiles, responses)) 306 r = ui.promptchoice(b'%s %s' % (difffiles, responses))
307 if r == 0: # yes 307 if r == 0: # yes
308 pass 308 pass
309 elif r == 1: # no 309 elif r == 1: # no
310 continue 310 continue
311 elif r == 2: # skip 311 elif r == 2: # skip
329 # This is because either we need to wait for confirmation 329 # This is because either we need to wait for confirmation
330 # from the user between each invocation, or because, as far 330 # from the user between each invocation, or because, as far
331 # as we know, the tool doesn't have a GUI, in which case 331 # as we know, the tool doesn't have a GUI, in which case
332 # we can't run multiple CLI programs at the same time. 332 # we can't run multiple CLI programs at the same time.
333 ui.debug( 333 ui.debug(
334 'running %r in %s\n' % (pycompat.bytestr(curcmdline), tmproot) 334 b'running %r in %s\n' % (pycompat.bytestr(curcmdline), tmproot)
335 ) 335 )
336 ui.system(curcmdline, cwd=tmproot, blockedtag='extdiff') 336 ui.system(curcmdline, cwd=tmproot, blockedtag=b'extdiff')
337 else: 337 else:
338 # Run the comparison program but don't wait, as we're 338 # Run the comparison program but don't wait, as we're
339 # going to rapid-fire each file diff and then wait on 339 # going to rapid-fire each file diff and then wait on
340 # the whole group. 340 # the whole group.
341 ui.debug( 341 ui.debug(
342 'running %r in %s (backgrounded)\n' 342 b'running %r in %s (backgrounded)\n'
343 % (pycompat.bytestr(curcmdline), tmproot) 343 % (pycompat.bytestr(curcmdline), tmproot)
344 ) 344 )
345 proc = _systembackground(curcmdline, cwd=tmproot) 345 proc = _systembackground(curcmdline, cwd=tmproot)
346 waitprocs.append(proc) 346 waitprocs.append(proc)
347 347
348 if waitprocs: 348 if waitprocs:
349 with ui.timeblockedsection('extdiff'): 349 with ui.timeblockedsection(b'extdiff'):
350 for proc in waitprocs: 350 for proc in waitprocs:
351 proc.wait() 351 proc.wait()
352 352
353 353
354 def dodiff(ui, repo, cmdline, pats, opts, guitool=False): 354 def dodiff(ui, repo, cmdline, pats, opts, guitool=False):
358 - copy to a temp structure if diffing working revision with 358 - copy to a temp structure if diffing working revision with
359 another one and more than 1 file is changed 359 another one and more than 1 file is changed
360 - just invoke the diff for a single file in the working dir 360 - just invoke the diff for a single file in the working dir
361 ''' 361 '''
362 362
363 revs = opts.get('rev') 363 revs = opts.get(b'rev')
364 change = opts.get('change') 364 change = opts.get(b'change')
365 do3way = '$parent2' in cmdline 365 do3way = b'$parent2' in cmdline
366 366
367 if revs and change: 367 if revs and change:
368 msg = _('cannot specify --rev and --change at the same time') 368 msg = _(b'cannot specify --rev and --change at the same time')
369 raise error.Abort(msg) 369 raise error.Abort(msg)
370 elif change: 370 elif change:
371 ctx2 = scmutil.revsingle(repo, change, None) 371 ctx2 = scmutil.revsingle(repo, change, None)
372 ctx1a, ctx1b = ctx2.p1(), ctx2.p2() 372 ctx1a, ctx1b = ctx2.p1(), ctx2.p2()
373 else: 373 else:
375 if not revs: 375 if not revs:
376 ctx1b = repo[None].p2() 376 ctx1b = repo[None].p2()
377 else: 377 else:
378 ctx1b = repo[nullid] 378 ctx1b = repo[nullid]
379 379
380 perfile = opts.get('per_file') 380 perfile = opts.get(b'per_file')
381 confirm = opts.get('confirm') 381 confirm = opts.get(b'confirm')
382 382
383 node1a = ctx1a.node() 383 node1a = ctx1a.node()
384 node1b = ctx1b.node() 384 node1b = ctx1b.node()
385 node2 = ctx2.node() 385 node2 = ctx2.node()
386 386
387 # Disable 3-way merge if there is only one parent 387 # Disable 3-way merge if there is only one parent
388 if do3way: 388 if do3way:
389 if node1b == nullid: 389 if node1b == nullid:
390 do3way = False 390 do3way = False
391 391
392 subrepos = opts.get('subrepos') 392 subrepos = opts.get(b'subrepos')
393 393
394 matcher = scmutil.match(repo[node2], pats, opts) 394 matcher = scmutil.match(repo[node2], pats, opts)
395 395
396 if opts.get('patch'): 396 if opts.get(b'patch'):
397 if subrepos: 397 if subrepos:
398 raise error.Abort(_('--patch cannot be used with --subrepos')) 398 raise error.Abort(_(b'--patch cannot be used with --subrepos'))
399 if perfile: 399 if perfile:
400 raise error.Abort(_('--patch cannot be used with --per-file')) 400 raise error.Abort(_(b'--patch cannot be used with --per-file'))
401 if node2 is None: 401 if node2 is None:
402 raise error.Abort(_('--patch requires two revisions')) 402 raise error.Abort(_(b'--patch requires two revisions'))
403 else: 403 else:
404 mod_a, add_a, rem_a = map( 404 mod_a, add_a, rem_a = map(
405 set, repo.status(node1a, node2, matcher, listsubrepos=subrepos)[:3] 405 set, repo.status(node1a, node2, matcher, listsubrepos=subrepos)[:3]
406 ) 406 )
407 if do3way: 407 if do3way:
414 modadd = mod_a | add_a | mod_b | add_b 414 modadd = mod_a | add_a | mod_b | add_b
415 common = modadd | rem_a | rem_b 415 common = modadd | rem_a | rem_b
416 if not common: 416 if not common:
417 return 0 417 return 0
418 418
419 tmproot = pycompat.mkdtemp(prefix='extdiff.') 419 tmproot = pycompat.mkdtemp(prefix=b'extdiff.')
420 try: 420 try:
421 if not opts.get('patch'): 421 if not opts.get(b'patch'):
422 # Always make a copy of node1a (and node1b, if applicable) 422 # Always make a copy of node1a (and node1b, if applicable)
423 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) 423 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
424 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[ 424 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[
425 0 425 0
426 ] 426 ]
427 rev1a = '@%d' % repo[node1a].rev() 427 rev1a = b'@%d' % repo[node1a].rev()
428 if do3way: 428 if do3way:
429 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) 429 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
430 dir1b = snapshot( 430 dir1b = snapshot(
431 ui, repo, dir1b_files, node1b, tmproot, subrepos 431 ui, repo, dir1b_files, node1b, tmproot, subrepos
432 )[0] 432 )[0]
433 rev1b = '@%d' % repo[node1b].rev() 433 rev1b = b'@%d' % repo[node1b].rev()
434 else: 434 else:
435 dir1b = None 435 dir1b = None
436 rev1b = '' 436 rev1b = b''
437 437
438 fnsandstat = [] 438 fnsandstat = []
439 439
440 # If node2 in not the wc or there is >1 change, copy it 440 # If node2 in not the wc or there is >1 change, copy it
441 dir2root = '' 441 dir2root = b''
442 rev2 = '' 442 rev2 = b''
443 if node2: 443 if node2:
444 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0] 444 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
445 rev2 = '@%d' % repo[node2].rev() 445 rev2 = b'@%d' % repo[node2].rev()
446 elif len(common) > 1: 446 elif len(common) > 1:
447 # we only actually need to get the files to copy back to 447 # we only actually need to get the files to copy back to
448 # the working dir in this case (because the other cases 448 # the working dir in this case (because the other cases
449 # are: diffing 2 revisions or single file -- in which case 449 # are: diffing 2 revisions or single file -- in which case
450 # the file is already directly passed to the diff tool). 450 # the file is already directly passed to the diff tool).
451 dir2, fnsandstat = snapshot( 451 dir2, fnsandstat = snapshot(
452 ui, repo, modadd, None, tmproot, subrepos 452 ui, repo, modadd, None, tmproot, subrepos
453 ) 453 )
454 else: 454 else:
455 # This lets the diff tool open the changed file directly 455 # This lets the diff tool open the changed file directly
456 dir2 = '' 456 dir2 = b''
457 dir2root = repo.root 457 dir2root = repo.root
458 458
459 label1a = rev1a 459 label1a = rev1a
460 label1b = rev1b 460 label1b = rev1b
461 label2 = rev2 461 label2 = rev2
474 if not os.path.isfile(dir1b): 474 if not os.path.isfile(dir1b):
475 dir1b = os.devnull 475 dir1b = os.devnull
476 dir2 = os.path.join(dir2root, dir2, common_file) 476 dir2 = os.path.join(dir2root, dir2, common_file)
477 label2 = common_file + rev2 477 label2 = common_file + rev2
478 else: 478 else:
479 template = 'hg-%h.patch' 479 template = b'hg-%h.patch'
480 with formatter.nullformatter(ui, 'extdiff', {}) as fm: 480 with formatter.nullformatter(ui, b'extdiff', {}) as fm:
481 cmdutil.export( 481 cmdutil.export(
482 repo, 482 repo,
483 [repo[node1a].rev(), repo[node2].rev()], 483 [repo[node1a].rev(), repo[node2].rev()],
484 fm, 484 fm,
485 fntemplate=repo.vfs.reljoin(tmproot, template), 485 fntemplate=repo.vfs.reljoin(tmproot, template),
505 plabel2=label1b, 505 plabel2=label1b,
506 child=dir2, 506 child=dir2,
507 clabel=label2, 507 clabel=label2,
508 ) 508 )
509 ui.debug( 509 ui.debug(
510 'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot) 510 b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot)
511 ) 511 )
512 ui.system(cmdline, cwd=tmproot, blockedtag='extdiff') 512 ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff')
513 else: 513 else:
514 # Run the external tool once for each pair of files 514 # Run the external tool once for each pair of files
515 _runperfilediff( 515 _runperfilediff(
516 cmdline, 516 cmdline,
517 repo.root, 517 repo.root,
543 cpstat[stat.ST_MTIME] != st[stat.ST_MTIME] 543 cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
544 or cpstat.st_size != st.st_size 544 or cpstat.st_size != st.st_size
545 or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100) 545 or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)
546 ): 546 ):
547 ui.debug( 547 ui.debug(
548 'file changed while diffing. ' 548 b'file changed while diffing. '
549 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn) 549 b'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)
550 ) 550 )
551 util.copyfile(copy_fn, working_fn) 551 util.copyfile(copy_fn, working_fn)
552 552
553 return 1 553 return 1
554 finally: 554 finally:
555 ui.note(_('cleaning up temp directory\n')) 555 ui.note(_(b'cleaning up temp directory\n'))
556 shutil.rmtree(tmproot) 556 shutil.rmtree(tmproot)
557 557
558 558
559 extdiffopts = ( 559 extdiffopts = (
560 [ 560 [
561 ('o', 'option', [], _('pass option to comparison program'), _('OPT')),
562 ('r', 'rev', [], _('revision'), _('REV')),
563 ('c', 'change', '', _('change made by revision'), _('REV')),
564 ( 561 (
565 '', 562 b'o',
566 'per-file', 563 b'option',
564 [],
565 _(b'pass option to comparison program'),
566 _(b'OPT'),
567 ),
568 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
569 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
570 (
571 b'',
572 b'per-file',
567 False, 573 False,
568 _('compare each file instead of revision snapshots'), 574 _(b'compare each file instead of revision snapshots'),
569 ), 575 ),
570 ( 576 (
571 '', 577 b'',
572 'confirm', 578 b'confirm',
573 False, 579 False,
574 _('prompt user before each external program invocation'), 580 _(b'prompt user before each external program invocation'),
575 ), 581 ),
576 ('', 'patch', None, _('compare patches for two revisions')), 582 (b'', b'patch', None, _(b'compare patches for two revisions')),
577 ] 583 ]
578 + cmdutil.walkopts 584 + cmdutil.walkopts
579 + cmdutil.subrepoopts 585 + cmdutil.subrepoopts
580 ) 586 )
581 587
582 588
583 @command( 589 @command(
584 'extdiff', 590 b'extdiff',
585 [('p', 'program', '', _('comparison program to run'), _('CMD')),] 591 [(b'p', b'program', b'', _(b'comparison program to run'), _(b'CMD')),]
586 + extdiffopts, 592 + extdiffopts,
587 _('hg extdiff [OPT]... [FILE]...'), 593 _(b'hg extdiff [OPT]... [FILE]...'),
588 helpcategory=command.CATEGORY_FILE_CONTENTS, 594 helpcategory=command.CATEGORY_FILE_CONTENTS,
589 inferrepo=True, 595 inferrepo=True,
590 ) 596 )
591 def extdiff(ui, repo, *pats, **opts): 597 def extdiff(ui, repo, *pats, **opts):
592 '''use external program to diff repository (or selected files) 598 '''use external program to diff repository (or selected files)
618 624
619 The --confirm option will prompt the user before each invocation of 625 The --confirm option will prompt the user before each invocation of
620 the external program. It is ignored if --per-file isn't specified. 626 the external program. It is ignored if --per-file isn't specified.
621 ''' 627 '''
622 opts = pycompat.byteskwargs(opts) 628 opts = pycompat.byteskwargs(opts)
623 program = opts.get('program') 629 program = opts.get(b'program')
624 option = opts.get('option') 630 option = opts.get(b'option')
625 if not program: 631 if not program:
626 program = 'diff' 632 program = b'diff'
627 option = option or ['-Npru'] 633 option = option or [b'-Npru']
628 cmdline = ' '.join(map(procutil.shellquote, [program] + option)) 634 cmdline = b' '.join(map(procutil.shellquote, [program] + option))
629 return dodiff(ui, repo, cmdline, pats, opts) 635 return dodiff(ui, repo, cmdline, pats, opts)
630 636
631 637
632 class savedcmd(object): 638 class savedcmd(object):
633 """use external program to diff repository (or selected files) 639 """use external program to diff repository (or selected files)
653 self._cmdline = cmdline 659 self._cmdline = cmdline
654 self._isgui = isgui 660 self._isgui = isgui
655 661
656 def __call__(self, ui, repo, *pats, **opts): 662 def __call__(self, ui, repo, *pats, **opts):
657 opts = pycompat.byteskwargs(opts) 663 opts = pycompat.byteskwargs(opts)
658 options = ' '.join(map(procutil.shellquote, opts['option'])) 664 options = b' '.join(map(procutil.shellquote, opts[b'option']))
659 if options: 665 if options:
660 options = ' ' + options 666 options = b' ' + options
661 return dodiff( 667 return dodiff(
662 ui, repo, self._cmdline + options, pats, opts, guitool=self._isgui 668 ui, repo, self._cmdline + options, pats, opts, guitool=self._isgui
663 ) 669 )
664 670
665 671
666 def uisetup(ui): 672 def uisetup(ui):
667 for cmd, path in ui.configitems('extdiff'): 673 for cmd, path in ui.configitems(b'extdiff'):
668 path = util.expandpath(path) 674 path = util.expandpath(path)
669 if cmd.startswith('cmd.'): 675 if cmd.startswith(b'cmd.'):
670 cmd = cmd[4:] 676 cmd = cmd[4:]
671 if not path: 677 if not path:
672 path = procutil.findexe(cmd) 678 path = procutil.findexe(cmd)
673 if path is None: 679 if path is None:
674 path = filemerge.findexternaltool(ui, cmd) or cmd 680 path = filemerge.findexternaltool(ui, cmd) or cmd
675 diffopts = ui.config('extdiff', 'opts.' + cmd) 681 diffopts = ui.config(b'extdiff', b'opts.' + cmd)
676 cmdline = procutil.shellquote(path) 682 cmdline = procutil.shellquote(path)
677 if diffopts: 683 if diffopts:
678 cmdline += ' ' + diffopts 684 cmdline += b' ' + diffopts
679 isgui = ui.configbool('extdiff', 'gui.' + cmd) 685 isgui = ui.configbool(b'extdiff', b'gui.' + cmd)
680 elif cmd.startswith('opts.') or cmd.startswith('gui.'): 686 elif cmd.startswith(b'opts.') or cmd.startswith(b'gui.'):
681 continue 687 continue
682 else: 688 else:
683 if path: 689 if path:
684 # case "cmd = path opts" 690 # case "cmd = path opts"
685 cmdline = path 691 cmdline = path
689 path = procutil.findexe(cmd) 695 path = procutil.findexe(cmd)
690 if path is None: 696 if path is None:
691 path = filemerge.findexternaltool(ui, cmd) or cmd 697 path = filemerge.findexternaltool(ui, cmd) or cmd
692 cmdline = procutil.shellquote(path) 698 cmdline = procutil.shellquote(path)
693 diffopts = False 699 diffopts = False
694 isgui = ui.configbool('extdiff', 'gui.' + cmd) 700 isgui = ui.configbool(b'extdiff', b'gui.' + cmd)
695 # look for diff arguments in [diff-tools] then [merge-tools] 701 # look for diff arguments in [diff-tools] then [merge-tools]
696 if not diffopts: 702 if not diffopts:
697 key = cmd + '.diffargs' 703 key = cmd + b'.diffargs'
698 for section in ('diff-tools', 'merge-tools'): 704 for section in (b'diff-tools', b'merge-tools'):
699 args = ui.config(section, key) 705 args = ui.config(section, key)
700 if args: 706 if args:
701 cmdline += ' ' + args 707 cmdline += b' ' + args
702 if isgui is None: 708 if isgui is None:
703 isgui = ui.configbool(section, cmd + '.gui') or False 709 isgui = ui.configbool(section, cmd + b'.gui') or False
704 break 710 break
705 command( 711 command(
706 cmd, 712 cmd,
707 extdiffopts[:], 713 extdiffopts[:],
708 _('hg %s [OPTION]... [FILE]...') % cmd, 714 _(b'hg %s [OPTION]... [FILE]...') % cmd,
709 helpcategory=command.CATEGORY_FILE_CONTENTS, 715 helpcategory=command.CATEGORY_FILE_CONTENTS,
710 inferrepo=True, 716 inferrepo=True,
711 )(savedcmd(path, cmdline, isgui)) 717 )(savedcmd(path, cmdline, isgui))
712 718
713 719