PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/GitCommands/Git/GitModule.cs

https://github.com/eisnerd/gitextensions
C# | 2302 lines | 1871 code | 402 blank | 29 comment | 235 complexity | 1e6ca115657c17ec60e11043597c9cb1 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Security.Permissions;
  8. using System.Text;
  9. using System.Text.RegularExpressions;
  10. using System.Windows.Forms;
  11. using GitCommands.Config;
  12. using JetBrains.Annotations;
  13. using PatchApply;
  14. namespace GitCommands
  15. {
  16. /// <summary>
  17. /// Class provide non-static methods for manipulation with git module.
  18. /// You can create several instances for submodules.
  19. /// </summary>
  20. public sealed class GitModule
  21. {
  22. private static readonly Regex DefaultHeadPattern = new Regex("refs/remotes/[^/]+/HEAD", RegexOptions.Compiled);
  23. public GitModule()
  24. {
  25. }
  26. public GitModule(string workingdir)
  27. {
  28. WorkingDir = workingdir;
  29. }
  30. private string _workingdir;
  31. private GitModule _superprojectModule;
  32. private string _submoduleName;
  33. public string WorkingDir
  34. {
  35. get
  36. {
  37. return _workingdir;
  38. }
  39. set
  40. {
  41. _workingdir = FindGitWorkingDir(value.Trim());
  42. string superprojectDir = FindGitSuperprojectPath(out _submoduleName);
  43. _superprojectModule = superprojectDir == null ? null : new GitModule(superprojectDir);
  44. }
  45. }
  46. public string SubmoduleName
  47. {
  48. get
  49. {
  50. return _submoduleName;
  51. }
  52. }
  53. public GitModule SuperprojectModule
  54. {
  55. get
  56. {
  57. return _superprojectModule;
  58. }
  59. }
  60. private static string FixPath(string path)
  61. {
  62. return GitCommandHelpers.FixPath(path);
  63. }
  64. public bool ValidWorkingDir()
  65. {
  66. return ValidWorkingDir(_workingdir);
  67. }
  68. public static bool ValidWorkingDir(string dir)
  69. {
  70. if (string.IsNullOrEmpty(dir))
  71. return false;
  72. if (Directory.Exists(dir + Settings.PathSeparator.ToString() + ".git") || File.Exists(dir + Settings.PathSeparator.ToString() + ".git"))
  73. return true;
  74. return Directory.Exists(dir + Settings.PathSeparator.ToString() + "info") &&
  75. Directory.Exists(dir + Settings.PathSeparator.ToString() + "objects") &&
  76. Directory.Exists(dir + Settings.PathSeparator.ToString() + "refs");
  77. }
  78. public string GetGitDirectory()
  79. {
  80. return GetGitDirectory(_workingdir);
  81. }
  82. public bool IsBareRepository()
  83. {
  84. return IsBareRepository(_workingdir);
  85. }
  86. public string WorkingDirGitDir()
  87. {
  88. return WorkingDirGitDir(_workingdir);
  89. }
  90. /// <summary>
  91. /// This is a faster function to get the names of all submodules then the
  92. /// GetSubmodules() function. The command @git submodule is very slow.
  93. /// </summary>
  94. public IList<string> GetSubmodulesNames()
  95. {
  96. var configFile = GetSubmoduleConfigFile();
  97. return configFile.GetConfigSections().Select(configSection => configSection.SubSection).ToList();
  98. }
  99. public string GetGlobalSetting(string setting)
  100. {
  101. var configFile = GitCommandHelpers.GetGlobalConfig();
  102. return configFile.GetValue(setting);
  103. }
  104. public string GetGlobalPathSetting(string setting)
  105. {
  106. var configFile = GitCommandHelpers.GetGlobalConfig();
  107. return configFile.GetPathValue(setting);
  108. }
  109. public void SetGlobalSetting(string setting, string value)
  110. {
  111. var configFile = GitCommandHelpers.GetGlobalConfig();
  112. configFile.SetValue(setting, value);
  113. configFile.Save();
  114. }
  115. public void SetGlobalPathSetting(string setting, string value)
  116. {
  117. var configFile = GitCommandHelpers.GetGlobalConfig();
  118. configFile.SetPathValue(setting, value);
  119. configFile.Save();
  120. }
  121. public static string FindGitWorkingDir(string startDir)
  122. {
  123. if (string.IsNullOrEmpty(startDir))
  124. return "";
  125. if (!startDir.EndsWith(Settings.PathSeparator.ToString()) && !startDir.EndsWith(Settings.PathSeparatorWrong.ToString()))
  126. startDir += Settings.PathSeparator.ToString();
  127. var dir = startDir;
  128. while (dir.LastIndexOfAny(new[] { Settings.PathSeparator, Settings.PathSeparatorWrong }) > 0)
  129. {
  130. dir = dir.Substring(0, dir.LastIndexOfAny(new[] { Settings.PathSeparator, Settings.PathSeparatorWrong }));
  131. if (ValidWorkingDir(dir))
  132. return dir + Settings.PathSeparator.ToString();
  133. }
  134. return startDir;
  135. }
  136. public Encoding GetLogoutputEncoding()
  137. {
  138. string encodingString = GetLocalConfig().GetValue("i18n.logoutputencoding");
  139. if (string.IsNullOrEmpty(encodingString))
  140. encodingString = GitCommandHelpers.GetGlobalConfig().GetValue("i18n.logoutputencoding");
  141. if (string.IsNullOrEmpty(encodingString))
  142. encodingString = GetLocalConfig().GetValue("i18n.commitEncoding");
  143. if (string.IsNullOrEmpty(encodingString))
  144. encodingString = GitCommandHelpers.GetGlobalConfig().GetValue("i18n.commitEncoding");
  145. if (!string.IsNullOrEmpty(encodingString))
  146. {
  147. try
  148. {
  149. return Encoding.GetEncoding(encodingString);
  150. }
  151. catch (ArgumentException ex)
  152. {
  153. throw new Exception(ex.Message + Environment.NewLine + "Unsupported encoding set in git config file: " + encodingString + Environment.NewLine + "Please check the setting i18n.commitencoding in your local and/or global config files. Command aborted.", ex);
  154. }
  155. }
  156. return Encoding.UTF8;
  157. }
  158. public string RunCmd(string cmd)
  159. {
  160. return RunCmd(cmd, "");
  161. }
  162. public void RunRealCmd(string cmd, string arguments)
  163. {
  164. try
  165. {
  166. CreateAndStartCommand(cmd, arguments, true);
  167. }
  168. catch (Exception ex)
  169. {
  170. Trace.WriteLine(ex.Message);
  171. }
  172. }
  173. public void RunGitRealCmd(string arguments)
  174. {
  175. RunRealCmd(Settings.GitCommand, arguments);
  176. }
  177. public void RunRealCmdDetached(string cmd, string arguments)
  178. {
  179. try
  180. {
  181. CreateAndStartCommand(cmd, arguments, false);
  182. }
  183. catch (Exception ex)
  184. {
  185. Trace.WriteLine(ex.Message);
  186. }
  187. }
  188. private void CreateAndStartCommand(string cmd, string arguments, bool waitForExit)
  189. {
  190. GitCommandHelpers.SetEnvironmentVariable();
  191. Settings.GitLog.Log(cmd + " " + arguments);
  192. //process used to execute external commands
  193. var info = new ProcessStartInfo
  194. {
  195. UseShellExecute = true,
  196. ErrorDialog = false,
  197. RedirectStandardOutput = false,
  198. RedirectStandardInput = false,
  199. CreateNoWindow = false,
  200. FileName = cmd,
  201. Arguments = arguments,
  202. WorkingDirectory = _workingdir,
  203. WindowStyle = ProcessWindowStyle.Normal,
  204. LoadUserProfile = true
  205. };
  206. if (waitForExit)
  207. {
  208. using (var process = Process.Start(info))
  209. {
  210. process.WaitForExit();
  211. }
  212. }
  213. else
  214. {
  215. Process.Start(info);
  216. }
  217. }
  218. public void StartExternalCommand(string cmd, string arguments)
  219. {
  220. try
  221. {
  222. GitCommandHelpers.SetEnvironmentVariable();
  223. var processInfo = new ProcessStartInfo
  224. {
  225. UseShellExecute = false,
  226. RedirectStandardOutput = false,
  227. FileName = cmd,
  228. WorkingDirectory = _workingdir,
  229. Arguments = arguments,
  230. CreateNoWindow = true
  231. };
  232. using (var process = new Process { StartInfo = processInfo })
  233. {
  234. process.Start();
  235. }
  236. }
  237. catch (Exception ex)
  238. {
  239. MessageBox.Show(ex.ToString());
  240. }
  241. }
  242. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  243. public string RunCachableCmd(string cmd, string arguments, Encoding encoding)
  244. {
  245. if (encoding == null)
  246. encoding = Settings.LogOutputEncoding;
  247. string output;
  248. if (GitCommandCache.TryGet(arguments, encoding, out output))
  249. return output;
  250. byte[] cmdout, cmderr;
  251. RunCmdByte(cmd, arguments, out cmdout, out cmderr);
  252. GitCommandCache.Add(arguments, cmdout, cmderr);
  253. return EncodingHelper.GetString(cmdout, cmderr, encoding);
  254. }
  255. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  256. public string RunCachableCmd(string cmd, string arguments)
  257. {
  258. return RunCachableCmd(cmd, arguments, Settings.LogOutputEncoding);
  259. }
  260. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  261. public string RunCmd(string cmd, string arguments)
  262. {
  263. return RunCmd(cmd, arguments, string.Empty);
  264. }
  265. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  266. public string RunCmd(string cmd, string arguments, Encoding encoding)
  267. {
  268. return RunCmd(cmd, arguments, null, encoding);
  269. }
  270. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  271. public string RunCmd(string cmd, string arguments, string stdInput, Encoding encoding)
  272. {
  273. int exitCode;
  274. return RunCmd(cmd, arguments, out exitCode, stdInput, encoding);
  275. }
  276. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  277. public string RunCmd(string cmd, string arguments, string stdInput)
  278. {
  279. int exitCode;
  280. return RunCmd(cmd, arguments, out exitCode, stdInput);
  281. }
  282. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  283. public string RunCmd(string cmd, string arguments, out int exitCode)
  284. {
  285. return RunCmd(cmd, arguments, out exitCode, null);
  286. }
  287. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  288. public string RunCmd(string cmd, string arguments, out int exitCode, string stdInput)
  289. {
  290. return RunCmd(cmd, arguments, out exitCode, stdInput, Settings.LogOutputEncoding);
  291. }
  292. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  293. public string RunCmd(string cmd, string arguments, out int exitCode, string stdInput, Encoding encoding)
  294. {
  295. byte[] output, error;
  296. exitCode = RunCmdByte(cmd, arguments, stdInput, out output, out error);
  297. return EncodingHelper.GetString(output, error, encoding);
  298. }
  299. private int RunCmdByte(string cmd, string arguments, out byte[] output, out byte[] error)
  300. {
  301. return RunCmdByte(cmd, arguments, null, out output, out error);
  302. }
  303. private int RunCmdByte(string cmd, string arguments, string stdInput, out byte[] output, out byte[] error)
  304. {
  305. try
  306. {
  307. GitCommandHelpers.SetEnvironmentVariable();
  308. arguments = arguments.Replace("$QUOTE$", "\\\"");
  309. int exitCode = GitCommandHelpers.CreateAndStartProcess(arguments, cmd, _workingdir, out output, out error, stdInput);
  310. return exitCode;
  311. }
  312. catch (Win32Exception)
  313. {
  314. output = error = null;
  315. return 1;
  316. }
  317. }
  318. public string RunGitCmd(string arguments, out int exitCode, string stdInput)
  319. {
  320. return RunGitCmd(arguments, out exitCode, stdInput, Settings.LogOutputEncoding);
  321. }
  322. public string RunGitCmd(string arguments, out int exitCode, string stdInput, Encoding encoding)
  323. {
  324. return RunCmd(Settings.GitCommand, arguments, out exitCode, stdInput, encoding);
  325. }
  326. public string RunGitCmd(string arguments, out int exitCode)
  327. {
  328. return RunGitCmd(arguments, out exitCode, null);
  329. }
  330. public string RunGitCmd(string arguments, string stdInput)
  331. {
  332. int exitCode;
  333. return RunGitCmd(arguments, out exitCode, stdInput);
  334. }
  335. public string RunGitCmd(string arguments, string stdInput, Encoding encoding)
  336. {
  337. int exitCode;
  338. return RunGitCmd(arguments, out exitCode, stdInput, encoding);
  339. }
  340. public string RunGitCmd(string arguments)
  341. {
  342. return RunGitCmd(arguments, (string)null);
  343. }
  344. public string RunGitCmd(string arguments, Encoding encoding)
  345. {
  346. return RunGitCmd(arguments, null, encoding);
  347. }
  348. public string RunBatchFile(string batchFile)
  349. {
  350. string tempFileName = Path.ChangeExtension(Path.GetTempFileName(), ".cmd");
  351. using (var writer = new StreamWriter(tempFileName))
  352. {
  353. writer.WriteLine("@prompt $G");
  354. writer.Write(batchFile);
  355. }
  356. string result = Settings.Module.RunCmd("cmd.exe", "/C \"" + tempFileName + "\"");
  357. File.Delete(tempFileName);
  358. return result;
  359. }
  360. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
  361. public void RunCmdAsync(string cmd, string arguments)
  362. {
  363. GitCommandHelpers.SetEnvironmentVariable();
  364. Settings.GitLog.Log(cmd + " " + arguments);
  365. //process used to execute external commands
  366. var info = new ProcessStartInfo
  367. {
  368. UseShellExecute = true,
  369. ErrorDialog = true,
  370. RedirectStandardOutput = false,
  371. RedirectStandardInput = false,
  372. RedirectStandardError = false,
  373. LoadUserProfile = true,
  374. CreateNoWindow = false,
  375. FileName = cmd,
  376. Arguments = arguments,
  377. WorkingDirectory = _workingdir,
  378. WindowStyle = ProcessWindowStyle.Hidden
  379. };
  380. try
  381. {
  382. Process.Start(info);
  383. }
  384. catch (Win32Exception ex)
  385. {
  386. Trace.WriteLine(ex);
  387. }
  388. }
  389. public void EditNotes(string revision)
  390. {
  391. string editor = GetEffectivePathSetting("core.editor").ToLower();
  392. if (editor.Contains("gitextensions") || editor.Contains("notepad") ||
  393. editor.Contains("notepad++"))
  394. {
  395. RunGitCmd("notes edit " + revision);
  396. }
  397. else
  398. {
  399. RunRealCmd(Settings.GitCommand, "notes edit " + revision);
  400. }
  401. }
  402. public bool InTheMiddleOfConflictedMerge()
  403. {
  404. return !string.IsNullOrEmpty(RunGitCmd("ls-files -z --unmerged"));
  405. }
  406. public List<GitItem> GetConflictedFiles()
  407. {
  408. var unmergedFiles = new List<GitItem>();
  409. var fileName = "";
  410. foreach (var file in GetUnmergedFileListing())
  411. {
  412. if (file.IndexOf('\t') <= 0)
  413. continue;
  414. if (file.Substring(file.IndexOf('\t') + 1) == fileName)
  415. continue;
  416. fileName = file.Substring(file.IndexOf('\t') + 1);
  417. unmergedFiles.Add(new GitItem { FileName = fileName });
  418. }
  419. return unmergedFiles;
  420. }
  421. private IEnumerable<string> GetUnmergedFileListing()
  422. {
  423. return RunGitCmd("ls-files -z --unmerged").Split(new[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  424. }
  425. public bool HandleConflictSelectBase(string fileName)
  426. {
  427. if (!HandleConflictsSaveSide(fileName, fileName, "1"))
  428. return false;
  429. RunGitCmd("add -- \"" + fileName + "\"");
  430. return true;
  431. }
  432. public bool HandleConflictSelectLocal(string fileName)
  433. {
  434. if (!HandleConflictsSaveSide(fileName, fileName, "2"))
  435. return false;
  436. RunGitCmd("add -- \"" + fileName + "\"");
  437. return true;
  438. }
  439. public bool HandleConflictSelectRemote(string fileName)
  440. {
  441. if (!HandleConflictsSaveSide(fileName, fileName, "3"))
  442. return false;
  443. RunGitCmd("add -- \"" + fileName + "\"");
  444. return true;
  445. }
  446. public bool HandleConflictsSaveSide(string fileName, string saveAs, string side)
  447. {
  448. Directory.SetCurrentDirectory(_workingdir);
  449. side = GetSide(side);
  450. fileName = FixPath(fileName);
  451. var unmerged = RunGitCmd("ls-files -z --unmerged \"" + fileName + "\"").Split(new char[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  452. foreach (var file in unmerged)
  453. {
  454. string fileStage = null;
  455. int findSecondWhitespace = file.IndexOfAny(new[] { ' ', '\t' });
  456. if (findSecondWhitespace >= 0) fileStage = file.Substring(findSecondWhitespace).Trim();
  457. findSecondWhitespace = fileStage.IndexOfAny(new[] { ' ', '\t' });
  458. if (findSecondWhitespace >= 0) fileStage = fileStage.Substring(findSecondWhitespace).Trim();
  459. if (string.IsNullOrEmpty(fileStage))
  460. continue;
  461. if (fileStage.Trim()[0] != side[0])
  462. continue;
  463. var fileline = file.Split(new[] { ' ', '\t' });
  464. if (fileline.Length < 3)
  465. continue;
  466. Directory.SetCurrentDirectory(_workingdir);
  467. SaveBlobAs(saveAs, fileline[1]);
  468. return true;
  469. }
  470. return false;
  471. }
  472. public void SaveBlobAs(string saveAs, string blob)
  473. {
  474. using (var ms = (MemoryStream)GetFileStream(blob)) //Ugly, has implementation info.
  475. {
  476. string autocrlf = Settings.Module.GetEffectiveSetting("core.autocrlf").ToLower();
  477. bool convertcrlf = autocrlf == "true";
  478. byte[] buf = ms.ToArray();
  479. if (convertcrlf)
  480. {
  481. if (!FileHelper.IsBinaryFile(saveAs) && !FileHelper.IsBinaryFileAccordingToContent(buf))
  482. {
  483. StreamReader reader = new StreamReader(ms, Settings.FilesEncoding);
  484. String sfileout = reader.ReadToEnd();
  485. sfileout = sfileout.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n");
  486. buf = Settings.FilesEncoding.GetBytes(sfileout);
  487. }
  488. }
  489. using (FileStream fileOut = File.Create(saveAs))
  490. {
  491. fileOut.Write(buf, 0, buf.Length);
  492. }
  493. }
  494. }
  495. private static string GetSide(string side)
  496. {
  497. if (side.Equals("REMOTE", StringComparison.CurrentCultureIgnoreCase))
  498. side = "3";
  499. if (side.Equals("LOCAL", StringComparison.CurrentCultureIgnoreCase))
  500. side = "2";
  501. if (side.Equals("BASE", StringComparison.CurrentCultureIgnoreCase))
  502. side = "1";
  503. return side;
  504. }
  505. public string[] GetConflictedFiles(string filename)
  506. {
  507. Directory.SetCurrentDirectory(_workingdir);
  508. filename = FixPath(filename);
  509. string[] fileNames =
  510. {
  511. filename + ".BASE",
  512. filename + ".LOCAL",
  513. filename + ".REMOTE"
  514. };
  515. var unmerged = RunGitCmd("ls-files -z --unmerged \"" + filename + "\"").Split(new char[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  516. foreach (var file in unmerged)
  517. {
  518. string fileStage = null;
  519. int findSecondWhitespace = file.IndexOfAny(new[] { ' ', '\t' });
  520. if (findSecondWhitespace >= 0) fileStage = file.Substring(findSecondWhitespace).Trim();
  521. findSecondWhitespace = fileStage.IndexOfAny(new[] { ' ', '\t' });
  522. if (findSecondWhitespace >= 0) fileStage = fileStage.Substring(findSecondWhitespace).Trim();
  523. if (string.IsNullOrEmpty(fileStage))
  524. continue;
  525. int stage;
  526. if (!Int32.TryParse(fileStage.Trim()[0].ToString(), out stage))
  527. continue;
  528. var tempFile = RunGitCmd("checkout-index --temp --stage=" + stage + " -- " + "\"" + filename + "\"");
  529. tempFile = tempFile.Split('\t')[0];
  530. tempFile = Path.Combine(_workingdir, tempFile);
  531. var newFileName = Path.Combine(_workingdir, fileNames[stage - 1]);
  532. try
  533. {
  534. fileNames[stage - 1] = newFileName;
  535. var index = 1;
  536. while (File.Exists(fileNames[stage - 1]) && index < 50)
  537. {
  538. fileNames[stage - 1] = newFileName + index.ToString();
  539. index++;
  540. }
  541. File.Move(tempFile, fileNames[stage - 1]);
  542. }
  543. catch (Exception ex)
  544. {
  545. Trace.WriteLine(ex);
  546. }
  547. }
  548. if (!File.Exists(fileNames[0])) fileNames[0] = null;
  549. if (!File.Exists(fileNames[1])) fileNames[1] = null;
  550. if (!File.Exists(fileNames[2])) fileNames[2] = null;
  551. return fileNames;
  552. }
  553. public string[] GetConflictedFileNames(string filename)
  554. {
  555. filename = FixPath(filename);
  556. var fileNames = new string[3];
  557. var unmerged = RunGitCmd("ls-files -z --unmerged \"" + filename + "\"").Split(new[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  558. foreach (var file in unmerged)
  559. {
  560. int findSecondWhitespace = file.IndexOfAny(new[] { ' ', '\t' });
  561. string fileStage = findSecondWhitespace >= 0 ? file.Substring(findSecondWhitespace).Trim() : "";
  562. findSecondWhitespace = fileStage.IndexOfAny(new[] { ' ', '\t' });
  563. fileStage = findSecondWhitespace >= 0 ? fileStage.Substring(findSecondWhitespace).Trim() : "";
  564. int stage;
  565. if (Int32.TryParse(fileStage.Trim()[0].ToString(), out stage) && stage >= 1 && stage <= 3 && fileStage.Length > 2)
  566. {
  567. fileNames[stage - 1] = fileStage.Substring(2);
  568. }
  569. }
  570. return fileNames;
  571. }
  572. public static string GetGitDirectory(string repositoryPath)
  573. {
  574. if (File.Exists(repositoryPath + ".git"))
  575. {
  576. var lines = File.ReadAllLines(repositoryPath + ".git");
  577. foreach (string line in lines)
  578. {
  579. if (line.StartsWith("gitdir:"))
  580. {
  581. string path = line.Substring(7).Trim().Replace('/', '\\');
  582. if (Path.IsPathRooted(path))
  583. return path + Settings.PathSeparator.ToString();
  584. else
  585. return Path.GetFullPath(Path.Combine(repositoryPath, path + Settings.PathSeparator.ToString()));
  586. }
  587. }
  588. }
  589. return repositoryPath + ".git" + Settings.PathSeparator.ToString();
  590. }
  591. public string GetMergeMessage()
  592. {
  593. var file = GetGitDirectory() + "MERGE_MSG";
  594. return
  595. File.Exists(file)
  596. ? File.ReadAllText(file)
  597. : "";
  598. }
  599. public void RunGitK()
  600. {
  601. if (Settings.RunningOnUnix())
  602. {
  603. RunRealCmdDetached("gitk", "");
  604. }
  605. else
  606. {
  607. StartExternalCommand("cmd.exe", "/c \"\"" + Settings.GitCommand.Replace("git.cmd", "gitk.cmd")
  608. .Replace("bin\\git.exe", "cmd\\gitk.cmd")
  609. .Replace("bin/git.exe", "cmd/gitk.cmd") + "\" --branches --tags --remotes\"");
  610. }
  611. }
  612. public void RunGui()
  613. {
  614. if (Settings.RunningOnUnix())
  615. {
  616. RunRealCmdDetached("git", "gui");
  617. }
  618. else
  619. {
  620. StartExternalCommand("cmd.exe", "/c \"\"" + Settings.GitCommand + "\" gui\"");
  621. }
  622. }
  623. public void RunBash()
  624. {
  625. if (Settings.RunningOnUnix())
  626. {
  627. string[] termEmuCmds =
  628. {
  629. "gnome-terminal",
  630. "konsole",
  631. "Terminal",
  632. "xterm"
  633. };
  634. string args = "";
  635. string cmd = termEmuCmds.FirstOrDefault(termEmuCmd => !string.IsNullOrEmpty(RunCmd("which", termEmuCmd)));
  636. if (string.IsNullOrEmpty(cmd))
  637. {
  638. cmd = "bash";
  639. args = "--login -i";
  640. }
  641. RunRealCmdDetached(cmd, args);
  642. }
  643. else
  644. {
  645. if (File.Exists(Settings.GitBinDir + "bash.exe"))
  646. RunRealCmdDetached("cmd.exe", "/c \"\"" + Settings.GitBinDir + "bash\" --login -i\"");
  647. else
  648. RunRealCmdDetached("cmd.exe", "/c \"\"" + Settings.GitBinDir + "sh\" --login -i\"");
  649. }
  650. }
  651. public string Init(bool bare, bool shared)
  652. {
  653. if (bare && shared)
  654. return RunGitCmd("init --bare --shared=all");
  655. if (bare)
  656. return RunGitCmd("init --bare");
  657. return RunGitCmd("init");
  658. }
  659. public bool IsMerge(string commit)
  660. {
  661. string output = RunGitCmd("log -n 1 --format=format:%P \"" + commit + "\"");
  662. string[] parents = output.Split(' ');
  663. if (parents.Length > 1) return true;
  664. return false;
  665. }
  666. public GitRevision[] GetParents(string commit)
  667. {
  668. string output = RunGitCmd("log -n 1 --format=format:%P \"" + commit + "\"");
  669. string[] Parents = output.Split(' ');
  670. var ParentsRevisions = new GitRevision[Parents.Length];
  671. for (int i = 0; i < Parents.Length; i++)
  672. {
  673. const string formatString =
  674. /* Tree */ "%T%n" +
  675. /* Author Name */ "%aN%n" +
  676. /* Author Date */ "%ai%n" +
  677. /* Committer Name */ "%cN%n" +
  678. /* Committer Date */ "%ci%n" +
  679. /* Commit Message */ "%s";
  680. string cmd = "log -n 1 --format=format:" + formatString + " " + Parents[i];
  681. var RevInfo = RunGitCmd(cmd);
  682. string[] Infos = RevInfo.Split('\n');
  683. var Revision = new GitRevision(Parents[i])
  684. {
  685. TreeGuid = Infos[0],
  686. Author = Infos[1],
  687. Committer = Infos[3],
  688. Message = Infos[5]
  689. };
  690. DateTime Date;
  691. DateTime.TryParse(Infos[2], out Date);
  692. Revision.AuthorDate = Date;
  693. DateTime.TryParse(Infos[4], out Date);
  694. Revision.CommitDate = Date;
  695. ParentsRevisions[i] = Revision;
  696. }
  697. return ParentsRevisions;
  698. }
  699. public string CherryPick(string cherry, bool commit, string arguments)
  700. {
  701. return RunGitCmd(GitCommandHelpers.CherryPickCmd(cherry, commit, arguments));
  702. }
  703. public string ShowSha1(string sha1)
  704. {
  705. return GitCommandHelpers.ReEncodeShowString(RunCachableCmd(Settings.GitCommand, "show " + sha1, Settings.LosslessEncoding));
  706. }
  707. public string UserCommitCount()
  708. {
  709. return RunGitCmd("shortlog -s -n");
  710. }
  711. public string DeleteBranch(string branchName, bool force, bool remoteBranch)
  712. {
  713. return RunGitCmd(GitCommandHelpers.DeleteBranchCmd(branchName, force, remoteBranch));
  714. }
  715. public string DeleteTag(string tagName)
  716. {
  717. return RunGitCmd(GitCommandHelpers.DeleteTagCmd(tagName));
  718. }
  719. public string GetCurrentCheckout()
  720. {
  721. return RunGitCmd("log -g -1 HEAD --pretty=format:%H");
  722. }
  723. public string GetSuperprojectCurrentCheckout()
  724. {
  725. if (_superprojectModule == null)
  726. return "";
  727. var lines = _superprojectModule.RunGitCmd("submodule status --cached " + _submoduleName).Split('\n');
  728. if (lines.Length == 0)
  729. return "";
  730. string submodule = lines[0];
  731. if (submodule.Length < 43)
  732. return "";
  733. var currentCommitGuid = submodule.Substring(1, 40).Trim();
  734. return currentCommitGuid;
  735. }
  736. public int CommitCount()
  737. {
  738. int count;
  739. var arguments = "/c \"\"" + Settings.GitCommand + "\" rev-list --all --abbrev-commit | wc -l\"";
  740. return
  741. int.TryParse(RunCmd("cmd.exe", arguments), out count)
  742. ? count
  743. : 0;
  744. }
  745. public bool IsMergeCommit(string commitId)
  746. {
  747. return ExistsMergeCommit(commitId + "~1", commitId);
  748. }
  749. public bool ExistsMergeCommit(string startRev, string endRev)
  750. {
  751. if (startRev.IsNullOrEmpty() || endRev.IsNullOrEmpty())
  752. return false;
  753. string revisions = RunGitCmd("rev-list --parents --no-walk " + startRev + ".." + endRev);
  754. string[] revisionsTab = revisions.Split('\n');
  755. Func<string, bool> ex = (string parents) =>
  756. {
  757. string[] tab = parents.Split(' ');
  758. return tab.Length > 2 && tab.All(parent => GitRevision.Sha1HashRegex.IsMatch(parent));
  759. };
  760. return revisionsTab.Any(ex);
  761. }
  762. public ConfigFile GetSubmoduleConfigFile()
  763. {
  764. return new ConfigFile(_workingdir + ".gitmodules", true);
  765. }
  766. public string GetSubmoduleRemotePath(string name)
  767. {
  768. var configFile = GetSubmoduleConfigFile();
  769. return configFile.GetPathValue(string.Format("submodule.{0}.url", name.Trim())).Trim();
  770. }
  771. public string GetSubmoduleLocalPath(string name)
  772. {
  773. var configFile = GetSubmoduleConfigFile();
  774. return configFile.GetPathValue(string.Format("submodule.{0}.path", name.Trim())).Trim();
  775. }
  776. public string GetSubmoduleFullPath(string name)
  777. {
  778. return _workingdir + FixPath(GetSubmoduleLocalPath(name)) + Settings.PathSeparator.ToString();
  779. }
  780. public string FindGitSuperprojectPath(out string submoduleName)
  781. {
  782. submoduleName = null;
  783. if (String.IsNullOrEmpty(_workingdir))
  784. return null;
  785. string superprojectPath = null;
  786. if (File.Exists(_workingdir + ".git"))
  787. {
  788. var lines = File.ReadAllLines(_workingdir + ".git");
  789. foreach (string line in lines)
  790. {
  791. if (line.StartsWith("gitdir:"))
  792. {
  793. string gitpath = line.Substring(7).Trim();
  794. int pos = gitpath.IndexOf("/.git/");
  795. if (pos != -1)
  796. {
  797. gitpath = gitpath.Substring(0, pos + 1).Replace('/', '\\');
  798. if (File.Exists(gitpath + ".gitmodules") && ValidWorkingDir(gitpath))
  799. superprojectPath = gitpath;
  800. }
  801. }
  802. }
  803. }
  804. string currentPath = Path.GetDirectoryName(_workingdir); // remove last slash
  805. if (!string.IsNullOrEmpty(currentPath) &&
  806. superprojectPath == null)
  807. {
  808. string path = Path.GetDirectoryName(currentPath);
  809. if (!string.IsNullOrEmpty(path) &&
  810. (!File.Exists(path + Settings.PathSeparator.ToString() + ".gitmodules") || !ValidWorkingDir(path + Settings.PathSeparator.ToString())))
  811. {
  812. // Check upper directory
  813. path = Path.GetDirectoryName(path);
  814. if (!File.Exists(path + Settings.PathSeparator.ToString() + ".gitmodules") || !ValidWorkingDir(path + Settings.PathSeparator.ToString()))
  815. return null;
  816. }
  817. superprojectPath = path + Settings.PathSeparator.ToString();
  818. }
  819. if (!string.IsNullOrEmpty(superprojectPath))
  820. {
  821. var localPath = currentPath.Substring(superprojectPath.Length);
  822. var configFile = new ConfigFile(superprojectPath + ".gitmodules", true);
  823. foreach (ConfigSection configSection in configFile.GetConfigSections())
  824. {
  825. if (configSection.GetPathValue("path") == localPath)
  826. {
  827. submoduleName = configSection.SubSection;
  828. return superprojectPath;
  829. }
  830. }
  831. }
  832. return null;
  833. }
  834. internal static GitSubmodule CreateGitSubmodule(string submodule)
  835. {
  836. var gitSubmodule =
  837. new GitSubmodule
  838. {
  839. Initialized = submodule[0] != '-',
  840. UpToDate = submodule[0] != '+',
  841. CurrentCommitGuid = submodule.Substring(1, 40).Trim()
  842. };
  843. var name = submodule.Substring(42).Trim();
  844. if (name.Contains("("))
  845. {
  846. gitSubmodule.Name = name.Substring(0, name.IndexOf("(")).TrimEnd();
  847. gitSubmodule.Branch = name.Substring(name.IndexOf("(")).Trim(new[] { '(', ')', ' ' });
  848. }
  849. else
  850. gitSubmodule.Name = name;
  851. return gitSubmodule;
  852. }
  853. public string GetSubmoduleSummary(string submodule)
  854. {
  855. var arguments = string.Format("submodule summary {0}", submodule);
  856. return RunGitCmd(arguments);
  857. }
  858. public string Stash()
  859. {
  860. var arguments = GitCommandHelpers.StashSaveCmd(Settings.IncludeUntrackedFilesInAutoStash);
  861. return RunGitCmd(arguments);
  862. }
  863. public string StashApply()
  864. {
  865. return RunGitCmd("stash apply");
  866. }
  867. public string StashClear()
  868. {
  869. return RunGitCmd("stash clear");
  870. }
  871. public string ResetSoft(string commit)
  872. {
  873. return ResetSoft(commit, "");
  874. }
  875. public string ResetMixed(string commit)
  876. {
  877. return ResetMixed(commit, "");
  878. }
  879. public string ResetHard(string commit)
  880. {
  881. return ResetHard(commit, "");
  882. }
  883. public string ResetSoft(string commit, string file)
  884. {
  885. var args = "reset --soft";
  886. if (!string.IsNullOrEmpty(commit))
  887. args += " \"" + commit + "\"";
  888. if (!string.IsNullOrEmpty(file))
  889. args += " -- \"" + file + "\"";
  890. return RunGitCmd(args);
  891. }
  892. public string ResetMixed(string commit, string file)
  893. {
  894. var args = "reset --mixed";
  895. if (!string.IsNullOrEmpty(commit))
  896. args += " \"" + commit + "\"";
  897. if (!string.IsNullOrEmpty(file))
  898. args += " -- \"" + file + "\"";
  899. return RunGitCmd(args);
  900. }
  901. public string ResetHard(string commit, string file)
  902. {
  903. var args = "reset --hard";
  904. if (!string.IsNullOrEmpty(commit))
  905. args += " \"" + commit + "\"";
  906. if (!string.IsNullOrEmpty(file))
  907. args += " -- \"" + file + "\"";
  908. return RunGitCmd(args);
  909. }
  910. public string ResetFile(string file)
  911. {
  912. file = FixPath(file);
  913. return RunGitCmd("checkout-index --index --force -- \"" + file + "\"");
  914. }
  915. public string FormatPatch(string from, string to, string output, int start)
  916. {
  917. output = FixPath(output);
  918. var result = RunCmd(Settings.GitCommand,
  919. "format-patch -M -C -B --start-number " + start + " \"" + from + "\"..\"" + to +
  920. "\" -o \"" + output + "\"");
  921. return result;
  922. }
  923. public string FormatPatch(string from, string to, string output)
  924. {
  925. output = FixPath(output);
  926. var result = RunCmd(Settings.GitCommand,
  927. "format-patch -M -C -B \"" + from + "\"..\"" + to + "\" -o \"" + output + "\"");
  928. return result;
  929. }
  930. public string Tag(string tagName, string revision, bool annotation)
  931. {
  932. return annotation
  933. ? RunCmd(Settings.GitCommand,
  934. "tag \"" + tagName.Trim() + "\" -a -F \"" + WorkingDirGitDir() +
  935. "\\TAGMESSAGE\" -- \"" + revision + "\"")
  936. : RunGitCmd("tag \"" + tagName.Trim() + "\" \"" + revision + "\"");
  937. }
  938. public string Branch(string branchName, string revision, bool checkout)
  939. {
  940. return RunGitCmd(GitCommandHelpers.BranchCmd(branchName, revision, checkout));
  941. }
  942. public string CheckoutFiles(IEnumerable<string> fileList, string revision, bool force)
  943. {
  944. string files = fileList.Select(s => s.Quote()).Join(" ");
  945. return RunGitCmd("checkout " + force.AsForce() + revision.Quote() + " -- " + files);
  946. }
  947. public string Push(string path)
  948. {
  949. return RunGitCmd("push \"" + FixPath(path).Trim() + "\"");
  950. }
  951. public bool StartPageantForRemote(string remote)
  952. {
  953. var sshKeyFile = GetPuttyKeyFileForRemote(remote);
  954. if (string.IsNullOrEmpty(sshKeyFile))
  955. return false;
  956. StartPageantWithKey(sshKeyFile);
  957. return true;
  958. }
  959. public void StartPageantWithKey(string sshKeyFile)
  960. {
  961. StartExternalCommand(Settings.Pageant, "\"" + sshKeyFile + "\"");
  962. }
  963. public string GetPuttyKeyFileForRemote(string remote)
  964. {
  965. if (string.IsNullOrEmpty(remote) ||
  966. string.IsNullOrEmpty(Settings.Pageant) ||
  967. !Settings.AutoStartPageant ||
  968. !GitCommandHelpers.Plink())
  969. return "";
  970. return GetPathSetting(string.Format("remote.{0}.puttykeyfile", remote));
  971. }
  972. public string Fetch(string remote, string branch)
  973. {
  974. remote = FixPath(remote);
  975. Directory.SetCurrentDirectory(_workingdir);
  976. RunRealCmd("cmd.exe", " /k \"\"" + Settings.GitCommand + "\" " + FetchCmd(remote, null, branch) + "\"");
  977. return "Done";
  978. }
  979. public static bool PathIsUrl(string path)
  980. {
  981. return path.Contains(Settings.PathSeparator.ToString()) || path.Contains(Settings.PathSeparatorWrong.ToString());
  982. }
  983. public string FetchCmd(string remote, string remoteBranch, string localBranch)
  984. {
  985. var progressOption = "";
  986. if (GitCommandHelpers.VersionInUse.FetchCanAskForProgress)
  987. progressOption = "--progress ";
  988. if (string.IsNullOrEmpty(remote) && string.IsNullOrEmpty(remoteBranch) && string.IsNullOrEmpty(localBranch))
  989. return "fetch " + progressOption;
  990. return "fetch " + progressOption + GetFetchArgs(remote, remoteBranch, localBranch);
  991. }
  992. public string Pull(string remote, string remoteBranch, string localBranch, bool rebase)
  993. {
  994. remote = FixPath(remote);
  995. Directory.SetCurrentDirectory(_workingdir);
  996. RunRealCmd("cmd.exe", " /k \"\"" + Settings.GitCommand + "\" " + PullCmd(remote, localBranch, remoteBranch, rebase) + "\"");
  997. return "Done";
  998. }
  999. public string PullCmd(string remote, string remoteBranch, string localBranch, bool rebase)
  1000. {
  1001. var progressOption = "";
  1002. if (GitCommandHelpers.VersionInUse.FetchCanAskForProgress)
  1003. progressOption = "--progress ";
  1004. if (rebase && !string.IsNullOrEmpty(remoteBranch))
  1005. return "pull --rebase " + progressOption + remote + " refs/heads/" + remoteBranch;
  1006. if (rebase)
  1007. return "pull --rebase " + progressOption + remote;
  1008. return "pull " + progressOption + GetFetchArgs(remote, remoteBranch, localBranch);
  1009. }
  1010. private string GetFetchArgs(string remote, string remoteBranch, string localBranch)
  1011. {
  1012. remote = FixPath(remote);
  1013. //Remove spaces...
  1014. if (remoteBranch != null)
  1015. remoteBranch = remoteBranch.Replace(" ", "");
  1016. if (localBranch != null)
  1017. localBranch = localBranch.Replace(" ", "");
  1018. string remoteBranchArguments;
  1019. if (string.IsNullOrEmpty(remoteBranch))
  1020. remoteBranchArguments = "";
  1021. else
  1022. remoteBranchArguments = "+refs/heads/" + remoteBranch + "";
  1023. string localBranchArguments;
  1024. var remoteUrl = GetPathSetting(string.Format("remote.{0}.url", remote));
  1025. if (PathIsUrl(remote) && !string.IsNullOrEmpty(localBranch) && string.IsNullOrEmpty(remoteUrl))
  1026. localBranchArguments = ":refs/heads/" + localBranch + "";
  1027. else if (string.IsNullOrEmpty(localBranch) || PathIsUrl(remote) || string.IsNullOrEmpty(remoteUrl))
  1028. localBranchArguments = "";
  1029. else
  1030. localBranchArguments = ":" + "refs/remotes/" + remote.Trim() + "/" + localBranch + "";
  1031. return "\"" + remote.Trim() + "\" " + remoteBranchArguments + localBranchArguments;
  1032. }
  1033. public string ContinueRebase()
  1034. {
  1035. Directory.SetCurrentDirectory(_workingdir);
  1036. var result = RunGitCmd(GitCommandHelpers.ContinueRebaseCmd());
  1037. return result;
  1038. }
  1039. public string SkipRebase()
  1040. {
  1041. Directory.SetCurrentDirectory(_workingdir);
  1042. var result = RunGitCmd(GitCommandHelpers.SkipRebaseCmd());
  1043. return result;
  1044. }
  1045. public string GetRebaseDir()
  1046. {
  1047. string gitDirectory = GetGitDirectory();
  1048. if (Directory.Exists(gitDirectory + "rebase-merge" + Settings.PathSeparator.ToString()))
  1049. return gitDirectory + "rebase-merge" + Settings.PathSeparator.ToString();
  1050. if (Directory.Exists(gitDirectory + "rebase-apply" + Settings.PathSeparator.ToString()))
  1051. return gitDirectory + "rebase-apply" + Settings.PathSeparator.ToString();
  1052. if (Directory.Exists(gitDirectory + "rebase" + Settings.PathSeparator.ToString()))
  1053. return gitDirectory + "rebase" + Settings.PathSeparator.ToString();
  1054. return "";
  1055. }
  1056. public bool InTheMiddleOfBisect()
  1057. {
  1058. return File.Exists(WorkingDirGitDir() + Settings.PathSeparator.ToString() + "BISECT_START");
  1059. }
  1060. public bool InTheMiddleOfRebase()
  1061. {
  1062. return !File.Exists(GetRebaseDir() + "applying") &&
  1063. Directory.Exists(GetRebaseDir());
  1064. }
  1065. public bool InTheMiddleOfPatch()
  1066. {
  1067. return !File.Exists(GetRebaseDir() + "rebasing") &&
  1068. Directory.Exists(GetRebaseDir());
  1069. }
  1070. public string GetNextRebasePatch()
  1071. {
  1072. var file = GetRebaseDir() + "next";
  1073. return File.Exists(file) ? File.ReadAllText(file).Trim() : "";
  1074. }
  1075. public List<PatchFile> GetRebasePatchFiles()
  1076. {
  1077. var patchFiles = new List<PatchFile>();
  1078. var nextFile = GetNextRebasePatch();
  1079. int next;
  1080. int.TryParse(nextFile, out next);
  1081. var files = new string[0];
  1082. if (Directory.Exists(GetRebaseDir()))
  1083. files = Directory.GetFiles(GetRebaseDir());
  1084. foreach (var fullFileName in files)
  1085. {
  1086. int n;
  1087. var file = fullFileName.Substring(fullFileName.LastIndexOf(Settings.PathSeparator.ToString()) + 1);
  1088. if (!int.TryParse(file, out n))
  1089. continue;
  1090. var patchFile =
  1091. new PatchFile
  1092. {
  1093. Name = file,
  1094. FullName = fullFileName,
  1095. IsNext = n == next,
  1096. IsSkipped = n < next
  1097. };
  1098. if (File.Exists(GetRebaseDir() + file))
  1099. {
  1100. foreach (var line in File.ReadAllLines(GetRebaseDir() + file))
  1101. {
  1102. if (line.StartsWith("From: "))
  1103. if (line.IndexOf('<') > 0 && line.IndexOf('<') < line.Length)
  1104. patchFile.Author = line.Substring(6, line.IndexOf('<') - 6).Trim();
  1105. else
  1106. patchFile.Author = line.Substring(6).Trim();
  1107. if (line.StartsWith("Date: "))
  1108. if (line.IndexOf('+') > 0 && line.IndexOf('<') < line.Length)
  1109. patchFile.Date = line.Substring(6, line.IndexOf('+') - 6).Trim();
  1110. else
  1111. patchFile.Date = line.Substring(6).Trim();
  1112. if (line.StartsWith("Subject: ")) patchFile.Subject = line.Substring(9).Trim();
  1113. if (!string.IsNullOrEmpty(patchFile.Author) &&
  1114. !string.IsNullOrEmpty(patchFile.Date) &&
  1115. !string.IsNullOrEmpty(patchFile.Subject))
  1116. break;
  1117. }
  1118. }
  1119. patchFiles.Add(patchFile);
  1120. }
  1121. return patchFiles;
  1122. }
  1123. public string Rebase(string branch)
  1124. {
  1125. Directory.SetCurrentDirectory(_workingdir);
  1126. return RunGitCmd(GitCommandHelpers.RebaseCmd(branch, false, false, false));
  1127. }
  1128. public string AbortRebase()
  1129. {
  1130. Directory.SetCurrentDirectory(_workingdir);
  1131. return RunGitCmd(GitCommandHelpers.AbortRebaseCmd());
  1132. }
  1133. public string Resolved()
  1134. {
  1135. Directory.SetCurrentDirectory(_workingdir);
  1136. return RunGitCmd(GitCommandHelpers.ResolvedCmd());
  1137. }
  1138. public string Skip()
  1139. {
  1140. Directory.SetCurrentDirectory(_workingdir);
  1141. return RunGitCmd(GitCommandHelpers.SkipCmd());
  1142. }
  1143. public string Abort()
  1144. {
  1145. Directory.SetCurrentDirectory(_workingdir);
  1146. return RunGitCmd(GitCommandHelpers.AbortCmd());
  1147. }
  1148. public string Commit(bool amend)
  1149. {
  1150. return Commit(amend, "");
  1151. }
  1152. public string Commit(bool amend, string author)
  1153. {
  1154. return RunGitCmd(CommitCmd(amend, author));
  1155. }
  1156. public string CommitCmd(bool amend)
  1157. {
  1158. return CommitCmd(amend, false, "");
  1159. }
  1160. public string CommitCmd(bool amend, string author)
  1161. {
  1162. return CommitCmd(amend, false, author);
  1163. }
  1164. public string CommitCmd(bool amend, bool signOff, string author)
  1165. {
  1166. string command = "commit";
  1167. if (amend

Large files files are truncated, but you can click here to view the full file