Show More
@@ -382,6 +382,145 b' def diffpatch(ui, repo, node1a, node2, t' | |||||
382 | return 1 |
|
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 | def dodiff(ui, repo, cmdline, pats, opts, guitool=False): |
|
524 | def dodiff(ui, repo, cmdline, pats, opts, guitool=False): | |
386 | '''Do the actual diff: |
|
525 | '''Do the actual diff: | |
387 |
|
526 | |||
@@ -406,9 +545,6 b' def dodiff(ui, repo, cmdline, pats, opts' | |||||
406 | else: |
|
545 | else: | |
407 | ctx1b = repo[nullid] |
|
546 | ctx1b = repo[nullid] | |
408 |
|
547 | |||
409 | perfile = opts.get(b'per_file') |
|
|||
410 | confirm = opts.get(b'confirm') |
|
|||
411 |
|
||||
412 | node1a = ctx1a.node() |
|
548 | node1a = ctx1a.node() | |
413 | node1b = ctx1b.node() |
|
549 | node1b = ctx1b.node() | |
414 | node2 = ctx2.node() |
|
550 | node2 = ctx2.node() | |
@@ -418,33 +554,15 b' def dodiff(ui, repo, cmdline, pats, opts' | |||||
418 | if node1b == nullid: |
|
554 | if node1b == nullid: | |
419 | do3way = False |
|
555 | do3way = False | |
420 |
|
556 | |||
421 | subrepos = opts.get(b'subrepos') |
|
|||
422 |
|
||||
423 | matcher = scmutil.match(repo[node2], pats, opts) |
|
557 | matcher = scmutil.match(repo[node2], pats, opts) | |
424 |
|
558 | |||
425 | if opts.get(b'patch'): |
|
559 | if opts.get(b'patch'): | |
426 | if subrepos: |
|
560 | if opts.get(b'subrepos'): | |
427 | raise error.Abort(_(b'--patch cannot be used with --subrepos')) |
|
561 | raise error.Abort(_(b'--patch cannot be used with --subrepos')) | |
428 | if perfile: |
|
562 | if opts.get(b'per_file'): | |
429 | raise error.Abort(_(b'--patch cannot be used with --per-file')) |
|
563 | raise error.Abort(_(b'--patch cannot be used with --per-file')) | |
430 | if node2 is None: |
|
564 | if node2 is None: | |
431 | raise error.Abort(_(b'--patch requires two revisions')) |
|
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 | tmproot = pycompat.mkdtemp(prefix=b'extdiff.') |
|
567 | tmproot = pycompat.mkdtemp(prefix=b'extdiff.') | |
450 | try: |
|
568 | try: | |
@@ -453,119 +571,20 b' def dodiff(ui, repo, cmdline, pats, opts' | |||||
453 | ui, repo, node1a, node2, tmproot, matcher, cmdline, do3way |
|
571 | ui, repo, node1a, node2, tmproot, matcher, cmdline, do3way | |
454 | ) |
|
572 | ) | |
455 |
|
573 | |||
456 | # Always make a copy of node1a (and node1b, if applicable) |
|
574 | return diffrevs( | |
457 | dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) |
|
575 | ui, | |
458 | dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[0] |
|
576 | repo, | |
459 | rev1a = b'@%d' % repo[node1a].rev() |
|
577 | node1a, | |
460 | if do3way: |
|
578 | node1b, | |
461 | dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) |
|
579 | node2, | |
462 | dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot, subrepos)[ |
|
580 | matcher, | |
463 |
|
|
581 | tmproot, | |
464 |
|
|
582 | cmdline, | |
465 | rev1b = b'@%d' % repo[node1b].rev() |
|
583 | do3way, | |
466 | else: |
|
584 | guitool, | |
467 |
|
|
585 | opts, | |
468 | rev1b = b'' |
|
586 | ) | |
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 |
|
|||
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 | finally: |
|
588 | finally: | |
570 | ui.note(_(b'cleaning up temp directory\n')) |
|
589 | ui.note(_(b'cleaning up temp directory\n')) | |
571 | shutil.rmtree(tmproot) |
|
590 | shutil.rmtree(tmproot) |
General Comments 0
You need to be logged in to leave comments.
Login now