/GitUI/Script/ScriptRunner.cs

https://github.com/fraga/gitextensions · C# · 471 lines · 439 code · 28 blank · 4 comment · 106 complexity · 546fae0ea3051018c4e86cd91b4e1487 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Security.Policy;
  7. using System.Windows.Forms;
  8. using GitCommands;
  9. using GitCommands.Config;
  10. using GitUI.HelperDialogs;
  11. namespace GitUI.Script
  12. {
  13. /// <summary>Runs scripts.</summary>
  14. public static class ScriptRunner
  15. {
  16. /// <summary>Tries to run scripts identified by a <paramref name="command"/>
  17. /// and returns true if any executed.</summary>
  18. public static bool ExecuteScriptCommand(IWin32Window owner, GitModule aModule, int command, RevisionGrid revisionGrid = null)
  19. {
  20. var curScripts = ScriptManager.GetScripts();
  21. bool anyScriptExecuted = false;
  22. foreach (ScriptInfo s in curScripts)
  23. {
  24. if (s.HotkeyCommandIdentifier == command)
  25. {
  26. RunScript(owner, aModule, s.Name, revisionGrid);
  27. anyScriptExecuted = true;
  28. }
  29. }
  30. return anyScriptExecuted;
  31. }
  32. public static bool RunScript(IWin32Window owner, GitModule aModule, string script, RevisionGrid revisionGrid)
  33. {
  34. if (string.IsNullOrEmpty(script))
  35. return false;
  36. ScriptInfo scriptInfo = ScriptManager.GetScript(script);
  37. if (scriptInfo == null)
  38. {
  39. MessageBox.Show(owner, "Cannot find script: " + script, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  40. return false;
  41. }
  42. if (string.IsNullOrEmpty(scriptInfo.Command))
  43. return false;
  44. string argument = scriptInfo.Arguments;
  45. foreach (string option in Options)
  46. {
  47. if (string.IsNullOrEmpty(argument) || !argument.Contains(option))
  48. continue;
  49. if (!option.StartsWith("{s"))
  50. continue;
  51. if (revisionGrid != null)
  52. continue;
  53. MessageBox.Show(owner,
  54. string.Format("Option {0} is only supported when started from revision grid.", option),
  55. "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  56. return false;
  57. }
  58. return RunScript(owner, aModule, scriptInfo, revisionGrid);
  59. }
  60. private static string GetRemotePath(string url)
  61. {
  62. Uri uri;
  63. string path = "";
  64. if (Uri.TryCreate(url, UriKind.Absolute, out uri))
  65. path = uri.LocalPath;
  66. else if (Uri.TryCreate("ssh://" + url.Replace(":", "/"), UriKind.Absolute, out uri))
  67. path = uri.LocalPath;
  68. int pos = path.LastIndexOf(".");
  69. if (pos >= 0)
  70. path = path.Substring(0, pos);
  71. return path;
  72. }
  73. internal static bool RunScript(IWin32Window owner, GitModule aModule, ScriptInfo scriptInfo, RevisionGrid revisionGrid)
  74. {
  75. string originalCommand = scriptInfo.Command;
  76. string argument = scriptInfo.Arguments;
  77. string command = OverrideCommandWhenNecessary(originalCommand);
  78. var allSelectedRevisions = new List<GitRevision>();
  79. GitRevision selectedRevision = null;
  80. GitRevision currentRevision = null;
  81. var selectedLocalBranches = new List<GitRef>();
  82. var selectedRemoteBranches = new List<GitRef>();
  83. var selectedRemotes = new List<string>();
  84. var selectedBranches = new List<GitRef>();
  85. var selectedTags = new List<GitRef>();
  86. var currentLocalBranches = new List<GitRef>();
  87. var currentRemoteBranches = new List<GitRef>();
  88. var currentRemote = "";
  89. var currentBranches = new List<GitRef>();
  90. var currentTags = new List<GitRef>();
  91. foreach (string option in Options)
  92. {
  93. if (string.IsNullOrEmpty(argument) || !argument.Contains(option))
  94. continue;
  95. if (option.StartsWith("{c") && currentRevision == null)
  96. {
  97. currentRevision = GetCurrentRevision(aModule, revisionGrid, currentTags, currentLocalBranches, currentRemoteBranches, currentBranches, currentRevision);
  98. if (currentLocalBranches.Count == 1)
  99. currentRemote = aModule.GetSetting(string.Format("branch.{0}.remote", currentLocalBranches[0].Name));
  100. else
  101. {
  102. currentRemote = aModule.GetCurrentRemote();
  103. if (string.IsNullOrEmpty(currentRemote))
  104. currentRemote = aModule.GetSetting(string.Format("branch.{0}.remote",
  105. askToSpecify(currentLocalBranches, "Current Revision Branch")));
  106. }
  107. }
  108. else if (option.StartsWith("{s") && selectedRevision == null && revisionGrid != null)
  109. {
  110. allSelectedRevisions = revisionGrid.GetSelectedRevisions();
  111. allSelectedRevisions.Reverse(); // Put first clicked revisions first
  112. selectedRevision = CalculateSelectedRevision(revisionGrid, selectedRemoteBranches, selectedRemotes, selectedLocalBranches, selectedBranches, selectedTags);
  113. }
  114. string remote;
  115. string url;
  116. switch (option)
  117. {
  118. case "{sHashes}":
  119. argument = argument.Replace(option,
  120. string.Join(" ", allSelectedRevisions.Select(revision => revision.Guid).ToArray()));
  121. break;
  122. case "{sTag}":
  123. if (selectedTags.Count == 1)
  124. argument = argument.Replace(option, selectedTags[0].Name);
  125. else if (selectedTags.Count != 0)
  126. argument = argument.Replace(option, askToSpecify(selectedTags, "Selected Revision Tag"));
  127. else
  128. argument = argument.Replace(option, "");
  129. break;
  130. case "{sBranch}":
  131. if (selectedBranches.Count == 1)
  132. argument = argument.Replace(option, selectedBranches[0].Name);
  133. else if (selectedBranches.Count != 0)
  134. argument = argument.Replace(option,
  135. askToSpecify(selectedBranches, "Selected Revision Branch"));
  136. else
  137. argument = argument.Replace(option, "");
  138. break;
  139. case "{sLocalBranch}":
  140. if (selectedLocalBranches.Count == 1)
  141. argument = argument.Replace(option, selectedLocalBranches[0].Name);
  142. else if (selectedLocalBranches.Count != 0)
  143. argument = argument.Replace(option,
  144. askToSpecify(selectedLocalBranches,
  145. "Selected Revision Local Branch"));
  146. else
  147. argument = argument.Replace(option, "");
  148. break;
  149. case "{sRemoteBranch}":
  150. if (selectedRemoteBranches.Count == 1)
  151. argument = argument.Replace(option, selectedRemoteBranches[0].Name);
  152. else if (selectedRemoteBranches.Count != 0)
  153. argument = argument.Replace(option,
  154. askToSpecify(selectedRemoteBranches,
  155. "Selected Revision Remote Branch"));
  156. else
  157. argument = argument.Replace(option, "");
  158. break;
  159. case "{sRemote}":
  160. if (selectedRemotes.Count == 0)
  161. {
  162. argument = argument.Replace(option, "");
  163. break;
  164. }
  165. if (selectedRemotes.Count == 1)
  166. remote = selectedRemotes[0];
  167. else
  168. remote = askToSpecify(selectedRemotes, "Selected Revision Remote");
  169. argument = argument.Replace(option, remote);
  170. break;
  171. case "{sRemoteUrl}":
  172. if (selectedRemotes.Count == 0)
  173. {
  174. argument = argument.Replace(option, "");
  175. break;
  176. }
  177. if (selectedRemotes.Count == 1)
  178. remote = selectedRemotes[0];
  179. else
  180. remote = askToSpecify(selectedRemotes, "Selected Revision Remote");
  181. url = aModule.GetPathSetting(string.Format(SettingKeyString.RemoteUrl, remote));
  182. argument = argument.Replace(option, url);
  183. break;
  184. case "{sRemotePathFromUrl}":
  185. if (selectedRemotes.Count == 0)
  186. {
  187. argument = argument.Replace(option, "");
  188. break;
  189. }
  190. if (selectedRemotes.Count == 1)
  191. remote = selectedRemotes[0];
  192. else
  193. remote = askToSpecify(selectedRemotes, "Selected Revision Remote");
  194. url = aModule.GetPathSetting(string.Format(SettingKeyString.RemoteUrl, remote));
  195. argument = argument.Replace(option, GetRemotePath(url));
  196. break;
  197. case "{sHash}":
  198. argument = argument.Replace(option, selectedRevision.Guid);
  199. break;
  200. case "{sMessage}":
  201. argument = argument.Replace(option, selectedRevision.Message);
  202. break;
  203. case "{sAuthor}":
  204. argument = argument.Replace(option, selectedRevision.Author);
  205. break;
  206. case "{sCommitter}":
  207. argument = argument.Replace(option, selectedRevision.Committer);
  208. break;
  209. case "{sAuthorDate}":
  210. argument = argument.Replace(option, selectedRevision.AuthorDate.ToString());
  211. break;
  212. case "{sCommitDate}":
  213. argument = argument.Replace(option, selectedRevision.CommitDate.ToString());
  214. break;
  215. case "{cTag}":
  216. if (currentTags.Count == 1)
  217. argument = argument.Replace(option, currentTags[0].Name);
  218. else if (currentTags.Count != 0)
  219. argument = argument.Replace(option, askToSpecify(currentTags, "Current Revision Tag"));
  220. else
  221. argument = argument.Replace(option, "");
  222. break;
  223. case "{cBranch}":
  224. if (currentBranches.Count == 1)
  225. argument = argument.Replace(option, currentBranches[0].Name);
  226. else if (currentBranches.Count != 0)
  227. argument = argument.Replace(option,
  228. askToSpecify(currentBranches, "Current Revision Branch"));
  229. else
  230. argument = argument.Replace(option, "");
  231. break;
  232. case "{cLocalBranch}":
  233. if (currentLocalBranches.Count == 1)
  234. argument = argument.Replace(option, currentLocalBranches[0].Name);
  235. else if (currentLocalBranches.Count != 0)
  236. argument = argument.Replace(option,
  237. askToSpecify(currentLocalBranches,
  238. "Current Revision Local Branch"));
  239. else
  240. argument = argument.Replace(option, "");
  241. break;
  242. case "{cRemoteBranch}":
  243. if (currentRemoteBranches.Count == 1)
  244. argument = argument.Replace(option, currentRemoteBranches[0].Name);
  245. else if (currentRemoteBranches.Count != 0)
  246. argument = argument.Replace(option,
  247. askToSpecify(currentRemoteBranches,
  248. "Current Revision Remote Branch"));
  249. else
  250. argument = argument.Replace(option, "");
  251. break;
  252. case "{cHash}":
  253. argument = argument.Replace(option, currentRevision.Guid);
  254. break;
  255. case "{cMessage}":
  256. argument = argument.Replace(option, currentRevision.Message);
  257. break;
  258. case "{cAuthor}":
  259. argument = argument.Replace(option, currentRevision.Author);
  260. break;
  261. case "{cCommitter}":
  262. argument = argument.Replace(option, currentRevision.Committer);
  263. break;
  264. case "{cAuthorDate}":
  265. argument = argument.Replace(option, currentRevision.AuthorDate.ToString());
  266. break;
  267. case "{cCommitDate}":
  268. argument = argument.Replace(option, currentRevision.CommitDate.ToString());
  269. break;
  270. case "{cDefaultRemote}":
  271. if (string.IsNullOrEmpty(currentRemote))
  272. {
  273. argument = argument.Replace(option, "");
  274. break;
  275. }
  276. argument = argument.Replace(option, currentRemote);
  277. break;
  278. case "{cDefaultRemoteUrl}":
  279. if (string.IsNullOrEmpty(currentRemote))
  280. {
  281. argument = argument.Replace(option, "");
  282. break;
  283. }
  284. url = aModule.GetPathSetting(string.Format(SettingKeyString.RemoteUrl, currentRemote));
  285. argument = argument.Replace(option, url);
  286. break;
  287. case "{cDefaultRemotePathFromUrl}":
  288. if (string.IsNullOrEmpty(currentRemote))
  289. {
  290. argument = argument.Replace(option, "");
  291. break;
  292. }
  293. url = aModule.GetPathSetting(string.Format(SettingKeyString.RemoteUrl, currentRemote));
  294. argument = argument.Replace(option, GetRemotePath(url));
  295. break;
  296. case "{UserInput}":
  297. using (SimplePrompt Prompt = new SimplePrompt())
  298. {
  299. Prompt.ShowDialog();
  300. argument = argument.Replace(option, Prompt.UserInput);
  301. }
  302. break;
  303. }
  304. }
  305. if (!scriptInfo.RunInBackground)
  306. FormProcess.ShowDialog(owner, command, argument, aModule.WorkingDir, null, true);
  307. else
  308. {
  309. if (originalCommand.Equals("{openurl}", StringComparison.CurrentCultureIgnoreCase))
  310. Process.Start(argument);
  311. else
  312. aModule.RunExternalCmdDetached(command, argument);
  313. }
  314. return !scriptInfo.RunInBackground;
  315. }
  316. private static GitRevision CalculateSelectedRevision(RevisionGrid revisionGrid, List<GitRef> selectedRemoteBranches,
  317. List<string> selectedRemotes, List<GitRef> selectedLocalBranches,
  318. List<GitRef> selectedBranches, List<GitRef> selectedTags)
  319. {
  320. GitRevision selectedRevision = revisionGrid.GetRevision(revisionGrid.LastRowIndex);
  321. foreach (GitRef head in selectedRevision.Refs)
  322. {
  323. if (head.IsTag)
  324. selectedTags.Add(head);
  325. else if (head.IsHead || head.IsRemote)
  326. {
  327. selectedBranches.Add(head);
  328. if (head.IsRemote)
  329. {
  330. selectedRemoteBranches.Add(head);
  331. if (!selectedRemotes.Contains(head.Remote))
  332. selectedRemotes.Add(head.Remote);
  333. }
  334. else
  335. selectedLocalBranches.Add(head);
  336. }
  337. }
  338. return selectedRevision;
  339. }
  340. private static GitRevision GetCurrentRevision(GitModule aModule, RevisionGrid RevisionGrid, List<GitRef> currentTags, List<GitRef> currentLocalBranches,
  341. List<GitRef> currentRemoteBranches, List<GitRef> currentBranches,
  342. GitRevision currentRevision)
  343. {
  344. if (currentRevision == null)
  345. {
  346. IList<GitRef> refs;
  347. if (RevisionGrid == null)
  348. {
  349. string currentRevisionGuid = aModule.GetCurrentCheckout();
  350. refs = aModule.GetRefs(true, true).Where(gitRef => gitRef.Guid == currentRevisionGuid).ToList();
  351. }
  352. else
  353. {
  354. currentRevision = RevisionGrid.GetCurrentRevision();
  355. refs = currentRevision.Refs;
  356. }
  357. foreach (GitRef gitRef in refs)
  358. {
  359. if (gitRef.IsTag)
  360. currentTags.Add(gitRef);
  361. else if (gitRef.IsHead || gitRef.IsRemote)
  362. {
  363. currentBranches.Add(gitRef);
  364. if (gitRef.IsRemote)
  365. currentRemoteBranches.Add(gitRef);
  366. else
  367. currentLocalBranches.Add(gitRef);
  368. }
  369. }
  370. }
  371. return currentRevision;
  372. }
  373. private static string[] Options
  374. {
  375. get
  376. {
  377. string[] options =
  378. {
  379. "{sHashes}",
  380. "{sTag}",
  381. "{sBranch}",
  382. "{sLocalBranch}",
  383. "{sRemoteBranch}",
  384. "{sRemote}",
  385. "{sRemoteUrl}",
  386. "{sRemotePathFromUrl}",
  387. "{sHash}",
  388. "{sMessage}",
  389. "{sAuthor}",
  390. "{sCommitter}",
  391. "{sAuthorDate}",
  392. "{sCommitDate}",
  393. "{cTag}",
  394. "{cBranch}",
  395. "{cLocalBranch}",
  396. "{cRemoteBranch}",
  397. "{cHash}",
  398. "{cMessage}",
  399. "{cAuthor}",
  400. "{cCommitter}",
  401. "{cAuthorDate}",
  402. "{cCommitDate}",
  403. "{cDefaultRemote}",
  404. "{cDefaultRemoteUrl}",
  405. "{cDefaultRemotePathFromUrl}",
  406. "{UserInput}"
  407. };
  408. return options;
  409. }
  410. }
  411. private static string OverrideCommandWhenNecessary(string originalCommand)
  412. {
  413. //Make sure we are able to run git, even if git is not in the path
  414. if (originalCommand.Equals("git", StringComparison.CurrentCultureIgnoreCase) ||
  415. originalCommand.Equals("{git}", StringComparison.CurrentCultureIgnoreCase))
  416. return AppSettings.GitCommand;
  417. if (originalCommand.Equals("gitextensions", StringComparison.CurrentCultureIgnoreCase) ||
  418. originalCommand.Equals("{gitextensions}", StringComparison.CurrentCultureIgnoreCase) ||
  419. originalCommand.Equals("gitex", StringComparison.CurrentCultureIgnoreCase) ||
  420. originalCommand.Equals("{gitex}", StringComparison.CurrentCultureIgnoreCase))
  421. return AppSettings.GetGitExtensionsFullPath();
  422. if (originalCommand.Equals("{openurl}", StringComparison.CurrentCultureIgnoreCase))
  423. return "explorer";
  424. return originalCommand;
  425. }
  426. private static string askToSpecify(IEnumerable<GitRef> options, string title)
  427. {
  428. using (var f = new FormRunScriptSpecify(options, title))
  429. {
  430. f.ShowDialog();
  431. return f.ret;
  432. }
  433. }
  434. private static string askToSpecify(IEnumerable<string> options, string title)
  435. {
  436. using (var f = new FormRunScriptSpecify(options, title))
  437. {
  438. f.ShowDialog();
  439. return f.ret;
  440. }
  441. }
  442. }
  443. }