mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-13 17:23:11 -05:00
Add "external diff" command to SCM groups and files
This commit is contained in:
21
package.json
21
package.json
@@ -1068,6 +1068,11 @@
|
||||
"light": "images/light/icon-add.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "gitlens.externalDiff",
|
||||
"title": "Open Changes (with difftool)",
|
||||
"category": "GitLens"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.resetSuppressedWarnings",
|
||||
"title": "Reset Suppressed Warnings",
|
||||
@@ -1520,17 +1525,22 @@
|
||||
{
|
||||
"command": "gitlens.openChangedFiles",
|
||||
"when": "gitlens:enabled",
|
||||
"group": "1_gitlens@1"
|
||||
"group": "2_gitlens@1"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.closeUnchangedFiles",
|
||||
"when": "gitlens:enabled",
|
||||
"group": "1_gitlens@2"
|
||||
"group": "2_gitlens@2"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.externalDiff",
|
||||
"when": "gitlens:enabled",
|
||||
"group": "2_gitlens@3"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.stashSave",
|
||||
"when": "gitlens:enabled",
|
||||
"group": "2_gitlens@1"
|
||||
"group": "3_gitlens@1"
|
||||
}
|
||||
],
|
||||
"scm/resourceState/context": [
|
||||
@@ -1539,6 +1549,11 @@
|
||||
"when": "gitlens:enabled && gitlens:hasRemotes",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.externalDiff",
|
||||
"when": "gitlens:enabled",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "gitlens.diffWithRevision",
|
||||
"when": "gitlens:enabled",
|
||||
|
||||
@@ -14,6 +14,7 @@ export * from './commands/diffWithNext';
|
||||
export * from './commands/diffWithPrevious';
|
||||
export * from './commands/diffWithRevision';
|
||||
export * from './commands/diffWithWorking';
|
||||
export * from './commands/externalDiff';
|
||||
export * from './commands/openChangedFiles';
|
||||
export * from './commands/openBranchesInRemote';
|
||||
export * from './commands/openBranchInRemote';
|
||||
|
||||
@@ -19,6 +19,7 @@ export type Commands =
|
||||
'gitlens.diffWithRevision' |
|
||||
'gitlens.diffWithWorking' |
|
||||
'gitlens.diffLineWithWorking' |
|
||||
'gitlens.externalDiff' |
|
||||
'gitlens.openChangedFiles' |
|
||||
'gitlens.openBranchesInRemote' |
|
||||
'gitlens.openBranchInRemote' |
|
||||
@@ -61,6 +62,7 @@ export const Commands = {
|
||||
DiffWithRevision: 'gitlens.diffWithRevision' as Commands,
|
||||
DiffWithWorking: 'gitlens.diffWithWorking' as Commands,
|
||||
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
|
||||
ExternalDiff: 'gitlens.externalDiff' as Commands,
|
||||
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
|
||||
OpenBranchesInRemote: 'gitlens.openBranchesInRemote' as Commands,
|
||||
OpenBranchInRemote: 'gitlens.openBranchInRemote' as Commands,
|
||||
|
||||
115
src/commands/externalDiff.ts
Normal file
115
src/commands/externalDiff.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
'use strict';
|
||||
import { commands, SourceControlResourceState, Uri, window } from 'vscode';
|
||||
import { Command, Commands } from './common';
|
||||
import { BuiltInCommands } from '../constants';
|
||||
import { CommandContext } from '../commands';
|
||||
import { GitService } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { Messages } from '../messages';
|
||||
|
||||
enum Status {
|
||||
INDEX_MODIFIED,
|
||||
INDEX_ADDED,
|
||||
INDEX_DELETED,
|
||||
INDEX_RENAMED,
|
||||
INDEX_COPIED,
|
||||
|
||||
MODIFIED,
|
||||
DELETED,
|
||||
UNTRACKED,
|
||||
IGNORED,
|
||||
|
||||
ADDED_BY_US,
|
||||
ADDED_BY_THEM,
|
||||
DELETED_BY_US,
|
||||
DELETED_BY_THEM,
|
||||
BOTH_ADDED,
|
||||
BOTH_DELETED,
|
||||
BOTH_MODIFIED
|
||||
}
|
||||
|
||||
enum ResourceGroupType {
|
||||
Merge,
|
||||
Index,
|
||||
WorkingTree
|
||||
}
|
||||
|
||||
interface Resource extends SourceControlResourceState {
|
||||
readonly resourceGroupType: ResourceGroupType;
|
||||
readonly type: Status;
|
||||
}
|
||||
|
||||
class ExternalDiffFile {
|
||||
constructor(public uri: Uri, public staged: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ExternalDiffCommandArgs {
|
||||
files?: ExternalDiffFile[];
|
||||
}
|
||||
|
||||
export class ExternalDiffCommand extends Command {
|
||||
constructor(private git: GitService) {
|
||||
super(Commands.ExternalDiff);
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: ExternalDiffCommandArgs = {}): Promise<any> {
|
||||
if (context.type === 'scm-states') {
|
||||
args = { ...args };
|
||||
args.files = context.scmResourceStates.map<ExternalDiffFile>((_: Resource) => new ExternalDiffFile(_.resourceUri, _.resourceGroupType === ResourceGroupType.Index));
|
||||
|
||||
return this.execute(args);
|
||||
} else if (context.type === 'scm-groups') {
|
||||
const isModified = (status: Status): boolean => status === Status.BOTH_MODIFIED || status === Status.INDEX_MODIFIED || status === Status.MODIFIED;
|
||||
|
||||
args = { ...args };
|
||||
args.files = context.scmResourceGroups[0].resourceStates.filter((_: Resource) => isModified(_.type)).map<ExternalDiffFile>((_: Resource) => new ExternalDiffFile(_.resourceUri, _.resourceGroupType === ResourceGroupType.Index));
|
||||
|
||||
return this.execute(args);
|
||||
}
|
||||
|
||||
return this.execute(args);
|
||||
}
|
||||
|
||||
async execute(args: ExternalDiffCommandArgs = {}) {
|
||||
try {
|
||||
const diffTool = await this.git.getConfig('diff.tool');
|
||||
if (!diffTool) {
|
||||
const result = await window.showWarningMessage(`Unable to open file compare because there is no Git diff tool configured`, 'View Git Docs');
|
||||
if (!result) return undefined;
|
||||
|
||||
return commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://git-scm.com/docs/git-config#git-config-difftool'));
|
||||
}
|
||||
|
||||
const repoPath = await this.git.getRepoPathFromUri(undefined);
|
||||
if (!repoPath) return Messages.showNoRepositoryWarningMessage(`Unable to open changed files`);
|
||||
|
||||
if (!args.files) {
|
||||
const status = await this.git.getStatusForRepo(repoPath);
|
||||
if (status === undefined) return window.showWarningMessage(`Unable to open changed files`);
|
||||
|
||||
args.files = [];
|
||||
|
||||
for (const file of status.files) {
|
||||
if (file.indexStatus === 'M') {
|
||||
args.files.push(new ExternalDiffFile(file.Uri, true));
|
||||
}
|
||||
|
||||
if (file.workTreeStatus === 'M') {
|
||||
args.files.push(new ExternalDiffFile(file.Uri, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of args.files) {
|
||||
this.git.openDiffTool(repoPath, file.uri, file.staged);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
catch (ex) {
|
||||
Logger.error(ex, 'ExternalDiffCommand');
|
||||
return window.showErrorMessage(`Unable to open external diff. See output channel for more details`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
import { commands, ExtensionContext, extensions, languages, window, workspace } from 'vscode';
|
||||
import { AnnotationController } from './annotations/annotationController';
|
||||
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
|
||||
import { ExternalDiffCommand } from './commands';
|
||||
import { OpenBranchesInRemoteCommand, OpenBranchInRemoteCommand, OpenCommitInRemoteCommand, OpenFileInRemoteCommand, OpenInRemoteCommand, OpenRepoInRemoteCommand } from './commands';
|
||||
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
|
||||
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithBranchCommand, DiffWithCommand, DiffWithNextCommand, DiffWithPreviousCommand, DiffWithRevisionCommand, DiffWithWorkingCommand } from './commands';
|
||||
@@ -99,6 +100,7 @@ export async function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { }));
|
||||
|
||||
context.subscriptions.push(new CloseUnchangedFilesCommand(git));
|
||||
context.subscriptions.push(new ExternalDiffCommand(git));
|
||||
context.subscriptions.push(new OpenChangedFilesCommand(git));
|
||||
context.subscriptions.push(new CopyMessageToClipboardCommand(git));
|
||||
context.subscriptions.push(new CopyShaToClipboardCommand(git));
|
||||
|
||||
@@ -276,6 +276,17 @@ export class Git {
|
||||
return gitCommand({ cwd: repoPath }, ...params);
|
||||
}
|
||||
|
||||
static difftool_fileDiff(repoPath: string, fileName: string, staged: boolean) {
|
||||
const params = [`difftool`, `--no-prompt`];
|
||||
if (staged) {
|
||||
params.push('--staged');
|
||||
}
|
||||
params.push('--');
|
||||
params.push(fileName);
|
||||
|
||||
return gitCommand({ cwd: repoPath }, ...params);
|
||||
}
|
||||
|
||||
static log(repoPath: string, sha?: string, maxCount?: number, reverse: boolean = false) {
|
||||
const params = [...defaultLogParams, `-m`];
|
||||
if (maxCount && !reverse) {
|
||||
|
||||
@@ -26,6 +26,8 @@ export interface IGitStatusFile {
|
||||
status: GitStatusFileStatus;
|
||||
fileName: string;
|
||||
originalFileName?: string;
|
||||
workTreeStatus: GitStatusFileStatus;
|
||||
indexStatus: GitStatusFileStatus;
|
||||
}
|
||||
|
||||
export interface IGitStatusFileWithCommit extends IGitStatusFile {
|
||||
@@ -36,7 +38,7 @@ export class GitStatusFile implements IGitStatusFile {
|
||||
|
||||
originalFileName?: string;
|
||||
|
||||
constructor(public repoPath: string, public status: GitStatusFileStatus, public fileName: string, public staged: boolean, originalFileName?: string) {
|
||||
constructor(public repoPath: string, public status: GitStatusFileStatus, public workTreeStatus: GitStatusFileStatus, public indexStatus: GitStatusFileStatus, public fileName: string, public staged: boolean, originalFileName?: string) {
|
||||
this.originalFileName = originalFileName;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ interface FileStatusEntry {
|
||||
status: GitStatusFileStatus;
|
||||
fileName: string;
|
||||
originalFileName: string;
|
||||
workTreeStatus: GitStatusFileStatus;
|
||||
indexStatus: GitStatusFileStatus;
|
||||
}
|
||||
|
||||
const aheadStatusV1Regex = /(?:ahead ([0-9]+))/;
|
||||
@@ -69,7 +71,7 @@ export class GitStatusParser {
|
||||
else {
|
||||
entry = this._parseFileEntry(rawStatus, fileName);
|
||||
}
|
||||
status.files.push(new GitStatusFile(repoPath, entry.status, entry.fileName, entry.staged, entry.originalFileName));
|
||||
status.files.push(new GitStatusFile(repoPath, entry.status, entry.workTreeStatus, entry.indexStatus, entry.fileName, entry.staged, entry.originalFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +119,7 @@ export class GitStatusParser {
|
||||
}
|
||||
|
||||
if (entry !== undefined) {
|
||||
status.files.push(new GitStatusFile(repoPath, entry.status, entry.fileName, entry.staged, entry.originalFileName));
|
||||
status.files.push(new GitStatusFile(repoPath, entry.status, entry.workTreeStatus, entry.indexStatus, entry.fileName, entry.staged, entry.originalFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,7 +133,9 @@ export class GitStatusParser {
|
||||
status: (indexStatus || workTreeStatus || '?') as GitStatusFileStatus,
|
||||
fileName: fileName,
|
||||
originalFileName: originalFileName,
|
||||
staged: !!indexStatus
|
||||
staged: !!indexStatus,
|
||||
indexStatus: indexStatus,
|
||||
workTreeStatus: workTreeStatus
|
||||
} as FileStatusEntry;
|
||||
}
|
||||
}
|
||||
@@ -1036,6 +1036,12 @@ export class GitService extends Disposable {
|
||||
return !!result;
|
||||
}
|
||||
|
||||
openDiffTool(repoPath: string, uri: Uri, staged: boolean) {
|
||||
Logger.log(`openDiffTool('${repoPath}', '${uri}', ${staged})`);
|
||||
|
||||
return Git.difftool_fileDiff(repoPath, uri.fsPath, staged);
|
||||
}
|
||||
|
||||
openDirectoryDiff(repoPath: string, sha1: string, sha2?: string) {
|
||||
Logger.log(`openDirectoryDiff('${repoPath}', ${sha1}, ${sha2})`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user