##// END OF EJS Templates
extdiff: refactor logic to diff revs of versions of files...
Pulkit Goyal -
r45687:da2e69a2 default
parent child Browse files
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 0
581 tmproot,
464 ]
582 cmdline,
465 rev1b = b'@%d' % repo[node1b].rev()
583 do3way,
466 else:
584 guitool,
467 dir1b = None
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