mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-02-16 18:48:45 -05:00
Adds 'Show Stashed Changes` command
Adds experimental 'Apply Stashed Changes' command Adds experimental 'Delete Stashed Changes' to stashed changes quick pick
This commit is contained in:
18
package.json
18
package.json
@@ -458,6 +458,11 @@
|
|||||||
"title": "Show Repository Status",
|
"title": "Show Repository Status",
|
||||||
"category": "GitLens"
|
"category": "GitLens"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.showQuickStashList",
|
||||||
|
"title": "Show Stashed Changes",
|
||||||
|
"category": "GitLens"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.copyShaToClipboard",
|
"command": "gitlens.copyShaToClipboard",
|
||||||
"title": "Copy Commit Sha to Clipboard",
|
"title": "Copy Commit Sha to Clipboard",
|
||||||
@@ -487,6 +492,11 @@
|
|||||||
"command": "gitlens.openFileInRemote",
|
"command": "gitlens.openFileInRemote",
|
||||||
"title": "Open File in Remote",
|
"title": "Open File in Remote",
|
||||||
"category": "GitLens"
|
"category": "GitLens"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.stashApply",
|
||||||
|
"title": "Apply Stashed Changes",
|
||||||
|
"category": "GitLens"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"menus": {
|
"menus": {
|
||||||
@@ -567,6 +577,10 @@
|
|||||||
"command": "gitlens.showQuickRepoStatus",
|
"command": "gitlens.showQuickRepoStatus",
|
||||||
"when": "gitlens:enabled"
|
"when": "gitlens:enabled"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.showQuickStashList",
|
||||||
|
"when": "gitlens:enabled"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.copyShaToClipboard",
|
"command": "gitlens.copyShaToClipboard",
|
||||||
"when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable"
|
"when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable"
|
||||||
@@ -590,6 +604,10 @@
|
|||||||
{
|
{
|
||||||
"command": "gitlens.openFileInRemote",
|
"command": "gitlens.openFileInRemote",
|
||||||
"when": "editorTextFocus && gitlens:enabled && gitlens:hasRemotes"
|
"when": "editorTextFocus && gitlens:enabled && gitlens:hasRemotes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitlens.stashApply",
|
||||||
|
"when": "gitlens:enabled && config.gitlens.insiders"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"explorer/context": [
|
"explorer/context": [
|
||||||
|
|||||||
@@ -27,5 +27,8 @@ export * from './commands/showQuickFileHistory';
|
|||||||
export * from './commands/showQuickBranchHistory';
|
export * from './commands/showQuickBranchHistory';
|
||||||
export * from './commands/showQuickCurrentBranchHistory';
|
export * from './commands/showQuickCurrentBranchHistory';
|
||||||
export * from './commands/showQuickRepoStatus';
|
export * from './commands/showQuickRepoStatus';
|
||||||
|
export * from './commands/showQuickStashList';
|
||||||
|
export * from './commands/stashApply';
|
||||||
|
export * from './commands/stashDelete';
|
||||||
export * from './commands/toggleBlame';
|
export * from './commands/toggleBlame';
|
||||||
export * from './commands/toggleCodeLens';
|
export * from './commands/toggleCodeLens';
|
||||||
@@ -10,7 +10,8 @@ export type Commands = 'gitlens.closeUnchangedFiles' | 'gitlens.copyMessageToCli
|
|||||||
'gitlens.showLastQuickPick' | 'gitlens.showQuickBranchHistory' |
|
'gitlens.showLastQuickPick' | 'gitlens.showQuickBranchHistory' |
|
||||||
'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' |
|
'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' |
|
||||||
'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' |
|
'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' |
|
||||||
'gitlens.showQuickRepoStatus' |
|
'gitlens.showQuickRepoStatus' | 'gitlens.showQuickStashList' |
|
||||||
|
'gitlens.stashApply' | 'gitlens.stashDelete' | 'gitlens.stashSave' |
|
||||||
'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
|
'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
|
||||||
export const Commands = {
|
export const Commands = {
|
||||||
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
|
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
|
||||||
@@ -37,6 +38,9 @@ export const Commands = {
|
|||||||
ShowQuickBranchHistory: 'gitlens.showQuickBranchHistory' as Commands,
|
ShowQuickBranchHistory: 'gitlens.showQuickBranchHistory' as Commands,
|
||||||
ShowQuickCurrentBranchHistory: 'gitlens.showQuickRepoHistory' as Commands,
|
ShowQuickCurrentBranchHistory: 'gitlens.showQuickRepoHistory' as Commands,
|
||||||
ShowQuickRepoStatus: 'gitlens.showQuickRepoStatus' as Commands,
|
ShowQuickRepoStatus: 'gitlens.showQuickRepoStatus' as Commands,
|
||||||
|
ShowQuickStashList: 'gitlens.showQuickStashList' as Commands,
|
||||||
|
StashApply: 'gitlens.stashApply' as Commands,
|
||||||
|
StashDelete: 'gitlens.stashDelete' as Commands,
|
||||||
ToggleBlame: 'gitlens.toggleBlame' as Commands,
|
ToggleBlame: 'gitlens.toggleBlame' as Commands,
|
||||||
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
|
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!commit || !(commit instanceof GitLogCommit) || commit.type !== 'repo') {
|
if (!commit || (commit.type !== 'repo' && commit.type !== 'stash')) {
|
||||||
if (repoLog) {
|
if (repoLog) {
|
||||||
commit = repoLog.commits.get(sha);
|
commit = repoLog.commits.get(sha);
|
||||||
// If we can't find the commit, kill the repoLog
|
// If we can't find the commit, kill the repoLog
|
||||||
@@ -88,7 +88,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
|
|||||||
return pick.execute();
|
return pick.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, pick.gitUri, pick.sha, undefined, currentCommand);
|
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, pick.gitUri, pick.sha, commit, currentCommand);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error(ex, 'ShowQuickCommitDetailsCommand');
|
Logger.error(ex, 'ShowQuickCommitDetailsCommand');
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!commit || !(commit instanceof GitLogCommit) || commit.type !== 'file') {
|
if (!commit || (commit.type !== 'file' && commit.type !== 'stash')) {
|
||||||
if (fileLog) {
|
if (fileLog) {
|
||||||
commit = fileLog.commits.get(sha);
|
commit = fileLog.commits.get(sha);
|
||||||
// If we can't find the commit, kill the fileLog
|
// If we can't find the commit, kill the fileLog
|
||||||
|
|||||||
42
src/commands/showQuickStashList.ts
Normal file
42
src/commands/showQuickStashList.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
'use strict';
|
||||||
|
import { commands, TextEditor, Uri, window } from 'vscode';
|
||||||
|
import { ActiveEditorCachedCommand, Commands } from './common';
|
||||||
|
import { GitService, GitUri } from '../gitService';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
import { CommandQuickPickItem, StashListQuickPick } from '../quickPicks';
|
||||||
|
|
||||||
|
export class ShowQuickStashListCommand extends ActiveEditorCachedCommand {
|
||||||
|
|
||||||
|
constructor(private git: GitService) {
|
||||||
|
super(Commands.ShowQuickStashList);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(editor: TextEditor, uri?: Uri, goBackCommand?: CommandQuickPickItem) {
|
||||||
|
if (!(uri instanceof Uri)) {
|
||||||
|
uri = editor && editor.document && editor.document.uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const repoPath = await this.git.getRepoPathFromUri(uri, this.git.repoPath);
|
||||||
|
if (!repoPath) return window.showWarningMessage(`Unable to show stash list`);
|
||||||
|
|
||||||
|
const stash = await this.git.getStashList(repoPath);
|
||||||
|
const pick = await StashListQuickPick.show(stash, undefined, goBackCommand);
|
||||||
|
if (!pick) return undefined;
|
||||||
|
|
||||||
|
if (pick instanceof CommandQuickPickItem) {
|
||||||
|
return pick.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands.executeCommand(Commands.ShowQuickCommitDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
|
||||||
|
new CommandQuickPickItem({
|
||||||
|
label: `go back \u21A9`,
|
||||||
|
description: `\u00a0 \u2014 \u00a0\u00a0 to the stash list`
|
||||||
|
}, Commands.ShowQuickStashList, [uri, goBackCommand]));
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
Logger.error(ex, 'ShowQuickStashListCommand');
|
||||||
|
return window.showErrorMessage(`Unable to show stash list. See output channel for more details`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/commands/stashApply.ts
Normal file
48
src/commands/stashApply.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
'use strict';
|
||||||
|
import { MessageItem, window } from 'vscode';
|
||||||
|
import { GitService, GitStashCommit } from '../gitService';
|
||||||
|
import { Command, Commands } from './common';
|
||||||
|
import { CommitQuickPickItem, StashListQuickPick } from '../quickPicks';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
|
export class StashApplyCommand extends Command {
|
||||||
|
|
||||||
|
constructor(private git: GitService) {
|
||||||
|
super(Commands.StashApply);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(stashItem: { stashName: string, message: string }, confirm: boolean = true, deleteAfter: boolean = false) {
|
||||||
|
if (!this.git.config.insiders) return undefined;
|
||||||
|
|
||||||
|
if (!stashItem || !stashItem.stashName) {
|
||||||
|
const stash = await this.git.getStashList(this.git.repoPath);
|
||||||
|
if (!stash) return window.showInformationMessage(`There are no stashed changes`);
|
||||||
|
|
||||||
|
const pick = await StashListQuickPick.show(stash, 'Apply stashed changes to your working tree\u2026');
|
||||||
|
if (!pick || !(pick instanceof CommitQuickPickItem)) return undefined;
|
||||||
|
|
||||||
|
stashItem = pick.commit as GitStashCommit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (confirm) {
|
||||||
|
const message = stashItem.message.length > 80 ? `${stashItem.message.substring(0, 80)}\u2026` : stashItem.message;
|
||||||
|
const result = await window.showWarningMessage(`Apply stashed changes '${message}' to your working tree?`, { title: 'Yes, delete after applying' } as MessageItem, { title: 'Yes' } as MessageItem, { title: 'No', isCloseAffordance: true } as MessageItem);
|
||||||
|
if (!result || result.title === 'No') return undefined;
|
||||||
|
|
||||||
|
deleteAfter = result.title !== 'Yes';
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.git.stashApply(this.git.repoPath, stashItem.stashName, 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.showErrorMessage(`Unable to apply stash. Your working tree changes would be overwritten.`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return window.showErrorMessage(`Unable to apply stash. See output channel for more details`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/commands/stashDelete.ts
Normal file
31
src/commands/stashDelete.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
'use strict';
|
||||||
|
import { MessageItem, window } from 'vscode';
|
||||||
|
import { GitService } from '../gitService';
|
||||||
|
import { Command, Commands } from './common';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
|
export class StashDeleteCommand extends Command {
|
||||||
|
|
||||||
|
constructor(private git: GitService) {
|
||||||
|
super(Commands.StashDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(stashItem: { stashName: string, message: string }, confirm: boolean = true) {
|
||||||
|
if (!this.git.config.insiders) return undefined;
|
||||||
|
if (!stashItem || !stashItem.stashName) return undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (confirm) {
|
||||||
|
const message = stashItem.message.length > 80 ? `${stashItem.message.substring(0, 80)}\u2026` : stashItem.message;
|
||||||
|
const result = await window.showWarningMessage(`Delete stashed changes '${message}'?`, { title: 'Yes' } as MessageItem, { title: 'No', isCloseAffordance: true } as MessageItem);
|
||||||
|
if (!result || result.title !== 'Yes') return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.git.stashDelete(this.git.repoPath, stashItem.stashName);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
Logger.error(ex, 'StashDeleteCommand');
|
||||||
|
return window.showErrorMessage(`Unable to delete stash. See output channel for more details`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,9 @@ import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './comm
|
|||||||
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
|
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
|
||||||
import { ShowBlameCommand, ToggleBlameCommand } from './commands';
|
import { ShowBlameCommand, ToggleBlameCommand } from './commands';
|
||||||
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
|
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
|
||||||
import { ShowLastQuickPickCommand, ShowQuickBranchHistoryCommand, ShowQuickCurrentBranchHistoryCommand, ShowQuickCommitDetailsCommand, ShowQuickCommitFileDetailsCommand, ShowQuickFileHistoryCommand, ShowQuickRepoStatusCommand} from './commands';
|
import { ShowLastQuickPickCommand, ShowQuickBranchHistoryCommand, ShowQuickCurrentBranchHistoryCommand, ShowQuickCommitDetailsCommand, ShowQuickCommitFileDetailsCommand, ShowQuickFileHistoryCommand } from './commands';
|
||||||
|
import { ShowQuickRepoStatusCommand, ShowQuickStashListCommand } from './commands';
|
||||||
|
import { StashApplyCommand, StashDeleteCommand } from './commands';
|
||||||
import { ToggleCodeLensCommand } from './commands';
|
import { ToggleCodeLensCommand } from './commands';
|
||||||
import { Keyboard } from './commands';
|
import { Keyboard } from './commands';
|
||||||
import { IConfig } from './configuration';
|
import { IConfig } from './configuration';
|
||||||
@@ -115,6 +117,9 @@ export async function activate(context: ExtensionContext) {
|
|||||||
context.subscriptions.push(new ShowQuickCommitFileDetailsCommand(git));
|
context.subscriptions.push(new ShowQuickCommitFileDetailsCommand(git));
|
||||||
context.subscriptions.push(new ShowQuickFileHistoryCommand(git));
|
context.subscriptions.push(new ShowQuickFileHistoryCommand(git));
|
||||||
context.subscriptions.push(new ShowQuickRepoStatusCommand(git));
|
context.subscriptions.push(new ShowQuickRepoStatusCommand(git));
|
||||||
|
context.subscriptions.push(new ShowQuickStashListCommand(git));
|
||||||
|
context.subscriptions.push(new StashApplyCommand(git));
|
||||||
|
context.subscriptions.push(new StashDeleteCommand(git));
|
||||||
context.subscriptions.push(new ToggleCodeLensCommand(git));
|
context.subscriptions.push(new ToggleCodeLensCommand(git));
|
||||||
|
|
||||||
Telemetry.trackEvent('initialized', Objects.flatten(config, 'config', true));
|
Telemetry.trackEvent('initialized', Objects.flatten(config, 'config', true));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as tmp from 'tmp';
|
|||||||
export * from './models/models';
|
export * from './models/models';
|
||||||
export * from './parsers/blameParser';
|
export * from './parsers/blameParser';
|
||||||
export * from './parsers/logParser';
|
export * from './parsers/logParser';
|
||||||
|
export * from './parsers/stashParser';
|
||||||
export * from './parsers/statusParser';
|
export * from './parsers/statusParser';
|
||||||
export * from './remotes/provider';
|
export * from './remotes/provider';
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ let git: IGit;
|
|||||||
|
|
||||||
// `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nparents %P%nsummary %B%nfilename ?`
|
// `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nparents %P%nsummary %B%nfilename ?`
|
||||||
const defaultLogParams = [`log`, `--name-status`, `--full-history`, `-M`, `--date=iso8601`, `--format=%H -%nauthor %an%nauthor-date %ai%nparents %P%nsummary %B%nfilename ?`];
|
const defaultLogParams = [`log`, `--name-status`, `--full-history`, `-M`, `--date=iso8601`, `--format=%H -%nauthor %an%nauthor-date %ai%nparents %P%nsummary %B%nfilename ?`];
|
||||||
|
const defaultStashParams = [`stash`, `list`, `--name-status`, `--full-history`, `-M`, `--format=%H -%nauthor-date %ai%nreflog-selector %gd%nsummary %B%nfilename ?`];
|
||||||
|
|
||||||
async function gitCommand(cwd: string, ...args: any[]) {
|
async function gitCommand(cwd: string, ...args: any[]) {
|
||||||
try {
|
try {
|
||||||
@@ -163,14 +165,6 @@ export class Git {
|
|||||||
return gitCommand(repoPath, ...params);
|
return gitCommand(repoPath, ...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static show(repoPath: string, fileName: string, branchOrSha: string) {
|
|
||||||
const [file, root] = Git.splitPath(fileName, repoPath);
|
|
||||||
branchOrSha = branchOrSha.replace('^', '');
|
|
||||||
|
|
||||||
if (Git.isUncommitted(branchOrSha)) return Promise.reject(new Error(`sha=${branchOrSha} is uncommitted`));
|
|
||||||
return gitCommand(root, 'show', `${branchOrSha}:./${file}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static log(repoPath: string, sha?: string, maxCount?: number, reverse: boolean = false) {
|
static log(repoPath: string, sha?: string, maxCount?: number, reverse: boolean = false) {
|
||||||
const params = [...defaultLogParams, `-m`];
|
const params = [...defaultLogParams, `-m`];
|
||||||
if (maxCount && !reverse) {
|
if (maxCount && !reverse) {
|
||||||
@@ -227,26 +221,44 @@ export class Git {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static remote(repoPath: string): Promise<string> {
|
static remote(repoPath: string): Promise<string> {
|
||||||
const params = ['remote', '-v'];
|
return gitCommand(repoPath, 'remote', '-v');
|
||||||
return gitCommand(repoPath, ...params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static remote_url(repoPath: string, remote: string): Promise<string> {
|
static remote_url(repoPath: string, remote: string): Promise<string> {
|
||||||
const params = ['remote', 'get-url', remote];
|
return gitCommand(repoPath, 'remote', 'get-url', remote);
|
||||||
return gitCommand(repoPath, ...params);
|
}
|
||||||
|
|
||||||
|
static show(repoPath: string, fileName: string, branchOrSha: string) {
|
||||||
|
const [file, root] = Git.splitPath(fileName, repoPath);
|
||||||
|
branchOrSha = branchOrSha.replace('^', '');
|
||||||
|
|
||||||
|
if (Git.isUncommitted(branchOrSha)) return Promise.reject(new Error(`sha=${branchOrSha} is uncommitted`));
|
||||||
|
return gitCommand(root, 'show', `${branchOrSha}:./${file}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
static stash_apply(repoPath: string, stashName: string, deleteAfter: boolean) {
|
||||||
|
if (!stashName) return undefined;
|
||||||
|
return gitCommand(repoPath, 'stash', deleteAfter ? 'pop' : 'apply', stashName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static stash_delete(repoPath: string, stashName: string) {
|
||||||
|
if (!stashName) return undefined;
|
||||||
|
return gitCommand(repoPath, 'stash', 'drop', stashName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static stash_list(repoPath: string) {
|
||||||
|
return gitCommand(repoPath, ...defaultStashParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
static status(repoPath: string, porcelainVersion: number = 1): Promise<string> {
|
static status(repoPath: string, porcelainVersion: number = 1): Promise<string> {
|
||||||
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
||||||
const params = ['status', porcelain, '--branch'];
|
return gitCommand(repoPath, 'status', porcelain, '--branch');
|
||||||
return gitCommand(repoPath, ...params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static status_file(repoPath: string, fileName: string, porcelainVersion: number = 1): Promise<string> {
|
static status_file(repoPath: string, fileName: string, porcelainVersion: number = 1): Promise<string> {
|
||||||
const [file, root] = Git.splitPath(fileName, repoPath);
|
const [file, root] = Git.splitPath(fileName, repoPath);
|
||||||
|
|
||||||
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
||||||
const params = ['status', porcelain, file];
|
return gitCommand(root, 'status', porcelain, file);
|
||||||
return gitCommand(root, ...params);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ export interface IGitCommitLine {
|
|||||||
code?: string;
|
code?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GitCommitType = 'blame' | 'file' | 'repo';
|
export type GitCommitType = 'blame' | 'file' | 'repo' | 'stash';
|
||||||
|
|
||||||
export class GitCommit implements IGitCommit {
|
export class GitCommit implements IGitCommit {
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,6 @@ export * from './commit';
|
|||||||
export * from './log';
|
export * from './log';
|
||||||
export * from './logCommit';
|
export * from './logCommit';
|
||||||
export * from './remote';
|
export * from './remote';
|
||||||
|
export * from './stash';
|
||||||
|
export * from './stashCommit';
|
||||||
export * from './status';
|
export * from './status';
|
||||||
7
src/git/models/stash.ts
Normal file
7
src/git/models/stash.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
'use strict';
|
||||||
|
import { GitStashCommit } from './stashCommit';
|
||||||
|
|
||||||
|
export interface IGitStash {
|
||||||
|
repoPath: string;
|
||||||
|
commits: Map<string, GitStashCommit>;
|
||||||
|
}
|
||||||
24
src/git/models/stashCommit.ts
Normal file
24
src/git/models/stashCommit.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
import { IGitCommitLine } from './commit';
|
||||||
|
import { GitLogCommit } from './logCommit';
|
||||||
|
import { IGitStatusFile, GitStatusFileStatus } from './status';
|
||||||
|
|
||||||
|
export class GitStashCommit extends GitLogCommit {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public stashName: string,
|
||||||
|
repoPath: string,
|
||||||
|
sha: string,
|
||||||
|
fileName: string,
|
||||||
|
date: Date,
|
||||||
|
message: string,
|
||||||
|
status?: GitStatusFileStatus,
|
||||||
|
fileStatuses?: IGitStatusFile[],
|
||||||
|
lines?: IGitCommitLine[],
|
||||||
|
originalFileName?: string,
|
||||||
|
previousSha?: string,
|
||||||
|
previousFileName?: string
|
||||||
|
) {
|
||||||
|
super('stash', repoPath, sha, fileName, undefined, date, message, status, fileStatuses, lines, originalFileName, previousSha, previousFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
147
src/git/parsers/stashParser.ts
Normal file
147
src/git/parsers/stashParser.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Git, GitStashCommit, GitStatusFileStatus, IGitStash, IGitStatusFile } from './../git';
|
||||||
|
// import { Logger } from '../../logger';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
interface IStashEntry {
|
||||||
|
sha: string;
|
||||||
|
date?: string;
|
||||||
|
fileNames?: string;
|
||||||
|
fileStatuses?: IGitStatusFile[];
|
||||||
|
summary?: string;
|
||||||
|
stashName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GitStashParser {
|
||||||
|
|
||||||
|
private static _parseEntries(data: string): IStashEntry[] {
|
||||||
|
if (!data) return undefined;
|
||||||
|
|
||||||
|
const lines = data.split('\n');
|
||||||
|
if (!lines.length) return undefined;
|
||||||
|
|
||||||
|
const entries: IStashEntry[] = [];
|
||||||
|
|
||||||
|
let entry: IStashEntry;
|
||||||
|
let position = -1;
|
||||||
|
while (++position < lines.length) {
|
||||||
|
let lineParts = lines[position].split(' ');
|
||||||
|
if (lineParts.length < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
if (!Git.shaRegex.test(lineParts[0])) continue;
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
sha: lineParts[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lineParts[0]) {
|
||||||
|
case 'author-date':
|
||||||
|
entry.date = `${lineParts[1]}T${lineParts[2]}${lineParts[3]}`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'summary':
|
||||||
|
entry.summary = lineParts.slice(1).join(' ').trim();
|
||||||
|
while (++position < lines.length) {
|
||||||
|
const next = lines[position];
|
||||||
|
if (!next) break;
|
||||||
|
if (next === 'filename ?') {
|
||||||
|
position--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.summary += `\n${lines[position]}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'reflog-selector':
|
||||||
|
entry.stashName = lineParts.slice(1).join(' ').trim();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'filename':
|
||||||
|
const nextLine = lines[position + 1];
|
||||||
|
// If the next line isn't blank, make sure it isn't starting a new commit
|
||||||
|
if (nextLine && Git.shaRegex.test(nextLine)) continue;
|
||||||
|
|
||||||
|
position++;
|
||||||
|
|
||||||
|
while (++position < lines.length) {
|
||||||
|
const line = lines[position];
|
||||||
|
lineParts = line.split(' ');
|
||||||
|
|
||||||
|
if (Git.shaRegex.test(lineParts[0])) {
|
||||||
|
position--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.fileStatuses == null) {
|
||||||
|
entry.fileStatuses = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = {
|
||||||
|
status: line[0] as GitStatusFileStatus,
|
||||||
|
fileName: line.substring(1),
|
||||||
|
originalFileName: undefined as string
|
||||||
|
} as IGitStatusFile;
|
||||||
|
this._parseFileName(status);
|
||||||
|
|
||||||
|
entry.fileStatuses.push(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.fileStatuses) {
|
||||||
|
entry.fileNames = entry.fileStatuses.filter(_ => !!_.fileName).map(_ => _.fileName).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.push(entry);
|
||||||
|
entry = undefined;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parse(data: string, repoPath: string): IGitStash {
|
||||||
|
const entries = this._parseEntries(data);
|
||||||
|
if (!entries) return undefined;
|
||||||
|
|
||||||
|
const commits: Map<string, GitStashCommit> = new Map();
|
||||||
|
|
||||||
|
for (let i = 0, len = entries.length; i < len; i++) {
|
||||||
|
const entry = entries[i];
|
||||||
|
|
||||||
|
let commit = commits.get(entry.sha);
|
||||||
|
if (!commit) {
|
||||||
|
commit = new GitStashCommit(entry.stashName, repoPath, entry.sha, entry.fileNames, moment(entry.date).toDate(), entry.summary, undefined, entry.fileStatuses);
|
||||||
|
commits.set(entry.sha, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
repoPath: repoPath,
|
||||||
|
commits: commits
|
||||||
|
} as IGitStash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) {
|
||||||
|
const index = entry.fileName.indexOf('\t') + 1;
|
||||||
|
if (index) {
|
||||||
|
const next = entry.fileName.indexOf('\t', index) + 1;
|
||||||
|
if (next) {
|
||||||
|
entry.originalFileName = entry.fileName.substring(index, next - 1);
|
||||||
|
entry.fileName = entry.fileName.substring(next);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entry.fileName = entry.fileName.substring(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import { Disposable, Event, EventEmitter, ExtensionContext, FileSystemWatcher, l
|
|||||||
import { CommandContext, setCommandContext } from './commands';
|
import { CommandContext, setCommandContext } from './commands';
|
||||||
import { CodeLensVisibility, IConfig } from './configuration';
|
import { CodeLensVisibility, IConfig } from './configuration';
|
||||||
import { DocumentSchemes } from './constants';
|
import { DocumentSchemes } from './constants';
|
||||||
import { Git, GitBlameParser, GitBranch, GitCommit, GitLogCommit, GitLogParser, GitRemote, GitStatusFile, GitStatusParser, IGitAuthor, IGitBlame, IGitBlameLine, IGitBlameLines, IGitLog, IGitStatus } from './git/git';
|
import { Git, GitBlameParser, GitBranch, GitCommit, GitLogCommit, GitLogParser, GitRemote, GitStashParser, GitStatusFile, GitStatusParser, IGitAuthor, IGitBlame, IGitBlameLine, IGitBlameLines, IGitLog, IGitStash, IGitStatus } from './git/git';
|
||||||
import { IGitUriData, GitUri } from './git/gitUri';
|
import { IGitUriData, GitUri } from './git/gitUri';
|
||||||
import { GitCodeLensProvider } from './gitCodeLensProvider';
|
import { GitCodeLensProvider } from './gitCodeLensProvider';
|
||||||
import { Logger } from './logger';
|
import { Logger } from './logger';
|
||||||
@@ -667,6 +667,13 @@ export class GitService extends Disposable {
|
|||||||
return (await this.getRepoPathFromFile(gitUri.fsPath)) || fallbackRepoPath;
|
return (await this.getRepoPathFromFile(gitUri.fsPath)) || fallbackRepoPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getStashList(repoPath: string): Promise<IGitStash> {
|
||||||
|
Logger.log(`getStash('${repoPath}')`);
|
||||||
|
|
||||||
|
const data = await Git.stash_list(repoPath);
|
||||||
|
return GitStashParser.parse(data, repoPath);
|
||||||
|
}
|
||||||
|
|
||||||
async getStatusForFile(repoPath: string, fileName: string): Promise<GitStatusFile> {
|
async getStatusForFile(repoPath: string, fileName: string): Promise<GitStatusFile> {
|
||||||
Logger.log(`getStatusForFile('${repoPath}', '${fileName}')`);
|
Logger.log(`getStatusForFile('${repoPath}', '${fileName}')`);
|
||||||
|
|
||||||
@@ -738,6 +745,18 @@ export class GitService extends Disposable {
|
|||||||
return Git.difftool_dirDiff(repoPath, sha1, sha2);
|
return Git.difftool_dirDiff(repoPath, sha1, sha2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stashApply(repoPath: string, stashName: string, deleteAfter: boolean = false) {
|
||||||
|
Logger.log(`stashApply('${repoPath}', ${stashName}, ${deleteAfter})`);
|
||||||
|
|
||||||
|
return Git.stash_apply(repoPath, stashName, deleteAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
stashDelete(repoPath: string, stashName: string) {
|
||||||
|
Logger.log(`stashDelete('${repoPath}', ${stashName}})`);
|
||||||
|
|
||||||
|
return Git.stash_delete(repoPath, stashName);
|
||||||
|
}
|
||||||
|
|
||||||
toggleCodeLens(editor: TextEditor) {
|
toggleCodeLens(editor: TextEditor) {
|
||||||
if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
|
if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
|
||||||
(!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled)) return;
|
(!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled)) return;
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ export * from './quickPicks/commitFileDetails';
|
|||||||
export * from './quickPicks/branchHistory';
|
export * from './quickPicks/branchHistory';
|
||||||
export * from './quickPicks/fileHistory';
|
export * from './quickPicks/fileHistory';
|
||||||
export * from './quickPicks/remotes';
|
export * from './quickPicks/remotes';
|
||||||
export * from './quickPicks/repoStatus';
|
export * from './quickPicks/repoStatus';
|
||||||
|
export * from './quickPicks/stashList';
|
||||||
@@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
|
|||||||
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
|
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
|
||||||
import { Commands, Keyboard, KeyNoopCommand } from '../commands';
|
import { Commands, Keyboard, KeyNoopCommand } from '../commands';
|
||||||
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './common';
|
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './common';
|
||||||
import { getGitStatusIcon, Git, GitCommit, GitLogCommit, GitService, GitStatusFileStatus, GitUri, IGitLog, IGitStatusFile } from '../gitService';
|
import { getGitStatusIcon, Git, GitCommit, GitLogCommit, GitService, GitStashCommit, GitStatusFileStatus, GitUri, IGitLog, IGitStatusFile } from '../gitService';
|
||||||
import { OpenRemotesCommandQuickPickItem } from './remotes';
|
import { OpenRemotesCommandQuickPickItem } from './remotes';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@@ -72,27 +72,44 @@ export class CommitDetailsQuickPick {
|
|||||||
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, repoLog?: IGitLog): Promise<CommitWithFileStatusQuickPickItem | CommandQuickPickItem | undefined> {
|
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, repoLog?: IGitLog): Promise<CommitWithFileStatusQuickPickItem | CommandQuickPickItem | undefined> {
|
||||||
const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map(fs => new CommitWithFileStatusQuickPickItem(commit, fs));
|
const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map(fs => new CommitWithFileStatusQuickPickItem(commit, fs));
|
||||||
|
|
||||||
|
const stash = commit.type === 'stash';
|
||||||
|
const type = stash ? 'Stash' : 'Commit';
|
||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
||||||
|
if (stash && git.config.insiders) {
|
||||||
|
items.splice(index++, 0, new CommandQuickPickItem({
|
||||||
|
label: `$(repo-forked) Apply Stash`,
|
||||||
|
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
||||||
|
}, Commands.StashApply, [commit as GitStashCommit, true, false]));
|
||||||
|
|
||||||
|
items.splice(index++, 0, new CommandQuickPickItem({
|
||||||
|
label: `$(x) Delete Stash`,
|
||||||
|
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
||||||
|
}, Commands.StashDelete, [commit as GitStashCommit, true]));
|
||||||
|
}
|
||||||
|
|
||||||
items.splice(index++, 0, new CommandQuickPickItem({
|
items.splice(index++, 0, new CommandQuickPickItem({
|
||||||
label: `$(clippy) Copy Commit Sha to Clipboard`,
|
label: `$(clippy) Copy ${type} Sha to Clipboard`,
|
||||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
|
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
|
||||||
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
|
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
|
||||||
|
|
||||||
items.splice(index++, 0, new CommandQuickPickItem({
|
items.splice(index++, 0, new CommandQuickPickItem({
|
||||||
label: `$(clippy) Copy Commit Message to Clipboard`,
|
label: `$(clippy) Copy ${type} Message to Clipboard`,
|
||||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
||||||
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
|
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
|
||||||
|
|
||||||
const remotes = Arrays.uniqueBy(await git.getRemotes(git.repoPath), _ => _.url, _ => !!_.provider);
|
if (!stash) {
|
||||||
if (remotes.length) {
|
const remotes = Arrays.uniqueBy(await git.getRemotes(git.repoPath), _ => _.url, _ => !!_.provider);
|
||||||
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'commit', commit.sha, currentCommand));
|
if (remotes.length) {
|
||||||
}
|
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'commit', commit.sha, currentCommand));
|
||||||
|
}
|
||||||
|
|
||||||
items.splice(index++, 0, new CommandQuickPickItem({
|
items.splice(index++, 0, new CommandQuickPickItem({
|
||||||
label: `$(git-compare) Directory Compare with Previous Commit`,
|
label: `$(git-compare) Directory Compare with Previous Commit`,
|
||||||
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousShortSha || `${commit.shortSha}^`} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.shortSha}`
|
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousShortSha || `${commit.shortSha}^`} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.shortSha}`
|
||||||
}, Commands.DiffDirectory, [commit.uri, commit.previousSha || `${commit.sha}^`, commit.sha]));
|
}, Commands.DiffDirectory, [commit.uri, commit.previousSha || `${commit.sha}^`, commit.sha]));
|
||||||
|
}
|
||||||
|
|
||||||
items.splice(index++, 0, new CommandQuickPickItem({
|
items.splice(index++, 0, new CommandQuickPickItem({
|
||||||
label: `$(git-compare) Directory Compare with Working Tree`,
|
label: `$(git-compare) Directory Compare with Working Tree`,
|
||||||
@@ -117,50 +134,52 @@ export class CommitDetailsQuickPick {
|
|||||||
|
|
||||||
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
||||||
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
||||||
// If we have the full history, we are good
|
if (!stash) {
|
||||||
if (repoLog && !repoLog.truncated && !repoLog.sha) {
|
// If we have the full history, we are good
|
||||||
previousCommand = commit.previousSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, repoLog]);
|
if (repoLog && !repoLog.truncated && !repoLog.sha) {
|
||||||
nextCommand = commit.nextSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, repoLog]);
|
previousCommand = commit.previousSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, repoLog]);
|
||||||
}
|
nextCommand = commit.nextSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, repoLog]);
|
||||||
else {
|
}
|
||||||
previousCommand = async () => {
|
else {
|
||||||
let log = repoLog;
|
previousCommand = async () => {
|
||||||
let c = log && log.commits.get(commit.sha);
|
let log = repoLog;
|
||||||
|
let c = log && log.commits.get(commit.sha);
|
||||||
|
|
||||||
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
|
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
|
||||||
if (!c || !c.previousSha) {
|
if (!c || !c.previousSha) {
|
||||||
log = await git.getLogForRepo(commit.repoPath, commit.sha, git.config.advanced.maxQuickHistory);
|
log = await git.getLogForRepo(commit.repoPath, commit.sha, git.config.advanced.maxQuickHistory);
|
||||||
c = log && log.commits.get(commit.sha);
|
c = log && log.commits.get(commit.sha);
|
||||||
|
|
||||||
if (c) {
|
if (c) {
|
||||||
// Copy over next info, since it is trustworthy at this point
|
// Copy over next info, since it is trustworthy at this point
|
||||||
c.nextSha = commit.nextSha;
|
c.nextSha = commit.nextSha;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!c || !c.previousSha) return KeyNoopCommand;
|
||||||
if (!c || !c.previousSha) return KeyNoopCommand;
|
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
|
||||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
nextCommand = async () => {
|
nextCommand = async () => {
|
||||||
let log = repoLog;
|
let log = repoLog;
|
||||||
let c = log && log.commits.get(commit.sha);
|
let c = log && log.commits.get(commit.sha);
|
||||||
|
|
||||||
// If we can't find the commit or the next commit isn't available (since it isn't trustworthy)
|
// If we can't find the commit or the next commit isn't available (since it isn't trustworthy)
|
||||||
if (!c || !c.nextSha) {
|
if (!c || !c.nextSha) {
|
||||||
log = undefined;
|
log = undefined;
|
||||||
c = undefined;
|
c = undefined;
|
||||||
|
|
||||||
// Try to find the next commit
|
// Try to find the next commit
|
||||||
const nextLog = await git.getLogForRepo(commit.repoPath, commit.sha, 1, true);
|
const nextLog = await git.getLogForRepo(commit.repoPath, commit.sha, 1, true);
|
||||||
const next = nextLog && Iterables.first(nextLog.commits.values());
|
const next = nextLog && Iterables.first(nextLog.commits.values());
|
||||||
if (next && next.sha !== commit.sha) {
|
if (next && next.sha !== commit.sha) {
|
||||||
c = commit;
|
c = commit;
|
||||||
c.nextSha = next.sha;
|
c.nextSha = next.sha;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!c || !c.nextSha) return KeyNoopCommand;
|
||||||
if (!c || !c.nextSha) return KeyNoopCommand;
|
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
|
||||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const scope = await Keyboard.instance.beginScope({
|
const scope = await Keyboard.instance.beginScope({
|
||||||
@@ -172,7 +191,7 @@ export class CommitDetailsQuickPick {
|
|||||||
const pick = await window.showQuickPick(items, {
|
const pick = await window.showQuickPick(items, {
|
||||||
matchOnDescription: true,
|
matchOnDescription: true,
|
||||||
matchOnDetail: true,
|
matchOnDetail: true,
|
||||||
placeHolder: `${commit.shortSha} \u00a0\u2022\u00a0 ${commit.author}, ${moment(commit.date).fromNow()} \u00a0\u2022\u00a0 ${commit.message}`,
|
placeHolder: `${commit.shortSha} \u00a0\u2022\u00a0 ${commit.author ? `${commit.author}, ` : ''}${moment(commit.date).fromNow()} \u00a0\u2022\u00a0 ${commit.message}`,
|
||||||
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
|
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
|
||||||
onDidSelectItem: (item: QuickPickItem) => {
|
onDidSelectItem: (item: QuickPickItem) => {
|
||||||
scope.setKeyCommand('right', item);
|
scope.setKeyCommand('right', item);
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export class CommitFileDetailsQuickPick {
|
|||||||
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: IGitLog): Promise<CommandQuickPickItem | undefined> {
|
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: IGitLog): Promise<CommandQuickPickItem | undefined> {
|
||||||
const items: CommandQuickPickItem[] = [];
|
const items: CommandQuickPickItem[] = [];
|
||||||
|
|
||||||
|
const stash = commit.type === 'stash';
|
||||||
|
|
||||||
const workingName = (commit.workingFileName && path.basename(commit.workingFileName)) || path.basename(commit.fileName);
|
const workingName = (commit.workingFileName && path.basename(commit.workingFileName)) || path.basename(commit.fileName);
|
||||||
|
|
||||||
const isUncommitted = commit.isUncommitted;
|
const isUncommitted = commit.isUncommitted;
|
||||||
@@ -44,16 +46,18 @@ export class CommitFileDetailsQuickPick {
|
|||||||
if (!commit) return undefined;
|
if (!commit) return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(new CommandQuickPickItem({
|
if (!stash) {
|
||||||
label: `$(git-commit) Show Commit Details`,
|
|
||||||
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.shortSha}`
|
|
||||||
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), commit.sha, commit, currentCommand]));
|
|
||||||
|
|
||||||
if (commit.previousSha) {
|
|
||||||
items.push(new CommandQuickPickItem({
|
items.push(new CommandQuickPickItem({
|
||||||
label: `$(git-compare) Compare with Previous Commit`,
|
label: `$(git-commit) Show Commit Details`,
|
||||||
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousShortSha} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.shortSha}`
|
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.shortSha}`
|
||||||
}, Commands.DiffWithPrevious, [commit.uri, commit]));
|
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), commit.sha, commit, currentCommand]));
|
||||||
|
|
||||||
|
if (commit.previousSha) {
|
||||||
|
items.push(new CommandQuickPickItem({
|
||||||
|
label: `$(git-compare) Compare with Previous Commit`,
|
||||||
|
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousShortSha} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.shortSha}`
|
||||||
|
}, Commands.DiffWithPrevious, [commit.uri, commit]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commit.workingFileName) {
|
if (commit.workingFileName) {
|
||||||
@@ -63,15 +67,17 @@ export class CommitFileDetailsQuickPick {
|
|||||||
}, Commands.DiffWithWorking, [Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), commit]));
|
}, Commands.DiffWithWorking, [Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), commit]));
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(new CommandQuickPickItem({
|
if (!stash) {
|
||||||
label: `$(clippy) Copy Commit Sha to Clipboard`,
|
items.push(new CommandQuickPickItem({
|
||||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
|
label: `$(clippy) Copy Commit Sha to Clipboard`,
|
||||||
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
|
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
|
||||||
|
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
|
||||||
|
|
||||||
items.push(new CommandQuickPickItem({
|
items.push(new CommandQuickPickItem({
|
||||||
label: `$(clippy) Copy Commit Message to Clipboard`,
|
label: `$(clippy) Copy Commit Message to Clipboard`,
|
||||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
||||||
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
|
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
|
||||||
|
}
|
||||||
|
|
||||||
items.push(new OpenCommitFileCommandQuickPickItem(commit));
|
items.push(new OpenCommitFileCommandQuickPickItem(commit));
|
||||||
if (commit.workingFileName) {
|
if (commit.workingFileName) {
|
||||||
@@ -80,7 +86,9 @@ export class CommitFileDetailsQuickPick {
|
|||||||
|
|
||||||
const remotes = Arrays.uniqueBy(await git.getRemotes(git.repoPath), _ => _.url, _ => !!_.provider);
|
const remotes = Arrays.uniqueBy(await git.getRemotes(git.repoPath), _ => _.url, _ => !!_.provider);
|
||||||
if (remotes.length) {
|
if (remotes.length) {
|
||||||
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'file', commit.fileName, undefined, commit.sha, currentCommand));
|
if (!stash) {
|
||||||
|
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'file', commit.fileName, undefined, commit.sha, currentCommand));
|
||||||
|
}
|
||||||
if (commit.workingFileName) {
|
if (commit.workingFileName) {
|
||||||
const branch = await git.getBranch(commit.repoPath || git.repoPath);
|
const branch = await git.getBranch(commit.repoPath || git.repoPath);
|
||||||
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'working-file', commit.workingFileName, branch.name, undefined, currentCommand));
|
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'working-file', commit.workingFileName, branch.name, undefined, currentCommand));
|
||||||
@@ -105,55 +113,57 @@ export class CommitFileDetailsQuickPick {
|
|||||||
|
|
||||||
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
||||||
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
||||||
// If we have the full history, we are good
|
if (!stash) {
|
||||||
if (fileLog && !fileLog.truncated && !fileLog.sha) {
|
// If we have the full history, we are good
|
||||||
previousCommand = commit.previousSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, fileLog]);
|
if (fileLog && !fileLog.truncated && !fileLog.sha) {
|
||||||
nextCommand = commit.nextSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, fileLog]);
|
previousCommand = commit.previousSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, fileLog]);
|
||||||
}
|
nextCommand = commit.nextSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, fileLog]);
|
||||||
else {
|
}
|
||||||
previousCommand = async () => {
|
else {
|
||||||
let log = fileLog;
|
previousCommand = async () => {
|
||||||
let c = log && log.commits.get(commit.sha);
|
let log = fileLog;
|
||||||
|
let c = log && log.commits.get(commit.sha);
|
||||||
|
|
||||||
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
|
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
|
||||||
if (!c || !c.previousSha) {
|
if (!c || !c.previousSha) {
|
||||||
log = await git.getLogForFile(commit.repoPath, uri.fsPath, commit.sha, git.config.advanced.maxQuickHistory);
|
log = await git.getLogForFile(commit.repoPath, uri.fsPath, commit.sha, git.config.advanced.maxQuickHistory);
|
||||||
c = log && log.commits.get(commit.sha);
|
c = log && log.commits.get(commit.sha);
|
||||||
// Since we exclude merge commits in file log, just grab the first returned commit
|
// Since we exclude merge commits in file log, just grab the first returned commit
|
||||||
if (!c && commit.isMerge) {
|
if (!c && commit.isMerge) {
|
||||||
c = Iterables.first(log.commits.values());
|
c = Iterables.first(log.commits.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c) {
|
||||||
|
// Copy over next info, since it is trustworthy at this point
|
||||||
|
c.nextSha = commit.nextSha;
|
||||||
|
c.nextFileName = commit.nextFileName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!c || !c.previousSha) return KeyNoopCommand;
|
||||||
|
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
|
||||||
|
};
|
||||||
|
|
||||||
if (c) {
|
nextCommand = async () => {
|
||||||
// Copy over next info, since it is trustworthy at this point
|
let log = fileLog;
|
||||||
c.nextSha = commit.nextSha;
|
let c = log && log.commits.get(commit.sha);
|
||||||
c.nextFileName = commit.nextFileName;
|
|
||||||
|
// If we can't find the commit or the next commit isn't available (since it isn't trustworthy)
|
||||||
|
if (!c || !c.nextSha) {
|
||||||
|
log = undefined;
|
||||||
|
c = undefined;
|
||||||
|
|
||||||
|
// Try to find the next commit
|
||||||
|
const next = await git.findNextCommit(commit.repoPath, uri.fsPath, commit.sha);
|
||||||
|
if (next && next.sha !== commit.sha) {
|
||||||
|
c = commit;
|
||||||
|
c.nextSha = next.sha;
|
||||||
|
c.nextFileName = next.originalFileName || next.fileName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!c || !c.nextSha) return KeyNoopCommand;
|
||||||
if (!c || !c.previousSha) return KeyNoopCommand;
|
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
|
||||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
nextCommand = async () => {
|
|
||||||
let log = fileLog;
|
|
||||||
let c = log && log.commits.get(commit.sha);
|
|
||||||
|
|
||||||
// If we can't find the commit or the next commit isn't available (since it isn't trustworthy)
|
|
||||||
if (!c || !c.nextSha) {
|
|
||||||
log = undefined;
|
|
||||||
c = undefined;
|
|
||||||
|
|
||||||
// Try to find the next commit
|
|
||||||
const next = await git.findNextCommit(commit.repoPath, uri.fsPath, commit.sha);
|
|
||||||
if (next && next.sha !== commit.sha) {
|
|
||||||
c = commit;
|
|
||||||
c.nextSha = next.sha;
|
|
||||||
c.nextFileName = next.originalFileName || next.fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!c || !c.nextSha) return KeyNoopCommand;
|
|
||||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const scope = await Keyboard.instance.beginScope({
|
const scope = await Keyboard.instance.beginScope({
|
||||||
|
|||||||
@@ -117,7 +117,12 @@ export class CommitQuickPickItem implements QuickPickItem {
|
|||||||
detail: string;
|
detail: string;
|
||||||
|
|
||||||
constructor(public commit: GitCommit, descriptionSuffix: string = '') {
|
constructor(public commit: GitCommit, descriptionSuffix: string = '') {
|
||||||
this.label = `${commit.author}, ${moment(commit.date).fromNow()}`;
|
if (commit.author) {
|
||||||
|
this.label = `${commit.author}, ${moment(commit.date).fromNow()}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.label = `${moment(commit.date).fromNow()}`;
|
||||||
|
}
|
||||||
this.description = `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.shortSha}${descriptionSuffix}`;
|
this.description = `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.shortSha}${descriptionSuffix}`;
|
||||||
this.detail = commit.message;
|
this.detail = commit.message;
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/quickPicks/stashList.ts
Normal file
32
src/quickPicks/stashList.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
'use strict';
|
||||||
|
import { Iterables } from '../system';
|
||||||
|
import { QuickPickOptions, window } from 'vscode';
|
||||||
|
import { Keyboard } from '../commands';
|
||||||
|
import { IGitStash } from '../gitService';
|
||||||
|
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut } from '../quickPicks';
|
||||||
|
|
||||||
|
export class StashListQuickPick {
|
||||||
|
|
||||||
|
static async show(stash: IGitStash, placeHolder?: string, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
|
||||||
|
const items = ((stash && Array.from(Iterables.map(stash.commits.values(), c => new CommitQuickPickItem(c, ` \u2014 ${c.fileNames}`)))) || []) as (CommitQuickPickItem | CommandQuickPickItem)[];
|
||||||
|
|
||||||
|
if (goBackCommand) {
|
||||||
|
items.splice(0, 0, goBackCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
const scope = await Keyboard.instance.beginScope({ left: goBackCommand });
|
||||||
|
|
||||||
|
const pick = await window.showQuickPick(items, {
|
||||||
|
matchOnDescription: true,
|
||||||
|
placeHolder: placeHolder || `stash list \u2014 search by message, filename, or sha`,
|
||||||
|
ignoreFocusOut: getQuickPickIgnoreFocusOut()
|
||||||
|
// onDidSelectItem: (item: QuickPickItem) => {
|
||||||
|
// scope.setKeyCommand('right', item);
|
||||||
|
// }
|
||||||
|
} as QuickPickOptions);
|
||||||
|
|
||||||
|
await scope.dispose();
|
||||||
|
|
||||||
|
return pick;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user