comparison hgext/extdiff.py @ 45126:48c38018bd77

extdiff: refactor logic which does diff of patches The current extdiff code is hard to understand on first look. Took me few hours to grasp the code. Before adding more things, decided to do some refactoring. Differential Revision: https://phab.mercurial-scm.org/D8686
author Pulkit Goyal <7895pulkit@gmail.com>
date Tue, 07 Jul 2020 12:42:39 +0530
parents 8e8fd938ca07
children da2e69a278df
comparison
equal deleted inserted replaced
45125:f55099982bc5 45126:48c38018bd77
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,