From 039d61184517af83f10f3ac582a26bcd9921d5f0 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 1 May 2023 17:07:22 -0400 Subject: [PATCH] Fixes #2660 use git location for terminal run cmds --- src/env/node/git/git.ts | 33 ++++++++++++++++++++++++++++++++- src/env/node/git/localGitProvider.ts | 17 ++++++++++++++++- src/env/node/git/shell.ts | 15 ++++++++++++++- src/git/gitProvider.ts | 10 +++++++++- src/git/gitProviderService.ts | 11 +++++++++++ src/git/models/repository.ts | 28 +++++++++++++--------------- src/terminal.ts | 13 +------------ 7 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/env/node/git/git.ts b/src/env/node/git/git.ts index b874a43..b32e7ee 100644 --- a/src/env/node/git/git.ts +++ b/src/env/node/git/git.ts @@ -17,15 +17,19 @@ import { GitLogParser } from '../../../git/parsers/logParser'; import { GitReflogParser } from '../../../git/parsers/reflogParser'; import { GitTagParser } from '../../../git/parsers/tagParser'; import { splitAt } from '../../../system/array'; +import { configuration } from '../../../system/configuration'; import { join } from '../../../system/iterable'; import { Logger } from '../../../system/logger'; import { LogLevel, slowCallWarningThreshold } from '../../../system/logger.constants'; import { dirname, isAbsolute, isFolderGlob, joinPaths, normalizePath, splitPath } from '../../../system/path'; import { getDurationMilliseconds } from '../../../system/string'; +import { getEditorCommand } from '../../../system/utils'; import { compare, fromString } from '../../../system/version'; +import { ensureGitTerminal } from '../../../terminal'; +import { isWindows } from '../platform'; import type { GitLocation } from './locator'; import type { RunOptions } from './shell'; -import { fsExists, run, RunError } from './shell'; +import { fsExists, getWindowsShortPath, run, RunError } from './shell'; const emptyArray = Object.freeze([]) as unknown as any[]; const emptyObj = Object.freeze({}); @@ -2045,6 +2049,33 @@ export class Git { } } + async runCommandViaTerminal(cwd: string, command: string, args: string[], options?: { execute?: boolean }) { + const location = await this.getLocation(); + const git = location.path ?? 'git'; + + const coreEditorConfig = configuration.get('terminal.overrideGitEditor') + ? `-c "core.editor=${getEditorCommand()}" ` + : ''; + + const parsedArgs = args.map(arg => (arg.startsWith('#') || /['();$|>&<]/.test(arg) ? `"${arg}"` : arg)); + + let text; + if (git.includes(' ') && isWindows) { + const shortenedPath = await getWindowsShortPath(git); + text = `${shortenedPath} -C "${cwd}" ${coreEditorConfig}${command} ${parsedArgs.join(' ')}`; + } else { + text = `${git.includes(' ') ? `"${git}"` : git} -C "${cwd}" ${coreEditorConfig}${command} ${parsedArgs.join( + ' ', + )}`; + } + + this.logGitCommand(`[TERM] ${text}`, 0); + + const terminal = ensureGitTerminal(); + terminal.show(false); + terminal.sendText(text, options?.execute ?? false); + } + private _gitOutput: OutputChannel | undefined; private logGitCommand(command: string, duration: number, ex?: Error): void { diff --git a/src/env/node/git/localGitProvider.ts b/src/env/node/git/localGitProvider.ts index 3cf2509..62cf023 100644 --- a/src/env/node/git/localGitProvider.ts +++ b/src/env/node/git/localGitProvider.ts @@ -369,7 +369,9 @@ export class LocalGitProvider implements GitProvider, Disposable { } else { Logger.log( scope, - `Git (${location.version}) found in ${location.path === 'git' ? 'PATH' : location.path} [${getDurationMilliseconds(start)}ms]`, + `Git (${location.version}) found in ${ + location.path === 'git' ? 'PATH' : location.path + } [${getDurationMilliseconds(start)}ms]`, ); } @@ -4660,6 +4662,19 @@ export class LocalGitProvider implements GitProvider, Disposable { } } + @log({ args: { 2: false } }) + async runGitCommandViaTerminal( + repoPath: string, + command: string, + args: string[], + options?: { execute?: boolean }, + ): Promise { + await this.git.runCommandViaTerminal(repoPath, command, args, options); + + // Right now we are reliant on the Repository class to fire the change event (as a stop gap if we don't detect a change through the normal mechanisms) + // setTimeout(() => this.fireChange(RepositoryChange.Unknown), 2500); + } + @log() validateBranchOrTagName(repoPath: string, ref: string): Promise { return this.git.check_ref_format(ref, repoPath); diff --git a/src/env/node/git/shell.ts b/src/env/node/git/shell.ts index 55c6ad1..783572c 100644 --- a/src/env/node/git/shell.ts +++ b/src/env/node/git/shell.ts @@ -1,5 +1,5 @@ import type { ExecException } from 'child_process'; -import { execFile } from 'child_process'; +import { exec, execFile } from 'child_process'; import type { Stats } from 'fs'; import { exists, existsSync, statSync } from 'fs'; import { join as joinPaths } from 'path'; @@ -116,6 +116,19 @@ export function findExecutable(exe: string, args: string[]): { cmd: string; args return { cmd: exe, args: args }; } +export async function getWindowsShortPath(path: string): Promise { + return new Promise((resolve, reject) => { + exec(`for %I in ("${path}") do @echo %~sI`, (error, stdout, _stderr) => { + if (error != null) { + reject(error); + return; + } + + resolve(stdout.trim().replace(/\\/g, '/')); + }); + }); +} + export interface RunOptions { cancellation?: CancellationToken; cwd?: string; diff --git a/src/git/gitProvider.ts b/src/git/gitProvider.ts index dc8f813..e49617a 100644 --- a/src/git/gitProvider.ts +++ b/src/git/gitProvider.ts @@ -437,7 +437,7 @@ export interface GitProvider extends Disposable { }, ): Promise; searchCommits( - repoPath: string | Uri, + repoPath: string, search: SearchQuery, options?: { cancellation?: CancellationToken; @@ -445,6 +445,14 @@ export interface GitProvider extends Disposable { ordering?: 'date' | 'author-date' | 'topo'; }, ): Promise; + + runGitCommandViaTerminal?( + repoPath: string, + command: string, + args: string[], + options?: { execute?: boolean }, + ): Promise; + validateBranchOrTagName(repoPath: string, ref: string): Promise; validateReference(repoPath: string, ref: string): Promise; diff --git a/src/git/gitProviderService.ts b/src/git/gitProviderService.ts index a748163..a3531bf 100644 --- a/src/git/gitProviderService.ts +++ b/src/git/gitProviderService.ts @@ -2649,6 +2649,17 @@ export class GitProviderService implements Disposable { return provider.searchCommits(path, search, options); } + @log({ args: false }) + async runGitCommandViaTerminal( + repoPath: string | Uri, + command: string, + args: string[], + options?: { execute?: boolean }, + ): Promise { + const { provider, path } = this.getProvider(repoPath); + return provider.runGitCommandViaTerminal?.(path, command, args, options); + } + @log() validateBranchOrTagName(repoPath: string | Uri, ref: string): Promise { const { provider, path } = this.getProvider(repoPath); diff --git a/src/git/models/repository.ts b/src/git/models/repository.ts index 3a55e37..a82b99b 100644 --- a/src/git/models/repository.ts +++ b/src/git/models/repository.ts @@ -21,7 +21,6 @@ import { getLoggableName, Logger } from '../../system/logger'; import { getLogScope } from '../../system/logger.scope'; import { updateRecordValue } from '../../system/object'; import { basename, normalizePath } from '../../system/path'; -import { runGitCommandInTerminal } from '../../terminal'; import type { GitDir, GitProviderDescriptor, GitRepositoryCaches } from '../gitProvider'; import type { RemoteProviders } from '../remotes/remoteProviders'; import { loadRemoteProviders } from '../remotes/remoteProviders'; @@ -490,7 +489,7 @@ export class Repository implements Disposable { @log() branch(...args: string[]) { - this.runTerminalCommand('branch', ...args); + void this.runTerminalCommand('branch', ...args); } @log() @@ -505,7 +504,7 @@ export class Repository implements Disposable { if (options?.force) { args.push('--force'); } - this.runTerminalCommand('branch', ...args, ...branches.map(b => b.ref)); + void this.runTerminalCommand('branch', ...args, ...branches.map(b => b.ref)); if (options?.remote) { const trackingBranches = localBranches.filter(b => b.upstream != null); @@ -515,7 +514,7 @@ export class Repository implements Disposable { ); for (const [remote, branches] of branchesByOrigin.entries()) { - this.runTerminalCommand( + void this.runTerminalCommand( 'push', '-d', remote, @@ -531,14 +530,14 @@ export class Repository implements Disposable { const branchesByOrigin = groupByMap(remoteBranches, b => getRemoteNameFromBranchName(b.name)); for (const [remote, branches] of branchesByOrigin.entries()) { - this.runTerminalCommand('push', '-d', remote, ...branches.map(b => getNameWithoutRemote(b))); + void this.runTerminalCommand('push', '-d', remote, ...branches.map(b => getNameWithoutRemote(b))); } } } @log() cherryPick(...args: string[]) { - this.runTerminalCommand('cherry-pick', ...args); + void this.runTerminalCommand('cherry-pick', ...args); } containsUri(uri: Uri) { @@ -782,7 +781,7 @@ export class Repository implements Disposable { @log() merge(...args: string[]) { - this.runTerminalCommand('merge', ...args); + void this.runTerminalCommand('merge', ...args); } @gate() @@ -923,7 +922,7 @@ export class Repository implements Disposable { @log() rebase(configs: string[] | undefined, ...args: string[]) { - this.runTerminalCommand( + void this.runTerminalCommand( configs != null && configs.length !== 0 ? `${configs.join(' ')} rebase` : 'rebase', ...args, ); @@ -931,7 +930,7 @@ export class Repository implements Disposable { @log() reset(...args: string[]) { - this.runTerminalCommand('reset', ...args); + void this.runTerminalCommand('reset', ...args); } @log({ singleLine: true }) @@ -965,7 +964,7 @@ export class Repository implements Disposable { @log() revert(...args: string[]) { - this.runTerminalCommand('revert', ...args); + void this.runTerminalCommand('revert', ...args); } @debug() @@ -1128,7 +1127,7 @@ export class Repository implements Disposable { @log() tag(...args: string[]) { - this.runTerminalCommand('tag', ...args); + void this.runTerminalCommand('tag', ...args); } @log() @@ -1138,7 +1137,7 @@ export class Repository implements Disposable { } const args = ['--delete']; - this.runTerminalCommand('tag', ...args, ...tags.map(t => t.ref)); + void this.runTerminalCommand('tag', ...args, ...tags.map(t => t.ref)); } @debug() @@ -1217,9 +1216,8 @@ export class Repository implements Disposable { this._onDidChangeFileSystem.fire(e); } - private runTerminalCommand(command: string, ...args: string[]) { - const parsedArgs = args.map(arg => (arg.startsWith('#') || /['();$|>&<]/.test(arg) ? `"${arg}"` : arg)); - runGitCommandInTerminal(command, parsedArgs.join(' '), this.path, true); + private async runTerminalCommand(command: string, ...args: string[]) { + await this.container.git.runGitCommandViaTerminal?.(this.uri, command, args, { execute: true }); setTimeout(() => this.fireChange(RepositoryChange.Unknown), 2500); } diff --git a/src/terminal.ts b/src/terminal.ts index 498c3df..8e6d725 100644 --- a/src/terminal.ts +++ b/src/terminal.ts @@ -1,15 +1,13 @@ import type { Disposable, Terminal } from 'vscode'; import { window } from 'vscode'; import { Container } from './container'; -import { configuration } from './system/configuration'; -import { getEditorCommand } from './system/utils'; let _terminal: Terminal | undefined; let _disposable: Disposable | undefined; const extensionTerminalName = 'GitLens'; -function ensureTerminal(): Terminal { +export function ensureGitTerminal(): Terminal { if (_terminal == null) { _terminal = window.createTerminal(extensionTerminalName); _disposable = window.onDidCloseTerminal((e: Terminal) => { @@ -25,12 +23,3 @@ function ensureTerminal(): Terminal { return _terminal; } - -export function runGitCommandInTerminal(command: string, args: string, cwd: string, execute: boolean = false) { - const terminal = ensureTerminal(); - terminal.show(false); - const coreEditorConfig = configuration.get('terminal.overrideGitEditor') - ? `-c "core.editor=${getEditorCommand()}" ` - : ''; - terminal.sendText(`git -C "${cwd}" ${coreEditorConfig}${command} ${args}`, execute); -}