提交 31c21ce7 编写于 作者: J Joao Moreno

Merge branch 'stashing' of https://github.com/Krzysztof-Cieslak/vscode into...

Merge branch 'stashing' of https://github.com/Krzysztof-Cieslak/vscode into Krzysztof-Cieslak-stashing
...@@ -251,6 +251,21 @@ ...@@ -251,6 +251,21 @@
"command": "git.ignore", "command": "git.ignore",
"title": "%command.ignore%", "title": "%command.ignore%",
"category": "Git" "category": "Git"
},
{
"command": "git.stash",
"title": "%command.stash%",
"category": "Git"
},
{
"command": "git.stashPop",
"title": "%command.stashPop%",
"category": "Git"
},
{
"command": "git.stashPopLatest",
"title": "%command.stashPopLatest%",
"category": "Git"
} }
], ],
"menus": { "menus": {
...@@ -402,6 +417,18 @@ ...@@ -402,6 +417,18 @@
{ {
"command": "git.showOutput", "command": "git.showOutput",
"when": "config.git.enabled && scmProvider == git && gitState == idle" "when": "config.git.enabled && scmProvider == git && gitState == idle"
},
{
"command": "git.stash",
"when": "config.git.enabled && scmProvider == git && gitState == idle"
},
{
"command": "git.stashPop",
"when": "config.git.enabled && scmProvider == git && gitState == idle"
},
{
"command": "git.stashPopLatest",
"when": "config.git.enabled && scmProvider == git && gitState == idle"
} }
], ],
"scm/title": [ "scm/title": [
...@@ -501,7 +528,22 @@ ...@@ -501,7 +528,22 @@
}, },
{ {
"command": "git.showOutput", "command": "git.showOutput",
"group": "5_output", "group": "6_output",
"when": "config.git.enabled && scmProvider == git && gitState == idle"
},
{
"command": "git.stash",
"group": "5_stash",
"when": "config.git.enabled && scmProvider == git && gitState == idle"
},
{
"command": "git.stashPop",
"group": "5_stash",
"when": "config.git.enabled && scmProvider == git && gitState == idle"
},
{
"command": "git.stashPopLatest",
"group": "5_stash",
"when": "config.git.enabled && scmProvider == git && gitState == idle" "when": "config.git.enabled && scmProvider == git && gitState == idle"
} }
], ],
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
"command.publish": "Publish Branch", "command.publish": "Publish Branch",
"command.showOutput": "Show Git Output", "command.showOutput": "Show Git Output",
"command.ignore": "Add File to .gitignore", "command.ignore": "Add File to .gitignore",
"command.stash": "Stash",
"command.stashPop": "Stash Pop",
"command.stashPopLatest": "Stash Pop Latest",
"config.enabled": "Whether git is enabled", "config.enabled": "Whether git is enabled",
"config.path": "Path to the git executable", "config.path": "Path to the git executable",
"config.autorefresh": "Whether auto refreshing is enabled", "config.autorefresh": "Whether auto refreshing is enabled",
......
...@@ -1092,6 +1092,47 @@ export class CommandCenter { ...@@ -1092,6 +1092,47 @@ export class CommandCenter {
await this.model.ignore(uris); await this.model.ignore(uris);
} }
@command('git.stash')
async stash() : Promise<void> {
const noUnstagedChanges = this.model.workingTreeGroup.resources.length === 0;
if (noUnstagedChanges){
window.showInformationMessage(localize('no changes stash', "There are no changes to stash."));
return;
}
return await this.model.stash();
}
@command('git.stashPop')
async stashPop(): Promise<void> {
let stashes = await this.model.getStashes();
const noStashes = stashes.length === 0;
if (noStashes){
window.showInformationMessage(localize('no stashes', "There are no stashes to restore."));
return;
}
const picks = stashes.map(r => { return { label: `#${r.id}: ${r.description}`, description: "", derails: "", id: r.id }; });
const placeHolder = localize('pick stash', "Pick a stash");
const choice = await window.showQuickPick(picks, { placeHolder });
if (!choice) {
return;
}
return await this.model.stash(true, choice.id);
}
@command('git.stashPopLatest')
async stashPopLatest(): Promise<void> {
let stashes = await this.model.getStashes();
const noStashes = stashes.length === 0;
if (noStashes){
window.showInformationMessage(localize('no stashes', "There are no stashes to restore."));
return;
}
return await this.model.stash(true);
}
private createCommand(id: string, key: string, method: Function, skipModelCheck: boolean): (...args: any[]) => any { private createCommand(id: string, key: string, method: Function, skipModelCheck: boolean): (...args: any[]) => any {
const result = (...args) => { const result = (...args) => {
if (!skipModelCheck && !this.model) { if (!skipModelCheck && !this.model) {
......
...@@ -33,6 +33,11 @@ export interface Remote { ...@@ -33,6 +33,11 @@ export interface Remote {
url: string; url: string;
} }
export interface Stash {
id : string;
description: string;
}
export enum RefType { export enum RefType {
Head, Head,
RemoteHead, RemoteHead,
...@@ -277,7 +282,10 @@ export const GitErrorCodes = { ...@@ -277,7 +282,10 @@ export const GitErrorCodes = {
RepositoryNotFound: 'RepositoryNotFound', RepositoryNotFound: 'RepositoryNotFound',
RepositoryIsLocked: 'RepositoryIsLocked', RepositoryIsLocked: 'RepositoryIsLocked',
BranchNotFullyMerged: 'BranchNotFullyMerged', BranchNotFullyMerged: 'BranchNotFullyMerged',
NoRemoteReference: 'NoRemoteReference' NoRemoteReference: 'NoRemoteReference',
NoLocalChanges: 'NoLocalChanges',
NoStashFound: 'NoStashFound',
LocalChangesOverwritten: 'LocalChangesOverwritten'
}; };
function getGitErrorCode(stderr: string): string | undefined { function getGitErrorCode(stderr: string): string | undefined {
...@@ -834,6 +842,32 @@ export class Repository { ...@@ -834,6 +842,32 @@ export class Repository {
} }
} }
async stash(pop: boolean = false, index?: string): Promise<void> {
try {
const args = ['stash'];
if (pop) {
args.push('pop');
if (index) {
args.push(`stash@{${index}}`);
}
}
await this.run(args);
} catch (err) {
if (/No local changes to save/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.NoLocalChanges;
}
else if (/No stash found/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.NoStashFound;
}
else if (/error: Your local changes to the following files would be overwritten/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.LocalChangesOverwritten;
}
throw err;
}
}
getStatus(limit = 5000): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> { getStatus(limit = 5000): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> {
return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => { return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => {
const parser = new GitStatusParser(); const parser = new GitStatusParser();
...@@ -921,6 +955,18 @@ export class Repository { ...@@ -921,6 +955,18 @@ export class Repository {
.filter(ref => !!ref) as Ref[]; .filter(ref => !!ref) as Ref[];
} }
async getStashes(): Promise<Stash[]> {
const result = await this.run(['stash', 'list']);
const regex = /^stash@{(\d+)}:(.+)/;
const rawStashes = result.stdout.trim().split('\n')
.filter(b => !!b)
.map(line => regex.exec(line))
.filter(g => !!g)
.map((groups: RegExpExecArray) => ({ id: groups[1], description: groups[2] }));
return uniqBy(rawStashes, remote => remote.id);
}
async getRemotes(): Promise<Remote[]> { async getRemotes(): Promise<Remote[]> {
const result = await this.run(['remote', '--verbose']); const result = await this.run(['remote', '--verbose']);
const regex = /^([^\s]+)\s+([^\s]+)\s/; const regex = /^([^\s]+)\s+([^\s]+)\s/;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
'use strict'; 'use strict';
import { Uri, Command, EventEmitter, Event, SourceControlResourceState, SourceControlResourceDecorations, Disposable, ProgressLocation, window, workspace, WorkspaceEdit } from 'vscode'; import { Uri, Command, EventEmitter, Event, SourceControlResourceState, SourceControlResourceDecorations, Disposable, ProgressLocation, window, workspace, WorkspaceEdit } from 'vscode';
import { Git, Repository, Ref, Branch, Remote, Commit, GitErrorCodes } from './git'; import { Git, Repository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash } from './git';
import { anyEvent, eventToPromise, filterEvent, EmptyDisposable, combinedDisposable, dispose } from './util'; import { anyEvent, eventToPromise, filterEvent, EmptyDisposable, combinedDisposable, dispose } from './util';
import { memoize, throttle, debounce } from './decorators'; import { memoize, throttle, debounce } from './decorators';
import * as path from 'path'; import * as path from 'path';
...@@ -215,7 +215,8 @@ export enum Operation { ...@@ -215,7 +215,8 @@ export enum Operation {
DeleteBranch = 1 << 16, DeleteBranch = 1 << 16,
Merge = 1 << 17, Merge = 1 << 17,
Ignore = 1 << 18, Ignore = 1 << 18,
Tag = 1 << 19 Tag = 1 << 19,
Stash = 1 << 20
} }
// function getOperationName(operation: Operation): string { // function getOperationName(operation: Operation): string {
...@@ -542,6 +543,11 @@ export class Model implements Disposable { ...@@ -542,6 +543,11 @@ export class Model implements Disposable {
}); });
} }
@throttle
async stash(pop: boolean = false, index?: string): Promise<void> {
return await this.run(Operation.Stash, () => this.repository.stash(pop, index));
}
async getCommitTemplate(): Promise<string> { async getCommitTemplate(): Promise<string> {
return await this.run(Operation.GetCommitTemplate, async () => this.repository.getCommitTemplate()); return await this.run(Operation.GetCommitTemplate, async () => this.repository.getCommitTemplate());
} }
...@@ -568,6 +574,10 @@ export class Model implements Disposable { ...@@ -568,6 +574,10 @@ export class Model implements Disposable {
}); });
} }
async getStashes(): Promise<Stash[]> {
return await this.getStashes();
}
private async run<T>(operation: Operation, runOperation: () => Promise<T> = () => Promise.resolve<any>(null)): Promise<T> { private async run<T>(operation: Operation, runOperation: () => Promise<T> = () => Promise.resolve<any>(null)): Promise<T> {
const run = async () => { const run = async () => {
this._operations = this._operations.start(operation); this._operations = this._operations.start(operation);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册