diff --git a/package.json b/package.json index 6ea091d..25b134b 100644 --- a/package.json +++ b/package.json @@ -499,6 +499,11 @@ "category": "GitLens" }, { + "command": "gitlens.openBranchInRemote", + "title": "Open Branch in Remote", + "category": "GitLens" + }, + { "command": "gitlens.openCommitInRemote", "title": "Open Line Commit in Remote", "category": "GitLens" @@ -509,6 +514,11 @@ "category": "GitLens" }, { + "command": "gitlens.openRepoInRemote", + "title": "Open Repository in Remote", + "category": "GitLens" + }, + { "command": "gitlens.stashApply", "title": "Apply Stashed Changes", "category": "GitLens" @@ -618,6 +628,10 @@ "when": "gitlens:enabled" }, { + "command": "gitlens.openBranchInRemote", + "when": "gitlens:hasRemotes" + }, + { "command": "gitlens.openCommitInRemote", "when": "gitlens:isBlameable && gitlens:hasRemotes" }, @@ -626,6 +640,10 @@ "when": "gitlens:isTracked && gitlens:hasRemotes" }, { + "command": "gitlens.openRepoInRemote", + "when": "gitlens:hasRemotes" + }, + { "command": "gitlens.stashApply", "when": "gitlens:enabled && config.gitlens.insiders" }, @@ -899,7 +917,7 @@ "@types/mocha": "2.2.41", "@types/node": "7.0.18", "@types/tmp": "0.0.33", - "mocha": "3.3.0", + "mocha": "3.4.1", "tslint": "5.2.0", "typescript": "2.3.2", "vscode": "1.1.0" diff --git a/src/commands.ts b/src/commands.ts index 03a6e74..2f33c49 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -14,9 +14,11 @@ export * from './commands/diffWithNext'; export * from './commands/diffWithPrevious'; export * from './commands/diffWithWorking'; export * from './commands/openChangedFiles'; +export * from './commands/openBranchInRemote'; export * from './commands/openCommitInRemote'; export * from './commands/openFileInRemote'; export * from './commands/openInRemote'; +export * from './commands/openRepoInRemote'; export * from './commands/showBlame'; export * from './commands/showBlameHistory'; export * from './commands/showFileHistory'; diff --git a/src/commands/common.ts b/src/commands/common.ts index 1d3eb4f..e850379 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -6,7 +6,7 @@ import { Telemetry } from '../telemetry'; export type Commands = 'gitlens.closeUnchangedFiles' | 'gitlens.copyMessageToClipboard' | 'gitlens.copyShaToClipboard' | 'gitlens.diffDirectory' | 'gitlens.diffWithBranch' | 'gitlens.diffWithNext' | 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' | - 'gitlens.openChangedFiles' | 'gitlens.openCommitInRemote' | 'gitlens.openFileInRemote' | 'gitlens.openInRemote' | + 'gitlens.openChangedFiles' | 'gitlens.openBranchInRemote' | 'gitlens.openCommitInRemote' | 'gitlens.openFileInRemote' | 'gitlens.openInRemote' | 'gitlens.openRepoInRemote' | 'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showCommitSearch' | 'gitlens.showFileHistory' | 'gitlens.showLastQuickPick' | 'gitlens.showQuickBranchHistory' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | @@ -26,9 +26,11 @@ export const Commands = { DiffWithWorking: 'gitlens.diffWithWorking' as Commands, DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands, OpenChangedFiles: 'gitlens.openChangedFiles' as Commands, + OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands, OpenCommitInRemote: 'gitlens.openCommitInRemote' as Commands, OpenFileInRemote: 'gitlens.openFileInRemote' as Commands, OpenInRemote: 'gitlens.openInRemote' as Commands, + OpenRepoInRemote: 'gitlens.openRepoInRemote' as Commands, ShowBlame: 'gitlens.showBlame' as Commands, ShowBlameHistory: 'gitlens.showBlameHistory' as Commands, ShowCommitSearch: 'gitlens.showCommitSearch' as Commands, diff --git a/src/commands/openBranchInRemote.ts b/src/commands/openBranchInRemote.ts new file mode 100644 index 0000000..74ff5d1 --- /dev/null +++ b/src/commands/openBranchInRemote.ts @@ -0,0 +1,55 @@ +'use strict'; +import { Arrays } from '../system'; +import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode'; +import { ActiveEditorCommand, Commands, getCommandUri } from './common'; +import { GitService, GitUri } from '../gitService'; +import { Logger } from '../logger'; +import { BranchesQuickPick, CommandQuickPickItem } from '../quickPicks'; +import { OpenInRemoteCommandArgs } from './openInRemote'; + +export interface OpenBranchInRemoteCommandArgs { + branch?: string; +} + +export class OpenBranchInRemoteCommand extends ActiveEditorCommand { + + constructor(private git: GitService) { + super(Commands.OpenBranchInRemote); + } + + async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, args: OpenBranchInRemoteCommandArgs = {}) { + uri = getCommandUri(uri, editor); + + const gitUri = uri && await GitUri.fromUri(uri, this.git); + + const repoPath = gitUri === undefined ? this.git.repoPath : gitUri.repoPath; + if (!repoPath) return undefined; + + try { + if (args.branch === undefined) { + const branches = await this.git.getBranches(repoPath); + + const pick = await BranchesQuickPick.show(branches, `Show history for branch\u2026`); + if (pick === undefined) return undefined; + + if (pick instanceof CommandQuickPickItem) return undefined; + + args.branch = pick.branch.name; + if (args.branch === undefined) return undefined; + } + + const remotes = Arrays.uniqueBy(await this.git.getRemotes(repoPath), _ => _.url, _ => !!_.provider); + return commands.executeCommand(Commands.OpenInRemote, uri, { + resource: { + type: 'branch', + branch: args.branch + }, + remotes + } as OpenInRemoteCommandArgs); + } + catch (ex) { + Logger.error(ex, 'OpenBranchInRemoteCommandArgs'); + return window.showErrorMessage(`Unable to open branch in remote provider. See output channel for more details`); + } + } +} \ No newline at end of file diff --git a/src/commands/openInRemote.ts b/src/commands/openInRemote.ts index 64056a6..d4193b8 100644 --- a/src/commands/openInRemote.ts +++ b/src/commands/openInRemote.ts @@ -32,6 +32,16 @@ export class OpenInRemoteCommand extends ActiveEditorCommand { let placeHolder: string = ''; switch (args.resource.type) { case 'branch': + // Check to see if the remote is in the branch + const index = args.resource.branch.indexOf('/'); + if (index >= 0) { + const remoteName = args.resource.branch.substring(0, index); + const remote = args.remotes.find(r => r.name === remoteName); + if (remote !== undefined) { + args.resource.branch = args.resource.branch.substring(index + 1); + args.remotes = [remote]; + } + } placeHolder = `open ${args.resource.branch} branch in\u2026`; break; @@ -64,6 +74,11 @@ export class OpenInRemoteCommand extends ActiveEditorCommand { break; } + if (args.remotes.length === 1) { + const command = new OpenRemoteCommandQuickPickItem(args.remotes[0], args.resource); + return command.execute(); + } + const pick = await RemotesQuickPick.show(args.remotes, placeHolder, args.resource, args.goBackCommand); if (pick === undefined) return undefined; diff --git a/src/commands/openRepoInRemote.ts b/src/commands/openRepoInRemote.ts new file mode 100644 index 0000000..90ec904 --- /dev/null +++ b/src/commands/openRepoInRemote.ts @@ -0,0 +1,37 @@ +'use strict'; +import { Arrays } from '../system'; +import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode'; +import { ActiveEditorCommand, Commands, getCommandUri } from './common'; +import { GitService, GitUri } from '../gitService'; +import { Logger } from '../logger'; +import { OpenInRemoteCommandArgs } from './openInRemote'; + +export class OpenRepoInRemoteCommand extends ActiveEditorCommand { + + constructor(private git: GitService) { + super(Commands.OpenRepoInRemote); + } + + async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri) { + uri = getCommandUri(uri, editor); + + const gitUri = uri && await GitUri.fromUri(uri, this.git); + + const repoPath = gitUri === undefined ? this.git.repoPath : gitUri.repoPath; + if (!repoPath) return undefined; + + try { + const remotes = Arrays.uniqueBy(await this.git.getRemotes(repoPath), _ => _.url, _ => !!_.provider); + return commands.executeCommand(Commands.OpenInRemote, uri, { + resource: { + type: 'repo' + }, + remotes + } as OpenInRemoteCommandArgs); + } + catch (ex) { + Logger.error(ex, 'OpenRepoInRemoteCommand'); + return window.showErrorMessage(`Unable to open repository in remote provider. See output channel for more details`); + } + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 49757ae..d4d6c4c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,7 +5,7 @@ import { BlameActiveLineController } from './blameActiveLineController'; import { BlameAnnotationController } from './blameAnnotationController'; import { CommandContext, setCommandContext } from './commands'; import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands'; -import { OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand } from './commands'; +import { OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands'; import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands'; import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands'; import { ShowBlameCommand, ToggleBlameCommand } from './commands'; @@ -93,9 +93,11 @@ export async function activate(context: ExtensionContext) { context.subscriptions.push(new DiffWithNextCommand(git)); context.subscriptions.push(new DiffWithPreviousCommand(git)); context.subscriptions.push(new DiffWithWorkingCommand(git)); + context.subscriptions.push(new OpenBranchInRemoteCommand(git)); context.subscriptions.push(new OpenCommitInRemoteCommand(git)); context.subscriptions.push(new OpenFileInRemoteCommand(git)); context.subscriptions.push(new OpenInRemoteCommand()); + context.subscriptions.push(new OpenRepoInRemoteCommand(git)); context.subscriptions.push(new ShowBlameCommand(annotationController)); context.subscriptions.push(new ToggleBlameCommand(annotationController)); context.subscriptions.push(new ShowBlameHistoryCommand(git)); diff --git a/src/git/remotes/provider.ts b/src/git/remotes/provider.ts index 8967d89..814bc0c 100644 --- a/src/git/remotes/provider.ts +++ b/src/git/remotes/provider.ts @@ -3,10 +3,11 @@ import { commands, Range, Uri } from 'vscode'; import { BuiltInCommands } from '../../constants'; import { GitLogCommit } from '../../gitService'; -export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'working-file'; +export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'repo' | 'working-file'; export type RemoteResource = { type: 'branch', branch: string } | { type: 'commit', sha: string } | { type: 'file', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string } | + { type: 'repo' } | { type: 'working-file', branch?: string, fileName: string, range?: Range }; export function getNameFromRemoteResource(resource: RemoteResource) { @@ -14,6 +15,7 @@ export function getNameFromRemoteResource(resource: RemoteResource) { case 'branch': return 'Branch'; case 'commit': return 'Commit'; case 'file': return 'File'; + case 'repo': return 'Repository'; case 'working-file': return 'Working File'; default: return ''; } @@ -47,11 +49,17 @@ export abstract class RemoteProvider { return this.openCommit(resource.sha); case 'file': return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range); + case 'repo': + return this.openRepo(); case 'working-file': return this.openFile(resource.fileName, resource.branch, undefined, resource.range); } } + openRepo() { + return this._openUrl(this.baseUrl); + } + openBranch(branch: string) { return this._openUrl(this.getUrlForBranch(branch)); } diff --git a/src/quickPicks/remotes.ts b/src/quickPicks/remotes.ts index 7b91853..07375c5 100644 --- a/src/quickPicks/remotes.ts +++ b/src/quickPicks/remotes.ts @@ -58,6 +58,10 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem { } break; + case 'repo': + description = `$(repo) Repository`; + break; + case 'working-file': description = `$(file-text) ${path.basename(resource.fileName)}`; break;