1
1
import * as fs from "fs" ;
2
+ import * as os from "os" ;
2
3
import * as path from "path" ;
3
4
import {
4
5
commands ,
5
6
Disposable ,
6
7
LineChange ,
7
8
Position ,
9
+ ProgressLocation ,
8
10
Range ,
9
11
SourceControlResourceState ,
10
12
TextDocumentShowOptions ,
@@ -21,20 +23,23 @@ import {
21
23
inputSwitchChangelist
22
24
} from "./changelistItems" ;
23
25
import {
26
+ IAuth ,
24
27
ICommand ,
25
28
ICommandOptions ,
29
+ ICpOptions ,
26
30
Status ,
27
31
SvnUriAction
28
32
} from "./common/types" ;
29
33
import { getConflictPickOptions } from "./conflictItems" ;
30
- import { selectBranch } from "./helpers/branch" ;
34
+ import { getBranchName , selectBranch } from "./helpers/branch" ;
31
35
import { configuration } from "./helpers/configuration" ;
32
36
import { inputIgnoreList } from "./ignoreitems" ;
33
37
import { applyLineChanges } from "./lineChanges" ;
34
38
import { inputCommitMessage } from "./messages" ;
35
39
import { Model } from "./model" ;
36
40
import { Repository } from "./repository" ;
37
41
import { Resource } from "./resource" ;
42
+ import { Svn , svnErrorCodes } from "./svn" ;
38
43
import IncommingChangeNode from "./treeView/nodes/incomingChangeNode" ;
39
44
import IncomingChangeNode from "./treeView/nodes/incomingChangeNode" ;
40
45
import { fromSvnUri , toSvnUri } from "./uri" ;
@@ -62,7 +67,7 @@ function command(
62
67
export class SvnCommands implements IDisposable {
63
68
private disposables : Disposable [ ] ;
64
69
65
- constructor ( private model : Model ) {
70
+ constructor ( private model : Model , private svn : Svn ) {
66
71
this . disposables = svnCommands . map ( ( { commandId, method, options } ) => {
67
72
const command = this . createCommand ( method , options ) ;
68
73
if ( options . diff && hasSupportToRegisterDiffCommand ( ) ) {
@@ -121,32 +126,170 @@ export class SvnCommands implements IDisposable {
121
126
await commands . executeCommand ( "vscode.open" , resourceUri ) ;
122
127
}
123
128
124
- @command ( "svn.promptAuth" , { repository : true } )
125
- public async promptAuth ( repository : Repository ) : Promise < boolean > {
129
+ @command ( "svn.promptAuth" )
130
+ public async promptAuth (
131
+ prevUsername ?: string ,
132
+ prevPassword ?: string
133
+ ) : Promise < IAuth | undefined > {
126
134
const username = await window . showInputBox ( {
127
135
placeHolder : "Svn repository username" ,
128
136
prompt : "Please enter your username" ,
129
- value : repository . username
137
+ value : prevUsername
130
138
} ) ;
131
139
132
140
if ( username === undefined ) {
133
- return false ;
141
+ return ;
134
142
}
135
143
136
144
const password = await window . showInputBox ( {
137
145
placeHolder : "Svn repository password" ,
138
146
prompt : "Please enter your password" ,
147
+ value : prevPassword ,
139
148
password : true
140
149
} ) ;
141
150
142
- if ( username === undefined ) {
143
- return false ;
151
+ if ( password === undefined ) {
152
+ return ;
153
+ }
154
+
155
+ const auth : IAuth = {
156
+ username,
157
+ password
158
+ } ;
159
+
160
+ return auth ;
161
+ }
162
+
163
+ @command ( "svn.checkout" )
164
+ public async checkout ( url ?: string ) : Promise < void > {
165
+ if ( ! url ) {
166
+ url = await window . showInputBox ( {
167
+ prompt : "Repository URL" ,
168
+ ignoreFocusOut : true
169
+ } ) ;
170
+ }
171
+
172
+ if ( ! url ) {
173
+ return ;
174
+ }
175
+
176
+ let defaultCheckoutDirectory =
177
+ configuration . get < string > ( "defaultCheckoutDirectory" ) || os . homedir ( ) ;
178
+ defaultCheckoutDirectory = defaultCheckoutDirectory . replace (
179
+ / ^ ~ / ,
180
+ os . homedir ( )
181
+ ) ;
182
+
183
+ const uris = await window . showOpenDialog ( {
184
+ canSelectFiles : false ,
185
+ canSelectFolders : true ,
186
+ canSelectMany : false ,
187
+ defaultUri : Uri . file ( defaultCheckoutDirectory ) ,
188
+ openLabel : "Select Repository Location"
189
+ } ) ;
190
+
191
+ if ( ! uris || uris . length === 0 ) {
192
+ return ;
193
+ }
194
+
195
+ const uri = uris [ 0 ] ;
196
+ const parentPath = uri . fsPath ;
197
+
198
+ let folderName : string | undefined ;
199
+
200
+ // Get folder name from branch
201
+ const branch = getBranchName ( url ) ;
202
+ if ( branch ) {
203
+ const baseUrl = url . replace ( / \/ / g, "/" ) . replace ( branch . path , "" ) ;
204
+ folderName = path . basename ( baseUrl ) ;
205
+ }
206
+
207
+ folderName = await window . showInputBox ( {
208
+ prompt : "Folder name" ,
209
+ value : folderName ,
210
+ ignoreFocusOut : true
211
+ } ) ;
212
+
213
+ if ( ! folderName ) {
214
+ return ;
144
215
}
145
216
146
- repository . username = username ;
147
- repository . password = password ;
217
+ const repositoryPath = path . join ( parentPath , folderName ) ;
218
+
219
+ // Use Notification location if supported
220
+ let location = ProgressLocation . Window ;
221
+ if ( ( ProgressLocation as any ) . Notification ) {
222
+ location = ( ProgressLocation as any ) . Notification ;
223
+ }
224
+
225
+ const progressOptions = {
226
+ location,
227
+ title : `Checkout svn repository '${ url } '...` ,
228
+ cancellable : true
229
+ } ;
230
+
231
+ let attempt = 0 ;
148
232
149
- return true ;
233
+ const opt : ICpOptions = { } ;
234
+
235
+ while ( true ) {
236
+ attempt ++ ;
237
+ try {
238
+ await window . withProgress ( progressOptions , async ( ) => {
239
+ const args = [ "checkout" , url , repositoryPath ] ;
240
+ await this . svn . exec ( parentPath , args , opt ) ;
241
+ } ) ;
242
+ break ;
243
+ } catch ( err ) {
244
+ if (
245
+ err . svnErrorCode === svnErrorCodes . AuthorizationFailed &&
246
+ attempt <= 3
247
+ ) {
248
+ const auth = ( await commands . executeCommand (
249
+ "svn.promptAuth" ,
250
+ opt . username
251
+ ) ) as IAuth ;
252
+ if ( auth ) {
253
+ opt . username = auth . username ;
254
+ opt . password = auth . password ;
255
+ continue ;
256
+ }
257
+ }
258
+ throw err ;
259
+ }
260
+ }
261
+
262
+ const choices = [ ] ;
263
+ let message = "Would you like to open the checked out repository?" ;
264
+ const open = "Open Repository" ;
265
+ choices . push ( open ) ;
266
+
267
+ const addToWorkspace = "Add to Workspace" ;
268
+ if (
269
+ workspace . workspaceFolders &&
270
+ ( workspace as any ) . updateWorkspaceFolders // For VSCode >= 1.21
271
+ ) {
272
+ message =
273
+ "Would you like to open the checked out repository, or add it to the current workspace?" ;
274
+ choices . push ( addToWorkspace ) ;
275
+ }
276
+
277
+ const result = await window . showInformationMessage ( message , ...choices ) ;
278
+
279
+ const openFolder = result === open ;
280
+
281
+ if ( openFolder ) {
282
+ commands . executeCommand ( "vscode.openFolder" , Uri . file ( repositoryPath ) ) ;
283
+ } else if ( result === addToWorkspace ) {
284
+ // For VSCode >= 1.21
285
+ ( workspace as any ) . updateWorkspaceFolders (
286
+ workspace . workspaceFolders ! . length ,
287
+ 0 ,
288
+ {
289
+ uri : Uri . file ( repositoryPath )
290
+ }
291
+ ) ;
292
+ }
150
293
}
151
294
152
295
@command ( "svn.commitWithMessage" , { repository : true } )
0 commit comments