diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e985ba..3f1ebd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Fixed +- Fixes [#1183](https://github.com/eamodio/vscode-gitlens/issues/1183) - stash all changes has no effect when the number of files is large - Fixes [#1308](https://github.com/eamodio/vscode-gitlens/issues/1308) - Escape quotes for PRs titles - Fixes [#1309](https://github.com/eamodio/vscode-gitlens/issues/1309) - "Fetch" not working on remote branches - Fixes an issue where many views wouldn't refresh properly when going from no items to some items diff --git a/src/commands/git/stash.ts b/src/commands/git/stash.ts index 9cf221f..23f76d8 100644 --- a/src/commands/git/stash.ts +++ b/src/commands/git/stash.ts @@ -496,7 +496,7 @@ export class StashGitCommand extends QuickCommand { } catch (ex) { Logger.error(ex, context.title); - const msg: string = ex?.toString() ?? ''; + const msg: string = ex?.message ?? ex?.toString() ?? ''; if (msg.includes('newer version of Git')) { void window.showErrorMessage(`Unable to stash changes. ${msg}`); diff --git a/src/git/git.ts b/src/git/git.ts index 7bea0e1..6249598 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -25,6 +25,7 @@ export type GitDiffFilter = 'A' | 'C' | 'D' | 'M' | 'R' | 'T' | 'U' | 'X' | 'B' const emptyArray = (Object.freeze([]) as any) as any[]; const emptyObj = Object.freeze({}); const emptyStr = ''; +export const maxGitCliLength = 30000; const slash = '/'; const textDecoder = new TextDecoder('utf8'); @@ -1308,18 +1309,19 @@ export namespace Git { ); } - export function stash__push( + export async function stash__push( repoPath: string, message?: string, { includeUntracked, keepIndex, pathspecs, - }: { includeUntracked?: boolean; keepIndex?: boolean; pathspecs?: string[] } = {}, - ) { + stdin, + }: { includeUntracked?: boolean; keepIndex?: boolean; pathspecs?: string[]; stdin?: boolean } = {}, + ): Promise { const params = ['stash', 'push']; - if (includeUntracked || (pathspecs !== undefined && pathspecs.length !== 0)) { + if (includeUntracked || (pathspecs != null && pathspecs.length !== 0)) { params.push('-u'); } @@ -1331,12 +1333,23 @@ export namespace Git { params.push('-m', message); } + if (stdin && pathspecs != null && pathspecs.length !== 0) { + void (await git( + { cwd: repoPath, stdin: pathspecs.join('\0') }, + ...params, + '--pathspec-from-file=-', + '--pathspec-file-nul', + )); + + return; + } + params.push('--'); - if (pathspecs !== undefined && pathspecs.length !== 0) { + if (pathspecs != null && pathspecs.length !== 0) { params.push(...pathspecs); } - return git({ cwd: repoPath }, ...params); + void (await git({ cwd: repoPath }, ...params)); } export function status( diff --git a/src/git/gitService.ts b/src/git/gitService.ts index 7f87bde..764c67d 100644 --- a/src/git/gitService.ts +++ b/src/git/gitService.ts @@ -66,6 +66,7 @@ import { GitTagParser, GitTree, GitTreeParser, + maxGitCliLength, PullRequest, PullRequestDateFormatting, PullRequestState, @@ -3897,21 +3898,39 @@ export class GitService implements Disposable { ) { if (uris == null) return Git.stash__push(repoPath, message, options); - GitService.ensureGitVersion('2.13.2', 'Stashing individual files'); + GitService.ensureGitVersion( + '2.13.2', + 'Stashing individual files', + ' Please retry by stashing everything or install a more recent version of Git.', + ); const pathspecs = uris.map(u => `./${Git.splitPath(u.fsPath, repoPath)[0]}`); - return Git.stash__push(repoPath, message, { ...options, pathspecs: pathspecs }); + + const stdinVersion = '2.30.0'; + const stdin = GitService.compareGitVersion(stdinVersion) !== -1; + // If we don't support stdin, then error out if we are over the maximum allowed git cli length + if (!stdin && Arrays.countStringLength(pathspecs) > maxGitCliLength) { + GitService.ensureGitVersion( + stdinVersion, + `Stashing so many files (${pathspecs.length}) at once`, + ' Please retry by stashing fewer files or install a more recent version of Git.', + ); + } + + return Git.stash__push(repoPath, message, { + ...options, + pathspecs: pathspecs, + stdin: stdin, + }); } static compareGitVersion(version: string) { return Versions.compare(Versions.fromString(Git.getGitVersion()), Versions.fromString(version)); } - - static ensureGitVersion(version: string, feature: string): void { - const gitVersion = Git.getGitVersion(); - if (Versions.compare(Versions.fromString(gitVersion), Versions.fromString(version)) === -1) { + static ensureGitVersion(version: string, prefix: string, suffix: string): void { + if (GitService.compareGitVersion(version) === -1) { throw new Error( - `${feature} requires a newer version of Git (>= ${version}) than is currently installed (${gitVersion}). Please install a more recent version of Git to use this GitLens feature.`, + `${prefix} requires a newer version of Git (>= ${version}) than is currently installed (${Git.getGitVersion()}).${suffix}`, ); } } diff --git a/src/system/array.ts b/src/system/array.ts index e7b79df..a0a87b2 100644 --- a/src/system/array.ts +++ b/src/system/array.ts @@ -17,6 +17,14 @@ export function chunk(source: T[], size: number): T[][] { return chunks; } +export function countStringLength(source: string[]): number { + let length = 0; + for (const s of source) { + length += s.length; + } + return length; +} + export function countUniques(source: T[], accessor: (item: T) => string): Record { const uniqueCounts = Object.create(null) as Record; for (const item of source) {