您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

225 行
7.3 KiB

  1. 'use strict';
  2. import { commands, SourceControlResourceState, Uri, window } from 'vscode';
  3. import { BuiltInCommands, GlyphChars } from '../constants';
  4. import { Container } from '../container';
  5. import { GitService, GitUri } from '../git/gitService';
  6. import { Logger } from '../logger';
  7. import { Messages } from '../messages';
  8. import { Arrays } from '../system';
  9. import {
  10. Command,
  11. CommandContext,
  12. Commands,
  13. getRepoPathOrPrompt,
  14. isCommandViewContextWithFileCommit,
  15. isCommandViewContextWithFileRefs
  16. } from './common';
  17. enum Status {
  18. INDEX_MODIFIED,
  19. INDEX_ADDED,
  20. INDEX_DELETED,
  21. INDEX_RENAMED,
  22. INDEX_COPIED,
  23. MODIFIED,
  24. DELETED,
  25. UNTRACKED,
  26. IGNORED,
  27. ADDED_BY_US,
  28. ADDED_BY_THEM,
  29. DELETED_BY_US,
  30. DELETED_BY_THEM,
  31. BOTH_ADDED,
  32. BOTH_DELETED,
  33. BOTH_MODIFIED
  34. }
  35. enum ResourceGroupType {
  36. Merge,
  37. Index,
  38. WorkingTree
  39. }
  40. interface Resource extends SourceControlResourceState {
  41. readonly resourceGroupType: ResourceGroupType;
  42. readonly type: Status;
  43. }
  44. class ExternalDiffFile {
  45. constructor(
  46. public readonly uri: Uri,
  47. public readonly staged: boolean,
  48. public readonly ref1?: string,
  49. public readonly ref2?: string
  50. ) {}
  51. }
  52. export interface ExternalDiffCommandArgs {
  53. files?: ExternalDiffFile[];
  54. }
  55. export class ExternalDiffCommand extends Command {
  56. constructor() {
  57. super([Commands.ExternalDiff, Commands.ExternalDiffAll]);
  58. }
  59. protected async preExecute(context: CommandContext, args: ExternalDiffCommandArgs = {}): Promise<any> {
  60. if (isCommandViewContextWithFileCommit(context)) {
  61. args = { ...args };
  62. const ref1 = GitService.isUncommitted(context.node.commit.previousFileSha)
  63. ? ''
  64. : context.node.commit.previousFileSha;
  65. const ref2 = context.node.commit.isUncommitted ? '' : context.node.commit.sha;
  66. args.files = [
  67. new ExternalDiffFile(
  68. GitUri.fromFile(context.node.file, context.node.file.repoPath || context.node.repoPath),
  69. context.node.commit.isStagedUncommitted || context.node.file.indexStatus !== undefined,
  70. ref1,
  71. ref2
  72. )
  73. ];
  74. return this.execute(args);
  75. }
  76. if (isCommandViewContextWithFileRefs(context)) {
  77. args = { ...args };
  78. args.files = [
  79. new ExternalDiffFile(
  80. GitUri.fromFile(context.node.file, context.node.file.repoPath || context.node.repoPath),
  81. context.node.file.indexStatus !== undefined,
  82. context.node.ref1,
  83. context.node.ref2
  84. )
  85. ];
  86. return this.execute(args);
  87. }
  88. if (args.files === undefined) {
  89. if (context.type === 'scm-states') {
  90. args = { ...args };
  91. args.files = context.scmResourceStates.map(
  92. r =>
  93. new ExternalDiffFile(
  94. r.resourceUri,
  95. (r as Resource).resourceGroupType === ResourceGroupType.Index
  96. )
  97. );
  98. }
  99. else if (context.type === 'scm-groups') {
  100. args = { ...args };
  101. args.files = Arrays.filterMap(
  102. context.scmResourceGroups[0].resourceStates,
  103. r =>
  104. this.isModified(r)
  105. ? new ExternalDiffFile(
  106. r.resourceUri,
  107. (r as Resource).resourceGroupType === ResourceGroupType.Index
  108. )
  109. : undefined
  110. );
  111. }
  112. }
  113. if (context.command === Commands.ExternalDiffAll) {
  114. if (args.files === undefined) {
  115. const repoPath = await getRepoPathOrPrompt(
  116. undefined,
  117. `Open changes from which repository${GlyphChars.Ellipsis}`
  118. );
  119. if (!repoPath) return undefined;
  120. const status = await Container.git.getStatusForRepo(repoPath);
  121. if (status === undefined) {
  122. return window.showInformationMessage("The repository doesn't have any changes");
  123. }
  124. args.files = [];
  125. for (const file of status.files) {
  126. if (file.indexStatus === 'M') {
  127. args.files.push(new ExternalDiffFile(file.uri, true));
  128. }
  129. if (file.workingTreeStatus === 'M') {
  130. args.files.push(new ExternalDiffFile(file.uri, false));
  131. }
  132. }
  133. }
  134. }
  135. return this.execute(args);
  136. }
  137. private isModified(resource: SourceControlResourceState) {
  138. const status = (resource as Resource).type;
  139. return status === Status.BOTH_MODIFIED || status === Status.INDEX_MODIFIED || status === Status.MODIFIED;
  140. }
  141. async execute(args: ExternalDiffCommandArgs = {}) {
  142. try {
  143. let repoPath;
  144. if (args.files === undefined) {
  145. const editor = window.activeTextEditor;
  146. if (editor === undefined) return undefined;
  147. repoPath = await Container.git.getRepoPathOrActive(undefined, editor);
  148. if (!repoPath) return undefined;
  149. const uri = editor.document.uri;
  150. const status = await Container.git.getStatusForFile(repoPath, uri.fsPath);
  151. if (status === undefined) {
  152. return window.showInformationMessage("The current file doesn't have any changes");
  153. }
  154. args.files = [];
  155. if (status.indexStatus === 'M') {
  156. args.files.push(new ExternalDiffFile(status.uri, true));
  157. }
  158. if (status.workingTreeStatus === 'M') {
  159. args.files.push(new ExternalDiffFile(status.uri, false));
  160. }
  161. }
  162. else {
  163. repoPath = await Container.git.getRepoPath(args.files[0].uri.fsPath);
  164. if (!repoPath) return undefined;
  165. }
  166. const tool = await Container.git.getDiffTool(repoPath);
  167. if (tool === undefined) {
  168. const result = await window.showWarningMessage(
  169. `Unable to open changes in diff tool. No Git diff tool is configured`,
  170. 'View Git Docs'
  171. );
  172. if (!result) return undefined;
  173. return commands.executeCommand(
  174. BuiltInCommands.Open,
  175. Uri.parse('https://git-scm.com/docs/git-config#git-config-difftool')
  176. );
  177. }
  178. for (const file of args.files) {
  179. void Container.git.openDiffTool(repoPath, file.uri, {
  180. ref1: file.ref1,
  181. ref2: file.ref2,
  182. staged: file.staged,
  183. tool: tool
  184. });
  185. }
  186. return undefined;
  187. }
  188. catch (ex) {
  189. Logger.error(ex, 'ExternalDiffCommand');
  190. return Messages.showGenericErrorMessage('Unable to open changes in diff tool');
  191. }
  192. }
  193. }