소스 검색

Adds stash apply|drop|pop|push git commands

Changes stash[Apply|Delete|Save] commands to use new git commands
main
Eric Amodio 5 년 전
부모
커밋
7b94606bfe
13개의 변경된 파일647개의 추가작업 그리고 206개의 파일을 삭제
  1. +10
    -3
      package.json
  2. +11
    -7
      src/commands/git/merge.ts
  3. +11
    -7
      src/commands/git/rebase.ts
  4. +503
    -0
      src/commands/git/stash.ts
  5. +9
    -2
      src/commands/gitCommands.ts
  6. +15
    -3
      src/commands/quickCommand.ts
  7. +16
    -91
      src/commands/stashApply.ts
  8. +16
    -40
      src/commands/stashDelete.ts
  9. +16
    -37
      src/commands/stashSave.ts
  10. +23
    -8
      src/git/git.ts
  11. +9
    -4
      src/git/gitService.ts
  12. +0
    -2
      src/quickpicks/commitQuickPick.ts
  13. +8
    -2
      src/quickpicks/gitQuickPicks.ts

+ 10
- 3
package.json 파일 보기

@ -485,7 +485,8 @@
"type": "array",
"default": [
"checkout",
"fetch"
"fetch",
"stash-push"
],
"items": {
"type": "string",
@ -493,13 +494,19 @@
"checkout",
"fetch",
"pull",
"push"
"push",
"stash-apply",
"stash-pop",
"stash-push"
],
"enumDescriptions": [
"Skips checkout command confirmation",
"Skips fetch command confirmation",
"Skips pull command confirmation",
"Skips push command confirmation"
"Skips push command confirmation",
"Skips stash apply command confirmation",
"Skips stash pop command confirmation",
"Skips stash push command confirmation"
]
},
"minItems": 0,

+ 11
- 7
src/commands/git/merge.ts 파일 보기

@ -4,7 +4,12 @@ import { Container } from '../../container';
import { GitBranch, GitTag, Repository } from '../../git/gitService';
import { GlyphChars } from '../../constants';
import { getBranchesAndOrTags, QuickCommandBase, QuickInputStep, QuickPickStep, StepState } from '../quickCommand';
import { BranchQuickPickItem, RepositoryQuickPickItem, TagQuickPickItem } from '../../quickpicks';
import {
BackOrCancelQuickPickItem,
BranchQuickPickItem,
RepositoryQuickPickItem,
TagQuickPickItem
} from '../../quickpicks';
import { Strings } from '../../system';
import { runGitCommandInTerminal } from '../../terminal';
import { Logger } from '../../logger';
@ -100,14 +105,13 @@ export class MergeGitCommand extends QuickCommandBase {
if (count === 0) {
const step = this.createConfirmStep(
`Confirm ${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${state.repo.formattedName}`,
[
{
[],
{
cancel: BackOrCancelQuickPickItem.create(true, true, {
label: `Cancel ${this.title}`,
description: '',
detail: `${state.destination.name} is up to date with ${state.source.name}`
}
],
false
})
}
);
yield step;

+ 11
- 7
src/commands/git/rebase.ts 파일 보기

@ -4,7 +4,12 @@ import { Container } from '../../container';
import { GitBranch, GitTag, Repository } from '../../git/gitService';
import { GlyphChars } from '../../constants';
import { getBranchesAndOrTags, QuickCommandBase, QuickInputStep, QuickPickStep, StepState } from '../quickCommand';
import { BranchQuickPickItem, RepositoryQuickPickItem, TagQuickPickItem } from '../../quickpicks';
import {
BackOrCancelQuickPickItem,
BranchQuickPickItem,
RepositoryQuickPickItem,
TagQuickPickItem
} from '../../quickpicks';
import { Strings } from '../../system';
import { runGitCommandInTerminal } from '../../terminal';
import { Logger } from '../../logger';
@ -100,14 +105,13 @@ export class RebaseGitCommand extends QuickCommandBase {
if (count === 0) {
const step = this.createConfirmStep(
`Confirm ${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${state.repo.formattedName}`,
[
{
[],
{
cancel: BackOrCancelQuickPickItem.create(true, true, {
label: `Cancel ${this.title}`,
description: '',
detail: `${state.destination.name} is up to date with ${state.source.name}`
}
],
false
})
}
);
yield step;

+ 503
- 0
src/commands/git/stash.ts 파일 보기

@ -0,0 +1,503 @@
'use strict';
import { QuickPickItem, Uri, window } from 'vscode';
import { Container } from '../../container';
import { GitStashCommit, GitUri, Repository } from '../../git/gitService';
import { BreakQuickCommand, QuickCommandBase, QuickInputStep, QuickPickStep, StepState } from '../quickCommand';
import { BackOrCancelQuickPickItem, CommitQuickPickItem, RepositoryQuickPickItem } from '../../quickpicks';
import { Iterables, Strings } from '../../system';
import { GlyphChars } from '../../constants';
import { Logger } from '../../logger';
import { Messages } from '../../messages';
interface ApplyState {
subcommand: 'apply';
repo: Repository;
stash: { stashName: string; message: string; repoPath: string };
flags: string[];
}
interface DropState {
subcommand: 'drop';
repo: Repository;
stash: { stashName: string; message: string; repoPath: string };
flags: string[];
}
interface PopState {
subcommand: 'pop';
repo: Repository;
stash: { stashName: string; message: string; repoPath: string };
flags: string[];
}
interface PushState {
subcommand: 'push';
repo: Repository;
message?: string;
uris?: Uri[];
flags: string[];
}
type State = ApplyState | DropState | PopState | PushState;
type StashStepState<T> = Partial<T> & { counter: number; repo: Repository; skipConfirmation?: boolean };
interface StashSubcommandQuickPickItem extends QuickPickItem {
item: State['subcommand'];
}
export interface CommandArgs {
readonly command: 'stash';
state?: Partial<State>;
skipConfirmation?: boolean;
}
export class StashGitCommand extends QuickCommandBase<State> {
constructor(args?: CommandArgs) {
super('stash', 'Stash');
if (args === undefined || args.state === undefined) return;
let counter = 0;
if (args.state.subcommand !== undefined) {
counter++;
}
if (args.state.repo !== undefined) {
counter++;
}
switch (args.state.subcommand) {
case 'apply':
case 'drop':
case 'pop':
if (args.state.stash !== undefined) {
counter++;
}
break;
case 'push':
if (args.state.message !== undefined) {
counter++;
}
break;
}
if (
args.skipConfirmation === undefined &&
Container.config.gitCommands.skipConfirmations.includes(`${this.label}-${args.state.subcommand}`)
) {
args.skipConfirmation = true;
}
this._initialState = {
counter: counter,
skipConfirmation: counter > 0 && args.skipConfirmation,
...args.state
};
}
protected async *steps(): AsyncIterableIterator<QuickPickStep | QuickInputStep> {
const state: StepState<State> = this._initialState === undefined ? { counter: 0 } : this._initialState;
let oneRepo = false;
while (true) {
try {
if (state.subcommand === undefined || state.counter < 1) {
const step = this.createPickStep<StashSubcommandQuickPickItem>({
title: this.title,
placeholder: `Choose a ${this.label} command`,
items: [
{
label: 'apply',
picked: state.subcommand === 'apply',
item: 'apply'
},
{
label: 'drop',
picked: state.subcommand === 'drop',
item: 'drop'
},
{
label: 'pop',
picked: state.subcommand === 'pop',
item: 'pop'
},
{
label: 'push',
picked: state.subcommand === 'push',
item: 'push'
}
]
});
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
break;
}
state.subcommand = selection[0].item;
}
if (state.repo === undefined || state.counter < 2) {
const repos = [...(await Container.git.getOrderedRepositories())];
if (repos.length === 1) {
oneRepo = true;
state.counter++;
state.repo = repos[0];
}
else {
const step = this.createPickStep<RepositoryQuickPickItem>({
title: `${this.title} ${state.subcommand}`,
placeholder: 'Choose a repository',
items: await Promise.all(
repos.map(r =>
RepositoryQuickPickItem.create(r, r.id === (state.repo && state.repo.id), {
branch: true,
fetched: true,
status: true
})
)
)
});
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
continue;
}
state.repo = selection[0].item;
}
}
switch (state.subcommand) {
case 'apply':
case 'pop':
yield* this.applyOrPop(state as StashStepState<ApplyState | PopState>);
break;
case 'drop':
yield* this.drop(state as StashStepState<DropState>);
break;
case 'push':
yield* this.push(state as StashStepState<PushState>);
break;
default:
return;
}
if (oneRepo) {
state.counter--;
}
continue;
}
catch (ex) {
if (ex instanceof BreakQuickCommand) return;
Logger.error(ex, `${this.title}.${state.subcommand}`);
switch (state.subcommand) {
case 'apply':
case 'pop':
if (
ex.message.includes(
'Your local changes to the following files would be overwritten by merge'
)
) {
void window.showWarningMessage(
'Unable to apply stash. Your working tree changes would be overwritten'
);
return;
}
else if (ex.message.includes('Auto-merging') && ex.message.includes('CONFLICT')) {
void window.showInformationMessage('Stash applied with conflicts');
return;
}
void Messages.showGenericErrorMessage(
`Unable to apply stash \u2014 ${ex.message.trim().replace(/\n+?/g, '; ')}`
);
return;
case 'drop':
void Messages.showGenericErrorMessage('Unable to delete stash');
return;
case 'push':
if (ex.message.includes('newer version of Git')) {
void window.showErrorMessage(`Unable to stash changes. ${ex.message}`);
return;
}
void Messages.showGenericErrorMessage('Unable to stash changes');
return;
}
throw ex;
}
}
}
private async *applyOrPop(
state: StashStepState<ApplyState> | StashStepState<PopState>
): AsyncIterableIterator<QuickPickStep | QuickInputStep> {
while (true) {
if (state.stash === undefined || state.counter < 3) {
const stash = await Container.git.getStashList(state.repo.path);
const step = this.createPickStep<CommitQuickPickItem<GitStashCommit>>({
title: `${this.title} ${state.subcommand}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repo.formattedName
}`,
placeholder:
stash === undefined
? `${state.repo.formattedName} has no stashed changes`
: 'Choose a stash to apply to your working tree',
items:
stash === undefined
? [BackOrCancelQuickPickItem.create(false, true), BackOrCancelQuickPickItem.create()]
: [
...Iterables.map(stash.commits.values(), c =>
CommitQuickPickItem.create(
c,
c.stashName === (state.stash && state.stash.stashName),
{
compact: true
}
)
)
]
});
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
break;
}
state.stash = selection[0].item;
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
const message =
state.stash.message.length > 80
? `${state.stash.message.substring(0, 80)}${GlyphChars.Ellipsis}`
: state.stash.message;
const step = this.createConfirmStep<QuickPickItem & { command: 'apply' | 'pop'; item: string[] }>(
`Confirm ${this.title} ${state.subcommand}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repo.formattedName
}`,
[
{
label: `${this.title} ${state.subcommand}`,
description: `${state.stash.stashName}${Strings.pad(GlyphChars.Dash, 2, 2)}${message}`,
detail:
state.subcommand === 'pop'
? `Will delete ${
state.stash!.stashName
} and apply the changes to the working tree of ${state.repo.formattedName}`
: `Will apply the changes from ${state.stash!.stashName} to the working tree of ${
state.repo.formattedName
}`,
command: state.subcommand!,
item: []
},
// Alternate confirmation (if pop then apply, and vice versa)
{
label: `${this.title} ${state.subcommand === 'pop' ? 'apply' : 'pop'}`,
description: `${state.stash!.stashName}${Strings.pad(GlyphChars.Dash, 2, 2)}${message}`,
detail:
state.subcommand === 'pop'
? `Will apply the changes from ${state.stash!.stashName} to the working tree of ${
state.repo.formattedName
}`
: `Will delete ${
state.stash!.stashName
} and apply the changes to the working tree of ${state.repo.formattedName}`,
command: state.subcommand === 'pop' ? 'apply' : 'pop',
item: []
}
],
{ placeholder: `Confirm ${this.title} ${state.subcommand}` }
);
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
break;
}
state.subcommand = selection[0].command;
state.flags = selection[0].item;
}
void Container.git.stashApply(state.repo.path, state.stash!.stashName, state.subcommand === 'pop');
throw new BreakQuickCommand();
}
}
private async *drop(state: StashStepState<DropState>): AsyncIterableIterator<QuickPickStep | QuickInputStep> {
while (true) {
if (state.stash === undefined || state.counter < 3) {
const stash = await Container.git.getStashList(state.repo.path);
const step = this.createPickStep<CommitQuickPickItem<GitStashCommit>>({
title: `${this.title} ${state.subcommand}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repo.formattedName
}`,
placeholder:
stash === undefined
? `${state.repo.formattedName} has no stashed changes`
: 'Choose a stash to delete',
items:
stash === undefined
? [BackOrCancelQuickPickItem.create(false, true), BackOrCancelQuickPickItem.create()]
: [
...Iterables.map(stash.commits.values(), c =>
CommitQuickPickItem.create(
c,
c.stashName === (state.stash && state.stash.stashName),
{
compact: true
}
)
)
]
});
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
break;
}
state.stash = selection[0].item;
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
const message =
state.stash.message.length > 80
? `${state.stash.message.substring(0, 80)}${GlyphChars.Ellipsis}`
: state.stash.message;
const step = this.createConfirmStep<QuickPickItem>(
`Confirm ${this.title} ${state.subcommand}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repo.formattedName
}`,
[
{
label: `${this.title} ${state.subcommand}`,
description: `${state.stash.stashName}${Strings.pad(GlyphChars.Dash, 2, 2)}${message}`,
detail: `Will delete ${state.stash!.stashName}`
}
],
{ placeholder: `Confirm ${this.title} ${state.subcommand}` }
);
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
break;
}
}
void Container.git.stashDelete(state.repo.path, state.stash!.stashName);
throw new BreakQuickCommand();
}
}
// eslint-disable-next-line require-await
private async *push(state: StashStepState<PushState>): AsyncIterableIterator<QuickPickStep | QuickInputStep> {
while (true) {
if (state.message === undefined || state.counter < 3) {
const step = this.createInputStep({
title: `${this.title} ${state.subcommand}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repo.formattedName
}`,
placeholder: 'Please provide a stash message',
value: state.message
// validate: (value: string | undefined): [boolean, string | undefined] => [value != null, undefined]
});
const value = yield step;
if (!this.canMoveNext(step, state, value)) {
break;
}
state.message = value;
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
const step = this.createConfirmStep<QuickPickItem & { item: string[] }>(
`Confirm ${this.title} ${state.subcommand}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repo.formattedName
}`,
state.uris === undefined || state.uris.length === 0
? [
{
label: `${this.title} ${state.subcommand}`,
description: state.message,
detail: 'Will stash uncommitted changes',
item: []
},
{
label: `${this.title} ${state.subcommand}`,
description: state.message,
detail: 'Will stash uncommitted changes, including untracked files',
item: ['--include-untracked']
},
{
label: `${this.title} ${state.subcommand}`,
description: state.message,
detail: 'Will stash uncommitted changes, but will keep staged files intact',
item: ['--keep-index']
}
]
: [
{
label: `${this.title} ${state.subcommand}`,
description: state.message,
detail: `Will stash changes in ${
state.uris.length === 1
? GitUri.getFormattedPath(state.uris[0], { relativeTo: state.repo.path })
: `${state.uris.length} files`
}`,
item: []
}
],
{ placeholder: `Confirm ${this.title} ${state.subcommand}` }
);
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
break;
}
state.flags = selection[0].item;
}
void Container.git.stashSave(state.repo.path, state.message, state.uris, {
includeUntracked: state.flags.includes('--include-untracked'),
keepIndex: state.flags.includes('--keep-index')
});
throw new BreakQuickCommand();
}
}
}

+ 9
- 2
src/commands/gitCommands.ts 파일 보기

@ -11,10 +11,16 @@ import { MergeGitCommand } from './git/merge';
import { CommandArgs as PullCommandArgs, PullGitCommand } from './git/pull';
import { CommandArgs as PushCommandArgs, PushGitCommand } from './git/push';
import { RebaseGitCommand } from './git/rebase';
import { CommandArgs as StashCommandArgs, StashGitCommand } from './git/stash';
const sanitizeLabel = /\$\(.+?\)|\W/g;
export type GitCommandsCommandArgs = CheckoutCommandArgs | FetchCommandArgs | PullCommandArgs | PushCommandArgs;
export type GitCommandsCommandArgs =
| CheckoutCommandArgs
| FetchCommandArgs
| PullCommandArgs
| PushCommandArgs
| StashCommandArgs;
class PickCommandStep implements QuickPickStep {
readonly buttons = [];
@ -30,7 +36,8 @@ class PickCommandStep implements QuickPickStep {
new FetchGitCommand(args && args.command === 'fetch' ? args : undefined),
new PullGitCommand(args && args.command === 'pull' ? args : undefined),
new PushGitCommand(args && args.command === 'push' ? args : undefined),
new RebaseGitCommand()
new RebaseGitCommand(),
new StashGitCommand(args && args.command === 'stash' ? args : undefined)
];
}

+ 15
- 3
src/commands/quickCommand.ts 파일 보기

@ -5,6 +5,12 @@ import { BackOrCancelQuickPickItem } from '../quickpicks';
export * from './quickCommand.helpers';
export class BreakQuickCommand extends Error {
constructor() {
super('break');
}
}
export enum Directive {
Back = 'back'
}
@ -108,12 +114,18 @@ export abstract class QuickCommandBase implements QuickPickItem {
protected createConfirmStep<T extends QuickPickItem>(
title: string,
confirmations: T[],
cancellable: boolean = true
{
cancel,
placeholder
}: {
cancel?: BackOrCancelQuickPickItem;
placeholder?: string;
} = {}
): QuickPickStep<T> {
return this.createPickStep<T>({
placeholder: `Confirm ${this.title}`,
placeholder: placeholder || `Confirm ${this.title}`,
title: title,
items: cancellable ? [...confirmations, BackOrCancelQuickPickItem.create()] : confirmations,
items: [...confirmations, cancel || BackOrCancelQuickPickItem.create()],
selectedItems: [confirmations[0]]
});
}

+ 16
- 91
src/commands/stashApply.ts 파일 보기

@ -1,23 +1,19 @@
'use strict';
import { window } from 'vscode';
import { GlyphChars } from '../constants';
import { commands } from 'vscode';
import { Container } from '../container';
import { GitStashCommit } from '../git/gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem, StashListQuickPick } from '../quickpicks';
import { CommandQuickPickItem } from '../quickpicks';
import {
command,
Command,
CommandContext,
Commands,
getRepoPathOrPrompt,
isCommandViewContextWithCommit,
isCommandViewContextWithRepo
} from './common';
import { GitCommandsCommandArgs } from '../commands';
export interface StashApplyCommandArgs {
confirm?: boolean;
deleteAfter?: boolean;
repoPath?: string;
stashItem?: { stashName: string; message: string; repoPath: string };
@ -31,11 +27,10 @@ export class StashApplyCommand extends Command {
super(Commands.StashApply);
}
protected preExecute(context: CommandContext, args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) {
protected preExecute(context: CommandContext, args: StashApplyCommandArgs = { deleteAfter: false }) {
if (isCommandViewContextWithCommit<GitStashCommit>(context)) {
args = { ...args };
args.stashItem = context.node.commit;
return this.execute(args);
}
else if (isCommandViewContextWithRepo(context)) {
args = { ...args };
@ -45,90 +40,20 @@ export class StashApplyCommand extends Command {
return this.execute(args);
}
async execute(args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) {
args = { ...args };
if (args.stashItem === undefined || args.stashItem.stashName === undefined) {
if (args.repoPath === undefined) {
args.repoPath = await getRepoPathOrPrompt(
`Apply stashed changes from which repository${GlyphChars.Ellipsis}`,
args.goBackCommand
);
}
if (!args.repoPath) return undefined;
const progressCancellation = StashListQuickPick.showProgress('apply');
try {
const stash = await Container.git.getStashList(args.repoPath);
if (stash === undefined) return window.showInformationMessage('There are no stashed changes');
if (progressCancellation.token.isCancellationRequested) return undefined;
const currentCommand = new CommandQuickPickItem(
{
label: `go back ${GlyphChars.ArrowBack}`,
description: 'to apply stashed changes'
},
Commands.StashApply,
[args]
);
const pick = await StashListQuickPick.show(
stash,
'apply',
progressCancellation,
args.goBackCommand,
currentCommand
);
if (pick instanceof CommandQuickPickItem) return pick.execute();
if (pick === undefined) {
return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
}
args.goBackCommand = currentCommand;
args.stashItem = pick.item;
}
finally {
progressCancellation.cancel();
}
async execute(args: StashApplyCommandArgs = { deleteAfter: false }) {
let repo;
if (args.stashItem !== undefined || args.repoPath !== undefined) {
repo = await Container.git.getRepository((args.stashItem && args.stashItem.repoPath) || args.repoPath!);
}
try {
if (args.confirm) {
const message =
args.stashItem.message.length > 80
? `${args.stashItem.message.substring(0, 80)}${GlyphChars.Ellipsis}`
: args.stashItem.message;
const result = await window.showWarningMessage(
`Apply stashed changes '${message}' to your working tree?`,
{ title: 'Yes, delete after applying' },
{ title: 'Yes' },
{ title: 'No', isCloseAffordance: true }
);
if (result === undefined || result.title === 'No') {
return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
}
args.deleteAfter = result.title !== 'Yes';
}
return await Container.git.stashApply(args.stashItem.repoPath, args.stashItem.stashName, args.deleteAfter);
}
catch (ex) {
Logger.error(ex, 'StashApplyCommand');
if (ex.message.includes('Your local changes to the following files would be overwritten by merge')) {
return window.showWarningMessage(
'Unable to apply stash. Your working tree changes would be overwritten.'
);
}
else if (ex.message.includes('Auto-merging') && ex.message.includes('CONFLICT')) {
return window.showInformationMessage('Stash applied with conflicts');
const gitCommandArgs: GitCommandsCommandArgs = {
command: 'stash',
state: {
subcommand: args.deleteAfter ? 'pop' : 'apply',
repo: repo,
stash: args.stashItem
}
return Messages.showGenericErrorMessage(
`Unable to apply stash \u2014 ${ex.message.trim().replace(/\n+?/g, '; ')}`
);
}
};
return commands.executeCommand(Commands.GitCommands, gitCommandArgs);
}
}

+ 16
- 40
src/commands/stashDelete.ts 파일 보기

@ -1,15 +1,13 @@
'use strict';
import { window } from 'vscode';
import { GlyphChars } from '../constants';
import { commands } from 'vscode';
import { Container } from '../container';
import { GitStashCommit } from '../git/gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem } from '../quickpicks';
import { command, Command, CommandContext, Commands, isCommandViewContextWithCommit } from './common';
import { GitCommandsCommandArgs } from '../commands';
export interface StashDeleteCommandArgs {
confirm?: boolean;
repoPath?: string;
stashItem?: { stashName: string; message: string; repoPath: string };
goBackCommand?: CommandQuickPickItem;
@ -21,51 +19,29 @@ export class StashDeleteCommand extends Command {
super(Commands.StashDelete);
}
protected preExecute(context: CommandContext, args: StashDeleteCommandArgs = { confirm: true }) {
protected preExecute(context: CommandContext, args: StashDeleteCommandArgs = {}) {
if (isCommandViewContextWithCommit<GitStashCommit>(context)) {
args = { ...args };
args.stashItem = context.node.commit;
return this.execute(args);
}
return this.execute(args);
}
async execute(args: StashDeleteCommandArgs = { confirm: true }) {
args = { ...args };
if (
args.stashItem === undefined ||
args.stashItem.stashName === undefined ||
args.stashItem.repoPath === undefined
) {
return undefined;
async execute(args: StashDeleteCommandArgs = {}) {
let repo;
if (args.stashItem !== undefined || args.repoPath !== undefined) {
repo = await Container.git.getRepository((args.stashItem && args.stashItem.repoPath) || args.repoPath!);
}
if (args.confirm === undefined) {
args.confirm = true;
}
try {
if (args.confirm) {
const message =
args.stashItem.message.length > 80
? `${args.stashItem.message.substring(0, 80)}${GlyphChars.Ellipsis}`
: args.stashItem.message;
const result = await window.showWarningMessage(
`Delete stashed changes '${message}'?`,
{ title: 'Yes' },
{ title: 'No', isCloseAffordance: true }
);
if (result === undefined || result.title !== 'Yes') {
return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
}
const gitCommandArgs: GitCommandsCommandArgs = {
command: 'stash',
state: {
subcommand: 'drop',
repo: repo,
stash: args.stashItem
}
return await Container.git.stashDelete(args.stashItem.repoPath, args.stashItem.stashName);
}
catch (ex) {
Logger.error(ex, 'StashDeleteCommand');
return Messages.showGenericErrorMessage('Unable to delete stash');
}
};
return commands.executeCommand(Commands.GitCommands, gitCommandArgs);
}
}

+ 16
- 37
src/commands/stashSave.ts 파일 보기

@ -1,21 +1,18 @@
'use strict';
import { Uri, window } from 'vscode';
import { GlyphChars } from '../constants';
import { commands, Uri } from 'vscode';
import { Container } from '../container';
import { GitUri } from '../git/gitUri';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem } from '../quickpicks';
import {
command,
Command,
CommandContext,
Commands,
getRepoPathOrPrompt,
isCommandViewContextWithFile,
isCommandViewContextWithRepo,
isCommandViewContextWithRepoPath
} from './common';
import { GitCommandsCommandArgs } from '../commands';
export interface StashSaveCommandArgs {
message?: string;
@ -34,7 +31,8 @@ export class StashSaveCommand extends Command {
protected preExecute(context: CommandContext, args: StashSaveCommandArgs = {}) {
if (isCommandViewContextWithFile(context)) {
args = { ...args };
args.uris = [GitUri.fromFile(context.node.file, context.node.file.repoPath || context.node.repoPath)];
args.repoPath = context.node.file.repoPath || context.node.repoPath;
args.uris = [GitUri.fromFile(context.node.file, args.repoPath)];
}
else if (isCommandViewContextWithRepo(context)) {
args = { ...args };
@ -60,39 +58,20 @@ export class StashSaveCommand extends Command {
}
async execute(args: StashSaveCommandArgs = {}) {
args = { ...args };
const uri = args.uris !== undefined && args.uris.length !== 0 ? args.uris[0] : undefined;
if (args.repoPath === undefined) {
args.repoPath = await getRepoPathOrPrompt(
`Stash changes for which repository${GlyphChars.Ellipsis}`,
args.goBackCommand,
uri
);
let repo;
if (args.uris !== undefined || args.repoPath !== undefined) {
repo = await Container.git.getRepository((args.uris && args.uris[0]) || args.repoPath!);
}
if (!args.repoPath) return undefined;
try {
if (args.message == null) {
args.message = await window.showInputBox({
prompt: 'Please provide a stash message',
placeHolder: 'Stash message'
});
if (args.message === undefined) {
return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
}
const gitCommandArgs: GitCommandsCommandArgs = {
command: 'stash',
state: {
subcommand: 'push',
repo: repo,
message: args.message,
uris: args.uris
}
return await Container.git.stashSave(args.repoPath, args.message, args.uris);
}
catch (ex) {
Logger.error(ex, 'StashSaveCommand');
const msg = ex && ex.message;
if (msg.includes('newer version of Git')) {
return window.showErrorMessage(`Unable to save stash. ${msg}`);
}
return Messages.showGenericErrorMessage('Unable to save stash');
}
};
return commands.executeCommand(Commands.GitCommands, gitCommandArgs);
}
}

+ 23
- 8
src/git/git.ts 파일 보기

@ -1067,19 +1067,34 @@ export class Git {
);
}
static stash__push(repoPath: string, pathspecs: string[], message?: string) {
const params = ['stash', 'push', '-u'];
static stash__push(
repoPath: string,
message?: string,
{
includeUntracked,
keepIndex,
pathspecs
}: { includeUntracked?: boolean; keepIndex?: boolean; pathspecs?: string[] } = {}
) {
const params = ['stash', 'push'];
if (includeUntracked || (pathspecs !== undefined && pathspecs.length !== 0)) {
params.push('-u');
}
if (keepIndex) {
params.push('-k');
}
if (message) {
params.push('-m', message);
}
return git<string>({ cwd: repoPath }, ...params, '--', ...pathspecs);
}
static stash__save(repoPath: string, message?: string) {
const params = ['stash', 'save', '-u'];
if (message) {
params.push(message);
params.push('--');
if (pathspecs !== undefined && pathspecs.length !== 0) {
params.push(...pathspecs);
}
return git<string>({ cwd: repoPath }, ...params);
}

+ 9
- 4
src/git/gitService.ts 파일 보기

@ -2752,13 +2752,18 @@ export class GitService implements Disposable {
}
@log()
stashSave(repoPath: string, message?: string, uris?: Uri[]) {
if (uris === undefined) return Git.stash__save(repoPath, message);
stashSave(
repoPath: string,
message?: string,
uris?: Uri[],
options: { includeUntracked?: boolean; keepIndex?: boolean } = {}
) {
if (uris === undefined) return Git.stash__push(repoPath, message, options);
GitService.ensureGitVersion('2.13.2', 'Stashing individual files');
const pathspecs = uris.map(u => Git.splitPath(u.fsPath, repoPath)[0]);
return Git.stash__push(repoPath, pathspecs, message);
const pathspecs = uris.map(u => `./${Git.splitPath(u.fsPath, repoPath)[0]}`);
return Git.stash__push(repoPath, message, { ...options, pathspecs: pathspecs });
}
static compareGitVersion(version: string) {

+ 0
- 2
src/quickpicks/commitQuickPick.ts 파일 보기

@ -250,7 +250,6 @@ export class CommitQuickPick {
let remotes;
if (stash) {
const stashApplyCommmandArgs: StashApplyCommandArgs = {
confirm: true,
deleteAfter: false,
stashItem: commit as GitStashCommit,
goBackCommand: options.currentCommand
@ -269,7 +268,6 @@ export class CommitQuickPick {
);
const stashDeleteCommmandArgs: StashDeleteCommandArgs = {
confirm: true,
stashItem: commit as GitStashCommit,
goBackCommand: options.currentCommand
};

+ 8
- 2
src/quickpicks/gitQuickPicks.ts 파일 보기

@ -19,9 +19,15 @@ export interface BackOrCancelQuickPickItem extends QuickPickItem {
}
export namespace BackOrCancelQuickPickItem {
export function create(cancelled: boolean = true, picked?: boolean, label?: string) {
export function create(
cancelled: boolean = true,
picked?: boolean,
options: { label?: string; description?: string; detail?: string } = {}
) {
const item: BackOrCancelQuickPickItem = {
label: label || (cancelled ? 'Cancel' : 'Back'),
label: options.label || (cancelled ? 'Cancel' : 'Back'),
description: options.description || '',
detail: options.detail,
picked: picked,
cancelled: cancelled
};

불러오는 중...
취소
저장