@@ -32,7 +32,7 @@ import {
32
32
TabBarToolbarRegistry
33
33
} from '@theia/core/lib/browser/shell/tab-bar-toolbar' ;
34
34
import { EditorContextMenu , EditorManager , EditorOpenerOptions , EditorWidget } from '@theia/editor/lib/browser' ;
35
- import { Git , GitFileChange , GitFileStatus } from '../common' ;
35
+ import { Git , GitFileChange , GitFileStatus , GitWatcher , Repository } from '../common' ;
36
36
import { GitRepositoryTracker } from './git-repository-tracker' ;
37
37
import { GitAction , GitQuickOpenService } from './git-quick-open-service' ;
38
38
import { GitSyncService } from './git-sync-service' ;
@@ -42,6 +42,8 @@ import { GitErrorHandler } from '../browser/git-error-handler';
42
42
import { ScmWidget } from '@theia/scm/lib/browser/scm-widget' ;
43
43
import { ScmTreeWidget } from '@theia/scm/lib/browser/scm-tree-widget' ;
44
44
import { ScmCommand , ScmResource } from '@theia/scm/lib/browser/scm-provider' ;
45
+ import { LineRange } from '@theia/scm/lib/browser/dirty-diff/diff-computer' ;
46
+ import { DirtyDiffWidget , SCM_CHANGE_TITLE_MENU } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-widget' ;
45
47
import { ProgressService } from '@theia/core/lib/common/progress-service' ;
46
48
import { GitPreferences } from './git-preferences' ;
47
49
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution' ;
@@ -166,6 +168,18 @@ export namespace GIT_COMMANDS {
166
168
label : 'Stage All Changes' ,
167
169
iconClass : codicon ( 'add' )
168
170
} , 'vscode.git/package/command.stageAll' , GIT_CATEGORY_KEY ) ;
171
+ export const STAGE_CHANGE = Command . toLocalizedCommand ( {
172
+ id : 'git.stage.change' ,
173
+ category : GIT_CATEGORY ,
174
+ label : 'Stage Change' ,
175
+ iconClass : codicon ( 'add' )
176
+ } , 'vscode.git/package/command.stageChange' , GIT_CATEGORY_KEY ) ;
177
+ export const REVERT_CHANGE = Command . toLocalizedCommand ( {
178
+ id : 'git.revert.change' ,
179
+ category : GIT_CATEGORY ,
180
+ label : 'Revert Change' ,
181
+ iconClass : codicon ( 'discard' )
182
+ } , 'vscode.git/package/command.revertChange' , GIT_CATEGORY_KEY ) ;
169
183
export const UNSTAGE = Command . toLocalizedCommand ( {
170
184
id : 'git.unstage' ,
171
185
category : GIT_CATEGORY ,
@@ -280,6 +294,7 @@ export class GitContribution implements CommandContribution, MenuContribution, T
280
294
@inject ( GitPreferences ) protected readonly gitPreferences : GitPreferences ;
281
295
@inject ( DecorationsService ) protected readonly decorationsService : DecorationsService ;
282
296
@inject ( GitDecorationProvider ) protected readonly gitDecorationProvider : GitDecorationProvider ;
297
+ @inject ( GitWatcher ) protected readonly gitWatcher : GitWatcher ;
283
298
284
299
onStart ( ) : void {
285
300
this . updateStatusBar ( ) ;
@@ -385,6 +400,15 @@ export class GitContribution implements CommandContribution, MenuContribution, T
385
400
commandId : GIT_COMMANDS . DISCARD_ALL . id ,
386
401
when : 'scmProvider == git && scmResourceGroup == workingTree || scmProvider == git && scmResourceGroup == untrackedChanges' ,
387
402
} ) ;
403
+
404
+ menus . registerMenuAction ( SCM_CHANGE_TITLE_MENU , {
405
+ commandId : GIT_COMMANDS . STAGE_CHANGE . id ,
406
+ when : 'scmProvider == git'
407
+ } ) ;
408
+ menus . registerMenuAction ( SCM_CHANGE_TITLE_MENU , {
409
+ commandId : GIT_COMMANDS . REVERT_CHANGE . id ,
410
+ when : 'scmProvider == git'
411
+ } ) ;
388
412
}
389
413
390
414
registerCommands ( registry : CommandRegistry ) : void {
@@ -573,6 +597,14 @@ export class GitContribution implements CommandContribution, MenuContribution, T
573
597
isEnabled : widget => this . workspaceService . opened && ( ! widget || widget instanceof ScmWidget ) && ! this . repositoryProvider . selectedRepository ,
574
598
isVisible : widget => this . workspaceService . opened && ( ! widget || widget instanceof ScmWidget ) && ! this . repositoryProvider . selectedRepository
575
599
} ) ;
600
+ registry . registerCommand ( GIT_COMMANDS . STAGE_CHANGE , {
601
+ execute : ( widget : DirtyDiffWidget ) => this . withProgress ( ( ) => this . stageChange ( widget ) ) ,
602
+ isEnabled : widget => widget instanceof DirtyDiffWidget
603
+ } ) ;
604
+ registry . registerCommand ( GIT_COMMANDS . REVERT_CHANGE , {
605
+ execute : ( widget : DirtyDiffWidget ) => this . withProgress ( ( ) => this . revertChange ( widget ) ) ,
606
+ isEnabled : widget => widget instanceof DirtyDiffWidget
607
+ } ) ;
576
608
}
577
609
async amend ( ) : Promise < void > {
578
610
{
@@ -922,6 +954,62 @@ export class GitContribution implements CommandContribution, MenuContribution, T
922
954
923
955
}
924
956
957
+ async stageChange ( widget : DirtyDiffWidget ) : Promise < void > {
958
+ const scmRepository = this . repositoryProvider . selectedScmRepository ;
959
+ if ( ! scmRepository ) {
960
+ return ;
961
+ }
962
+
963
+ const repository = scmRepository . provider . repository ;
964
+
965
+ const path = Repository . relativePath ( repository , widget . uri ) ?. toString ( ) ;
966
+ if ( ! path ) {
967
+ return ;
968
+ }
969
+
970
+ const { currentChange } = widget ;
971
+ if ( ! currentChange ) {
972
+ return ;
973
+ }
974
+
975
+ const dataToStage = await widget . getContentWithSelectedChanges ( change => change === currentChange ) ;
976
+
977
+ try {
978
+ const hash = ( await this . git . exec ( repository , [ 'hash-object' , '--stdin' , '-w' , '--path' , path ] , { stdin : dataToStage , stdinEncoding : 'utf8' } ) ) . stdout . trim ( ) ;
979
+
980
+ let mode = ( await this . git . exec ( repository , [ 'ls-files' , '--format=%(objectmode)' , '--' , path ] ) ) . stdout . split ( '\n' ) . filter ( line => ! ! line . trim ( ) ) [ 0 ] ;
981
+ if ( ! mode ) {
982
+ mode = '100644' ; // regular non-executable file
983
+ }
984
+
985
+ await this . git . exec ( repository , [ 'update-index' , '--add' , '--cacheinfo' , mode , hash , path ] ) ;
986
+
987
+ // enforce a notification as there would be no status update if the file had been staged already
988
+ this . gitWatcher . onGitChanged ( { source : repository , status : await this . git . status ( repository ) } ) ;
989
+ } catch ( error ) {
990
+ this . gitErrorHandler . handleError ( error ) ;
991
+ }
992
+
993
+ widget . editor . cursor = LineRange . getStartPosition ( currentChange . currentRange ) ;
994
+ }
995
+
996
+ async revertChange ( widget : DirtyDiffWidget ) : Promise < void > {
997
+ const { currentChange } = widget ;
998
+ if ( ! currentChange ) {
999
+ return ;
1000
+ }
1001
+
1002
+ const editor = widget . editor . getControl ( ) ;
1003
+ editor . pushUndoStop ( ) ;
1004
+ editor . executeEdits ( 'Revert Change' , [ {
1005
+ range : editor . getModel ( ) ! . getFullModelRange ( ) ,
1006
+ text : await widget . getContentWithSelectedChanges ( change => change !== currentChange )
1007
+ } ] ) ;
1008
+ editor . pushUndoStop ( ) ;
1009
+
1010
+ widget . editor . cursor = LineRange . getStartPosition ( currentChange . currentRange ) ;
1011
+ }
1012
+
925
1013
/**
926
1014
* It should be aligned with https://code.visualstudio.com/api/references/theme-color#git-colors
927
1015
*/
0 commit comments