mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-15 17:25:33 -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",
|
||||
"category": "GitLens"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.showQuickStashList",
|
||||
"title": "Show Stashed Changes",
|
||||
"category": "GitLens"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.copyShaToClipboard",
|
||||
"title": "Copy Commit Sha to Clipboard",
|
||||
@@ -487,6 +492,11 @@
|
||||
"command": "gitlens.openFileInRemote",
|
||||
"title": "Open File in Remote",
|
||||
"category": "GitLens"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.stashApply",
|
||||
"title": "Apply Stashed Changes",
|
||||
"category": "GitLens"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -567,6 +577,10 @@
|
||||
"command": "gitlens.showQuickRepoStatus",
|
||||
"when": "gitlens:enabled"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.showQuickStashList",
|
||||
"when": "gitlens:enabled"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.copyShaToClipboard",
|
||||
"when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable"
|
||||
@@ -590,6 +604,10 @@
|
||||
{
|
||||
"command": "gitlens.openFileInRemote",
|
||||
"when": "editorTextFocus && gitlens:enabled && gitlens:hasRemotes"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.stashApply",
|
||||
"when": "gitlens:enabled && config.gitlens.insiders"
|
||||
}
|
||||
],
|
||||
"explorer/context": [
|
||||
|
||||
@@ -27,5 +27,8 @@ export * from './commands/showQuickFileHistory';
|
||||
export * from './commands/showQuickBranchHistory';
|
||||
export * from './commands/showQuickCurrentBranchHistory';
|
||||
export * from './commands/showQuickRepoStatus';
|
||||
export * from './commands/showQuickStashList';
|
||||
export * from './commands/stashApply';
|
||||
export * from './commands/stashDelete';
|
||||
export * from './commands/toggleBlame';
|
||||
export * from './commands/toggleCodeLens';
|
||||
@@ -10,7 +10,8 @@ export type Commands = 'gitlens.closeUnchangedFiles' | 'gitlens.copyMessageToCli
|
||||
'gitlens.showLastQuickPick' | 'gitlens.showQuickBranchHistory' |
|
||||
'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' |
|
||||
'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' |
|
||||
'gitlens.showQuickRepoStatus' |
|
||||
'gitlens.showQuickRepoStatus' | 'gitlens.showQuickStashList' |
|
||||
'gitlens.stashApply' | 'gitlens.stashDelete' | 'gitlens.stashSave' |
|
||||
'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
|
||||
export const Commands = {
|
||||
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
|
||||
@@ -37,6 +38,9 @@ export const Commands = {
|
||||
ShowQuickBranchHistory: 'gitlens.showQuickBranchHistory' as Commands,
|
||||
ShowQuickCurrentBranchHistory: 'gitlens.showQuickRepoHistory' 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,
|
||||
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
|
||||
};
|
||||
|
||||
@@ -46,7 +46,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
|
||||
}
|
||||
|
||||
try {
|
||||
if (!commit || !(commit instanceof GitLogCommit) || commit.type !== 'repo') {
|
||||
if (!commit || (commit.type !== 'repo' && commit.type !== 'stash')) {
|
||||
if (repoLog) {
|
||||
commit = repoLog.commits.get(sha);
|
||||
// If we can't find the commit, kill the repoLog
|
||||
@@ -88,7 +88,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
|
||||
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) {
|
||||
Logger.error(ex, 'ShowQuickCommitDetailsCommand');
|
||||
|
||||
@@ -44,7 +44,7 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
|
||||
}
|
||||
|
||||
try {
|
||||
if (!commit || !(commit instanceof GitLogCommit) || commit.type !== 'file') {
|
||||
if (!commit || (commit.type !== 'file' && commit.type !== 'stash')) {
|
||||
if (fileLog) {
|
||||
commit = fileLog.commits.get(sha);
|
||||
// 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 { ShowBlameCommand, ToggleBlameCommand } 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 { Keyboard } from './commands';
|
||||
import { IConfig } from './configuration';
|
||||
@@ -115,6 +117,9 @@ export async function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(new ShowQuickCommitFileDetailsCommand(git));
|
||||
context.subscriptions.push(new ShowQuickFileHistoryCommand(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));
|
||||
|
||||
Telemetry.trackEvent('initialized', Objects.flatten(config, 'config', true));
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as tmp from 'tmp';
|
||||
export * from './models/models';
|
||||
export * from './parsers/blameParser';
|
||||
export * from './parsers/logParser';
|
||||
export * from './parsers/stashParser';
|
||||
export * from './parsers/statusParser';
|
||||
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 ?`
|
||||
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[]) {
|
||||
try {
|
||||
@@ -163,14 +165,6 @@ export class Git {
|
||||
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) {
|
||||
const params = [...defaultLogParams, `-m`];
|
||||
if (maxCount && !reverse) {
|
||||
@@ -227,26 +221,44 @@ export class Git {
|
||||
}
|
||||
|
||||
static remote(repoPath: string): Promise<string> {
|
||||
const params = ['remote', '-v'];
|
||||
return gitCommand(repoPath, ...params);
|
||||
return gitCommand(repoPath, 'remote', '-v');
|
||||
}
|
||||
|
||||
static remote_url(repoPath: string, remote: string): Promise<string> {
|
||||
const params = ['remote', 'get-url', remote];
|
||||
return gitCommand(repoPath, ...params);
|
||||
return gitCommand(repoPath, 'remote', 'get-url', remote);
|
||||
}
|
||||
|
||||
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> {
|
||||
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
||||
const params = ['status', porcelain, '--branch'];
|
||||
return gitCommand(repoPath, ...params);
|
||||
return gitCommand(repoPath, 'status', porcelain, '--branch');
|
||||
}
|
||||
|
||||
static status_file(repoPath: string, fileName: string, porcelainVersion: number = 1): Promise<string> {
|
||||
const [file, root] = Git.splitPath(fileName, repoPath);
|
||||
|
||||
const porcelain = porcelainVersion >= 2 ? `--porcelain=v${porcelainVersion}` : '--porcelain';
|
||||
const params = ['status', porcelain, file];
|
||||
return gitCommand(root, ...params);
|
||||
return gitCommand(root, 'status', porcelain, file);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export interface IGitCommitLine {
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export type GitCommitType = 'blame' | 'file' | 'repo';
|
||||
export type GitCommitType = 'blame' | 'file' | 'repo' | 'stash';
|
||||
|
||||
export class GitCommit implements IGitCommit {
|
||||
|
||||
|
||||
@@ -5,4 +5,6 @@ export * from './commit';
|
||||
export * from './log';
|
||||
export * from './logCommit';
|
||||
export * from './remote';
|
||||
export * from './stash';
|
||||
export * from './stashCommit';
|
||||
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 { CodeLensVisibility, IConfig } from './configuration';
|
||||
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 { GitCodeLensProvider } from './gitCodeLensProvider';
|
||||
import { Logger } from './logger';
|
||||
@@ -667,6 +667,13 @@ export class GitService extends Disposable {
|
||||
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> {
|
||||
Logger.log(`getStatusForFile('${repoPath}', '${fileName}')`);
|
||||
|
||||
@@ -738,6 +745,18 @@ export class GitService extends Disposable {
|
||||
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) {
|
||||
if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
|
||||
(!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/fileHistory';
|
||||
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 { Commands, Keyboard, KeyNoopCommand } from '../commands';
|
||||
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 * as moment from 'moment';
|
||||
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> {
|
||||
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;
|
||||
|
||||
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({
|
||||
label: `$(clippy) Copy Commit Sha to Clipboard`,
|
||||
label: `$(clippy) Copy ${type} Sha to Clipboard`,
|
||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
|
||||
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
|
||||
|
||||
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}`
|
||||
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
|
||||
|
||||
const remotes = Arrays.uniqueBy(await git.getRemotes(git.repoPath), _ => _.url, _ => !!_.provider);
|
||||
if (remotes.length) {
|
||||
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'commit', commit.sha, currentCommand));
|
||||
}
|
||||
if (!stash) {
|
||||
const remotes = Arrays.uniqueBy(await git.getRemotes(git.repoPath), _ => _.url, _ => !!_.provider);
|
||||
if (remotes.length) {
|
||||
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'commit', commit.sha, currentCommand));
|
||||
}
|
||||
|
||||
items.splice(index++, 0, new CommandQuickPickItem({
|
||||
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}`
|
||||
}, Commands.DiffDirectory, [commit.uri, commit.previousSha || `${commit.sha}^`, commit.sha]));
|
||||
items.splice(index++, 0, new CommandQuickPickItem({
|
||||
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}`
|
||||
}, Commands.DiffDirectory, [commit.uri, commit.previousSha || `${commit.sha}^`, commit.sha]));
|
||||
}
|
||||
|
||||
items.splice(index++, 0, new CommandQuickPickItem({
|
||||
label: `$(git-compare) Directory Compare with Working Tree`,
|
||||
@@ -117,50 +134,52 @@ export class CommitDetailsQuickPick {
|
||||
|
||||
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
||||
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
||||
// If we have the full history, we are good
|
||||
if (repoLog && !repoLog.truncated && !repoLog.sha) {
|
||||
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 () => {
|
||||
let log = repoLog;
|
||||
let c = log && log.commits.get(commit.sha);
|
||||
if (!stash) {
|
||||
// If we have the full history, we are good
|
||||
if (repoLog && !repoLog.truncated && !repoLog.sha) {
|
||||
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 () => {
|
||||
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 (!c || !c.previousSha) {
|
||||
log = await git.getLogForRepo(commit.repoPath, commit.sha, git.config.advanced.maxQuickHistory);
|
||||
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 (!c || !c.previousSha) {
|
||||
log = await git.getLogForRepo(commit.repoPath, commit.sha, git.config.advanced.maxQuickHistory);
|
||||
c = log && log.commits.get(commit.sha);
|
||||
|
||||
if (c) {
|
||||
// Copy over next info, since it is trustworthy at this point
|
||||
c.nextSha = commit.nextSha;
|
||||
if (c) {
|
||||
// Copy over next info, since it is trustworthy at this point
|
||||
c.nextSha = commit.nextSha;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!c || !c.previousSha) return KeyNoopCommand;
|
||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
|
||||
};
|
||||
if (!c || !c.previousSha) return KeyNoopCommand;
|
||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
|
||||
};
|
||||
|
||||
nextCommand = async () => {
|
||||
let log = repoLog;
|
||||
let c = log && log.commits.get(commit.sha);
|
||||
nextCommand = async () => {
|
||||
let log = repoLog;
|
||||
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;
|
||||
// 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 nextLog = await git.getLogForRepo(commit.repoPath, commit.sha, 1, true);
|
||||
const next = nextLog && Iterables.first(nextLog.commits.values());
|
||||
if (next && next.sha !== commit.sha) {
|
||||
c = commit;
|
||||
c.nextSha = next.sha;
|
||||
// Try to find the next commit
|
||||
const nextLog = await git.getLogForRepo(commit.repoPath, commit.sha, 1, true);
|
||||
const next = nextLog && Iterables.first(nextLog.commits.values());
|
||||
if (next && next.sha !== commit.sha) {
|
||||
c = commit;
|
||||
c.nextSha = next.sha;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!c || !c.nextSha) return KeyNoopCommand;
|
||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
|
||||
};
|
||||
if (!c || !c.nextSha) return KeyNoopCommand;
|
||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const scope = await Keyboard.instance.beginScope({
|
||||
@@ -172,7 +191,7 @@ export class CommitDetailsQuickPick {
|
||||
const pick = await window.showQuickPick(items, {
|
||||
matchOnDescription: 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(),
|
||||
onDidSelectItem: (item: QuickPickItem) => {
|
||||
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> {
|
||||
const items: CommandQuickPickItem[] = [];
|
||||
|
||||
const stash = commit.type === 'stash';
|
||||
|
||||
const workingName = (commit.workingFileName && path.basename(commit.workingFileName)) || path.basename(commit.fileName);
|
||||
|
||||
const isUncommitted = commit.isUncommitted;
|
||||
@@ -44,16 +46,18 @@ export class CommitFileDetailsQuickPick {
|
||||
if (!commit) return undefined;
|
||||
}
|
||||
|
||||
items.push(new CommandQuickPickItem({
|
||||
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) {
|
||||
if (!stash) {
|
||||
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]));
|
||||
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({
|
||||
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) {
|
||||
@@ -63,15 +67,17 @@ export class CommitFileDetailsQuickPick {
|
||||
}, Commands.DiffWithWorking, [Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), commit]));
|
||||
}
|
||||
|
||||
items.push(new CommandQuickPickItem({
|
||||
label: `$(clippy) Copy Commit Sha to Clipboard`,
|
||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
|
||||
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
|
||||
if (!stash) {
|
||||
items.push(new CommandQuickPickItem({
|
||||
label: `$(clippy) Copy Commit Sha to Clipboard`,
|
||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
|
||||
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
|
||||
|
||||
items.push(new CommandQuickPickItem({
|
||||
label: `$(clippy) Copy Commit Message to Clipboard`,
|
||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
||||
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
|
||||
items.push(new CommandQuickPickItem({
|
||||
label: `$(clippy) Copy Commit Message to Clipboard`,
|
||||
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
|
||||
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
|
||||
}
|
||||
|
||||
items.push(new OpenCommitFileCommandQuickPickItem(commit));
|
||||
if (commit.workingFileName) {
|
||||
@@ -80,7 +86,9 @@ export class CommitFileDetailsQuickPick {
|
||||
|
||||
const remotes = Arrays.uniqueBy(await git.getRemotes(git.repoPath), _ => _.url, _ => !!_.provider);
|
||||
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) {
|
||||
const branch = await git.getBranch(commit.repoPath || git.repoPath);
|
||||
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 nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
|
||||
// If we have the full history, we are good
|
||||
if (fileLog && !fileLog.truncated && !fileLog.sha) {
|
||||
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 () => {
|
||||
let log = fileLog;
|
||||
let c = log && log.commits.get(commit.sha);
|
||||
if (!stash) {
|
||||
// If we have the full history, we are good
|
||||
if (fileLog && !fileLog.truncated && !fileLog.sha) {
|
||||
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 () => {
|
||||
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 (!c || !c.previousSha) {
|
||||
log = await git.getLogForFile(commit.repoPath, uri.fsPath, commit.sha, git.config.advanced.maxQuickHistory);
|
||||
c = log && log.commits.get(commit.sha);
|
||||
// Since we exclude merge commits in file log, just grab the first returned commit
|
||||
if (!c && commit.isMerge) {
|
||||
c = Iterables.first(log.commits.values());
|
||||
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
|
||||
if (!c || !c.previousSha) {
|
||||
log = await git.getLogForFile(commit.repoPath, uri.fsPath, commit.sha, git.config.advanced.maxQuickHistory);
|
||||
c = log && log.commits.get(commit.sha);
|
||||
// Since we exclude merge commits in file log, just grab the first returned commit
|
||||
if (!c && commit.isMerge) {
|
||||
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) {
|
||||
// Copy over next info, since it is trustworthy at this point
|
||||
c.nextSha = commit.nextSha;
|
||||
c.nextFileName = commit.nextFileName;
|
||||
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.previousSha) return KeyNoopCommand;
|
||||
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]);
|
||||
};
|
||||
if (!c || !c.nextSha) return KeyNoopCommand;
|
||||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const scope = await Keyboard.instance.beginScope({
|
||||
|
||||
@@ -117,7 +117,12 @@ export class CommitQuickPickItem implements QuickPickItem {
|
||||
detail: 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.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