@@ -104,7 +104,7 @@ export function addCommands(
104104 settings : ISettingRegistry . ISettings ,
105105 trans : TranslationBundle
106106) : void {
107- const { commands, shell } = app ;
107+ const { commands, shell, serviceManager } = app ;
108108
109109 /**
110110 * Commit using a keystroke combination when in CommitBox.
@@ -419,16 +419,17 @@ export function addCommands(
419419 /**
420420 * Git display diff command - internal command
421421 *
422- * @params model {Git.Diff.IModel<string>} : The diff model to display
422+ * @params model {Git.Diff.IModel: The diff model to display
423423 * @params isText {boolean}: Optional, whether the content is a plain text
424+ * @params isMerge {boolean}: Optional, whether the diff is a merge conflict
424425 * @returns the main area widget or null
425426 */
426427 commands . addCommand ( CommandIDs . gitShowDiff , {
427428 label : trans . __ ( 'Show Diff' ) ,
428429 caption : trans . __ ( 'Display a file diff.' ) ,
429430 execute : async args => {
430431 const { model, isText } = args as any as {
431- model : Git . Diff . IModel < string > ;
432+ model : Git . Diff . IModel ;
432433 isText ?: boolean ;
433434 } ;
434435
@@ -470,26 +471,67 @@ export function addCommands(
470471
471472 diffWidget . toolbar . addItem ( 'spacer' , Toolbar . createSpacerItem ( ) ) ;
472473
473- const refreshButton = new ToolbarButton ( {
474- label : trans . __ ( 'Refresh' ) ,
475- onClick : async ( ) => {
476- await widget . refresh ( ) ;
477- refreshButton . hide ( ) ;
478- } ,
479- tooltip : trans . __ ( 'Refresh diff widget' ) ,
480- className : 'jp-git-diff-refresh'
481- } ) ;
482- refreshButton . hide ( ) ;
483- diffWidget . toolbar . addItem ( 'refresh' , refreshButton ) ;
484-
485- const refresh = ( ) => {
486- refreshButton . show ( ) ;
487- } ;
474+ // Do not allow the user to refresh during merge conflicts
475+ if ( model . hasConflict ) {
476+ const resolveButton = new ToolbarButton ( {
477+ label : trans . __ ( 'Mark as resolved' ) ,
478+ onClick : async ( ) => {
479+ if ( ! widget . isFileResolved ) {
480+ const result = await showDialog ( {
481+ title : trans . __ ( 'Resolve with conflicts' ) ,
482+ body : trans . __ (
483+ 'Are you sure you want to mark this file as resolved with merge conflicts?'
484+ )
485+ } ) ;
486+
487+ // Bail early if the user wants to finish resolving conflicts
488+ if ( ! result . button . accept ) {
489+ return ;
490+ }
491+ }
492+
493+ try {
494+ await serviceManager . contents . save (
495+ model . filename ,
496+ await widget . getResolvedFile ( )
497+ ) ;
498+ await gitModel . add ( model . filename ) ;
499+ await gitModel . refresh ( ) ;
500+ } catch ( reason ) {
501+ logger . log ( {
502+ message : reason . message ?? reason ,
503+ level : Level . ERROR
504+ } ) ;
505+ } finally {
506+ diffWidget . dispose ( ) ;
507+ }
508+ } ,
509+ tooltip : trans . __ ( 'Mark file as resolved' ) ,
510+ className : 'jp-git-diff-resolve'
511+ } ) ;
512+
513+ diffWidget . toolbar . addItem ( 'resolve' , resolveButton ) ;
514+ } else {
515+ const refreshButton = new ToolbarButton ( {
516+ label : trans . __ ( 'Refresh' ) ,
517+ onClick : async ( ) => {
518+ await widget . refresh ( ) ;
519+ refreshButton . hide ( ) ;
520+ } ,
521+ tooltip : trans . __ ( 'Refresh diff widget' ) ,
522+ className : 'jp-git-diff-refresh'
523+ } ) ;
524+
525+ refreshButton . hide ( ) ;
526+ diffWidget . toolbar . addItem ( 'refresh' , refreshButton ) ;
527+
528+ const refresh = ( ) => {
529+ refreshButton . show ( ) ;
530+ } ;
488531
489- model . changed . connect ( refresh ) ;
490- widget . disposed . connect ( ( ) => {
491- model . changed . disconnect ( refresh ) ;
492- } ) ;
532+ model . changed . connect ( refresh ) ;
533+ widget . disposed . connect ( ( ) => model . changed . disconnect ( refresh ) ) ;
534+ }
493535
494536 // Load the diff widget
495537 modelIsLoading . resolve ( ) ;
@@ -569,25 +611,29 @@ export function addCommands(
569611
570612 const repositoryPath = gitModel . getRelativeFilePath ( ) ;
571613 const filename = PathExt . join ( repositoryPath , filePath ) ;
572-
573- let diffContext = context ;
574- if ( ! diffContext ) {
575- const specialRef =
576- status === 'staged'
577- ? Git . Diff . SpecialRef . INDEX
578- : Git . Diff . SpecialRef . WORKING ;
579- diffContext = {
580- currentRef : specialRef ,
581- previousRef : 'HEAD'
582- } ;
583- }
614+ const specialRef =
615+ status === 'staged'
616+ ? Git . Diff . SpecialRef . INDEX
617+ : Git . Diff . SpecialRef . WORKING ;
618+
619+ const diffContext : Git . Diff . IContext =
620+ status === 'unmerged'
621+ ? {
622+ currentRef : 'HEAD' ,
623+ previousRef : 'MERGE_HEAD' ,
624+ baseRef : 'ORIG_HEAD'
625+ }
626+ : context ?? {
627+ currentRef : specialRef ,
628+ previousRef : 'HEAD'
629+ } ;
584630
585631 const challengerRef = Git . Diff . SpecialRef [ diffContext . currentRef as any ]
586632 ? { special : Git . Diff . SpecialRef [ diffContext . currentRef as any ] }
587633 : { git : diffContext . currentRef } ;
588634
589- // Create the diff widget
590- const model = new DiffModel < string > ( {
635+ // Base props used for Diff Model
636+ const props : Omit < Git . Diff . IModel , 'changed' | 'hasConflict' > = {
591637 challenger : {
592638 content : async ( ) => {
593639 return requestAPI < Git . IDiffContent > (
@@ -623,7 +669,32 @@ export function addCommands(
623669 source : diffContext . previousRef ,
624670 updateAt : Date . now ( )
625671 }
626- } ) ;
672+ } ;
673+
674+ if ( diffContext . baseRef ) {
675+ props . reference . label = trans . __ ( 'CURRENT' ) ;
676+ props . challenger . label = trans . __ ( 'INCOMING' ) ;
677+
678+ // Only add base when diff-ing merge conflicts
679+ props . base = {
680+ content : async ( ) => {
681+ return requestAPI < Git . IDiffContent > (
682+ URLExt . join ( repositoryPath , 'content' ) ,
683+ 'POST' ,
684+ {
685+ filename : filePath ,
686+ reference : { git : diffContext . baseRef }
687+ }
688+ ) . then ( data => data . content ) ;
689+ } ,
690+ label : trans . __ ( 'RESULT' ) ,
691+ source : diffContext . baseRef ,
692+ updateAt : Date . now ( )
693+ } ;
694+ }
695+
696+ // Create the diff widget
697+ const model = new DiffModel ( props ) ;
627698
628699 const widget = await commands . execute ( CommandIDs . gitShowDiff , {
629700 model,
0 commit comments