mercurial/filemerge.py
changeset 48586 fd9fe2658cda
parent 48585 07069fcd9a6e
child 48587 3c8cc987672e
equal deleted inserted replaced
48585:07069fcd9a6e 48586:fd9fe2658cda
   304             if newdata != data:
   304             if newdata != data:
   305                 util.writefile(file, newdata)
   305                 util.writefile(file, newdata)
   306 
   306 
   307 
   307 
   308 @internaltool(b'prompt', nomerge)
   308 @internaltool(b'prompt', nomerge)
   309 def _iprompt(repo, mynode, fcd, fco, fca, toolconf, labels=None):
   309 def _iprompt(repo, mynode, local, other, base, toolconf):
   310     """Asks the user which of the local `p1()` or the other `p2()` version to
   310     """Asks the user which of the local `p1()` or the other `p2()` version to
   311     keep as the merged version."""
   311     keep as the merged version."""
   312     ui = repo.ui
   312     ui = repo.ui
   313     fd = fcd.path()
   313     fd = local.fctx.path()
   314     uipathfn = scmutil.getuipathfn(repo)
   314     uipathfn = scmutil.getuipathfn(repo)
   315 
   315 
   316     # Avoid prompting during an in-memory merge since it doesn't support merge
   316     # Avoid prompting during an in-memory merge since it doesn't support merge
   317     # conflicts.
   317     # conflicts.
   318     if fcd.changectx().isinmemory():
   318     if local.fctx.changectx().isinmemory():
   319         raise error.InMemoryMergeConflictsError(
   319         raise error.InMemoryMergeConflictsError(
   320             b'in-memory merge does not support file conflicts'
   320             b'in-memory merge does not support file conflicts'
   321         )
   321         )
   322 
   322 
   323     prompts = partextras(labels)
   323     prompts = partextras([local.label, other.label])
   324     prompts[b'fd'] = uipathfn(fd)
   324     prompts[b'fd'] = uipathfn(fd)
   325     try:
   325     try:
   326         if fco.isabsent():
   326         if other.fctx.isabsent():
   327             index = ui.promptchoice(_localchangedotherdeletedmsg % prompts, 2)
   327             index = ui.promptchoice(_localchangedotherdeletedmsg % prompts, 2)
   328             choice = [b'local', b'other', b'unresolved'][index]
   328             choice = [b'local', b'other', b'unresolved'][index]
   329         elif fcd.isabsent():
   329         elif local.fctx.isabsent():
   330             index = ui.promptchoice(_otherchangedlocaldeletedmsg % prompts, 2)
   330             index = ui.promptchoice(_otherchangedlocaldeletedmsg % prompts, 2)
   331             choice = [b'other', b'local', b'unresolved'][index]
   331             choice = [b'other', b'local', b'unresolved'][index]
   332         else:
   332         else:
   333             # IMPORTANT: keep the last line of this prompt ("What do you want to
   333             # IMPORTANT: keep the last line of this prompt ("What do you want to
   334             # do?") very short, see comment next to _localchangedotherdeletedmsg
   334             # do?") very short, see comment next to _localchangedotherdeletedmsg
   345                 2,
   345                 2,
   346             )
   346             )
   347             choice = [b'local', b'other', b'unresolved'][index]
   347             choice = [b'local', b'other', b'unresolved'][index]
   348 
   348 
   349         if choice == b'other':
   349         if choice == b'other':
   350             return _iother(repo, mynode, fcd, fco, fca, toolconf, labels)
   350             return _iother(repo, mynode, local, other, base, toolconf)
   351         elif choice == b'local':
   351         elif choice == b'local':
   352             return _ilocal(repo, mynode, fcd, fco, fca, toolconf, labels)
   352             return _ilocal(repo, mynode, local, other, base, toolconf)
   353         elif choice == b'unresolved':
   353         elif choice == b'unresolved':
   354             return _ifail(repo, mynode, fcd, fco, fca, toolconf, labels)
   354             return _ifail(repo, mynode, local, other, base, toolconf)
   355     except error.ResponseExpected:
   355     except error.ResponseExpected:
   356         ui.write(b"\n")
   356         ui.write(b"\n")
   357         return _ifail(repo, mynode, fcd, fco, fca, toolconf, labels)
   357         return _ifail(repo, mynode, local, other, base, toolconf)
   358 
   358 
   359 
   359 
   360 @internaltool(b'local', nomerge)
   360 @internaltool(b'local', nomerge)
   361 def _ilocal(repo, mynode, fcd, fco, fca, toolconf, labels=None):
   361 def _ilocal(repo, mynode, local, other, base, toolconf):
   362     """Uses the local `p1()` version of files as the merged version."""
   362     """Uses the local `p1()` version of files as the merged version."""
   363     return 0, fcd.isabsent()
   363     return 0, local.fctx.isabsent()
   364 
   364 
   365 
   365 
   366 @internaltool(b'other', nomerge)
   366 @internaltool(b'other', nomerge)
   367 def _iother(repo, mynode, fcd, fco, fca, toolconf, labels=None):
   367 def _iother(repo, mynode, local, other, base, toolconf):
   368     """Uses the other `p2()` version of files as the merged version."""
   368     """Uses the other `p2()` version of files as the merged version."""
   369     if fco.isabsent():
   369     if other.fctx.isabsent():
   370         # local changed, remote deleted -- 'deleted' picked
   370         # local changed, remote deleted -- 'deleted' picked
   371         _underlyingfctxifabsent(fcd).remove()
   371         _underlyingfctxifabsent(local.fctx).remove()
   372         deleted = True
   372         deleted = True
   373     else:
   373     else:
   374         _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
   374         _underlyingfctxifabsent(local.fctx).write(
       
   375             other.fctx.data(), other.fctx.flags()
       
   376         )
   375         deleted = False
   377         deleted = False
   376     return 0, deleted
   378     return 0, deleted
   377 
   379 
   378 
   380 
   379 @internaltool(b'fail', nomerge)
   381 @internaltool(b'fail', nomerge)
   380 def _ifail(repo, mynode, fcd, fco, fca, toolconf, labels=None):
   382 def _ifail(repo, mynode, local, other, base, toolconf):
   381     """
   383     """
   382     Rather than attempting to merge files that were modified on both
   384     Rather than attempting to merge files that were modified on both
   383     branches, it marks them as unresolved. The resolve command must be
   385     branches, it marks them as unresolved. The resolve command must be
   384     used to resolve these conflicts."""
   386     used to resolve these conflicts."""
   385     # for change/delete conflicts write out the changed version, then fail
   387     # for change/delete conflicts write out the changed version, then fail
   386     if fcd.isabsent():
   388     if local.fctx.isabsent():
   387         _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
   389         _underlyingfctxifabsent(local.fctx).write(
       
   390             other.fctx.data(), other.fctx.flags()
       
   391         )
   388     return 1, False
   392     return 1, False
   389 
   393 
   390 
   394 
   391 def _underlyingfctxifabsent(filectx):
   395 def _underlyingfctxifabsent(filectx):
   392     """Sometimes when resolving, our fcd is actually an absentfilectx, but
   396     """Sometimes when resolving, our fcd is actually an absentfilectx, but
   397         return filectx.changectx()[filectx.path()]
   401         return filectx.changectx()[filectx.path()]
   398     else:
   402     else:
   399         return filectx
   403         return filectx
   400 
   404 
   401 
   405 
   402 def _premerge(repo, fcd, fco, fca, toolconf, backup, labels):
   406 def _premerge(repo, local, other, base, toolconf, backup):
   403     tool, toolpath, binary, symlink, scriptfn = toolconf
   407     tool, toolpath, binary, symlink, scriptfn = toolconf
   404     if symlink or fcd.isabsent() or fco.isabsent():
   408     if symlink or local.fctx.isabsent() or other.fctx.isabsent():
   405         return 1
   409         return 1
   406 
   410 
   407     ui = repo.ui
   411     ui = repo.ui
   408 
   412 
   409     validkeep = [b'keep', b'keep-merge3', b'keep-mergediff']
   413     validkeep = [b'keep', b'keep-merge3', b'keep-mergediff']
   424         mode = b'merge'
   428         mode = b'merge'
   425         if premerge == b'keep-mergediff':
   429         if premerge == b'keep-mergediff':
   426             mode = b'mergediff'
   430             mode = b'mergediff'
   427         elif premerge == b'keep-merge3':
   431         elif premerge == b'keep-merge3':
   428             mode = b'merge3'
   432             mode = b'merge3'
   429         local = simplemerge.MergeInput(fcd, labels[0])
       
   430         other = simplemerge.MergeInput(fco, labels[1])
       
   431         base = simplemerge.MergeInput(fca, labels[2])
       
   432         r = simplemerge.simplemerge(
   433         r = simplemerge.simplemerge(
   433             ui, local, base, other, quiet=True, mode=mode
   434             ui, local, base, other, quiet=True, mode=mode
   434         )
   435         )
   435         if not r:
   436         if not r:
   436             ui.debug(b" premerge successful\n")
   437             ui.debug(b" premerge successful\n")
   437             return 0
   438             return 0
   438         if premerge not in validkeep:
   439         if premerge not in validkeep:
   439             # restore from backup and try again
   440             # restore from backup and try again
   440             _restorebackup(fcd, backup)
   441             _restorebackup(local.fctx, backup)
   441     return 1  # continue merging
   442     return 1  # continue merging
   442 
   443 
   443 
   444 
   444 def _mergecheck(repo, mynode, fcd, fco, fca, toolconf):
   445 def _mergecheck(repo, mynode, fcd, fco, fca, toolconf):
   445     tool, toolpath, binary, symlink, scriptfn = toolconf
   446     tool, toolpath, binary, symlink, scriptfn = toolconf
   460         )
   461         )
   461         return False
   462         return False
   462     return True
   463     return True
   463 
   464 
   464 
   465 
   465 def _merge(repo, fcd, fco, fca, labels, mode):
   466 def _merge(repo, local, other, base, mode):
   466     """
   467     """
   467     Uses the internal non-interactive simple merge algorithm for merging
   468     Uses the internal non-interactive simple merge algorithm for merging
   468     files. It will fail if there are any conflicts and leave markers in
   469     files. It will fail if there are any conflicts and leave markers in
   469     the partially merged file. Markers will have two sections, one for each side
   470     the partially merged file. Markers will have two sections, one for each side
   470     of merge, unless mode equals 'union' which suppresses the markers."""
   471     of merge, unless mode equals 'union' which suppresses the markers."""
   471     ui = repo.ui
   472     ui = repo.ui
   472 
   473 
   473     local = simplemerge.MergeInput(fcd)
       
   474     local.label = labels[0]
       
   475     other = simplemerge.MergeInput(fco)
       
   476     other.label = labels[1]
       
   477     base = simplemerge.MergeInput(fca)
       
   478     base.label = labels[2]
       
   479     r = simplemerge.simplemerge(ui, local, base, other, mode=mode)
   474     r = simplemerge.simplemerge(ui, local, base, other, mode=mode)
   480     return True, r, False
   475     return True, r, False
   481 
   476 
   482 
   477 
   483 @internaltool(
   478 @internaltool(
   487         b"warning: conflicts while merging %s! "
   482         b"warning: conflicts while merging %s! "
   488         b"(edit, then use 'hg resolve --mark')\n"
   483         b"(edit, then use 'hg resolve --mark')\n"
   489     ),
   484     ),
   490     precheck=_mergecheck,
   485     precheck=_mergecheck,
   491 )
   486 )
   492 def _iunion(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   487 def _iunion(repo, mynode, local, other, base, toolconf, backup):
   493     """
   488     """
   494     Uses the internal non-interactive simple merge algorithm for merging
   489     Uses the internal non-interactive simple merge algorithm for merging
   495     files. It will use both left and right sides for conflict regions.
   490     files. It will use both left and right sides for conflict regions.
   496     No markers are inserted."""
   491     No markers are inserted."""
   497     return _merge(repo, fcd, fco, fca, labels, b'union')
   492     return _merge(repo, local, other, base, b'union')
   498 
   493 
   499 
   494 
   500 @internaltool(
   495 @internaltool(
   501     b'merge',
   496     b'merge',
   502     fullmerge,
   497     fullmerge,
   504         b"warning: conflicts while merging %s! "
   499         b"warning: conflicts while merging %s! "
   505         b"(edit, then use 'hg resolve --mark')\n"
   500         b"(edit, then use 'hg resolve --mark')\n"
   506     ),
   501     ),
   507     precheck=_mergecheck,
   502     precheck=_mergecheck,
   508 )
   503 )
   509 def _imerge(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   504 def _imerge(repo, mynode, local, other, base, toolconf, backup):
   510     """
   505     """
   511     Uses the internal non-interactive simple merge algorithm for merging
   506     Uses the internal non-interactive simple merge algorithm for merging
   512     files. It will fail if there are any conflicts and leave markers in
   507     files. It will fail if there are any conflicts and leave markers in
   513     the partially merged file. Markers will have two sections, one for each side
   508     the partially merged file. Markers will have two sections, one for each side
   514     of merge."""
   509     of merge."""
   515     return _merge(repo, fcd, fco, fca, labels, b'merge')
   510     return _merge(repo, local, other, base, b'merge')
   516 
   511 
   517 
   512 
   518 @internaltool(
   513 @internaltool(
   519     b'merge3',
   514     b'merge3',
   520     fullmerge,
   515     fullmerge,
   522         b"warning: conflicts while merging %s! "
   517         b"warning: conflicts while merging %s! "
   523         b"(edit, then use 'hg resolve --mark')\n"
   518         b"(edit, then use 'hg resolve --mark')\n"
   524     ),
   519     ),
   525     precheck=_mergecheck,
   520     precheck=_mergecheck,
   526 )
   521 )
   527 def _imerge3(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   522 def _imerge3(repo, mynode, local, other, base, toolconf, backup):
   528     """
   523     """
   529     Uses the internal non-interactive simple merge algorithm for merging
   524     Uses the internal non-interactive simple merge algorithm for merging
   530     files. It will fail if there are any conflicts and leave markers in
   525     files. It will fail if there are any conflicts and leave markers in
   531     the partially merged file. Marker will have three sections, one from each
   526     the partially merged file. Marker will have three sections, one from each
   532     side of the merge and one for the base content."""
   527     side of the merge and one for the base content."""
   533     return _merge(repo, fcd, fco, fca, labels, b'merge3')
   528     return _merge(repo, local, other, base, b'merge3')
   534 
   529 
   535 
   530 
   536 @internaltool(
   531 @internaltool(
   537     b'merge3-lie-about-conflicts',
   532     b'merge3-lie-about-conflicts',
   538     fullmerge,
   533     fullmerge,
   559         b"warning: conflicts while merging %s! "
   554         b"warning: conflicts while merging %s! "
   560         b"(edit, then use 'hg resolve --mark')\n"
   555         b"(edit, then use 'hg resolve --mark')\n"
   561     ),
   556     ),
   562     precheck=_mergecheck,
   557     precheck=_mergecheck,
   563 )
   558 )
   564 def _imerge_diff(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   559 def _imerge_diff(repo, mynode, local, other, base, toolconf, backup):
   565     """
   560     """
   566     Uses the internal non-interactive simple merge algorithm for merging
   561     Uses the internal non-interactive simple merge algorithm for merging
   567     files. It will fail if there are any conflicts and leave markers in
   562     files. It will fail if there are any conflicts and leave markers in
   568     the partially merged file. The marker will have two sections, one with the
   563     the partially merged file. The marker will have two sections, one with the
   569     content from one side of the merge, and one with a diff from the base
   564     content from one side of the merge, and one with a diff from the base
   570     content to the content on the other side. (experimental)"""
   565     content to the content on the other side. (experimental)"""
   571     return _merge(repo, fcd, fco, fca, labels, b'mergediff')
   566     return _merge(repo, local, other, base, b'mergediff')
   572 
   567 
   573 
   568 
   574 @internaltool(b'merge-local', mergeonly, precheck=_mergecheck)
   569 @internaltool(b'merge-local', mergeonly, precheck=_mergecheck)
   575 def _imergelocal(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   570 def _imergelocal(repo, mynode, local, other, base, toolconf, backup):
   576     """
   571     """
   577     Like :merge, but resolve all conflicts non-interactively in favor
   572     Like :merge, but resolve all conflicts non-interactively in favor
   578     of the local `p1()` changes."""
   573     of the local `p1()` changes."""
   579     return _merge(repo, fcd, fco, fca, labels, b'local')
   574     return _merge(repo, local, other, base, b'local')
   580 
   575 
   581 
   576 
   582 @internaltool(b'merge-other', mergeonly, precheck=_mergecheck)
   577 @internaltool(b'merge-other', mergeonly, precheck=_mergecheck)
   583 def _imergeother(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   578 def _imergeother(repo, mynode, local, other, base, toolconf, backup):
   584     """
   579     """
   585     Like :merge, but resolve all conflicts non-interactively in favor
   580     Like :merge, but resolve all conflicts non-interactively in favor
   586     of the other `p2()` changes."""
   581     of the other `p2()` changes."""
   587     return _merge(repo, fcd, fco, fca, labels, b'other')
   582     return _merge(repo, local, other, base, b'other')
   588 
   583 
   589 
   584 
   590 @internaltool(
   585 @internaltool(
   591     b'tagmerge',
   586     b'tagmerge',
   592     mergeonly,
   587     mergeonly,
   594         b"automatic tag merging of %s failed! "
   589         b"automatic tag merging of %s failed! "
   595         b"(use 'hg resolve --tool :merge' or another merge "
   590         b"(use 'hg resolve --tool :merge' or another merge "
   596         b"tool of your choice)\n"
   591         b"tool of your choice)\n"
   597     ),
   592     ),
   598 )
   593 )
   599 def _itagmerge(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   594 def _itagmerge(repo, mynode, local, other, base, toolconf, backup):
   600     """
   595     """
   601     Uses the internal tag merge algorithm (experimental).
   596     Uses the internal tag merge algorithm (experimental).
   602     """
   597     """
   603     success, status = tagmerge.merge(repo, fcd, fco, fca)
   598     success, status = tagmerge.merge(repo, local.fctx, other.fctx, base.fctx)
   604     return success, status, False
   599     return success, status, False
   605 
   600 
   606 
   601 
   607 @internaltool(b'dump', fullmerge, binary=True, symlink=True)
   602 @internaltool(b'dump', fullmerge, binary=True, symlink=True)
   608 def _idump(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   603 def _idump(repo, mynode, local, other, base, toolconf, backup):
   609     """
   604     """
   610     Creates three versions of the files to merge, containing the
   605     Creates three versions of the files to merge, containing the
   611     contents of local, other and base. These files can then be used to
   606     contents of local, other and base. These files can then be used to
   612     perform a merge manually. If the file to be merged is named
   607     perform a merge manually. If the file to be merged is named
   613     ``a.txt``, these files will accordingly be named ``a.txt.local``,
   608     ``a.txt``, these files will accordingly be named ``a.txt.local``,
   615     same directory as ``a.txt``.
   610     same directory as ``a.txt``.
   616 
   611 
   617     This implies premerge. Therefore, files aren't dumped, if premerge
   612     This implies premerge. Therefore, files aren't dumped, if premerge
   618     runs successfully. Use :forcedump to forcibly write files out.
   613     runs successfully. Use :forcedump to forcibly write files out.
   619     """
   614     """
   620     a = _workingpath(repo, fcd)
   615     a = _workingpath(repo, local.fctx)
   621     fd = fcd.path()
   616     fd = local.fctx.path()
   622 
   617 
   623     from . import context
   618     from . import context
   624 
   619 
   625     if isinstance(fcd, context.overlayworkingfilectx):
   620     if isinstance(local.fctx, context.overlayworkingfilectx):
   626         raise error.InMemoryMergeConflictsError(
   621         raise error.InMemoryMergeConflictsError(
   627             b'in-memory merge does not support the :dump tool.'
   622             b'in-memory merge does not support the :dump tool.'
   628         )
   623         )
   629 
   624 
   630     util.writefile(a + b".local", fcd.decodeddata())
   625     util.writefile(a + b".local", local.fctx.decodeddata())
   631     repo.wwrite(fd + b".other", fco.data(), fco.flags())
   626     repo.wwrite(fd + b".other", other.fctx.data(), other.fctx.flags())
   632     repo.wwrite(fd + b".base", fca.data(), fca.flags())
   627     repo.wwrite(fd + b".base", base.fctx.data(), base.fctx.flags())
   633     return False, 1, False
   628     return False, 1, False
   634 
   629 
   635 
   630 
   636 @internaltool(b'forcedump', mergeonly, binary=True, symlink=True)
   631 @internaltool(b'forcedump', mergeonly, binary=True, symlink=True)
   637 def _forcedump(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   632 def _forcedump(repo, mynode, local, other, base, toolconf, backup):
   638     """
   633     """
   639     Creates three versions of the files as same as :dump, but omits premerge.
   634     Creates three versions of the files as same as :dump, but omits premerge.
   640     """
   635     """
   641     return _idump(repo, mynode, fcd, fco, fca, toolconf, backup, labels=labels)
   636     return _idump(repo, mynode, local, other, base, toolconf, backup)
   642 
   637 
   643 
   638 
   644 def _xmergeimm(repo, mynode, fcd, fco, fca, toolconf, backup, labels=None):
   639 def _xmergeimm(repo, mynode, local, other, base, toolconf, backup):
   645     # In-memory merge simply raises an exception on all external merge tools,
   640     # In-memory merge simply raises an exception on all external merge tools,
   646     # for now.
   641     # for now.
   647     #
   642     #
   648     # It would be possible to run most tools with temporary files, but this
   643     # It would be possible to run most tools with temporary files, but this
   649     # raises the question of what to do if the user only partially resolves the
   644     # raises the question of what to do if the user only partially resolves the
   707         ui, tmpl, defaults=templatekw.keywords, resources=tres
   702         ui, tmpl, defaults=templatekw.keywords, resources=tres
   708     )
   703     )
   709     ui.status(t.renderdefault(props))
   704     ui.status(t.renderdefault(props))
   710 
   705 
   711 
   706 
   712 def _xmerge(repo, mynode, fcd, fco, fca, toolconf, backup, labels):
   707 def _xmerge(repo, mynode, local, other, base, toolconf, backup):
       
   708     fcd = local.fctx
       
   709     fco = other.fctx
       
   710     fca = base.fctx
   713     tool, toolpath, binary, symlink, scriptfn = toolconf
   711     tool, toolpath, binary, symlink, scriptfn = toolconf
   714     uipathfn = scmutil.getuipathfn(repo)
   712     uipathfn = scmutil.getuipathfn(repo)
   715     if fcd.isabsent() or fco.isabsent():
   713     if fcd.isabsent() or fco.isabsent():
   716         repo.ui.warn(
   714         repo.ui.warn(
   717             _(b'warning: %s cannot merge change/delete conflict for %s\n')
   715             _(b'warning: %s cannot merge change/delete conflict for %s\n')
   724     with _maketempfiles(
   722     with _maketempfiles(
   725         repo, fco, fca, repo.wvfs.join(backup.path()), b"$output" in args
   723         repo, fco, fca, repo.wvfs.join(backup.path()), b"$output" in args
   726     ) as temppaths:
   724     ) as temppaths:
   727         basepath, otherpath, localoutputpath = temppaths
   725         basepath, otherpath, localoutputpath = temppaths
   728         outpath = b""
   726         outpath = b""
   729         mylabel, otherlabel = labels[:2]
       
   730         baselabel = labels[2]
       
   731         env = {
   727         env = {
   732             b'HG_FILE': fcd.path(),
   728             b'HG_FILE': fcd.path(),
   733             b'HG_MY_NODE': short(mynode),
   729             b'HG_MY_NODE': short(mynode),
   734             b'HG_OTHER_NODE': short(fco.changectx().node()),
   730             b'HG_OTHER_NODE': short(fco.changectx().node()),
   735             b'HG_BASE_NODE': short(fca.changectx().node()),
   731             b'HG_BASE_NODE': short(fca.changectx().node()),
   736             b'HG_MY_ISLINK': b'l' in fcd.flags(),
   732             b'HG_MY_ISLINK': b'l' in fcd.flags(),
   737             b'HG_OTHER_ISLINK': b'l' in fco.flags(),
   733             b'HG_OTHER_ISLINK': b'l' in fco.flags(),
   738             b'HG_BASE_ISLINK': b'l' in fca.flags(),
   734             b'HG_BASE_ISLINK': b'l' in fca.flags(),
   739             b'HG_MY_LABEL': mylabel,
   735             b'HG_MY_LABEL': local.label,
   740             b'HG_OTHER_LABEL': otherlabel,
   736             b'HG_OTHER_LABEL': other.label,
   741             b'HG_BASE_LABEL': baselabel,
   737             b'HG_BASE_LABEL': base.label,
   742         }
   738         }
   743         ui = repo.ui
   739         ui = repo.ui
   744 
   740 
   745         if b"$output" in args:
   741         if b"$output" in args:
   746             # read input from backup, write to original
   742             # read input from backup, write to original
   749         replace = {
   745         replace = {
   750             b'local': localpath,
   746             b'local': localpath,
   751             b'base': basepath,
   747             b'base': basepath,
   752             b'other': otherpath,
   748             b'other': otherpath,
   753             b'output': outpath,
   749             b'output': outpath,
   754             b'labellocal': mylabel,
   750             b'labellocal': local.label,
   755             b'labelother': otherlabel,
   751             b'labelother': other.label,
   756             b'labelbase': baselabel,
   752             b'labelbase': base.label,
   757         }
   753         }
   758         args = util.interpolate(
   754         args = util.interpolate(
   759             br'\$',
   755             br'\$',
   760             replace,
   756             replace,
   761             args,
   757             args,
   803                 r = 1
   799                 r = 1
   804         repo.ui.debug(b'merge tool returned: %d\n' % r)
   800         repo.ui.debug(b'merge tool returned: %d\n' % r)
   805         return True, r, False
   801         return True, r, False
   806 
   802 
   807 
   803 
   808 def _formatlabel(input, template, pad):
   804 def _populate_label_detail(input, template, pad):
   809     """Applies the given template to the ctx, prefixed by the label.
   805     """Applies the given template to the ctx, prefixed by the label.
   810 
   806 
   811     Pad is the minimum width of the label prefix, so that multiple markers
   807     Pad is the minimum width of the label prefix, so that multiple markers
   812     can have aligned templated parts.
   808     can have aligned templated parts.
   813     """
   809     """
   824 
   820 
   825     # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
   821     # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
   826     input.label = stringutil.ellipsis(mark, 80 - 8)
   822     input.label = stringutil.ellipsis(mark, 80 - 8)
   827 
   823 
   828 
   824 
   829 def _formatlabels(repo, inputs, tool=None):
   825 def _populate_label_details(repo, inputs, tool=None):
   830     """Formats the given labels using the conflict marker template.
   826     """Formats the given labels using the conflict marker template.
   831 
   827 
   832     Returns a list of formatted labels.
   828     Returns a list of formatted labels.
   833     """
   829     """
   834     ui = repo.ui
   830     ui = repo.ui
   841         ui, template, defaults=templatekw.keywords, resources=tres
   837         ui, template, defaults=templatekw.keywords, resources=tres
   842     )
   838     )
   843 
   839 
   844     pad = max(len(input.label) for input in inputs)
   840     pad = max(len(input.label) for input in inputs)
   845 
   841 
   846     return [_formatlabel(input, tmpl, pad) for input in inputs]
   842     for input in inputs:
       
   843         _populate_label_detail(input, tmpl, pad)
   847 
   844 
   848 
   845 
   849 def partextras(labels):
   846 def partextras(labels):
   850     """Return a dictionary of extra labels for use in prompts to the user
   847     """Return a dictionary of extra labels for use in prompts to the user
   851 
   848 
  1049     base = simplemerge.MergeInput(fca, labels[2])
  1046     base = simplemerge.MergeInput(fca, labels[2])
  1050     if mergetype == nomerge:
  1047     if mergetype == nomerge:
  1051         return func(
  1048         return func(
  1052             repo,
  1049             repo,
  1053             mynode,
  1050             mynode,
  1054             fcd,
  1051             local,
  1055             fco,
  1052             other,
  1056             fca,
  1053             base,
  1057             toolconf,
  1054             toolconf,
  1058             [local.label, other.label, base.label],
       
  1059         )
  1055         )
  1060 
  1056 
  1061     if orig != fco.path():
  1057     if orig != fco.path():
  1062         ui.status(
  1058         ui.status(
  1063             _(b"merging %s and %s to %s\n")
  1059             _(b"merging %s and %s to %s\n")
  1096             if markerstyle != b'basic':
  1092             if markerstyle != b'basic':
  1097                 # respect 'tool's mergemarkertemplate (which defaults to
  1093                 # respect 'tool's mergemarkertemplate (which defaults to
  1098                 # command-templates.mergemarker)
  1094                 # command-templates.mergemarker)
  1099                 labeltool = tool
  1095                 labeltool = tool
  1100             if internalmarkerstyle != b'basic' or markerstyle != b'basic':
  1096             if internalmarkerstyle != b'basic' or markerstyle != b'basic':
  1101                 _formatlabels(repo, [local, other, base], tool=labeltool)
  1097                 _populate_label_details(
       
  1098                     repo, [local, other, base], tool=labeltool
       
  1099                 )
  1102 
  1100 
  1103             r = _premerge(
  1101             r = _premerge(
  1104                 repo,
  1102                 repo,
  1105                 fcd,
  1103                 local,
  1106                 fco,
  1104                 other,
  1107                 fca,
  1105                 base,
  1108                 toolconf,
  1106                 toolconf,
  1109                 backup,
  1107                 backup,
  1110                 labels=[local.label, other.label, base.label],
       
  1111             )
  1108             )
  1112             # we're done if premerge was successful (r is 0)
  1109             # we're done if premerge was successful (r is 0)
  1113             if not r:
  1110             if not r:
  1114                 return r, False
  1111                 return r, False
  1115 
  1112 
  1117             local.label = labels[0]
  1114             local.label = labels[0]
  1118             other.label = labels[1]
  1115             other.label = labels[1]
  1119             base.label = labels[2]
  1116             base.label = labels[2]
  1120 
  1117 
  1121         if markerstyle != b'basic':
  1118         if markerstyle != b'basic':
  1122             _formatlabels(repo, [local, other, base], tool=tool)
  1119             _populate_label_details(repo, [local, other, base], tool=tool)
  1123 
  1120 
  1124         needcheck, r, deleted = func(
  1121         needcheck, r, deleted = func(
  1125             repo,
  1122             repo,
  1126             mynode,
  1123             mynode,
  1127             fcd,
  1124             local,
  1128             fco,
  1125             other,
  1129             fca,
  1126             base,
  1130             toolconf,
  1127             toolconf,
  1131             backup,
  1128             backup,
  1132             labels=[local.label, other.label, base.label],
       
  1133         )
  1129         )
  1134 
  1130 
  1135         if needcheck:
  1131         if needcheck:
  1136             r = _check(repo, r, ui, tool, fcd, backup)
  1132             r = _check(repo, r, ui, tool, fcd, backup)
  1137 
  1133