348 with ui.timeblockedsection(b'extdiff'): |
348 with ui.timeblockedsection(b'extdiff'): |
349 for proc in waitprocs: |
349 for proc in waitprocs: |
350 proc.wait() |
350 proc.wait() |
351 |
351 |
352 |
352 |
|
353 def diffpatch(ui, repo, node1a, node2, tmproot, matcher, cmdline, do3way): |
|
354 template = b'hg-%h.patch' |
|
355 with formatter.nullformatter(ui, b'extdiff', {}) as fm: |
|
356 cmdutil.export( |
|
357 repo, |
|
358 [repo[node1a].rev(), repo[node2].rev()], |
|
359 fm, |
|
360 fntemplate=repo.vfs.reljoin(tmproot, template), |
|
361 match=matcher, |
|
362 ) |
|
363 label1a = cmdutil.makefilename(repo[node1a], template) |
|
364 label2 = cmdutil.makefilename(repo[node2], template) |
|
365 dir1a = repo.vfs.reljoin(tmproot, label1a) |
|
366 dir2 = repo.vfs.reljoin(tmproot, label2) |
|
367 dir1b = None |
|
368 label1b = None |
|
369 cmdline = formatcmdline( |
|
370 cmdline, |
|
371 repo.root, |
|
372 do3way=do3way, |
|
373 parent1=dir1a, |
|
374 plabel1=label1a, |
|
375 parent2=dir1b, |
|
376 plabel2=label1b, |
|
377 child=dir2, |
|
378 clabel=label2, |
|
379 ) |
|
380 ui.debug(b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot)) |
|
381 ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff') |
|
382 return 1 |
|
383 |
|
384 |
353 def dodiff(ui, repo, cmdline, pats, opts, guitool=False): |
385 def dodiff(ui, repo, cmdline, pats, opts, guitool=False): |
354 '''Do the actual diff: |
386 '''Do the actual diff: |
355 |
387 |
356 - copy to a temp structure if diffing 2 internal revisions |
388 - copy to a temp structure if diffing 2 internal revisions |
357 - copy to a temp structure if diffing working revision with |
389 - copy to a temp structure if diffing working revision with |
414 if not common: |
446 if not common: |
415 return 0 |
447 return 0 |
416 |
448 |
417 tmproot = pycompat.mkdtemp(prefix=b'extdiff.') |
449 tmproot = pycompat.mkdtemp(prefix=b'extdiff.') |
418 try: |
450 try: |
419 if not opts.get(b'patch'): |
451 if opts.get(b'patch'): |
420 # Always make a copy of node1a (and node1b, if applicable) |
452 return diffpatch( |
421 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) |
453 ui, repo, node1a, node2, tmproot, matcher, cmdline, do3way |
422 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[ |
454 ) |
|
455 |
|
456 # Always make a copy of node1a (and node1b, if applicable) |
|
457 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) |
|
458 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[0] |
|
459 rev1a = b'@%d' % repo[node1a].rev() |
|
460 if do3way: |
|
461 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) |
|
462 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot, subrepos)[ |
423 0 |
463 0 |
424 ] |
464 ] |
425 rev1a = b'@%d' % repo[node1a].rev() |
465 rev1b = b'@%d' % repo[node1b].rev() |
|
466 else: |
|
467 dir1b = None |
|
468 rev1b = b'' |
|
469 |
|
470 fnsandstat = [] |
|
471 |
|
472 # If node2 in not the wc or there is >1 change, copy it |
|
473 dir2root = b'' |
|
474 rev2 = b'' |
|
475 if node2: |
|
476 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0] |
|
477 rev2 = b'@%d' % repo[node2].rev() |
|
478 elif len(common) > 1: |
|
479 # we only actually need to get the files to copy back to |
|
480 # the working dir in this case (because the other cases |
|
481 # are: diffing 2 revisions or single file -- in which case |
|
482 # the file is already directly passed to the diff tool). |
|
483 dir2, fnsandstat = snapshot( |
|
484 ui, repo, modadd, None, tmproot, subrepos |
|
485 ) |
|
486 else: |
|
487 # This lets the diff tool open the changed file directly |
|
488 dir2 = b'' |
|
489 dir2root = repo.root |
|
490 |
|
491 label1a = rev1a |
|
492 label1b = rev1b |
|
493 label2 = rev2 |
|
494 |
|
495 # If only one change, diff the files instead of the directories |
|
496 # Handle bogus modifies correctly by checking if the files exist |
|
497 if len(common) == 1: |
|
498 common_file = util.localpath(common.pop()) |
|
499 dir1a = os.path.join(tmproot, dir1a, common_file) |
|
500 label1a = common_file + rev1a |
|
501 if not os.path.isfile(dir1a): |
|
502 dir1a = pycompat.osdevnull |
426 if do3way: |
503 if do3way: |
427 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) |
504 dir1b = os.path.join(tmproot, dir1b, common_file) |
428 dir1b = snapshot( |
505 label1b = common_file + rev1b |
429 ui, repo, dir1b_files, node1b, tmproot, subrepos |
506 if not os.path.isfile(dir1b): |
430 )[0] |
507 dir1b = pycompat.osdevnull |
431 rev1b = b'@%d' % repo[node1b].rev() |
508 dir2 = os.path.join(dir2root, dir2, common_file) |
432 else: |
509 label2 = common_file + rev2 |
433 dir1b = None |
|
434 rev1b = b'' |
|
435 |
|
436 fnsandstat = [] |
|
437 |
|
438 # If node2 in not the wc or there is >1 change, copy it |
|
439 dir2root = b'' |
|
440 rev2 = b'' |
|
441 if node2: |
|
442 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0] |
|
443 rev2 = b'@%d' % repo[node2].rev() |
|
444 elif len(common) > 1: |
|
445 # we only actually need to get the files to copy back to |
|
446 # the working dir in this case (because the other cases |
|
447 # are: diffing 2 revisions or single file -- in which case |
|
448 # the file is already directly passed to the diff tool). |
|
449 dir2, fnsandstat = snapshot( |
|
450 ui, repo, modadd, None, tmproot, subrepos |
|
451 ) |
|
452 else: |
|
453 # This lets the diff tool open the changed file directly |
|
454 dir2 = b'' |
|
455 dir2root = repo.root |
|
456 |
|
457 label1a = rev1a |
|
458 label1b = rev1b |
|
459 label2 = rev2 |
|
460 |
|
461 # If only one change, diff the files instead of the directories |
|
462 # Handle bogus modifies correctly by checking if the files exist |
|
463 if len(common) == 1: |
|
464 common_file = util.localpath(common.pop()) |
|
465 dir1a = os.path.join(tmproot, dir1a, common_file) |
|
466 label1a = common_file + rev1a |
|
467 if not os.path.isfile(dir1a): |
|
468 dir1a = pycompat.osdevnull |
|
469 if do3way: |
|
470 dir1b = os.path.join(tmproot, dir1b, common_file) |
|
471 label1b = common_file + rev1b |
|
472 if not os.path.isfile(dir1b): |
|
473 dir1b = pycompat.osdevnull |
|
474 dir2 = os.path.join(dir2root, dir2, common_file) |
|
475 label2 = common_file + rev2 |
|
476 else: |
|
477 template = b'hg-%h.patch' |
|
478 with formatter.nullformatter(ui, b'extdiff', {}) as fm: |
|
479 cmdutil.export( |
|
480 repo, |
|
481 [repo[node1a].rev(), repo[node2].rev()], |
|
482 fm, |
|
483 fntemplate=repo.vfs.reljoin(tmproot, template), |
|
484 match=matcher, |
|
485 ) |
|
486 label1a = cmdutil.makefilename(repo[node1a], template) |
|
487 label2 = cmdutil.makefilename(repo[node2], template) |
|
488 dir1a = repo.vfs.reljoin(tmproot, label1a) |
|
489 dir2 = repo.vfs.reljoin(tmproot, label2) |
|
490 dir1b = None |
|
491 label1b = None |
|
492 fnsandstat = [] |
|
493 |
510 |
494 if not perfile: |
511 if not perfile: |
495 # Run the external tool on the 2 temp directories or the patches |
512 # Run the external tool on the 2 temp directories or the patches |
496 cmdline = formatcmdline( |
513 cmdline = formatcmdline( |
497 cmdline, |
514 cmdline, |