mirror of
https://github.com/Microsoft/vscode
synced 2024-10-12 06:17:18 +00:00
Git - add open stash command (#201970)
* Initial implementation * Add button to quick pick * Improve stash picker * Remove quick pick buttons
This commit is contained in:
parent
0a90cb8a06
commit
cfebdd863a
|
@ -674,6 +674,12 @@
|
|||
"category": "Git",
|
||||
"enablement": "!operationInProgress"
|
||||
},
|
||||
{
|
||||
"command": "git.stashOpen",
|
||||
"title": "%command.stashOpen%",
|
||||
"category": "Git",
|
||||
"enablement": "!operationInProgress"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"title": "%command.timelineOpenDiff%",
|
||||
|
@ -1257,6 +1263,10 @@
|
|||
"command": "git.openRepositoriesInParentFolders",
|
||||
"when": "config.git.enabled && !git.missing && git.parentRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.openStash",
|
||||
"when": "config.git.enabled && !git.missing && config.multiDiffEditor.experimental.enabled"
|
||||
},
|
||||
{
|
||||
"command": "git.viewChanges",
|
||||
"when": "false"
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
"command.stashApplyLatest": "Apply Latest Stash",
|
||||
"command.stashDrop": "Drop Stash...",
|
||||
"command.stashDropAll": "Drop All Stashes...",
|
||||
"command.stashOpen": "Open Stash...",
|
||||
"command.timelineOpenDiff": "Open Changes",
|
||||
"command.timelineOpenCommit": "Open Commit",
|
||||
"command.timelineCopyCommitId": "Copy Commit ID",
|
||||
|
|
|
@ -3535,6 +3535,31 @@ export class CommandCenter {
|
|||
await repository.dropStash();
|
||||
}
|
||||
|
||||
@command('git.stashOpen', { repository: true })
|
||||
async stashOpen(repository: Repository): Promise<void> {
|
||||
const placeHolder = l10n.t('Pick a stash to open');
|
||||
const stash = await this.pickStash(repository, placeHolder);
|
||||
|
||||
if (!stash) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stashFiles = await repository.showStash(stash.index);
|
||||
|
||||
if (!stashFiles || stashFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const args: [Uri, Uri | undefined, Uri | undefined][] = [];
|
||||
|
||||
for (const file of stashFiles) {
|
||||
const fileUri = Uri.file(path.join(repository.root, file));
|
||||
args.push([fileUri, toGitUri(fileUri, `stash@{${stash.index}}`), fileUri]);
|
||||
}
|
||||
|
||||
commands.executeCommand('vscode.changes', `Git Stash #${stash.index}: ${stash.description}`, args);
|
||||
}
|
||||
|
||||
private async pickStash(repository: Repository, placeHolder: string): Promise<Stash | undefined> {
|
||||
const stashes = await repository.getStashes();
|
||||
|
||||
|
@ -3543,9 +3568,10 @@ export class CommandCenter {
|
|||
return;
|
||||
}
|
||||
|
||||
const picks = stashes.map(stash => ({ label: `#${stash.index}: ${stash.description}`, description: '', details: '', stash }));
|
||||
const picks = stashes.map(stash => ({ label: `#${stash.index}: ${stash.description}`, description: stash.branchName, stash }));
|
||||
const result = await window.showQuickPick(picks, { placeHolder });
|
||||
return result && result.stash;
|
||||
|
||||
return result?.stash;
|
||||
}
|
||||
|
||||
@command('git.timeline.openDiff', { repository: false })
|
||||
|
|
|
@ -37,6 +37,7 @@ export interface IFileStatus {
|
|||
export interface Stash {
|
||||
index: number;
|
||||
description: string;
|
||||
branchName?: string;
|
||||
}
|
||||
|
||||
interface MutableRemote extends Remote {
|
||||
|
@ -964,6 +965,38 @@ export function parseLsFiles(raw: string): LsFilesElement[] {
|
|||
.map(([, mode, object, stage, file]) => ({ mode, object, stage, file }));
|
||||
}
|
||||
|
||||
function parseGitStashes(raw: string): Stash[] {
|
||||
const result: Stash[] = [];
|
||||
const regex = /^stash@{(\d+)}:(.+)$/;
|
||||
const descriptionRegex = /(WIP\s)*on([^:]+):(.*)$/i;
|
||||
|
||||
for (const stash of raw.split('\n').filter(s => !!s)) {
|
||||
// Extract index and description
|
||||
const match = regex.exec(stash);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [, index, description] = match;
|
||||
|
||||
// Extract branch name from description
|
||||
const descriptionMatch = descriptionRegex.exec(description);
|
||||
if (!descriptionMatch) {
|
||||
result.push({ index: parseInt(index), description: description.trim() });
|
||||
continue;
|
||||
}
|
||||
|
||||
const [, wip, branchName, message] = descriptionMatch;
|
||||
result.push({
|
||||
index: parseInt(index),
|
||||
description: wip ? `WIP (${message.trim()})` : message.trim(),
|
||||
branchName: branchName.trim()
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface PullOptions {
|
||||
unshallow?: boolean;
|
||||
tags?: boolean;
|
||||
|
@ -2114,6 +2147,24 @@ export class Repository {
|
|||
}
|
||||
}
|
||||
|
||||
async showStash(index: number): Promise<string[] | undefined> {
|
||||
const args = ['stash', 'show', `stash@{${index}}`, '--name-only'];
|
||||
|
||||
try {
|
||||
const result = await this.exec(args);
|
||||
|
||||
return result.stdout.trim()
|
||||
.split('\n')
|
||||
.filter(line => !!line);
|
||||
} catch (err) {
|
||||
if (/No stash found/.test(err.stderr || '')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async getStatus(opts?: { limit?: number; ignoreSubmodules?: boolean; similarityThreshold?: number; untrackedChanges?: 'mixed' | 'separate' | 'hidden'; cancellationToken?: CancellationToken }): Promise<{ status: IFileStatus[]; statusLength: number; didHitLimit: boolean }> {
|
||||
if (opts?.cancellationToken && opts?.cancellationToken.isCancellationRequested) {
|
||||
throw new CancellationError();
|
||||
|
@ -2385,14 +2436,7 @@ export class Repository {
|
|||
|
||||
async getStashes(): Promise<Stash[]> {
|
||||
const result = await this.exec(['stash', 'list']);
|
||||
const regex = /^stash@{(\d+)}:(.+)$/;
|
||||
const rawStashes = result.stdout.trim().split('\n')
|
||||
.filter(b => !!b)
|
||||
.map(line => regex.exec(line) as RegExpExecArray)
|
||||
.filter(g => !!g)
|
||||
.map(([, index, description]: RegExpExecArray) => ({ index: parseInt(index), description }));
|
||||
|
||||
return rawStashes;
|
||||
return parseGitStashes(result.stdout.trim());
|
||||
}
|
||||
|
||||
async getRemotes(): Promise<Remote[]> {
|
||||
|
|
|
@ -1931,7 +1931,7 @@ export class Repository implements Disposable {
|
|||
}
|
||||
|
||||
async getStashes(): Promise<Stash[]> {
|
||||
return await this.repository.getStashes();
|
||||
return this.run(Operation.Stash, () => this.repository.getStashes());
|
||||
}
|
||||
|
||||
async createStash(message?: string, includeUntracked?: boolean, staged?: boolean): Promise<void> {
|
||||
|
@ -1958,6 +1958,10 @@ export class Repository implements Disposable {
|
|||
return await this.run(Operation.Stash, () => this.repository.applyStash(index));
|
||||
}
|
||||
|
||||
async showStash(index: number): Promise<string[] | undefined> {
|
||||
return await this.run(Operation.Stash, () => this.repository.showStash(index));
|
||||
}
|
||||
|
||||
async getCommitTemplate(): Promise<string> {
|
||||
return await this.run(Operation.GetCommitTemplate, async () => this.repository.getCommitTemplate());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue