mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 17:32:41 +00:00
Git - handle stashes that contain untracked files (#203572)
This commit is contained in:
parent
c0de87c441
commit
fe0632cbb2
|
@ -3709,25 +3709,44 @@ export class CommandCenter {
|
|||
}
|
||||
|
||||
const stashChanges = await repository.showStash(stash.index);
|
||||
const stashParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`;
|
||||
|
||||
if (!stashChanges || stashChanges.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)));
|
||||
}
|
||||
|
||||
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) {
|
||||
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) });
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -937,7 +937,7 @@ function parseGitDiffShortStat(data: string): CommitShortStat {
|
|||
return { files: parseInt(files), insertions: parseInt(insertions ?? '0'), deletions: parseInt(deletions ?? '0') };
|
||||
}
|
||||
|
||||
interface LsTreeElement {
|
||||
export interface LsTreeElement {
|
||||
mode: string;
|
||||
type: string;
|
||||
object: string;
|
||||
|
@ -1294,8 +1294,13 @@ export class Repository {
|
|||
return { mode, object, size: parseInt(size) };
|
||||
}
|
||||
|
||||
async lstree(treeish: string, path: string): Promise<LsTreeElement[]> {
|
||||
const { stdout } = await this.exec(['ls-tree', '-l', treeish, '--', sanitizePath(path)]);
|
||||
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);
|
||||
return parseLsTree(stdout);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ export const enum OperationKind {
|
|||
GetBranches = 'GetBranches',
|
||||
GetCommitTemplate = 'GetCommitTemplate',
|
||||
GetObjectDetails = 'GetObjectDetails',
|
||||
GetObjectFiles = 'GetObjectFiles',
|
||||
GetRefs = 'GetRefs',
|
||||
GetRemoteRefs = 'GetRemoteRefs',
|
||||
HashObject = 'HashObject',
|
||||
|
@ -65,12 +66,12 @@ export const enum OperationKind {
|
|||
export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchOperation | CheckIgnoreOperation | CherryPickOperation |
|
||||
CheckoutOperation | CheckoutTrackingOperation | CleanOperation | CommitOperation | ConfigOperation | DeleteBranchOperation |
|
||||
DeleteRefOperation | DeleteRemoteTagOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation |
|
||||
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;
|
||||
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;
|
||||
|
||||
type BaseOperation = { kind: OperationKind; blocking: boolean; readOnly: boolean; remote: boolean; retry: boolean; showProgress: boolean };
|
||||
export type AddOperation = BaseOperation & { kind: OperationKind.Add };
|
||||
|
@ -95,6 +96,7 @@ export type GetBranchOperation = BaseOperation & { kind: OperationKind.GetBranch
|
|||
export type GetBranchesOperation = BaseOperation & { kind: OperationKind.GetBranches };
|
||||
export type GetCommitTemplateOperation = BaseOperation & { kind: OperationKind.GetCommitTemplate };
|
||||
export type GetObjectDetailsOperation = BaseOperation & { kind: OperationKind.GetObjectDetails };
|
||||
export type GetObjectFilesOperation = BaseOperation & { kind: OperationKind.GetObjectFiles };
|
||||
export type GetRefsOperation = BaseOperation & { kind: OperationKind.GetRefs };
|
||||
export type GetRemoteRefsOperation = BaseOperation & { kind: OperationKind.GetRemoteRefs };
|
||||
export type HashObjectOperation = BaseOperation & { kind: OperationKind.HashObject };
|
||||
|
@ -151,6 +153,7 @@ export const Operation = {
|
|||
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,
|
||||
GetObjectFiles: { kind: OperationKind.GetObjectFiles, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectFilesOperation,
|
||||
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,
|
||||
|
|
|
@ -11,7 +11,7 @@ import TelemetryReporter from '@vscode/extension-telemetry';
|
|||
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';
|
||||
import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions, PullOptions } from './git';
|
||||
import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions, PullOptions, LsTreeElement } from './git';
|
||||
import { StatusBarCommands } from './statusbar';
|
||||
import { toGitUri } from './uri';
|
||||
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util';
|
||||
|
@ -1955,6 +1955,10 @@ export class Repository implements Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
getObjectFiles(ref: string): Promise<LsTreeElement[]> {
|
||||
return this.run(Operation.GetObjectFiles, () => this.repository.lstree(ref));
|
||||
}
|
||||
|
||||
getObjectDetails(ref: string, filePath: string): Promise<{ mode: string; object: string; size: number }> {
|
||||
return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, filePath));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue