@@ -340,6 +340,7 @@ class StashItem implements QuickPickItem {
340
340
341
341
interface ScmCommandOptions {
342
342
repository ?: boolean ;
343
+ repositoryFilter ?: ( 'repository' | 'submodule' | 'worktree' ) [ ] ;
343
344
}
344
345
345
346
interface ScmCommand {
@@ -1681,7 +1682,11 @@ export class CommandCenter {
1681
1682
1682
1683
@command ( 'git.diff.stageHunk' )
1683
1684
async diffStageHunk ( changes : DiffEditorSelectionHunkToolbarContext | undefined ) : Promise < void > {
1684
- this . diffStageHunkOrSelection ( changes ) ;
1685
+ if ( changes ) {
1686
+ this . diffStageHunkOrSelection ( changes ) ;
1687
+ } else {
1688
+ await this . stageHunkAtCursor ( ) ;
1689
+ }
1685
1690
}
1686
1691
1687
1692
@command ( 'git.diff.stageSelection' )
@@ -1719,6 +1724,36 @@ export class CommandCenter {
1719
1724
await repository . stage ( resource , result , modifiedDocument . encoding ) ) ;
1720
1725
}
1721
1726
1727
+ private async stageHunkAtCursor ( ) : Promise < void > {
1728
+ const textEditor = window . activeTextEditor ;
1729
+
1730
+ if ( ! textEditor ) {
1731
+ return ;
1732
+ }
1733
+
1734
+ const workingTreeDiffInformation = getWorkingTreeDiffInformation ( textEditor ) ;
1735
+ if ( ! workingTreeDiffInformation ) {
1736
+ return ;
1737
+ }
1738
+
1739
+ const workingTreeLineChanges = toLineChanges ( workingTreeDiffInformation ) ;
1740
+ const modifiedDocument = textEditor . document ;
1741
+ const cursorPosition = textEditor . selection . active ;
1742
+
1743
+ // Find the hunk that contains the cursor position
1744
+ const hunkAtCursor = workingTreeLineChanges . find ( change => {
1745
+ const hunkRange = getModifiedRange ( modifiedDocument , change ) ;
1746
+ return hunkRange . contains ( cursorPosition ) ;
1747
+ } ) ;
1748
+
1749
+ if ( ! hunkAtCursor ) {
1750
+ window . showInformationMessage ( l10n . t ( 'No hunk found at cursor position.' ) ) ;
1751
+ return ;
1752
+ }
1753
+
1754
+ await this . _stageChanges ( textEditor , [ hunkAtCursor ] ) ;
1755
+ }
1756
+
1722
1757
@command ( 'git.stageSelectedRanges' )
1723
1758
async stageSelectedChanges ( ) : Promise < void > {
1724
1759
const textEditor = window . activeTextEditor ;
@@ -2882,10 +2917,15 @@ export class CommandCenter {
2882
2917
try {
2883
2918
await item . run ( repository , opts ) ;
2884
2919
} catch ( err ) {
2885
- if ( err . gitErrorCode !== GitErrorCodes . DirtyWorkTree ) {
2920
+ if ( err . gitErrorCode !== GitErrorCodes . DirtyWorkTree && err . gitErrorCode !== GitErrorCodes . WorktreeAlreadyExists ) {
2886
2921
throw err ;
2887
2922
}
2888
2923
2924
+ if ( err . gitErrorCode === GitErrorCodes . WorktreeAlreadyExists ) {
2925
+ this . handleWorktreeError ( err ) ;
2926
+ return false ;
2927
+ }
2928
+
2889
2929
const stash = l10n . t ( 'Stash & Checkout' ) ;
2890
2930
const migrate = l10n . t ( 'Migrate Changes' ) ;
2891
2931
const force = l10n . t ( 'Force Checkout' ) ;
@@ -3351,33 +3391,45 @@ export class CommandCenter {
3351
3391
}
3352
3392
}
3353
3393
3354
- @command ( 'git.createWorktree' , { repository : true } )
3394
+ @command ( 'git.createWorktree' , { repository : true , repositoryFilter : [ 'repository' , 'submodule' ] } )
3355
3395
async createWorktree ( repository : Repository ) : Promise < void > {
3356
- await this . _createWorktree ( repository , undefined , undefined ) ;
3396
+ await this . _createWorktree ( repository ) ;
3357
3397
}
3358
3398
3359
- private async _createWorktree ( repository : Repository , worktreePath ?: string , name ?: string ) : Promise < void > {
3399
+ private async _createWorktree ( repository : Repository , worktreePath ?: string , name ?: string , newBranch ?: boolean ) : Promise < void > {
3360
3400
const config = workspace . getConfiguration ( 'git' ) ;
3361
3401
const showRefDetails = config . get < boolean > ( 'showReferenceDetails' ) === true ;
3362
3402
3363
3403
if ( ! name ) {
3404
+ const createBranch = new CreateBranchItem ( ) ;
3364
3405
const getBranchPicks = async ( ) => {
3365
- const refs = await repository . getRefs ( {
3366
- pattern : 'refs/heads' ,
3367
- includeCommitDetails : showRefDetails
3368
- } ) ;
3369
- const processors = [ new RefProcessor ( RefType . Head , BranchItem ) ] ;
3370
- const itemsProcessor = new RefItemsProcessor ( repository , processors ) ;
3371
- return itemsProcessor . processRefs ( refs ) ;
3406
+ const refs = await repository . getRefs ( { includeCommitDetails : showRefDetails } ) ;
3407
+ const itemsProcessor = new RefItemsProcessor ( repository , [
3408
+ new RefProcessor ( RefType . Head ) ,
3409
+ new RefProcessor ( RefType . RemoteHead ) ,
3410
+ new RefProcessor ( RefType . Tag )
3411
+ ] ) ;
3412
+ const branchItems = itemsProcessor . processRefs ( refs ) ;
3413
+ return [ createBranch , { label : '' , kind : QuickPickItemKind . Separator } , ...branchItems ] ;
3372
3414
} ;
3373
3415
3374
3416
const placeHolder = l10n . t ( 'Select a branch to create the new worktree from' ) ;
3375
3417
const choice = await this . pickRef ( getBranchPicks ( ) , placeHolder ) ;
3376
3418
3377
- if ( ! ( choice instanceof BranchItem ) || ! choice . refName ) {
3419
+ if ( choice === createBranch ) {
3420
+ const branchName = await this . promptForBranchName ( repository ) ;
3421
+
3422
+ if ( ! branchName ) {
3423
+ return ;
3424
+ }
3425
+
3426
+ newBranch = true ;
3427
+ name = branchName ;
3428
+ } else if ( choice instanceof BranchItem && choice . refName ) {
3429
+ name = choice . refName ;
3430
+ } else {
3378
3431
return ;
3379
3432
}
3380
- name = choice . refName ;
3381
3433
}
3382
3434
3383
3435
const disposables : Disposable [ ] = [ ] ;
@@ -3395,6 +3447,10 @@ export class CommandCenter {
3395
3447
dispose ( disposables ) ;
3396
3448
inputBox . dispose ( ) ;
3397
3449
3450
+ if ( ! worktreeName ) {
3451
+ return ;
3452
+ }
3453
+
3398
3454
// Default to view parent directory of repository root
3399
3455
const defaultUri = Uri . file ( path . dirname ( repository . root ) ) ;
3400
3456
@@ -3416,10 +3472,48 @@ export class CommandCenter {
3416
3472
3417
3473
worktreePath = path . join ( uris [ 0 ] . fsPath , worktreeName ) ;
3418
3474
3419
- await repository . worktree ( {
3420
- name : name ,
3421
- path : worktreePath ,
3422
- } ) ;
3475
+ try {
3476
+ await repository . worktree ( { name : name , path : worktreePath , newBranch : newBranch } ) ;
3477
+ } catch ( err ) {
3478
+ if ( err . gitErrorCode !== GitErrorCodes . WorktreeAlreadyExists ) {
3479
+ throw err ;
3480
+ }
3481
+
3482
+ this . handleWorktreeError ( err ) ;
3483
+ return ;
3484
+
3485
+ }
3486
+ }
3487
+
3488
+ private async handleWorktreeError ( err : any ) : Promise < void > {
3489
+ const errorMessage = err . stderr ;
3490
+ const match = errorMessage . match ( / w o r k t r e e a t ' ( [ ^ ' ] + ) ' / ) || errorMessage . match ( / ' ( [ ^ ' ] + ) ' / ) ;
3491
+ const path = match ? match [ 1 ] : undefined ;
3492
+
3493
+ if ( ! path ) {
3494
+ return ;
3495
+ }
3496
+
3497
+ const worktreeRepository = this . model . getRepository ( path ) || this . model . getRepository ( Uri . file ( path ) ) ;
3498
+
3499
+ if ( ! worktreeRepository ) {
3500
+ return ;
3501
+ }
3502
+
3503
+ const openWorktree = l10n . t ( 'Open in current window' ) ;
3504
+ const openWorktreeInNewWindow = l10n . t ( 'Open in new window' ) ;
3505
+ const message = l10n . t ( errorMessage ) ;
3506
+ const choice = await window . showWarningMessage ( message , { modal : true } , openWorktree , openWorktreeInNewWindow ) ;
3507
+
3508
+
3509
+
3510
+ if ( choice === openWorktree ) {
3511
+ await this . openWorktreeInCurrentWindow ( worktreeRepository ) ;
3512
+ } else if ( choice === openWorktreeInNewWindow ) {
3513
+ await this . openWorktreeInNewWindow ( worktreeRepository ) ;
3514
+ }
3515
+
3516
+ return ;
3423
3517
}
3424
3518
3425
3519
@command ( 'git.deleteWorktree' , { repository : true } )
@@ -3456,18 +3550,10 @@ export class CommandCenter {
3456
3550
}
3457
3551
}
3458
3552
3459
- @command ( 'git.deleteWorktreeFromPalette' )
3460
- async deleteWorktreeFromPalette ( ) : Promise < void > {
3461
- const mainRepository = this . model . repositories . find ( repo =>
3462
- ! repo . dotGit . commonPath
3463
- ) ;
3464
-
3465
- if ( ! mainRepository ) {
3466
- return ;
3467
- }
3468
-
3553
+ @command ( 'git.deleteWorktreeFromPalette' , { repository : true , repositoryFilter : [ 'repository' , 'submodule' ] } )
3554
+ async deleteWorktreeFromPalette ( repository : Repository ) : Promise < void > {
3469
3555
const worktreePicks = async ( ) : Promise < WorktreeDeleteItem [ ] | QuickPickItem [ ] > => {
3470
- const worktrees = await mainRepository . getWorktrees ( ) ;
3556
+ const worktrees = await repository . getWorktrees ( ) ;
3471
3557
return worktrees . length === 0
3472
3558
? [ { label : l10n . t ( '$(info) This repository has no worktrees.' ) } ]
3473
3559
: worktrees . map ( worktree => new WorktreeDeleteItem ( worktree ) ) ;
@@ -3477,14 +3563,13 @@ export class CommandCenter {
3477
3563
const choice = await this . pickRef < WorktreeDeleteItem | QuickPickItem > ( worktreePicks ( ) , placeHolder ) ;
3478
3564
3479
3565
if ( choice instanceof WorktreeDeleteItem ) {
3480
- await choice . run ( mainRepository ) ;
3566
+ await choice . run ( repository ) ;
3481
3567
}
3482
3568
}
3483
3569
3484
3570
@command ( 'git.openWorktree' , { repository : true } )
3485
- async openWorktreeInCurrentWindow ( repository : Repository , ...args : SourceControl [ ] ) : Promise < void > {
3486
- // If multiple repositories are selected, no action is taken
3487
- if ( args . length > 0 ) {
3571
+ async openWorktreeInCurrentWindow ( repository : Repository ) : Promise < void > {
3572
+ if ( ! repository ) {
3488
3573
return ;
3489
3574
}
3490
3575
@@ -3493,9 +3578,8 @@ export class CommandCenter {
3493
3578
}
3494
3579
3495
3580
@command ( 'git.openWorktreeInNewWindow' , { repository : true } )
3496
- async openWorktreeInNewWindow ( repository : Repository , ...args : SourceControl [ ] ) : Promise < void > {
3497
- // If multiple repositories are selected, no action is taken
3498
- if ( args . length > 0 ) {
3581
+ async openWorktreeInNewWindow ( repository : Repository ) : Promise < void > {
3582
+ if ( ! repository ) {
3499
3583
return ;
3500
3584
}
3501
3585
@@ -4863,10 +4947,8 @@ export class CommandCenter {
4863
4947
4864
4948
if ( repository ) {
4865
4949
repositoryPromise = Promise . resolve ( repository ) ;
4866
- } else if ( this . model . repositories . length === 1 ) {
4867
- repositoryPromise = Promise . resolve ( this . model . repositories [ 0 ] ) ;
4868
4950
} else {
4869
- repositoryPromise = this . model . pickRepository ( ) ;
4951
+ repositoryPromise = this . model . pickRepository ( options . repositoryFilter ) ;
4870
4952
}
4871
4953
4872
4954
result = repositoryPromise . then ( repository => {
0 commit comments