@@ -104,7 +104,7 @@ export function addCommands(
104
104
settings : ISettingRegistry . ISettings ,
105
105
trans : TranslationBundle
106
106
) : void {
107
- const { commands, shell } = app ;
107
+ const { commands, shell, serviceManager } = app ;
108
108
109
109
/**
110
110
* Commit using a keystroke combination when in CommitBox.
@@ -419,16 +419,17 @@ export function addCommands(
419
419
/**
420
420
* Git display diff command - internal command
421
421
*
422
- * @params model {Git.Diff.IModel<string>} : The diff model to display
422
+ * @params model {Git.Diff.IModel: The diff model to display
423
423
* @params isText {boolean}: Optional, whether the content is a plain text
424
+ * @params isMerge {boolean}: Optional, whether the diff is a merge conflict
424
425
* @returns the main area widget or null
425
426
*/
426
427
commands . addCommand ( CommandIDs . gitShowDiff , {
427
428
label : trans . __ ( 'Show Diff' ) ,
428
429
caption : trans . __ ( 'Display a file diff.' ) ,
429
430
execute : async args => {
430
431
const { model, isText } = args as any as {
431
- model : Git . Diff . IModel < string > ;
432
+ model : Git . Diff . IModel ;
432
433
isText ?: boolean ;
433
434
} ;
434
435
@@ -470,26 +471,67 @@ export function addCommands(
470
471
471
472
diffWidget . toolbar . addItem ( 'spacer' , Toolbar . createSpacerItem ( ) ) ;
472
473
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
+ } ;
488
531
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
+ }
493
535
494
536
// Load the diff widget
495
537
modelIsLoading . resolve ( ) ;
@@ -569,25 +611,29 @@ export function addCommands(
569
611
570
612
const repositoryPath = gitModel . getRelativeFilePath ( ) ;
571
613
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
+ } ;
584
630
585
631
const challengerRef = Git . Diff . SpecialRef [ diffContext . currentRef as any ]
586
632
? { special : Git . Diff . SpecialRef [ diffContext . currentRef as any ] }
587
633
: { git : diffContext . currentRef } ;
588
634
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' > = {
591
637
challenger : {
592
638
content : async ( ) => {
593
639
return requestAPI < Git . IDiffContent > (
@@ -623,7 +669,32 @@ export function addCommands(
623
669
source : diffContext . previousRef ,
624
670
updateAt : Date . now ( )
625
671
}
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 ) ;
627
698
628
699
const widget = await commands . execute ( CommandIDs . gitShowDiff , {
629
700
model,
0 commit comments