Procházet zdrojové kódy

Adds stash apply|drop|pop|push git commands

Changes stash[Apply|Delete|Save] commands to use new git commands
main
Eric Amodio před 5 roky
rodič
revize
7b94606bfe
13 změnil soubory, kde provedl 647 přidání a 206 odebrání
  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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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 Zobrazit soubor

@ -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
};

Načítá se…
Zrušit
Uložit