diff --git a/package.json b/package.json index 7e1a537..4fe822a 100644 --- a/package.json +++ b/package.json @@ -2751,11 +2751,6 @@ } }, { - "command": "gitlens.views.terminalCheckoutBranch", - "title": "Checkout Branch (via Terminal)", - "category": "GitLens" - }, - { "command": "gitlens.views.terminalCreateBranch", "title": "Create Branch (via Terminal)...", "category": "GitLens" @@ -2766,23 +2761,18 @@ "category": "GitLens" }, { - "command": "gitlens.views.terminalMergeBranch", - "title": "Merge Branch (via Terminal)", - "category": "GitLens" - }, - { - "command": "gitlens.views.terminalRebaseBranch", - "title": "Rebase (Interactive) Branch (via Terminal)", + "command": "gitlens.views.mergeBranchInto", + "title": "Merge Branch into Current", "category": "GitLens" }, { - "command": "gitlens.views.terminalRebaseBranchToRemote", - "title": "Rebase (Interactive) Branch to Remote (via Terminal)", + "command": "gitlens.views.rebaseOntoBranch", + "title": "Rebase Current onto Branch", "category": "GitLens" }, { - "command": "gitlens.views.terminalSquashBranchIntoCommit", - "title": "Squash Branch into Commit (via Terminal)", + "command": "gitlens.views.rebaseOntoUpstream", + "title": "Rebase Current onto Upstream", "category": "GitLens" }, { @@ -3654,10 +3644,6 @@ "when": "false" }, { - "command": "gitlens.views.terminalCheckoutBranch", - "when": "false" - }, - { "command": "gitlens.views.terminalCreateBranch", "when": "false" }, @@ -3666,19 +3652,15 @@ "when": "false" }, { - "command": "gitlens.views.terminalMergeBranch", - "when": "false" - }, - { - "command": "gitlens.views.terminalRebaseBranch", + "command": "gitlens.views.mergeBranchInto", "when": "false" }, { - "command": "gitlens.views.terminalRebaseBranchToRemote", + "command": "gitlens.views.rebaseOntoBranch", "when": "false" }, { - "command": "gitlens.views.terminalSquashBranchIntoCommit", + "command": "gitlens.views.rebaseOntoUpstream", "when": "false" }, { @@ -4440,6 +4422,21 @@ "group": "1_gitlens@1" }, { + "command": "gitlens.views.rebaseOntoUpstream", + "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?=.*?\\b\\+current\\b)(?=.*?\\b\\+tracking\\b)/", + "group": "1_gitlens_1@1" + }, + { + "command": "gitlens.views.mergeBranchInto", + "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?!.*?\\b\\+current\\b)/", + "group": "1_gitlens_1@2" + }, + { + "command": "gitlens.views.rebaseOntoBranch", + "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?!.*?\\b\\+current\\b)/", + "group": "1_gitlens_1@3" + }, + { "command": "gitlens.openBranchInRemote", "when": "viewItem =~ /gitlens:branch\\b(?=.*?\\b\\+(tracking|remote)\\b)/", "group": "2_gitlens@1" @@ -4490,31 +4487,6 @@ "group": "7_gitlens_more@2" }, { - "command": "gitlens.views.terminalCheckoutBranch", - "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?!.*?\\b\\+current\\b)/", - "group": "8_gitlens@1" - }, - { - "command": "gitlens.views.terminalRebaseBranchToRemote", - "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?=.*?\\b\\+current\\b)(?=.*?\\b\\+tracking\\b)/", - "group": "8_gitlens@1" - }, - { - "command": "gitlens.views.terminalMergeBranch", - "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?!.*?\\b\\+current\\b)/", - "group": "8_gitlens@2" - }, - { - "command": "gitlens.views.terminalRebaseBranch", - "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?!.*?\\b\\+current\\b)/", - "group": "8_gitlens@3" - }, - { - "command": "gitlens.views.terminalSquashBranchIntoCommit", - "when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?!.*?\\b\\+current\\b)/", - "group": "8_gitlens@4" - }, - { "command": "gitlens.views.terminalCreateBranch", "when": "!gitlens:readonly && viewItem =~ /gitlens:(branch|commit|tag)\\b/", "group": "8_gitlens@5" diff --git a/src/commands/git/cherry-pick.ts b/src/commands/git/cherry-pick.ts index 4df2480..3268679 100644 --- a/src/commands/git/cherry-pick.ts +++ b/src/commands/git/cherry-pick.ts @@ -19,13 +19,45 @@ import { Logger } from '../../logger'; interface State { repo: Repository; destination: GitBranch; - source: GitBranch | GitReference; + source?: GitBranch | GitReference; commits?: GitLogCommit[]; } +export interface CherryPickGitCommandArgs { + readonly command: 'cherry-pick'; + state?: Partial; +} + export class CherryPickGitCommand extends QuickCommandBase { - constructor() { + constructor(args?: CherryPickGitCommandArgs) { super('cherry-pick', 'cherry-pick', 'Cherry Pick', false, { description: 'via Terminal' }); + + if (args === undefined || args.state === undefined) return; + + let counter = 0; + if (args.state.repo !== undefined) { + counter++; + } + + if (args.state.destination !== undefined) { + counter++; + } + + if (args.state.source !== undefined) { + counter++; + + if (!GitBranch.is(args.state.source)) { + counter++; + } + } else if (args.state.commits !== undefined) { + counter++; + } + + this._initialState = { + counter: counter, + confirm: true, + ...args.state + }; } execute(state: State) { @@ -35,7 +67,7 @@ export class CherryPickGitCommand extends QuickCommandBase { runGitCommandInTerminal('cherry-pick', state.commits.map(c => c.sha).join(' '), state.repo.path, true); } - runGitCommandInTerminal('cherry-pick', state.source.ref, state.repo.path, true); + runGitCommandInTerminal('cherry-pick', state.source!.ref, state.repo.path, true); } protected async *steps(): StepAsyncGenerator { diff --git a/src/commands/git/merge.ts b/src/commands/git/merge.ts index 58a8a94..e0692ea 100644 --- a/src/commands/git/merge.ts +++ b/src/commands/git/merge.ts @@ -22,9 +22,31 @@ interface State { flags: string[]; } +export interface MergeGitCommandArgs { + readonly command: 'merge'; + state?: Partial; +} + export class MergeGitCommand extends QuickCommandBase { - constructor() { + constructor(args?: MergeGitCommandArgs) { super('merge', 'merge', 'Merge', false, { description: 'via Terminal' }); + + if (args === undefined || args.state === undefined) return; + + let counter = 0; + if (args.state.repo !== undefined) { + counter++; + } + + if (args.state.source !== undefined) { + counter++; + } + + this._initialState = { + counter: counter, + confirm: true, + ...args.state + }; } execute(state: State) { @@ -146,6 +168,15 @@ export class MergeGitCommand extends QuickCommandBase { count )} from ${state.source.name} into ${state.destination.name}`, item: ['--no-ff'] + }, + { + label: `Squash ${this.title}`, + description: `--squash ${state.source.name} into ${state.destination.name}`, + detail: `Will squash all commits into a single commit when merging ${Strings.pluralize( + 'commit', + count + )} from ${state.source.name} into ${state.destination.name}`, + item: ['--squash'] } ] ); diff --git a/src/commands/git/rebase.ts b/src/commands/git/rebase.ts index 8571fce..d6a1111 100644 --- a/src/commands/git/rebase.ts +++ b/src/commands/git/rebase.ts @@ -22,9 +22,31 @@ interface State { flags: string[]; } +export interface RebaseGitCommandArgs { + readonly command: 'rebase'; + state?: Partial; +} + export class RebaseGitCommand extends QuickCommandBase { - constructor() { + constructor(args?: RebaseGitCommandArgs) { super('rebase', 'rebase', 'Rebase', false, { description: 'via Terminal' }); + + if (args === undefined || args.state === undefined) return; + + let counter = 0; + if (args.state.repo !== undefined) { + counter++; + } + + if (args.state.source !== undefined) { + counter++; + } + + this._initialState = { + counter: counter, + confirm: true, + ...args.state + }; } execute(state: State) { diff --git a/src/commands/gitCommands.ts b/src/commands/gitCommands.ts index bf061e8..8cb67d9 100644 --- a/src/commands/gitCommands.ts +++ b/src/commands/gitCommands.ts @@ -11,12 +11,12 @@ import { StepSelection } from './quickCommand'; import { Directive, DirectiveQuickPickItem } from '../quickpicks'; -import { CherryPickGitCommand } from './git/cherry-pick'; +import { CherryPickGitCommand, CherryPickGitCommandArgs } from './git/cherry-pick'; import { FetchGitCommand, FetchGitCommandArgs } from './git/fetch'; -import { MergeGitCommand } from './git/merge'; +import { MergeGitCommand, MergeGitCommandArgs } from './git/merge'; import { PullGitCommand, PullGitCommandArgs } from './git/pull'; import { PushGitCommand, PushGitCommandArgs } from './git/push'; -import { RebaseGitCommand } from './git/rebase'; +import { RebaseGitCommand, RebaseGitCommandArgs } from './git/rebase'; import { StashGitCommand, StashGitCommandArgs } from './git/stash'; import { SwitchGitCommand, SwitchGitCommandArgs } from './git/switch'; import { Container } from '../container'; @@ -25,9 +25,12 @@ import { configuration } from '../configuration'; const sanitizeLabel = /\$\(.+?\)|\W/g; export type GitCommandsCommandArgs = + | CherryPickGitCommandArgs | FetchGitCommandArgs + | MergeGitCommandArgs | PullGitCommandArgs | PushGitCommandArgs + | RebaseGitCommandArgs | StashGitCommandArgs | SwitchGitCommandArgs; @@ -39,12 +42,12 @@ class PickCommandStep implements QuickPickStep { constructor(args?: GitCommandsCommandArgs) { this.items = [ - new CherryPickGitCommand(), - new MergeGitCommand(), + new CherryPickGitCommand(args && args.command === 'cherry-pick' ? args : undefined), + new MergeGitCommand(args && args.command === 'merge' ? args : undefined), new FetchGitCommand(args && args.command === 'fetch' ? args : undefined), new PullGitCommand(args && args.command === 'pull' ? args : undefined), new PushGitCommand(args && args.command === 'push' ? args : undefined), - new RebaseGitCommand(), + new RebaseGitCommand(args && args.command === 'rebase' ? args : undefined), new StashGitCommand(args && args.command === 'stash' ? args : undefined), new SwitchGitCommand(args && args.command === 'switch' ? args : undefined) ]; diff --git a/src/views/nodes/branchTrackingStatusNode.ts b/src/views/nodes/branchTrackingStatusNode.ts index c99de3c..74e546d 100644 --- a/src/views/nodes/branchTrackingStatusNode.ts +++ b/src/views/nodes/branchTrackingStatusNode.ts @@ -39,6 +39,10 @@ export class BranchTrackingStatusNode extends ViewNode implements }):status:upstream:(${this.status.upstream}):${this.direction}`; } + get repoPath(): string { + return this.uri.repoPath!; + } + async getChildren(): Promise { const ahead = this.direction === 'ahead'; const range = ahead diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts index fd62d8f..fe95c22 100644 --- a/src/views/viewCommands.ts +++ b/src/views/viewCommands.ts @@ -153,17 +153,12 @@ export class ViewCommands { this ); - commands.registerCommand('gitlens.views.terminalCheckoutBranch', this.terminalCheckoutBranch, this); + commands.registerCommand('gitlens.views.mergeBranchInto', this.merge, this); + commands.registerCommand('gitlens.views.rebaseOntoBranch', this.rebase, this); + commands.registerCommand('gitlens.views.rebaseOntoUpstream', this.rebaseToRemote, this); + commands.registerCommand('gitlens.views.terminalCreateBranch', this.terminalCreateBranch, this); commands.registerCommand('gitlens.views.terminalDeleteBranch', this.terminalDeleteBranch, this); - commands.registerCommand('gitlens.views.terminalMergeBranch', this.terminalMergeBranch, this); - commands.registerCommand('gitlens.views.terminalRebaseBranch', this.terminalRebaseBranch, this); - commands.registerCommand('gitlens.views.terminalRebaseBranchToRemote', this.terminalRebaseBranchToRemote, this); - commands.registerCommand( - 'gitlens.views.terminalSquashBranchIntoCommit', - this.terminalSquashBranchIntoCommit, - this - ); commands.registerCommand('gitlens.views.terminalCheckoutCommit', this.terminalCheckoutCommit, this); commands.registerCommand('gitlens.views.terminalCherryPickCommit', this.terminalCherryPickCommit, this); commands.registerCommand('gitlens.views.terminalPushCommit', this.terminalPushCommit, this); @@ -257,6 +252,46 @@ export class ViewCommands { } } + private async merge(node: BranchNode | TagNode) { + if (!(node instanceof BranchNode) && !(node instanceof TagNode)) return undefined; + + const repo = await Container.git.getRepository(node.repoPath); + + const args: GitCommandsCommandArgs = { + command: 'merge', + state: { repo: repo!, source: node instanceof BranchNode ? node.branch : node.tag } + }; + return commands.executeCommand(Commands.GitCommands, args); + } + + private async rebase(node: BranchNode | TagNode) { + if (!(node instanceof BranchNode) && !(node instanceof TagNode)) return undefined; + + const repo = await Container.git.getRepository(node.repoPath); + + const args: GitCommandsCommandArgs = { + command: 'rebase', + state: { repo: repo!, source: node instanceof BranchNode ? node.branch : node.tag } + }; + return commands.executeCommand(Commands.GitCommands, args); + } + + private async rebaseToRemote(node: BranchNode | BranchTrackingStatusNode) { + if (!(node instanceof BranchNode) && !(node instanceof BranchTrackingStatusNode)) return undefined; + + const upstream = node instanceof BranchNode ? node.branch.tracking : node.status.upstream; + + const repo = await Container.git.getRepository(node.repoPath); + const branches = await repo!.getBranches({ filter: b => b.remote && b.name === upstream }); + if (branches.length === 0) return undefined; + + const args: GitCommandsCommandArgs = { + command: 'rebase', + state: { repo: repo!, source: branches[0] } + }; + return commands.executeCommand(Commands.GitCommands, args); + } + private async restore(node: ViewRefFileNode) { if (!(node instanceof ViewRefFileNode)) return undefined; @@ -771,12 +806,6 @@ export class ViewCommands { return node.setComparisonNotation(comparisonNotation); } - terminalCheckoutBranch(node: BranchNode) { - if (!(node instanceof BranchNode)) return; - - runGitCommandInTerminal('checkout', `${node.ref}`, node.repoPath); - } - async terminalCreateBranch(node: ViewRefNode) { if (!(node instanceof ViewRefNode)) return; @@ -808,34 +837,6 @@ export class ViewCommands { } } - terminalMergeBranch(node: BranchNode) { - if (!(node instanceof BranchNode)) return; - - runGitCommandInTerminal('merge', `${node.ref}`, node.repoPath); - } - - terminalRebaseBranch(node: BranchNode) { - if (!(node instanceof BranchNode)) return; - - runGitCommandInTerminal('rebase', `-i ${node.ref}`, node.repoPath); - } - - terminalRebaseBranchToRemote(node: BranchNode | BranchTrackingStatusNode) { - if (node instanceof BranchNode) { - if (!node.branch.current || !node.branch.tracking) return; - - runGitCommandInTerminal('rebase', `-i ${node.branch.tracking}`, node.repoPath); - } else if (node instanceof BranchTrackingStatusNode) { - runGitCommandInTerminal('rebase', `-i ${node.status.upstream}`, node.status.repoPath); - } - } - - terminalSquashBranchIntoCommit(node: BranchNode) { - if (!(node instanceof BranchNode)) return; - - runGitCommandInTerminal('merge', `--squash ${node.ref}`, node.repoPath); - } - terminalCheckoutCommit(node: CommitNode) { if (!(node instanceof CommitNode)) return;