PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 1ms 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
  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)
  1168. command += " --amend";
  1169. if (signOff)
  1170. command += " --signoff";
  1171. if (!string.IsNullOrEmpty(author))
  1172. command += " --author=\"" + author + "\"";
  1173. var path = WorkingDirGitDir() + Settings.PathSeparator.ToString() + "COMMITMESSAGE\"";
  1174. command += " -F \"" + path;
  1175. return command;
  1176. }
  1177. public string Patch(string patchFile)
  1178. {
  1179. Directory.SetCurrentDirectory(_workingdir);
  1180. return RunGitCmd(GitCommandHelpers.PatchCmd(FixPath(patchFile)));
  1181. }
  1182. public string UpdateRemotes()
  1183. {
  1184. return RunGitCmd("remote update");
  1185. }
  1186. public string RemoveRemote(string name)
  1187. {
  1188. return RunGitCmd("remote rm \"" + name + "\"");
  1189. }
  1190. public string RenameRemote(string name, string newName)
  1191. {
  1192. return RunGitCmd("remote rename \"" + name + "\" \"" + newName + "\"");
  1193. }
  1194. public string Rename(string name, string newName)
  1195. {
  1196. return RunGitCmd("branch -m \"" + name + "\" \"" + newName + "\"");
  1197. }
  1198. public string AddRemote(string name, string path)
  1199. {
  1200. var location = FixPath(path);
  1201. if (string.IsNullOrEmpty(name))
  1202. return "Please enter a name.";
  1203. return
  1204. string.IsNullOrEmpty(location)
  1205. ? RunGitCmd(string.Format("remote add \"{0}\" \"\"", name))
  1206. : RunGitCmd(string.Format("remote add \"{0}\" \"{1}\"", name, location));
  1207. }
  1208. public string[] GetRemotes()
  1209. {
  1210. return GetRemotes(true);
  1211. }
  1212. public string[] GetRemotes(bool allowEmpty)
  1213. {
  1214. string remotes = RunGitCmd("remote show");
  1215. return allowEmpty ? remotes.Split('\n') : remotes.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
  1216. }
  1217. public ConfigFile GetLocalConfig()
  1218. {
  1219. return new ConfigFile(WorkingDirGitDir() + Settings.PathSeparator.ToString() + "config", true);
  1220. }
  1221. public string GetSetting(string setting)
  1222. {
  1223. var configFile = GetLocalConfig();
  1224. return configFile.GetValue(setting);
  1225. }
  1226. public string GetPathSetting(string setting)
  1227. {
  1228. var configFile = GetLocalConfig();
  1229. return configFile.GetPathValue(setting);
  1230. }
  1231. public string GetEffectiveSetting(string setting)
  1232. {
  1233. var localConfig = GetLocalConfig();
  1234. if (localConfig.HasValue(setting))
  1235. return localConfig.GetValue(setting);
  1236. return GitCommandHelpers.GetGlobalConfig().GetValue(setting);
  1237. }
  1238. public string GetEffectivePathSetting(string setting)
  1239. {
  1240. var localConfig = GetLocalConfig();
  1241. if (localConfig.HasValue(setting))
  1242. return localConfig.GetPathValue(setting);
  1243. return GitCommandHelpers.GetGlobalConfig().GetPathValue(setting);
  1244. }
  1245. public void UnsetSetting(string setting)
  1246. {
  1247. var configFile = GetLocalConfig();
  1248. configFile.RemoveSetting(setting);
  1249. configFile.Save();
  1250. }
  1251. public void SetSetting(string setting, string value)
  1252. {
  1253. var configFile = GetLocalConfig();
  1254. configFile.SetValue(setting, value);
  1255. configFile.Save();
  1256. }
  1257. public void SetPathSetting(string setting, string value)
  1258. {
  1259. var configFile = GetLocalConfig();
  1260. configFile.SetPathValue(setting, value);
  1261. configFile.Save();
  1262. }
  1263. public List<Patch> GetStashedItems(string stashName)
  1264. {
  1265. var patchManager = new PatchManager();
  1266. patchManager.LoadPatch(RunGitCmd("stash show -p " + stashName, Settings.LosslessEncoding), false, Settings.FilesEncoding);
  1267. return patchManager.Patches;
  1268. }
  1269. public List<GitStash> GetStashes()
  1270. {
  1271. var list = RunGitCmd("stash list").Split('\n');
  1272. var stashes = new List<GitStash>();
  1273. foreach (var stashString in list)
  1274. {
  1275. if (stashString.IndexOf(':') <= 0)
  1276. continue;
  1277. var stash = new GitStash
  1278. {
  1279. Name = stashString.Substring(0, stashString.IndexOf(':')).Trim()
  1280. };
  1281. if (stashString.IndexOf(':') + 1 < stashString.Length)
  1282. stash.Message = stashString.Substring(stashString.IndexOf(':') + 1).Trim();
  1283. stashes.Add(stash);
  1284. }
  1285. return stashes;
  1286. }
  1287. public Patch GetSingleDiff(string @from, string to, string fileName, string oldFileName, string extraDiffArguments, Encoding encoding)
  1288. {
  1289. if (!string.IsNullOrEmpty(fileName))
  1290. fileName = string.Concat("\"", FixPath(fileName), "\"");
  1291. if (!string.IsNullOrEmpty(oldFileName))
  1292. oldFileName = string.Concat("\"", FixPath(oldFileName), "\"");
  1293. from = FixPath(from);
  1294. to = FixPath(to);
  1295. string commitRange = string.Empty;
  1296. if (!to.IsNullOrEmpty())
  1297. commitRange = "\"" + to + "\"";
  1298. if (!from.IsNullOrEmpty())
  1299. commitRange = commitRange.Join(" ", "\"" + from + "\"");
  1300. if (Settings.UsePatienceDiffAlgorithm)
  1301. extraDiffArguments = string.Concat(extraDiffArguments, " --patience");
  1302. var patchManager = new PatchManager();
  1303. var arguments = string.Format("diff {0} -M -C {1} -- {2} {3}", extraDiffArguments, commitRange, fileName, oldFileName);
  1304. patchManager.LoadPatch(this.RunCachableCmd(Settings.GitCommand, arguments, Settings.LosslessEncoding), false, encoding);
  1305. return patchManager.Patches.Count > 0 ? patchManager.Patches[patchManager.Patches.Count - 1] : null;
  1306. }
  1307. public Patch GetSingleDiff(string @from, string to, string fileName, string extraDiffArguments, Encoding encoding)
  1308. {
  1309. return this.GetSingleDiff(from, to, fileName, null, extraDiffArguments, encoding);
  1310. }
  1311. public string GetStatusText(bool untracked)
  1312. {
  1313. string cmd = "status -s";
  1314. if (untracked)
  1315. cmd = cmd + " -u";
  1316. return RunGitCmd(cmd);
  1317. }
  1318. public string GetDiffFilesText(string from, string to)
  1319. {
  1320. return GetDiffFilesText(from, to, false);
  1321. }
  1322. public string GetDiffFilesText(string from, string to, bool noCache)
  1323. {
  1324. string cmd = "diff -M -C --name-status \"" + to + "\" \"" + from + "\"";
  1325. return noCache ? RunGitCmd(cmd) : this.RunCachableCmd(Settings.GitCommand, cmd, Settings.SystemEncoding);
  1326. }
  1327. public List<GitItemStatus> GetDiffFiles(string from, string to)
  1328. {
  1329. return GetDiffFiles(from, to, false);
  1330. }
  1331. public List<GitItemStatus> GetDiffFiles(string from, string to, bool noCache)
  1332. {
  1333. string cmd = "diff -M -C -z --name-status \"" + to + "\" \"" + from + "\"";
  1334. string result = noCache ? RunGitCmd(cmd) : this.RunCachableCmd(Settings.GitCommand, cmd, Settings.SystemEncoding);
  1335. return GitCommandHelpers.GetAllChangedFilesFromString(result, true);
  1336. }
  1337. public List<GitItemStatus> GetStashDiffFiles(string stashName)
  1338. {
  1339. bool gitShowsUntrackedFiles = false;
  1340. var list = GetDiffFiles(stashName, stashName + "^", true);
  1341. if (!gitShowsUntrackedFiles)
  1342. {
  1343. string untrackedTreeHash = RunGitCmd("log " + stashName + "^3 --pretty=format:\"%T\" --max-count=1");
  1344. if (GitRevision.Sha1HashRegex.IsMatch(untrackedTreeHash))
  1345. list.AddRange(GetTreeFiles(untrackedTreeHash, true));
  1346. }
  1347. return list;
  1348. }
  1349. public List<GitItemStatus> GetUntrackedFiles()
  1350. {
  1351. var status = RunCmd(Settings.GitCommand,
  1352. "ls-files -z --others --directory --no-empty-directory --exclude-standard");
  1353. return status.Split(new char[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries)
  1354. .Select(statusString => statusString.Trim())
  1355. .Where(statusString => !string.IsNullOrEmpty(statusString))
  1356. .Select(statusString => new GitItemStatus
  1357. {
  1358. IsNew = true,
  1359. IsChanged = false,
  1360. IsDeleted = false,
  1361. IsTracked = false,
  1362. Name = statusString
  1363. })
  1364. .ToList();
  1365. }
  1366. public List<GitItemStatus> GetTreeFiles(string treeGuid, bool full)
  1367. {
  1368. var tree = GetTree(treeGuid, full);
  1369. return tree
  1370. .Select(file => new GitItemStatus
  1371. {
  1372. IsNew = true,
  1373. IsChanged = false,
  1374. IsDeleted = false,
  1375. IsStaged = false,
  1376. Name = file.Name,
  1377. TreeGuid = file.Guid
  1378. })
  1379. .ToList();
  1380. }
  1381. public List<GitItemStatus> GetAllChangedFiles()
  1382. {
  1383. return GetAllChangedFiles(true, true);
  1384. }
  1385. public List<GitItemStatus> GetAllChangedFiles(bool excludeIgnoredFiles, bool untrackedFiles)
  1386. {
  1387. var status = RunGitCmd(GitCommandHelpers.GetAllChangedFilesCmd(excludeIgnoredFiles, untrackedFiles));
  1388. return GitCommandHelpers.GetAllChangedFilesFromString(status);
  1389. }
  1390. public List<GitItemStatus> GetTrackedChangedFiles()
  1391. {
  1392. var status = RunGitCmd(GitCommandHelpers.GetAllChangedFilesCmd(true, false));
  1393. return GitCommandHelpers.GetAllChangedFilesFromString(status);
  1394. }
  1395. public List<GitItemStatus> GetDeletedFiles()
  1396. {
  1397. var status = RunGitCmd("ls-files -z --deleted --exclude-standard");
  1398. var statusStrings = status.Split(new char[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  1399. var gitItemStatusList = new List<GitItemStatus>();
  1400. foreach (var statusString in statusStrings)
  1401. {
  1402. if (string.IsNullOrEmpty(statusString.Trim()))
  1403. continue;
  1404. gitItemStatusList.Add(
  1405. new GitItemStatus
  1406. {
  1407. IsNew = false,
  1408. IsChanged = false,
  1409. IsDeleted = true,
  1410. IsTracked = true,
  1411. Name = statusString.Trim()
  1412. });
  1413. }
  1414. return gitItemStatusList;
  1415. }
  1416. public bool FileIsStaged(string filename)
  1417. {
  1418. var status = RunGitCmd("diff -z --cached --numstat -- \"" + filename + "\"");
  1419. return !string.IsNullOrEmpty(status);
  1420. }
  1421. public IList<GitItemStatus> GetStagedFiles()
  1422. {
  1423. string status = RunGitCmd("diff -M -C -z --cached --name-status", Settings.SystemEncoding);
  1424. if (status.Length < 50 && status.Contains("fatal: No HEAD commit to compare"))
  1425. {
  1426. //This command is a little more expensive because it will return both staged and unstaged files
  1427. string command = GitCommandHelpers.GetAllChangedFilesCmd(true, false);
  1428. status = RunGitCmd(command, Settings.SystemEncoding);
  1429. List<GitItemStatus> stagedFiles = GitCommandHelpers.GetAllChangedFilesFromString(status, false);
  1430. return stagedFiles.Where(f => f.IsStaged).ToList<GitItemStatus>();
  1431. }
  1432. return GitCommandHelpers.GetAllChangedFilesFromString(status, true);
  1433. }
  1434. public IList<GitItemStatus> GetUnstagedFiles()
  1435. {
  1436. return GetAllChangedFiles().Where(x => !x.IsStaged).ToArray();
  1437. }
  1438. public IList<GitItemStatus> GitStatus()
  1439. {
  1440. return GitStatus(UntrackedFilesMode.Default, 0);
  1441. }
  1442. public IList<GitItemStatus> GitStatus(UntrackedFilesMode untrackedFilesMode, IgnoreSubmodulesMode ignoreSubmodulesMode)
  1443. {
  1444. if (!GitCommandHelpers.VersionInUse.SupportGitStatusPorcelain)
  1445. throw new Exception("The version of git you are using is not supported for this action. Please upgrade to git 1.7.3 or newer.");
  1446. string command = GitCommandHelpers.GetAllChangedFilesCmd(true, untrackedFilesMode, ignoreSubmodulesMode);
  1447. string status = RunGitCmd(command);
  1448. return GitCommandHelpers.GetAllChangedFilesFromString(status);
  1449. }
  1450. public string GetCurrentChanges(string fileName, string oldFileName, bool staged, string extraDiffArguments, Encoding encoding)
  1451. {
  1452. fileName = string.Concat("\"", FixPath(fileName), "\"");
  1453. if (!string.IsNullOrEmpty(oldFileName))
  1454. oldFileName = string.Concat("\"", FixPath(oldFileName), "\"");
  1455. if (Settings.UsePatienceDiffAlgorithm)
  1456. extraDiffArguments = string.Concat(extraDiffArguments, " --patience");
  1457. var args = string.Concat("diff ", extraDiffArguments, " -- ", fileName);
  1458. if (staged)
  1459. args = string.Concat("diff -M -C --cached", extraDiffArguments, " -- ", fileName, " ", oldFileName);
  1460. String result = RunGitCmd(args, Settings.LosslessEncoding);
  1461. var patchManager = new PatchManager();
  1462. patchManager.LoadPatch(result, false, encoding);
  1463. return patchManager.Patches.Count > 0 ? patchManager.Patches[patchManager.Patches.Count - 1].Text : string.Empty;
  1464. }
  1465. public string StageFile(string file)
  1466. {
  1467. return RunGitCmd("update-index --add" + " \"" + FixPath(file) + "\"");
  1468. }
  1469. public string StageFileToRemove(string file)
  1470. {
  1471. return RunGitCmd("update-index --remove" + " \"" + FixPath(file) + "\"");
  1472. }
  1473. public string UnstageFile(string file)
  1474. {
  1475. return RunGitCmd("rm" + " --cached \"" + FixPath(file) + "\"");
  1476. }
  1477. public string UnstageFileToRemove(string file)
  1478. {
  1479. return RunGitCmd("reset HEAD -- \"" + FixPath(file) + "\"");
  1480. }
  1481. public static bool IsBareRepository(string repositoryPath)
  1482. {
  1483. return !Directory.Exists(GetGitDirectory(repositoryPath));
  1484. }
  1485. /// <summary>
  1486. /// Dirty but fast. This sometimes fails.
  1487. /// </summary>
  1488. public static string GetSelectedBranchFast(string repositoryPath)
  1489. {
  1490. if (string.IsNullOrEmpty(repositoryPath))
  1491. return string.Empty;
  1492. string head;
  1493. string headFileName = Path.Combine(WorkingDirGitDir(repositoryPath), "HEAD");
  1494. if (File.Exists(headFileName))
  1495. {
  1496. head = File.ReadAllText(headFileName, Settings.SystemEncoding);
  1497. if (!head.Contains("ref:"))
  1498. return "(no branch)";
  1499. }
  1500. else
  1501. {
  1502. return string.Empty;
  1503. }
  1504. if (!string.IsNullOrEmpty(head))
  1505. {
  1506. return head.Replace("ref:", "").Replace("refs/heads/", string.Empty).Trim();
  1507. }
  1508. return string.Empty;
  1509. }
  1510. public string GetSelectedBranch(string repositoryPath)
  1511. {
  1512. string head = GetSelectedBranchFast(repositoryPath);
  1513. if (string.IsNullOrEmpty(head))
  1514. {
  1515. int exitcode;
  1516. head = RunGitCmd("symbolic-ref HEAD", out exitcode);
  1517. if (exitcode == 1)
  1518. return "(no branch)";
  1519. }
  1520. return head;
  1521. }
  1522. public string GetSelectedBranch()
  1523. {
  1524. return GetSelectedBranch(_workingdir);
  1525. }
  1526. public string GetRemoteBranch(string branch)
  1527. {
  1528. string remote = GetSetting(string.Format("branch.{0}.remote", branch));
  1529. string merge = GetSetting(string.Format("branch.{0}.merge", branch));
  1530. if (String.IsNullOrEmpty(remote) || String.IsNullOrEmpty(merge))
  1531. return "";
  1532. return remote + "/" + (merge.StartsWith("refs/heads/") ? merge.Substring(11) : merge);
  1533. }
  1534. public List<GitHead> GetRemoteHeads(string remote, bool tags, bool branches)
  1535. {
  1536. remote = FixPath(remote);
  1537. var tree = GetTreeFromRemoteHeads(remote, tags, branches);
  1538. return GetHeads(tree);
  1539. }
  1540. private string GetTreeFromRemoteHeads(string remote, bool tags, bool branches)
  1541. {
  1542. if (tags && branches)
  1543. return RunGitCmd("ls-remote --heads --tags \"" + remote + "\"");
  1544. if (tags)
  1545. return RunGitCmd("ls-remote --tags \"" + remote + "\"");
  1546. if (branches)
  1547. return RunGitCmd("ls-remote --heads \"" + remote + "\"");
  1548. return "";
  1549. }
  1550. public List<GitHead> GetHeads()
  1551. {
  1552. return GetHeads(true);
  1553. }
  1554. public List<GitHead> GetHeads(bool tags)
  1555. {
  1556. return GetHeads(tags, true);
  1557. }
  1558. public List<GitHead> GetHeads(bool tags, bool branches)
  1559. {
  1560. var tree = GetTree(tags, branches);
  1561. return GetHeads(tree);
  1562. }
  1563. public ICollection<string> GetMergedBranches()
  1564. {
  1565. return Settings.Module.RunGitCmd(GitCommandHelpers.MergedBranches()).Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
  1566. }
  1567. private string GetTree(bool tags, bool branches)
  1568. {
  1569. if (tags && branches)
  1570. return RunGitCmd("show-ref --dereference", Settings.SystemEncoding);
  1571. if (tags)
  1572. return RunGitCmd("show-ref --tags", Settings.SystemEncoding);
  1573. if (branches)
  1574. return RunGitCmd("show-ref --dereference --heads", Settings.SystemEncoding);
  1575. return "";
  1576. }
  1577. private List<GitHead> GetHeads(string tree)
  1578. {
  1579. var itemsStrings = tree.Split('\n');
  1580. var heads = new List<GitHead>();
  1581. var defaultHeads = new Dictionary<string, GitHead>(); // remote -> HEAD
  1582. var remotes = GetRemotes(false);
  1583. foreach (var itemsString in itemsStrings)
  1584. {
  1585. if (itemsString == null || itemsString.Length <= 42)
  1586. continue;
  1587. var completeName = itemsString.Substring(41).Trim();
  1588. var guid = itemsString.Substring(0, 40);
  1589. var remoteName = GetRemoteName(completeName, remotes);
  1590. var head = new GitHead(guid, completeName, remoteName);
  1591. if (DefaultHeadPattern.IsMatch(completeName))
  1592. defaultHeads[remoteName] = head;
  1593. else
  1594. heads.Add(head);
  1595. }
  1596. // do not show default head if remote has a branch on the same commit
  1597. GitHead defaultHead;
  1598. foreach (var head in heads.Where(head => defaultHeads.TryGetValue(head.Remote, out defaultHead) && head.Guid == defaultHead.Guid))
  1599. {
  1600. defaultHeads.Remove(head.Remote);
  1601. }
  1602. heads.AddRange(defaultHeads.Values);
  1603. return heads;
  1604. }
  1605. public static string GetRemoteName(string completeName, IEnumerable<string> remotes)
  1606. {
  1607. string trimmedName = completeName.StartsWith("refs/remotes/") ? completeName.Substring(13) : completeName;
  1608. foreach (string remote in remotes)
  1609. {
  1610. if (trimmedName.StartsWith(string.Concat(remote, "/")))
  1611. return remote;
  1612. }
  1613. return string.Empty;
  1614. }
  1615. public IList<string> GetFiles(IEnumerable<string> filePatterns)
  1616. {
  1617. var quotedPatterns = filePatterns
  1618. .Where(pattern => !pattern.Contains("\""))
  1619. .Select(pattern => pattern.Quote())
  1620. .Join(" ");
  1621. // filter duplicates out of the result because options -c and -m may return
  1622. // same files at times
  1623. return RunGitCmd("ls-files -z -o -m -c " + quotedPatterns)
  1624. .Split(new[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries)
  1625. .Distinct()
  1626. .ToList();
  1627. }
  1628. public List<GitItem> GetFileChanges(string file)
  1629. {
  1630. file = FixPath(file);
  1631. var tree = RunGitCmd("whatchanged --all -- \"" + file + "\"");
  1632. var itemsStrings = tree.Split('\n');
  1633. var items = new List<GitItem>();
  1634. GitItem item = null;
  1635. foreach (var itemsString in itemsStrings)
  1636. {
  1637. if (itemsString.StartsWith("commit "))
  1638. {
  1639. item = new GitItem { CommitGuid = itemsString.Substring(7).Trim() };
  1640. items.Add(item);
  1641. }
  1642. else if (item == null)
  1643. {
  1644. continue;
  1645. }
  1646. else if (itemsString.StartsWith("Author: "))
  1647. {
  1648. item.Author = itemsString.Substring(7).Trim();
  1649. }
  1650. else if (itemsString.StartsWith("Date: "))
  1651. {
  1652. item.Date = itemsString.Substring(7).Trim();
  1653. }
  1654. else if (!itemsString.StartsWith(":") && !string.IsNullOrEmpty(itemsString))
  1655. {
  1656. item.Name += itemsString.Trim() + Environment.NewLine;
  1657. }
  1658. else
  1659. {
  1660. if (itemsString.Length > 32)
  1661. item.Guid = itemsString.Substring(26, 7);
  1662. }
  1663. }
  1664. return items;
  1665. }
  1666. public string[] GetFullTree(string id)
  1667. {
  1668. string tree = this.RunCachableCmd(Settings.GitCommand, string.Format("ls-tree -z -r --name-only {0}", id), Settings.SystemEncoding);
  1669. return tree.Split(new char[] { '\0', '\n' });
  1670. }
  1671. public List<IGitItem> GetTree(string id, bool full)
  1672. {
  1673. string args = "-z".Join(" ", full ? "-r" : string.Empty);
  1674. var tree = this.RunCachableCmd(Settings.GitCommand, "ls-tree " + args + " \"" + id + "\"", Settings.SystemEncoding);
  1675. return GitItem.CreateIGitItemsFromString(tree);
  1676. }
  1677. public GitBlame Blame(string filename, string from)
  1678. {
  1679. return Blame(filename, from, null);
  1680. }
  1681. public GitBlame Blame(string filename, string from, string lines)
  1682. {
  1683. from = FixPath(from);
  1684. filename = FixPath(filename);
  1685. string blameCommand = string.Format("blame --porcelain -M -w -l{0} \"{1}\" -- \"{2}\"", lines != null ? " -L " + lines : "", from, filename);
  1686. var itemsStrings =
  1687. RunCachableCmd(
  1688. Settings.GitCommand,
  1689. blameCommand,
  1690. Settings.LosslessEncoding
  1691. )
  1692. .Split('\n');
  1693. GitBlame blame = new GitBlame();
  1694. GitBlameHeader blameHeader = null;
  1695. GitBlameLine blameLine = null;
  1696. for (int i = 0; i < itemsStrings.GetLength(0); i++)
  1697. {
  1698. try
  1699. {
  1700. string line = itemsStrings[i];
  1701. //The contents of the actual line is output after the above header, prefixed by a TAB. This is to allow adding more header elements later.
  1702. if (line.StartsWith("\t"))
  1703. {
  1704. blameLine.LineText = line.Substring(1) //trim ONLY first tab
  1705. .Trim(new char[] { '\r' }); //trim \r, this is a workaround for a \r\n bug
  1706. blameLine.LineText = GitCommandHelpers.ReEncodeStringFromLossless(blameLine.LineText, Settings.FilesEncoding);
  1707. }
  1708. else if (line.StartsWith("author-mail"))
  1709. blameHeader.AuthorMail = GitCommandHelpers.ReEncodeStringFromLossless(line.Substring("author-mail".Length).Trim());
  1710. else if (line.StartsWith("author-time"))
  1711. blameHeader.AuthorTime = DateTimeUtils.ParseUnixTime(line.Substring("author-time".Length).Trim());
  1712. else if (line.StartsWith("author-tz"))
  1713. blameHeader.AuthorTimeZone = line.Substring("author-tz".Length).Trim();
  1714. else if (line.StartsWith("author"))
  1715. {
  1716. blameHeader = new GitBlameHeader();
  1717. blameHeader.CommitGuid = blameLine.CommitGuid;
  1718. blameHeader.Author = GitCommandHelpers.ReEncodeStringFromLossless(line.Substring("author".Length).Trim());
  1719. blame.Headers.Add(blameHeader);
  1720. }
  1721. else if (line.StartsWith("committer-mail"))
  1722. blameHeader.CommitterMail = line.Substring("committer-mail".Length).Trim();
  1723. else if (line.StartsWith("committer-time"))
  1724. blameHeader.CommitterTime = DateTimeUtils.ParseUnixTime(line.Substring("committer-time".Length).Trim());
  1725. else if (line.StartsWith("committer-tz"))
  1726. blameHeader.CommitterTimeZone = line.Substring("committer-tz".Length).Trim();
  1727. else if (line.StartsWith("committer"))
  1728. blameHeader.Committer = GitCommandHelpers.ReEncodeStringFromLossless(line.Substring("committer".Length).Trim());
  1729. else if (line.StartsWith("summary"))
  1730. blameHeader.Summary = GitCommandHelpers.ReEncodeStringFromLossless(line.Substring("summary".Length).Trim());
  1731. else if (line.StartsWith("filename"))
  1732. blameHeader.FileName = GitCommandHelpers.ReEncodeFileNameFromLossless(line.Substring("filename".Length).Trim());
  1733. else if (line.IndexOf(' ') == 40) //SHA1, create new line!
  1734. {
  1735. blameLine = new GitBlameLine();
  1736. blameLine.CommitGuid = line.Substring(0, 40);
  1737. blame.Lines.Add(blameLine);
  1738. }
  1739. }
  1740. catch
  1741. {
  1742. //Catch all parser errors, and ignore them all!
  1743. //We should never get here...
  1744. Settings.GitLog.Log("Error parsing output from command: " + blameCommand + "\n\nPlease report a bug!");
  1745. }
  1746. }
  1747. return blame;
  1748. }
  1749. public string GetFileRevisionText(string file, string revision, Encoding encoding)
  1750. {
  1751. return
  1752. this.RunCachableCmd(
  1753. Settings.GitCommand,
  1754. string.Format("show {0}:\"{1}\"", revision, file.Replace('\\', '/')), encoding);
  1755. }
  1756. public string GetFileText(string id, Encoding encoding)
  1757. {
  1758. return RunCachableCmd(Settings.GitCommand, "cat-file blob \"" + id + "\"", encoding);
  1759. }
  1760. public string GetFileBlobHash(string fileName, string revision)
  1761. {
  1762. if (revision == GitRevision.UncommittedWorkingDirGuid) //working dir changes
  1763. {
  1764. return null;
  1765. }
  1766. else if (revision == GitRevision.IndexGuid) //index
  1767. {
  1768. string blob = RunGitCmd(string.Format("ls-files -s \"{0}\"", fileName));
  1769. string[] s = blob.Split(new char[] { ' ', '\t' });
  1770. if (s.Length >= 2)
  1771. return s[1];
  1772. else
  1773. return string.Empty;
  1774. }
  1775. else
  1776. {
  1777. string blob = RunGitCmd(string.Format("ls-tree -r {0} \"{1}\"", revision, fileName));
  1778. string[] s = blob.Split(new char[] { ' ', '\t' });
  1779. if (s.Length >= 3)
  1780. return s[2];
  1781. else
  1782. return string.Empty;
  1783. }
  1784. }
  1785. public static void StreamCopy(Stream input, Stream output)
  1786. {
  1787. int read;
  1788. var buffer = new byte[2048];
  1789. do
  1790. {
  1791. read = input.Read(buffer, 0, buffer.Length);
  1792. output.Write(buffer, 0, read);
  1793. } while (read > 0);
  1794. }
  1795. public Stream GetFileStream(string blob)
  1796. {
  1797. try
  1798. {
  1799. var newStream = new MemoryStream();
  1800. GitCommandHelpers.SetEnvironmentVariable();
  1801. Settings.GitLog.Log(Settings.GitCommand + " " + "cat-file blob " + blob);
  1802. //process used to execute external commands
  1803. var info = new ProcessStartInfo()
  1804. {
  1805. UseShellExecute = false,
  1806. ErrorDialog = false,
  1807. RedirectStandardOutput = true,
  1808. RedirectStandardInput = false,
  1809. RedirectStandardError = false,
  1810. CreateNoWindow = true,
  1811. FileName = "\"" + Settings.GitCommand + "\"",
  1812. Arguments = "cat-file blob " + blob,
  1813. WorkingDirectory = _workingdir,
  1814. WindowStyle = ProcessWindowStyle.Normal,
  1815. LoadUserProfile = true
  1816. };
  1817. using (var process = Process.Start(info))
  1818. {
  1819. StreamCopy(process.StandardOutput.BaseStream, newStream);
  1820. newStream.Position = 0;
  1821. process.WaitForExit();
  1822. return newStream;
  1823. }
  1824. }
  1825. catch (Win32Exception ex)
  1826. {
  1827. Trace.WriteLine(ex);
  1828. }
  1829. return null;
  1830. }
  1831. public string GetPreviousCommitMessage(int numberBack)
  1832. {
  1833. string msg = RunCmd(Settings.GitCommand, "log -n 1 HEAD~" + numberBack + " --pretty=format:%e%n%s%n%n%b ", Settings.LosslessEncoding);
  1834. int idx = msg.IndexOf("\n");
  1835. string encodingName = msg.Substring(0, idx);
  1836. msg = msg.Substring(idx + 1, msg.Length - idx - 1);
  1837. msg = GitCommandHelpers.ReEncodeCommitMessage(msg, encodingName);
  1838. return msg;
  1839. }
  1840. public string MergeBranch(string branch)
  1841. {
  1842. return RunGitCmd(GitCommandHelpers.MergeBranchCmd(branch, true, false, false, null));
  1843. }
  1844. public string OpenWithDifftool(string filename)
  1845. {
  1846. return OpenWithDifftool(filename, null, null);
  1847. }
  1848. public string OpenWithDifftool(string filename, string revision1)
  1849. {
  1850. return OpenWithDifftool(filename, revision1, null);
  1851. }
  1852. public string OpenWithDifftool(string filename, string revision1, string revision2)
  1853. {
  1854. return OpenWithDifftool(filename, revision1, revision2, string.Empty);
  1855. }
  1856. public string OpenWithDifftool(string filename, string revision1, string revision2, string extraDiffArguments)
  1857. {
  1858. var output = "";
  1859. string args = extraDiffArguments.Join(" ", revision2).Join(" ", revision1).Join(" ", "-- \"" + filename + "\"");
  1860. if (GitCommandHelpers.VersionInUse.GuiDiffToolExist)
  1861. RunCmdAsync(Settings.GitCommand,
  1862. "difftool --gui --no-prompt " + args);
  1863. else
  1864. output = RunCmd(Settings.GitCommand,
  1865. "difftool --no-prompt " + args);
  1866. return output;
  1867. }
  1868. public string RevParse(string revisionExpression)
  1869. {
  1870. string revparseCommand = string.Format("rev-parse \"{0}\"", revisionExpression);
  1871. int exitCode = 0;
  1872. string[] resultStrings = RunCmd(Settings.GitCommand, revparseCommand, out exitCode, "").Split('\n');
  1873. return exitCode == 0 ? resultStrings[0] : "";
  1874. }
  1875. public static string WorkingDirGitDir(string repositoryPath)
  1876. {
  1877. if (string.IsNullOrEmpty(repositoryPath))
  1878. return repositoryPath;
  1879. var candidatePath = GetGitDirectory(repositoryPath);
  1880. return Directory.Exists(candidatePath) ? candidatePath : repositoryPath;
  1881. }
  1882. // NOTE: probably the rules should be inlined to avoid external process call overhead
  1883. /// <summary>
  1884. /// Uses check-ref-format to ensure that a reference name is well formed.
  1885. /// </summary>
  1886. /// <param name="refName">Reference name to test.</param>
  1887. /// <returns>true if <see cref="refName"/> is valid reference name, otherwise false.</returns>
  1888. public bool CheckRefFormat([NotNull] string refName)
  1889. {
  1890. if (refName == null)
  1891. throw new ArgumentNullException("refName");
  1892. if (refName.IsNullOrWhiteSpace())
  1893. return false;
  1894. refName = refName.Replace("\"", "\\\"");
  1895. int exitCode;
  1896. RunCmd(Settings.GitCommand, string.Format("check-ref-format --allow-onelevel \"{0}\"", refName), out exitCode);
  1897. return exitCode == 0;
  1898. }
  1899. }
  1900. }