25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

225 lines
7.3 KiB

'use strict';
import { commands, SourceControlResourceState, Uri, window } from 'vscode';
import { BuiltInCommands, GlyphChars } from '../constants';
import { Container } from '../container';
import { GitService, GitUri } from '../git/gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { Arrays } from '../system';
import {
Command,
CommandContext,
Commands,
getRepoPathOrPrompt,
isCommandViewContextWithFileCommit,
isCommandViewContextWithFileRefs
} from './common';
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 readonly uri: Uri,
public readonly staged: boolean,
public readonly ref1?: string,
public readonly ref2?: string
) {}
}
export interface ExternalDiffCommandArgs {
files?: ExternalDiffFile[];
}
export class ExternalDiffCommand extends Command {
constructor() {
super([Commands.ExternalDiff, Commands.ExternalDiffAll]);
}
protected async preExecute(context: CommandContext, args: ExternalDiffCommandArgs = {}): Promise<any> {
if (isCommandViewContextWithFileCommit(context)) {
args = { ...args };
const ref1 = GitService.isUncommitted(context.node.commit.previousFileSha)
? ''
: context.node.commit.previousFileSha;
const ref2 = context.node.commit.isUncommitted ? '' : context.node.commit.sha;
args.files = [
new ExternalDiffFile(
GitUri.fromFile(context.node.file, context.node.file.repoPath || context.node.repoPath),
context.node.commit.isStagedUncommitted || context.node.file.indexStatus !== undefined,
ref1,
ref2
)
];
return this.execute(args);
}
if (isCommandViewContextWithFileRefs(context)) {
args = { ...args };
args.files = [
new ExternalDiffFile(
GitUri.fromFile(context.node.file, context.node.file.repoPath || context.node.repoPath),
context.node.file.indexStatus !== undefined,
context.node.ref1,
context.node.ref2
)
];
return this.execute(args);
}
if (args.files === undefined) {
if (context.type === 'scm-states') {
args = { ...args };
args.files = context.scmResourceStates.map(
r =>
new ExternalDiffFile(
r.resourceUri,
(r as Resource).resourceGroupType === ResourceGroupType.Index
)
);
}
else if (context.type === 'scm-groups') {
args = { ...args };
args.files = Arrays.filterMap(
context.scmResourceGroups[0].resourceStates,
r =>
this.isModified(r)
? new ExternalDiffFile(
r.resourceUri,
(r as Resource).resourceGroupType === ResourceGroupType.Index
)
: undefined
);
}
}
if (context.command === Commands.ExternalDiffAll) {
if (args.files === undefined) {
const repoPath = await getRepoPathOrPrompt(
undefined,
`Open changes from which repository${GlyphChars.Ellipsis}`
);
if (!repoPath) return undefined;
const status = await Container.git.getStatusForRepo(repoPath);
if (status === undefined) {
return window.showInformationMessage("The repository doesn't have any changes");
}
args.files = [];
for (const file of status.files) {
if (file.indexStatus === 'M') {
args.files.push(new ExternalDiffFile(file.uri, true));
}
if (file.workingTreeStatus === 'M') {
args.files.push(new ExternalDiffFile(file.uri, false));
}
}
}
}
return this.execute(args);
}
private isModified(resource: SourceControlResourceState) {
const status = (resource as Resource).type;
return status === Status.BOTH_MODIFIED || status === Status.INDEX_MODIFIED || status === Status.MODIFIED;
}
async execute(args: ExternalDiffCommandArgs = {}) {
try {
let repoPath;
if (args.files === undefined) {
const editor = window.activeTextEditor;
if (editor === undefined) return undefined;
repoPath = await Container.git.getRepoPathOrActive(undefined, editor);
if (!repoPath) return undefined;
const uri = editor.document.uri;
const status = await Container.git.getStatusForFile(repoPath, uri.fsPath);
if (status === undefined) {
return window.showInformationMessage("The current file doesn't have any changes");
}
args.files = [];
if (status.indexStatus === 'M') {
args.files.push(new ExternalDiffFile(status.uri, true));
}
if (status.workingTreeStatus === 'M') {
args.files.push(new ExternalDiffFile(status.uri, false));
}
}
else {
repoPath = await Container.git.getRepoPath(args.files[0].uri.fsPath);
if (!repoPath) return undefined;
}
const tool = await Container.git.getDiffTool(repoPath);
if (tool === undefined) {
const result = await window.showWarningMessage(
`Unable to open changes in diff tool. No Git diff tool is 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')
);
}
for (const file of args.files) {
void Container.git.openDiffTool(repoPath, file.uri, {
ref1: file.ref1,
ref2: file.ref2,
staged: file.staged,
tool: tool
});
}
return undefined;
}
catch (ex) {
Logger.error(ex, 'ExternalDiffCommand');
return Messages.showGenericErrorMessage('Unable to open changes in diff tool');
}
}
}