comparison mercurial/filemerge.py @ 35907:9037c29e9f53

filemerge: support passing labels to external merge tools This adds $labellocal, $labelother, and $labelbase to the replacement set for merge-tools.<tool>.args config variables, and to the environment as HG_MY_LABEL, HG_OTHER_LABEL, and HG_BASE_LABEL, respectively. We also add merge-tools.<tool>.mergemarkers and merge-tools.<tool>.mergemarkertemplate config variables as counterparts of the variables available in [ui]. We are intentionally *not* respecting ui.mergemarkers when calling out to external merge programs; too often the default template will be too wide to display comfortably in most GUIs. Differential Revision: https://phab.mercurial-scm.org/D2011
author Kyle Lippincott <spectral@google.com>
date Wed, 17 Jan 2018 17:35:05 -0800
parents c87926ebe096
children 3ab9d74dd1c5
comparison
equal deleted inserted replaced
35906:cd2342302928 35907:9037c29e9f53
511 unused, unused, unused, back = files 511 unused, unused, unused, back = files
512 a = _workingpath(repo, fcd) 512 a = _workingpath(repo, fcd)
513 b, c = _maketempfiles(repo, fco, fca) 513 b, c = _maketempfiles(repo, fco, fca)
514 try: 514 try:
515 out = "" 515 out = ""
516 mylabel, otherlabel = labels[:2]
517 if len(labels) >= 3:
518 baselabel = labels[2]
519 else:
520 baselabel = 'base'
516 env = {'HG_FILE': fcd.path(), 521 env = {'HG_FILE': fcd.path(),
517 'HG_MY_NODE': short(mynode), 522 'HG_MY_NODE': short(mynode),
518 'HG_OTHER_NODE': str(fco.changectx()), 523 'HG_OTHER_NODE': str(fco.changectx()),
519 'HG_BASE_NODE': str(fca.changectx()), 524 'HG_BASE_NODE': str(fca.changectx()),
520 'HG_MY_ISLINK': 'l' in fcd.flags(), 525 'HG_MY_ISLINK': 'l' in fcd.flags(),
521 'HG_OTHER_ISLINK': 'l' in fco.flags(), 526 'HG_OTHER_ISLINK': 'l' in fco.flags(),
522 'HG_BASE_ISLINK': 'l' in fca.flags(), 527 'HG_BASE_ISLINK': 'l' in fca.flags(),
528 'HG_MY_LABEL': mylabel,
529 'HG_OTHER_LABEL': otherlabel,
530 'HG_BASE_LABEL': baselabel,
523 } 531 }
524 ui = repo.ui 532 ui = repo.ui
525 533
526 args = _toolstr(ui, tool, "args") 534 args = _toolstr(ui, tool, "args")
527 if "$output" in args: 535 if "$output" in args:
528 # read input from backup, write to original 536 # read input from backup, write to original
529 out = a 537 out = a
530 a = repo.wvfs.join(back.path()) 538 a = repo.wvfs.join(back.path())
531 replace = {'local': a, 'base': b, 'other': c, 'output': out} 539 replace = {'local': a, 'base': b, 'other': c, 'output': out,
540 'labellocal': mylabel, 'labelother': otherlabel,
541 'labelbase': baselabel}
532 args = util.interpolate(br'\$', replace, args, 542 args = util.interpolate(br'\$', replace, args,
533 lambda s: util.shellquote(util.localpath(s))) 543 lambda s: util.shellquote(util.localpath(s)))
534 cmd = toolpath + ' ' + args 544 cmd = toolpath + ' ' + args
535 if _toolbool(ui, tool, "gui"): 545 if _toolbool(ui, tool, "gui"):
536 repo.ui.status(_('running merge tool %s for file %s\n') % 546 repo.ui.status(_('running merge tool %s for file %s\n') %
564 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ') 574 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
565 return util.ellipsis(mark, 80 - 8) 575 return util.ellipsis(mark, 80 - 8)
566 576
567 _defaultconflictlabels = ['local', 'other'] 577 _defaultconflictlabels = ['local', 'other']
568 578
569 def _formatlabels(repo, fcd, fco, fca, labels): 579 def _formatlabels(repo, fcd, fco, fca, labels, tool=None):
570 """Formats the given labels using the conflict marker template. 580 """Formats the given labels using the conflict marker template.
571 581
572 Returns a list of formatted labels. 582 Returns a list of formatted labels.
573 """ 583 """
574 cd = fcd.changectx() 584 cd = fcd.changectx()
575 co = fco.changectx() 585 co = fco.changectx()
576 ca = fca.changectx() 586 ca = fca.changectx()
577 587
578 ui = repo.ui 588 ui = repo.ui
579 template = ui.config('ui', 'mergemarkertemplate') 589 template = ui.config('ui', 'mergemarkertemplate')
590 if tool is not None:
591 template = _toolstr(ui, tool, 'mergemarkertemplate', template)
580 template = templater.unquotestring(template) 592 template = templater.unquotestring(template)
581 tres = formatter.templateresources(ui, repo) 593 tres = formatter.templateresources(ui, repo)
582 tmpl = formatter.maketemplater(ui, template, defaults=templatekw.keywords, 594 tmpl = formatter.maketemplater(ui, template, defaults=templatekw.keywords,
583 resources=tres) 595 resources=tres)
584 596
704 if tool in internals: 716 if tool in internals:
705 func = internals[tool] 717 func = internals[tool]
706 mergetype = func.mergetype 718 mergetype = func.mergetype
707 onfailure = func.onfailure 719 onfailure = func.onfailure
708 precheck = func.precheck 720 precheck = func.precheck
721 isexternal = False
709 else: 722 else:
710 if wctx.isinmemory(): 723 if wctx.isinmemory():
711 func = _xmergeimm 724 func = _xmergeimm
712 else: 725 else:
713 func = _xmerge 726 func = _xmerge
714 mergetype = fullmerge 727 mergetype = fullmerge
715 onfailure = _("merging %s failed!\n") 728 onfailure = _("merging %s failed!\n")
716 precheck = None 729 precheck = None
730 isexternal = True
717 731
718 toolconf = tool, toolpath, binary, symlink 732 toolconf = tool, toolpath, binary, symlink
719 733
720 if mergetype == nomerge: 734 if mergetype == nomerge:
721 r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels) 735 r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
741 755
742 back = _makebackup(repo, ui, wctx, fcd, premerge) 756 back = _makebackup(repo, ui, wctx, fcd, premerge)
743 files = (None, None, None, back) 757 files = (None, None, None, back)
744 r = 1 758 r = 1
745 try: 759 try:
746 markerstyle = ui.config('ui', 'mergemarkers') 760 internalmarkerstyle = ui.config('ui', 'mergemarkers')
761 if isexternal:
762 markerstyle = _toolstr(ui, tool, 'mergemarkers')
763 else:
764 markerstyle = internalmarkerstyle
765
747 if not labels: 766 if not labels:
748 labels = _defaultconflictlabels 767 labels = _defaultconflictlabels
768 formattedlabels = labels
749 if markerstyle != 'basic': 769 if markerstyle != 'basic':
750 labels = _formatlabels(repo, fcd, fco, fca, labels) 770 formattedlabels = _formatlabels(repo, fcd, fco, fca, labels,
771 tool=tool)
751 772
752 if premerge and mergetype == fullmerge: 773 if premerge and mergetype == fullmerge:
753 r = _premerge(repo, fcd, fco, fca, toolconf, files, labels=labels) 774 # conflict markers generated by premerge will use 'detailed'
775 # settings if either ui.mergemarkers or the tool's mergemarkers
776 # setting is 'detailed'. This way tools can have basic labels in
777 # space-constrained areas of the UI, but still get full information
778 # in conflict markers if premerge is 'keep' or 'keep-merge3'.
779 premergelabels = labels
780 labeltool = None
781 if markerstyle != 'basic':
782 # respect 'tool's mergemarkertemplate (which defaults to
783 # ui.mergemarkertemplate)
784 labeltool = tool
785 if internalmarkerstyle != 'basic' or markerstyle != 'basic':
786 premergelabels = _formatlabels(repo, fcd, fco, fca,
787 premergelabels, tool=labeltool)
788
789 r = _premerge(repo, fcd, fco, fca, toolconf, files,
790 labels=premergelabels)
754 # complete if premerge successful (r is 0) 791 # complete if premerge successful (r is 0)
755 return not r, r, False 792 return not r, r, False
756 793
757 needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca, 794 needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca,
758 toolconf, files, labels=labels) 795 toolconf, files, labels=formattedlabels)
759 796
760 if needcheck: 797 if needcheck:
761 r = _check(repo, r, ui, tool, fcd, files) 798 r = _check(repo, r, ui, tool, fcd, files)
762 799
763 if r: 800 if r: