Mercurial > public > mercurial-scm > hg
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 |