Change:
Verification steps: Open a folder/workspace that contains a git repository Modify an existing file, and save it Add a new file, add some contents to it, and save it Invoke the "Git: Stash (Include Untracked)" command, and create a new stash Invoke the "Git: View Stash", and pick the stash that you have created Confirm that all files from the stash have the correct content

Discussion:
} const stashChanges = await repository.showStash(stash.index); <del> const stashParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`; </del> if (!stashChanges || stashChanges.length === 0) { return; } <ins> // A stash commit can have up to 3 parents: // 1. The first parent is the commit that was HEAD when the stash was created. // 2. The second parent is the commit that represents the index when the stash was created. // 3. The third parent (when present) represents the untracked files when the stash was created. const stashFirstParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`; const stashUntrackedFilesParentCommit = stash.parents.length === 3 ? stash.parents[2] : undefined; const stashUntrackedFiles: string[] = []; if (stashUntrackedFilesParentCommit) { const untrackedFiles = await repository.getObjectFiles(stashUntrackedFilesParentCommit); stashUntrackedFiles.push(...untrackedFiles.map(f => path.join(repository.root, f.file))); } </ins> const title = `Git Stash #${stash.index}: ${stash.description}`; const multiDiffSourceUri = toGitUri(Uri.file(repository.root), `stash@{${stash.index}}`, { scheme: 'git-stash' }); const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = []; for (const change of stashChanges) { <del> if (change.status === Status.INDEX_ADDED) { resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, stash.hash) }); } else if (change.status === Status.DELETED) { resources.push({ originalUri: toGitUri(change.uri, stashParentCommit), modifiedUri: undefined }); } else if (change.status === Status.INDEX_RENAMED) { resources.push({ originalUri: toGitUri(change.originalUri, stashParentCommit), modifiedUri: toGitUri(change.uri, stash.hash) }); } else { resources.push({ originalUri: toGitUri(change.uri, stashParentCommit), modifiedUri: toGitUri(change.uri, stash.hash) }); </del> <ins> const isChangeUntracked = !!stashUntrackedFiles.find(f => pathEquals(f, change.uri.fsPath)); const modifiedUriRef = !isChangeUntracked ? stash.hash : stashUntrackedFilesParentCommit ?? stash.hash; switch (change.status) { case Status.INDEX_ADDED: resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, modifiedUriRef) }); break; case Status.DELETED: resources.push({ originalUri: toGitUri(change.uri, stashFirstParentCommit), modifiedUri: undefined }); break; case Status.INDEX_RENAMED: resources.push({ originalUri: toGitUri(change.originalUri, stashFirstParentCommit), modifiedUri: toGitUri(change.uri, modifiedUriRef) }); break; default: resources.push({ originalUri: toGitUri(change.uri, stashFirstParentCommit), modifiedUri: toGitUri(change.uri, modifiedUriRef) }); break; </ins> } }

return { files: parseInt(files), insertions: parseInt(insertions ?? '0'), deletions: parseInt(deletions ?? '0') }; } <del> interface LsTreeElement { </del> <ins> export interface LsTreeElement { </ins> mode: string; type: string; object: string;

return { mode, object, size: parseInt(size) }; } <del> async lstree(treeish: string, path: string): Promise<LsTreeElement[]> { const { stdout } = await this.exec(['ls-tree', '-l', treeish, '--', sanitizePath(path)]); </del> <ins> async lstree(treeish: string, path?: string): Promise<LsTreeElement[]> { const args = ['ls-tree', '-l', treeish]; if (path) { args.push('--', sanitizePath(path)); } const { stdout } = await this.exec(args); </ins> return parseLsTree(stdout); }

GetBranches = 'GetBranches', GetCommitTemplate = 'GetCommitTemplate', GetObjectDetails = 'GetObjectDetails', <ins> GetObjectFiles = 'GetObjectFiles', </ins> GetRefs = 'GetRefs', GetRemoteRefs = 'GetRemoteRefs', HashObject = 'HashObject',

export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchOperation | CheckIgnoreOperation | CherryPickOperation | CheckoutOperation | CheckoutTrackingOperation | CleanOperation | CommitOperation | ConfigOperation | DeleteBranchOperation | DeleteRefOperation | DeleteRemoteTagOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation | <del> GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetRefsOperation | GetRemoteRefsOperation | HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | MergeBaseOperation | MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | RemoveOperation | ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RefreshOperation | RevertFilesOperation | RevListOperation | RevParseOperation | SetBranchUpstreamOperation | ShowOperation | StageOperation | StatusOperation | StashOperation | SubmoduleUpdateOperation | SyncOperation | TagOperation; </del> <ins> GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetObjectFilesOperation | GetRefsOperation | GetRemoteRefsOperation | HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | MergeBaseOperation | MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | RemoveOperation | ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RefreshOperation | RevertFilesOperation | RevListOperation | RevParseOperation | SetBranchUpstreamOperation | ShowOperation | StageOperation | StatusOperation | StashOperation | SubmoduleUpdateOperation | SyncOperation | TagOperation; </ins> type BaseOperation = { kind: OperationKind; blocking: boolean; readOnly: boolean; remote: boolean; retry: boolean; showProgress: boolean }; export type AddOperation = BaseOperation & { kind: OperationKind.Add };

export type GetBranchesOperation = BaseOperation & { kind: OperationKind.GetBranches }; export type GetCommitTemplateOperation = BaseOperation & { kind: OperationKind.GetCommitTemplate }; export type GetObjectDetailsOperation = BaseOperation & { kind: OperationKind.GetObjectDetails }; <ins> export type GetObjectFilesOperation = BaseOperation & { kind: OperationKind.GetObjectFiles }; </ins> export type GetRefsOperation = BaseOperation & { kind: OperationKind.GetRefs }; export type GetRemoteRefsOperation = BaseOperation & { kind: OperationKind.GetRemoteRefs }; export type HashObjectOperation = BaseOperation & { kind: OperationKind.HashObject };

GetBranches: { kind: OperationKind.GetBranches, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetBranchesOperation, GetCommitTemplate: { kind: OperationKind.GetCommitTemplate, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetCommitTemplateOperation, GetObjectDetails: { kind: OperationKind.GetObjectDetails, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectDetailsOperation, <ins> GetObjectFiles: { kind: OperationKind.GetObjectFiles, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectFilesOperation, </ins> GetRefs: { kind: OperationKind.GetRefs, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetRefsOperation, GetRemoteRefs: { kind: OperationKind.GetRemoteRefs, blocking: false, readOnly: true, remote: true, retry: false, showProgress: false } as GetRemoteRefsOperation, HashObject: { kind: OperationKind.HashObject, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as HashObjectOperation,

import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, Remote, Status, CommitOptions, BranchQuery, FetchOptions, RefQuery, RefType } from './api/git'; import { AutoFetcher } from './autofetch'; import { debounce, memoize, throttle } from './decorators'; <del> import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions, PullOptions } from './git'; </del> <ins> import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions, PullOptions, LsTreeElement } from './git'; </ins> import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util';

}); } <ins> getObjectFiles(ref: string): Promise<LsTreeElement[]> { return this.run(Operation.GetObjectFiles, () => this.repository.lstree(ref)); } </ins> getObjectDetails(ref: string, filePath: string): Promise<{ mode: string; object: string; size: number }> { return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, filePath)); }