diff --git a/extensions/git/package.json b/extensions/git/package.json index 603b8d62ed4..752304346ed 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2033,7 +2033,7 @@ "type": "boolean", "scope": "resource", "description": "%config.useEditorAsCommitInput%", - "default": false + "default": true }, "git.verboseCommit": { "type": "boolean", @@ -2363,6 +2363,12 @@ "default": true, "description": "%config.terminalAuthentication%" }, + "git.terminalGitEditor": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%config.terminalGitEditor%" + }, "git.useCommitInputAsStashMessage": { "type": "boolean", "scope": "resource", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 5210e64de80..d17b31b71be 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -212,6 +212,7 @@ "config.requireGitUserConfig": "Controls whether to require explicit Git user configuration or allow Git to guess if missing.", "config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.", "config.terminalAuthentication": "Controls whether to enable VS Code to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", + "config.terminalGitEditor": "Controls whether to enable VS Code to be git editor for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", "config.timeline.showAuthor": "Controls whether to show the commit author in the Timeline view.", "config.timeline.showUncommitted": "Controls whether to show uncommitted changes in the Timeline view.", "config.timeline.date": "Controls which date to use for items in the Timeline view.", diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index d9d9378f8aa..cbf981eea19 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -8,9 +8,11 @@ import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; +import { ITerminalEnvironmentProvider } from './terminal'; -export class Askpass implements IIPCHandler { +export class Askpass implements IIPCHandler, ITerminalEnvironmentProvider { + private env: { [key: string]: string }; private disposable: IDisposable = EmptyDisposable; private cache = new Map(); private credentialsProviders = new Set(); @@ -19,6 +21,13 @@ export class Askpass implements IIPCHandler { if (ipc) { this.disposable = ipc.registerHandler('askpass', this); } + + this.env = { + GIT_ASKPASS: path.join(__dirname, this.ipc ? 'askpass.sh' : 'askpass-empty.sh'), + VSCODE_GIT_ASKPASS_NODE: process.execPath, + VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), + }; } async handle({ request, host }: { request: string; host: string }): Promise { @@ -64,25 +73,13 @@ export class Askpass implements IIPCHandler { } getEnv(): { [key: string]: string } { - if (!this.ipc) { - return { - GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') - }; - } - - const env: { [key: string]: string } = { - ...this.ipc.getEnv(), - VSCODE_GIT_ASKPASS_NODE: process.execPath, - VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', - VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js') - }; - const config = workspace.getConfiguration('git'); - if (config.get('useIntegratedAskPass')) { - env.GIT_ASKPASS = path.join(__dirname, 'askpass.sh'); - } + return config.get('useIntegratedAskPass') ? this.env : {}; + } - return env; + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useIntegratedAskPass') && config.get('terminalAuthentication') ? this.env : {}; } registerCredentialsProvider(provider: CredentialsProvider): Disposable { diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts index 0a2bb752afe..fb0688e4401 100644 --- a/extensions/git/src/gitEditor.ts +++ b/extensions/git/src/gitEditor.ts @@ -5,20 +5,29 @@ import * as path from 'path'; import { TabInputText, Uri, window, workspace } from 'vscode'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; +import { ITerminalEnvironmentProvider } from './terminal'; import { EmptyDisposable, IDisposable } from './util'; interface GitEditorRequest { commitMessagePath?: string; } -export class GitEditor implements IIPCHandler { +export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider { + private env: { [key: string]: string }; private disposable: IDisposable = EmptyDisposable; - constructor(private ipc?: IIPCServer) { + constructor(ipc?: IIPCServer) { if (ipc) { this.disposable = ipc.registerHandler('git-editor', this); } + + this.env = { + GIT_EDITOR: `"${path.join(__dirname, ipc ? 'git-editor.sh' : 'git-editor-empty.sh')}"`, + VSCODE_GIT_EDITOR_NODE: process.execPath, + VSCODE_GIT_EDITOR_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') + }; } async handle({ commitMessagePath }: GitEditorRequest): Promise { @@ -39,24 +48,13 @@ export class GitEditor implements IIPCHandler { } getEnv(): { [key: string]: string } { - if (!this.ipc) { - return { - GIT_EDITOR: `"${path.join(__dirname, 'git-editor-empty.sh')}"` - }; - } - - const env: { [key: string]: string } = { - VSCODE_GIT_EDITOR_NODE: process.execPath, - VSCODE_GIT_EDITOR_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', - VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') - }; - const config = workspace.getConfiguration('git'); - if (config.get('useEditorAsCommitInput')) { - env.GIT_EDITOR = `"${path.join(__dirname, 'git-editor.sh')}"`; - } + return config.get('useEditorAsCommitInput') ? this.env : {}; + } - return env; + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useEditorAsCommitInput') && config.get('terminalGitEditor') ? this.env : {}; } dispose(): void { diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index ad4583cbfe1..c9de357b1a6 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vscode'; +import { ITerminalEnvironmentProvider } from '../terminal'; import { toDisposable } from '../util'; import * as path from 'path'; import * as http from 'http'; @@ -27,7 +28,7 @@ export interface IIPCHandler { handle(request: any): Promise; } -export async function createIPCServer(context?: string): Promise { +export async function createIPCServer(context?: string): Promise { const server = http.createServer(); const hash = crypto.createHash('sha1'); @@ -65,7 +66,7 @@ export interface IIPCServer extends Disposable { registerHandler(name: string, handler: IIPCHandler): Disposable; } -class IPCServer implements IIPCServer, Disposable { +export class IPCServer implements IIPCServer, ITerminalEnvironmentProvider, Disposable { private handlers = new Map(); get ipcHandlePath(): string { return this._ipcHandlePath; } @@ -110,6 +111,10 @@ class IPCServer implements IIPCServer, Disposable { return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; } + getTerminalEnv(): { [key: string]: string } { + return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; + } + dispose(): void { this.handlers.clear(); this.server.close(); diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 46f612539fb..6fbc88cc9fd 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -25,7 +25,7 @@ import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; -import { createIPCServer, IIPCServer } from './ipc/ipcServer'; +import { createIPCServer, IPCServer } from './ipc/ipcServer'; import { GitEditor } from './gitEditor'; const deactivateTasks: { (): Promise }[] = []; @@ -62,22 +62,22 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu return !skip; }); - let ipc: IIPCServer | undefined = undefined; + let ipcServer: IPCServer | undefined = undefined; try { - ipc = await createIPCServer(context.storagePath); + ipcServer = await createIPCServer(context.storagePath); } catch (err) { outputChannelLogger.logError(`Failed to create git IPC: ${err}`); } - const askpass = new Askpass(ipc); + const askpass = new Askpass(ipcServer); disposables.push(askpass); - const gitEditor = new GitEditor(ipc); + const gitEditor = new GitEditor(ipcServer); disposables.push(gitEditor); - const environment = { ...askpass.getEnv(), ...gitEditor.getEnv() }; - const terminalEnvironmentManager = new TerminalEnvironmentManager(context, environment); + const environment = { ...askpass.getEnv(), ...gitEditor.getEnv(), ...ipcServer?.getEnv() }; + const terminalEnvironmentManager = new TerminalEnvironmentManager(context, [askpass, gitEditor, ipcServer]); disposables.push(terminalEnvironmentManager); outputChannelLogger.logInfo(localize('using git', "Using git {0} from {1}", info.version, info.path)); diff --git a/extensions/git/src/terminal.ts b/extensions/git/src/terminal.ts index 2eda93151d2..9501cc88bba 100644 --- a/extensions/git/src/terminal.ts +++ b/extensions/git/src/terminal.ts @@ -6,27 +6,15 @@ import { ExtensionContext, workspace } from 'vscode'; import { filterEvent, IDisposable } from './util'; +export interface ITerminalEnvironmentProvider { + getTerminalEnv(): { [key: string]: string }; +} + export class TerminalEnvironmentManager { private readonly disposable: IDisposable; - private _enabled = false; - private set enabled(enabled: boolean) { - if (this._enabled === enabled) { - return; - } - - this._enabled = enabled; - this.context.environmentVariableCollection.clear(); - - if (enabled) { - for (const name of Object.keys(this.env)) { - this.context.environmentVariableCollection.replace(name, this.env[name]); - } - } - } - - constructor(private readonly context: ExtensionContext, private readonly env: { [key: string]: string }) { + constructor(private readonly context: ExtensionContext, private readonly envProviders: (ITerminalEnvironmentProvider | undefined)[]) { this.disposable = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')) (this.refresh, this); @@ -35,7 +23,19 @@ export class TerminalEnvironmentManager { private refresh(): void { const config = workspace.getConfiguration('git', null); - this.enabled = config.get('enabled', true) && config.get('terminalAuthentication', true); + this.context.environmentVariableCollection.clear(); + + if (!config.get('enabled', true)) { + return; + } + + for (const envProvider of this.envProviders) { + const terminalEnv = envProvider?.getTerminalEnv() ?? {}; + + for (const name of Object.keys(terminalEnv)) { + this.context.environmentVariableCollection.replace(name, terminalEnv[name]); + } + } } dispose(): void {