Show More
@@ -382,6 +382,145 b' def diffpatch(ui, repo, node1a, node2, t' | |||
|
382 | 382 | return 1 |
|
383 | 383 | |
|
384 | 384 | |
|
385 | def diffrevs( | |
|
386 | ui, | |
|
387 | repo, | |
|
388 | node1a, | |
|
389 | node1b, | |
|
390 | node2, | |
|
391 | matcher, | |
|
392 | tmproot, | |
|
393 | cmdline, | |
|
394 | do3way, | |
|
395 | guitool, | |
|
396 | opts, | |
|
397 | ): | |
|
398 | ||
|
399 | subrepos = opts.get(b'subrepos') | |
|
400 | st = repo.status(node1a, node2, matcher, listsubrepos=subrepos) | |
|
401 | mod_a, add_a, rem_a = set(st.modified), set(st.added), set(st.removed) | |
|
402 | if do3way: | |
|
403 | stb = repo.status(node1b, node2, matcher, listsubrepos=subrepos) | |
|
404 | mod_b, add_b, rem_b = ( | |
|
405 | set(stb.modified), | |
|
406 | set(stb.added), | |
|
407 | set(stb.removed), | |
|
408 | ) | |
|
409 | else: | |
|
410 | mod_b, add_b, rem_b = set(), set(), set() | |
|
411 | modadd = mod_a | add_a | mod_b | add_b | |
|
412 | common = modadd | rem_a | rem_b | |
|
413 | if not common: | |
|
414 | return 0 | |
|
415 | # Always make a copy of node1a (and node1b, if applicable) | |
|
416 | dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) | |
|
417 | dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[0] | |
|
418 | rev1a = b'@%d' % repo[node1a].rev() | |
|
419 | if do3way: | |
|
420 | dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) | |
|
421 | dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot, subrepos)[0] | |
|
422 | rev1b = b'@%d' % repo[node1b].rev() | |
|
423 | else: | |
|
424 | dir1b = None | |
|
425 | rev1b = b'' | |
|
426 | ||
|
427 | fnsandstat = [] | |
|
428 | ||
|
429 | # If node2 in not the wc or there is >1 change, copy it | |
|
430 | dir2root = b'' | |
|
431 | rev2 = b'' | |
|
432 | if node2: | |
|
433 | dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0] | |
|
434 | rev2 = b'@%d' % repo[node2].rev() | |
|
435 | elif len(common) > 1: | |
|
436 | # we only actually need to get the files to copy back to | |
|
437 | # the working dir in this case (because the other cases | |
|
438 | # are: diffing 2 revisions or single file -- in which case | |
|
439 | # the file is already directly passed to the diff tool). | |
|
440 | dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot, subrepos) | |
|
441 | else: | |
|
442 | # This lets the diff tool open the changed file directly | |
|
443 | dir2 = b'' | |
|
444 | dir2root = repo.root | |
|
445 | ||
|
446 | label1a = rev1a | |
|
447 | label1b = rev1b | |
|
448 | label2 = rev2 | |
|
449 | ||
|
450 | # If only one change, diff the files instead of the directories | |
|
451 | # Handle bogus modifies correctly by checking if the files exist | |
|
452 | if len(common) == 1: | |
|
453 | common_file = util.localpath(common.pop()) | |
|
454 | dir1a = os.path.join(tmproot, dir1a, common_file) | |
|
455 | label1a = common_file + rev1a | |
|
456 | if not os.path.isfile(dir1a): | |
|
457 | dir1a = pycompat.osdevnull | |
|
458 | if do3way: | |
|
459 | dir1b = os.path.join(tmproot, dir1b, common_file) | |
|
460 | label1b = common_file + rev1b | |
|
461 | if not os.path.isfile(dir1b): | |
|
462 | dir1b = pycompat.osdevnull | |
|
463 | dir2 = os.path.join(dir2root, dir2, common_file) | |
|
464 | label2 = common_file + rev2 | |
|
465 | ||
|
466 | if not opts.get(b'per_file'): | |
|
467 | # Run the external tool on the 2 temp directories or the patches | |
|
468 | cmdline = formatcmdline( | |
|
469 | cmdline, | |
|
470 | repo.root, | |
|
471 | do3way=do3way, | |
|
472 | parent1=dir1a, | |
|
473 | plabel1=label1a, | |
|
474 | parent2=dir1b, | |
|
475 | plabel2=label1b, | |
|
476 | child=dir2, | |
|
477 | clabel=label2, | |
|
478 | ) | |
|
479 | ui.debug(b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot)) | |
|
480 | ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff') | |
|
481 | else: | |
|
482 | # Run the external tool once for each pair of files | |
|
483 | _runperfilediff( | |
|
484 | cmdline, | |
|
485 | repo.root, | |
|
486 | ui, | |
|
487 | guitool=guitool, | |
|
488 | do3way=do3way, | |
|
489 | confirm=opts.get(b'confirm'), | |
|
490 | commonfiles=common, | |
|
491 | tmproot=tmproot, | |
|
492 | dir1a=dir1a, | |
|
493 | dir1b=dir1b, | |
|
494 | dir2root=dir2root, | |
|
495 | dir2=dir2, | |
|
496 | rev1a=rev1a, | |
|
497 | rev1b=rev1b, | |
|
498 | rev2=rev2, | |
|
499 | ) | |
|
500 | ||
|
501 | for copy_fn, working_fn, st in fnsandstat: | |
|
502 | cpstat = os.lstat(copy_fn) | |
|
503 | # Some tools copy the file and attributes, so mtime may not detect | |
|
504 | # all changes. A size check will detect more cases, but not all. | |
|
505 | # The only certain way to detect every case is to diff all files, | |
|
506 | # which could be expensive. | |
|
507 | # copyfile() carries over the permission, so the mode check could | |
|
508 | # be in an 'elif' branch, but for the case where the file has | |
|
509 | # changed without affecting mtime or size. | |
|
510 | if ( | |
|
511 | cpstat[stat.ST_MTIME] != st[stat.ST_MTIME] | |
|
512 | or cpstat.st_size != st.st_size | |
|
513 | or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100) | |
|
514 | ): | |
|
515 | ui.debug( | |
|
516 | b'file changed while diffing. ' | |
|
517 | b'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn) | |
|
518 | ) | |
|
519 | util.copyfile(copy_fn, working_fn) | |
|
520 | ||
|
521 | return 1 | |
|
522 | ||
|
523 | ||
|
385 | 524 | def dodiff(ui, repo, cmdline, pats, opts, guitool=False): |
|
386 | 525 | '''Do the actual diff: |
|
387 | 526 | |
@@ -406,9 +545,6 b' def dodiff(ui, repo, cmdline, pats, opts' | |||
|
406 | 545 | else: |
|
407 | 546 | ctx1b = repo[nullid] |
|
408 | 547 | |
|
409 | perfile = opts.get(b'per_file') | |
|
410 | confirm = opts.get(b'confirm') | |
|
411 | ||
|
412 | 548 | node1a = ctx1a.node() |
|
413 | 549 | node1b = ctx1b.node() |
|
414 | 550 | node2 = ctx2.node() |
@@ -418,33 +554,15 b' def dodiff(ui, repo, cmdline, pats, opts' | |||
|
418 | 554 | if node1b == nullid: |
|
419 | 555 | do3way = False |
|
420 | 556 | |
|
421 | subrepos = opts.get(b'subrepos') | |
|
422 | ||
|
423 | 557 | matcher = scmutil.match(repo[node2], pats, opts) |
|
424 | 558 | |
|
425 | 559 | if opts.get(b'patch'): |
|
426 | if subrepos: | |
|
560 | if opts.get(b'subrepos'): | |
|
427 | 561 | raise error.Abort(_(b'--patch cannot be used with --subrepos')) |
|
428 | if perfile: | |
|
562 | if opts.get(b'per_file'): | |
|
429 | 563 | raise error.Abort(_(b'--patch cannot be used with --per-file')) |
|
430 | 564 | if node2 is None: |
|
431 | 565 | raise error.Abort(_(b'--patch requires two revisions')) |
|
432 | else: | |
|
433 | st = repo.status(node1a, node2, matcher, listsubrepos=subrepos) | |
|
434 | mod_a, add_a, rem_a = set(st.modified), set(st.added), set(st.removed) | |
|
435 | if do3way: | |
|
436 | stb = repo.status(node1b, node2, matcher, listsubrepos=subrepos) | |
|
437 | mod_b, add_b, rem_b = ( | |
|
438 | set(stb.modified), | |
|
439 | set(stb.added), | |
|
440 | set(stb.removed), | |
|
441 | ) | |
|
442 | else: | |
|
443 | mod_b, add_b, rem_b = set(), set(), set() | |
|
444 | modadd = mod_a | add_a | mod_b | add_b | |
|
445 | common = modadd | rem_a | rem_b | |
|
446 | if not common: | |
|
447 | return 0 | |
|
448 | 566 | |
|
449 | 567 | tmproot = pycompat.mkdtemp(prefix=b'extdiff.') |
|
450 | 568 | try: |
@@ -453,119 +571,20 b' def dodiff(ui, repo, cmdline, pats, opts' | |||
|
453 | 571 | ui, repo, node1a, node2, tmproot, matcher, cmdline, do3way |
|
454 | 572 | ) |
|
455 | 573 | |
|
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)[ | |
|
463 |
|
|
|
464 |
|
|
|
465 | rev1b = b'@%d' % repo[node1b].rev() | |
|
466 | else: | |
|
467 |
|
|
|
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 | |
|
503 | if do3way: | |
|
504 | dir1b = os.path.join(tmproot, dir1b, common_file) | |
|
505 | label1b = common_file + rev1b | |
|
506 | if not os.path.isfile(dir1b): | |
|
507 | dir1b = pycompat.osdevnull | |
|
508 | dir2 = os.path.join(dir2root, dir2, common_file) | |
|
509 | label2 = common_file + rev2 | |
|
574 | return diffrevs( | |
|
575 | ui, | |
|
576 | repo, | |
|
577 | node1a, | |
|
578 | node1b, | |
|
579 | node2, | |
|
580 | matcher, | |
|
581 | tmproot, | |
|
582 | cmdline, | |
|
583 | do3way, | |
|
584 | guitool, | |
|
585 | opts, | |
|
586 | ) | |
|
510 | 587 | |
|
511 | if not perfile: | |
|
512 | # Run the external tool on the 2 temp directories or the patches | |
|
513 | cmdline = formatcmdline( | |
|
514 | cmdline, | |
|
515 | repo.root, | |
|
516 | do3way=do3way, | |
|
517 | parent1=dir1a, | |
|
518 | plabel1=label1a, | |
|
519 | parent2=dir1b, | |
|
520 | plabel2=label1b, | |
|
521 | child=dir2, | |
|
522 | clabel=label2, | |
|
523 | ) | |
|
524 | ui.debug( | |
|
525 | b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot) | |
|
526 | ) | |
|
527 | ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff') | |
|
528 | else: | |
|
529 | # Run the external tool once for each pair of files | |
|
530 | _runperfilediff( | |
|
531 | cmdline, | |
|
532 | repo.root, | |
|
533 | ui, | |
|
534 | guitool=guitool, | |
|
535 | do3way=do3way, | |
|
536 | confirm=confirm, | |
|
537 | commonfiles=common, | |
|
538 | tmproot=tmproot, | |
|
539 | dir1a=dir1a, | |
|
540 | dir1b=dir1b, | |
|
541 | dir2root=dir2root, | |
|
542 | dir2=dir2, | |
|
543 | rev1a=rev1a, | |
|
544 | rev1b=rev1b, | |
|
545 | rev2=rev2, | |
|
546 | ) | |
|
547 | ||
|
548 | for copy_fn, working_fn, st in fnsandstat: | |
|
549 | cpstat = os.lstat(copy_fn) | |
|
550 | # Some tools copy the file and attributes, so mtime may not detect | |
|
551 | # all changes. A size check will detect more cases, but not all. | |
|
552 | # The only certain way to detect every case is to diff all files, | |
|
553 | # which could be expensive. | |
|
554 | # copyfile() carries over the permission, so the mode check could | |
|
555 | # be in an 'elif' branch, but for the case where the file has | |
|
556 | # changed without affecting mtime or size. | |
|
557 | if ( | |
|
558 | cpstat[stat.ST_MTIME] != st[stat.ST_MTIME] | |
|
559 | or cpstat.st_size != st.st_size | |
|
560 | or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100) | |
|
561 | ): | |
|
562 | ui.debug( | |
|
563 | b'file changed while diffing. ' | |
|
564 | b'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn) | |
|
565 | ) | |
|
566 | util.copyfile(copy_fn, working_fn) | |
|
567 | ||
|
568 | return 1 | |
|
569 | 588 | finally: |
|
570 | 589 | ui.note(_(b'cleaning up temp directory\n')) |
|
571 | 590 | shutil.rmtree(tmproot) |
General Comments 0
You need to be logged in to leave comments.
Login now