@@ -11,6 +11,8 @@ import { FileBrowser } from '@jupyterlab/filebrowser';
11
11
import { ITerminal } from '@jupyterlab/terminal' ;
12
12
import { IGitExtension } from './tokens' ;
13
13
import { doGitClone } from './widgets/gitClone' ;
14
+ import { GitPullPushDialog , Operation } from './widgets/gitPushPull' ;
15
+ import { GitCredentialsForm } from './widgets/CredentialsBox' ;
14
16
15
17
/**
16
18
* The command IDs used by the git plugin.
@@ -24,6 +26,8 @@ export namespace CommandIDs {
24
26
export const gitToggleDoubleClickDiff = 'git:toggle-double-click-diff' ;
25
27
export const gitAddRemote = 'git:add-remote' ;
26
28
export const gitClone = 'git:clone' ;
29
+ export const gitPush = 'git:push' ;
30
+ export const gitPull = 'git:pull' ;
27
31
}
28
32
29
33
/**
@@ -63,7 +67,8 @@ export function addCommands(
63
67
console . error ( e ) ;
64
68
main . dispose ( ) ;
65
69
}
66
- }
70
+ } ,
71
+ isEnabled : ( ) => model . pathRepository !== null
67
72
} ) ;
68
73
69
74
/** Add open/go to git interface command */
@@ -81,8 +86,8 @@ export function addCommands(
81
86
82
87
/** Add git init command */
83
88
commands . addCommand ( CommandIDs . gitInit , {
84
- label : 'Init ' ,
85
- caption : ' Create an empty Git repository or reinitialize an existing one' ,
89
+ label : 'Initialize a Repository ' ,
90
+ caption : 'Create an empty Git repository or reinitialize an existing one' ,
86
91
execute : async ( ) => {
87
92
const currentPath = fileBrowser . model . path ;
88
93
const result = await showDialog ( {
@@ -95,7 +100,8 @@ export function addCommands(
95
100
await model . init ( currentPath ) ;
96
101
model . pathRepository = currentPath ;
97
102
}
98
- }
103
+ } ,
104
+ isEnabled : ( ) => model . pathRepository === null
99
105
} ) ;
100
106
101
107
/** Open URL externally */
@@ -127,7 +133,7 @@ export function addCommands(
127
133
128
134
/** Command to add a remote Git repository */
129
135
commands . addCommand ( CommandIDs . gitAddRemote , {
130
- label : 'Add remote repository ' ,
136
+ label : 'Add Remote Repository ' ,
131
137
caption : 'Add a Git remote repository' ,
132
138
isEnabled : ( ) => model . pathRepository !== null ,
133
139
execute : async args => {
@@ -162,12 +168,90 @@ export function addCommands(
162
168
163
169
/** Add git clone command */
164
170
commands . addCommand ( CommandIDs . gitClone , {
165
- label : 'Clone' ,
171
+ label : 'Clone a Repository ' ,
166
172
caption : 'Clone a repository from a URL' ,
167
173
isEnabled : ( ) => model . pathRepository === null ,
168
174
execute : async ( ) => {
169
175
await doGitClone ( model , fileBrowser . model . path ) ;
170
176
fileBrowser . model . refresh ( ) ;
171
177
}
172
178
} ) ;
179
+
180
+ /** Add git push command */
181
+ commands . addCommand ( CommandIDs . gitPush , {
182
+ label : 'Push to Remote' ,
183
+ caption : 'Push code to remote repository' ,
184
+ isEnabled : ( ) => model . pathRepository !== null ,
185
+ execute : async ( ) => {
186
+ await Private . showGitOperationDialog ( model , Operation . Push ) . catch (
187
+ reason => {
188
+ console . error (
189
+ `Encountered an error when pushing changes. Error: ${ reason } `
190
+ ) ;
191
+ }
192
+ ) ;
193
+ }
194
+ } ) ;
195
+
196
+ /** Add git pull command */
197
+ commands . addCommand ( CommandIDs . gitPull , {
198
+ label : 'Pull from Remote' ,
199
+ caption : 'Pull latest code from remote repository' ,
200
+ isEnabled : ( ) => model . pathRepository !== null ,
201
+ execute : async ( ) => {
202
+ await Private . showGitOperationDialog ( model , Operation . Pull ) . catch (
203
+ reason => {
204
+ console . error (
205
+ `Encountered an error when pulling changes. Error: ${ reason } `
206
+ ) ;
207
+ }
208
+ ) ;
209
+ }
210
+ } ) ;
211
+ }
212
+
213
+ /* eslint-disable no-inner-declarations */
214
+ namespace Private {
215
+ /**
216
+ * Displays an error dialog when a Git operation fails.
217
+ *
218
+ * @private
219
+ * @param model - Git extension model
220
+ * @param operation - Git operation name
221
+ * @returns Promise for displaying a dialog
222
+ */
223
+ export async function showGitOperationDialog (
224
+ model : IGitExtension ,
225
+ operation : Operation
226
+ ) : Promise < void > {
227
+ const title = `Git ${ operation } ` ;
228
+ let result = await showDialog ( {
229
+ title : title ,
230
+ body : new GitPullPushDialog ( model , operation ) ,
231
+ buttons : [ Dialog . okButton ( { label : 'DISMISS' } ) ]
232
+ } ) ;
233
+ let retry = false ;
234
+ while ( ! result . button . accept ) {
235
+ const credentials = await showDialog ( {
236
+ title : 'Git credentials required' ,
237
+ body : new GitCredentialsForm (
238
+ 'Enter credentials for remote repository' ,
239
+ retry ? 'Incorrect username or password.' : ''
240
+ ) ,
241
+ buttons : [ Dialog . cancelButton ( ) , Dialog . okButton ( { label : 'OK' } ) ]
242
+ } ) ;
243
+
244
+ if ( ! credentials . button . accept ) {
245
+ break ;
246
+ }
247
+
248
+ result = await showDialog ( {
249
+ title : title ,
250
+ body : new GitPullPushDialog ( model , operation , credentials . value ) ,
251
+ buttons : [ Dialog . okButton ( { label : 'DISMISS' } ) ]
252
+ } ) ;
253
+ retry = true ;
254
+ }
255
+ }
173
256
}
257
+ /* eslint-enable no-inner-declarations */
0 commit comments