@@ -340,6 +340,7 @@ class StashItem implements QuickPickItem {
340340
341341interface ScmCommandOptions {
342342 repository ?: boolean ;
343+ repositoryFilter ?: ( 'repository' | 'submodule' | 'worktree' ) [ ] ;
343344}
344345
345346interface ScmCommand {
@@ -1681,7 +1682,11 @@ export class CommandCenter {
16811682
16821683 @command ( 'git.diff.stageHunk' )
16831684 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+ }
16851690 }
16861691
16871692 @command ( 'git.diff.stageSelection' )
@@ -1719,6 +1724,36 @@ export class CommandCenter {
17191724 await repository . stage ( resource , result , modifiedDocument . encoding ) ) ;
17201725 }
17211726
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+
17221757 @command ( 'git.stageSelectedRanges' )
17231758 async stageSelectedChanges ( ) : Promise < void > {
17241759 const textEditor = window . activeTextEditor ;
@@ -2882,10 +2917,15 @@ export class CommandCenter {
28822917 try {
28832918 await item . run ( repository , opts ) ;
28842919 } catch ( err ) {
2885- if ( err . gitErrorCode !== GitErrorCodes . DirtyWorkTree ) {
2920+ if ( err . gitErrorCode !== GitErrorCodes . DirtyWorkTree && err . gitErrorCode !== GitErrorCodes . WorktreeAlreadyExists ) {
28862921 throw err ;
28872922 }
28882923
2924+ if ( err . gitErrorCode === GitErrorCodes . WorktreeAlreadyExists ) {
2925+ this . handleWorktreeError ( err ) ;
2926+ return false ;
2927+ }
2928+
28892929 const stash = l10n . t ( 'Stash & Checkout' ) ;
28902930 const migrate = l10n . t ( 'Migrate Changes' ) ;
28912931 const force = l10n . t ( 'Force Checkout' ) ;
@@ -3351,33 +3391,45 @@ export class CommandCenter {
33513391 }
33523392 }
33533393
3354- @command ( 'git.createWorktree' , { repository : true } )
3394+ @command ( 'git.createWorktree' , { repository : true , repositoryFilter : [ 'repository' , 'submodule' ] } )
33553395 async createWorktree ( repository : Repository ) : Promise < void > {
3356- await this . _createWorktree ( repository , undefined , undefined ) ;
3396+ await this . _createWorktree ( repository ) ;
33573397 }
33583398
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 > {
33603400 const config = workspace . getConfiguration ( 'git' ) ;
33613401 const showRefDetails = config . get < boolean > ( 'showReferenceDetails' ) === true ;
33623402
33633403 if ( ! name ) {
3404+ const createBranch = new CreateBranchItem ( ) ;
33643405 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 ] ;
33723414 } ;
33733415
33743416 const placeHolder = l10n . t ( 'Select a branch to create the new worktree from' ) ;
33753417 const choice = await this . pickRef ( getBranchPicks ( ) , placeHolder ) ;
33763418
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 {
33783431 return ;
33793432 }
3380- name = choice . refName ;
33813433 }
33823434
33833435 const disposables : Disposable [ ] = [ ] ;
@@ -3395,6 +3447,10 @@ export class CommandCenter {
33953447 dispose ( disposables ) ;
33963448 inputBox . dispose ( ) ;
33973449
3450+ if ( ! worktreeName ) {
3451+ return ;
3452+ }
3453+
33983454 // Default to view parent directory of repository root
33993455 const defaultUri = Uri . file ( path . dirname ( repository . root ) ) ;
34003456
@@ -3416,10 +3472,48 @@ export class CommandCenter {
34163472
34173473 worktreePath = path . join ( uris [ 0 ] . fsPath , worktreeName ) ;
34183474
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 ;
34233517 }
34243518
34253519 @command ( 'git.deleteWorktree' , { repository : true } )
@@ -3456,18 +3550,10 @@ export class CommandCenter {
34563550 }
34573551 }
34583552
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 > {
34693555 const worktreePicks = async ( ) : Promise < WorktreeDeleteItem [ ] | QuickPickItem [ ] > => {
3470- const worktrees = await mainRepository . getWorktrees ( ) ;
3556+ const worktrees = await repository . getWorktrees ( ) ;
34713557 return worktrees . length === 0
34723558 ? [ { label : l10n . t ( '$(info) This repository has no worktrees.' ) } ]
34733559 : worktrees . map ( worktree => new WorktreeDeleteItem ( worktree ) ) ;
@@ -3477,14 +3563,13 @@ export class CommandCenter {
34773563 const choice = await this . pickRef < WorktreeDeleteItem | QuickPickItem > ( worktreePicks ( ) , placeHolder ) ;
34783564
34793565 if ( choice instanceof WorktreeDeleteItem ) {
3480- await choice . run ( mainRepository ) ;
3566+ await choice . run ( repository ) ;
34813567 }
34823568 }
34833569
34843570 @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 ) {
34883573 return ;
34893574 }
34903575
@@ -3493,9 +3578,8 @@ export class CommandCenter {
34933578 }
34943579
34953580 @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 ) {
34993583 return ;
35003584 }
35013585
@@ -4863,10 +4947,8 @@ export class CommandCenter {
48634947
48644948 if ( repository ) {
48654949 repositoryPromise = Promise . resolve ( repository ) ;
4866- } else if ( this . model . repositories . length === 1 ) {
4867- repositoryPromise = Promise . resolve ( this . model . repositories [ 0 ] ) ;
48684950 } else {
4869- repositoryPromise = this . model . pickRepository ( ) ;
4951+ repositoryPromise = this . model . pickRepository ( options . repositoryFilter ) ;
48704952 }
48714953
48724954 result = repositoryPromise . then ( repository => {
0 commit comments