/SparkleLib/Hg/SparkleRepoHg.cs

http://github.com/hbons/SparkleShare · C# · 319 lines · 213 code · 84 blank · 22 comment · 26 complexity · dd090276f739aa789e45ca90baabcb94 MD5 · raw file

  1. // SparkleShare, a collaboration and sharing tool.
  2. // Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.IO;
  20. using System.Text.RegularExpressions;
  21. namespace SparkleLib {
  22. public class SparkleRepoHg : SparkleRepoBase {
  23. public SparkleRepoHg (string path, SparkleBackend backend) :
  24. base (path, backend) { }
  25. public override string Identifier {
  26. get {
  27. SparkleHg hg = new SparkleHg (LocalPath, "log -r : --limit 1 --template \"{node}\"");
  28. hg.Start ();
  29. hg.WaitForExit ();
  30. return hg.StandardOutput.ReadToEnd ();
  31. }
  32. }
  33. public override string CurrentRevision {
  34. get {
  35. SparkleHg hg = new SparkleHg (LocalPath, "log --limit 1 --template \"{node}\"");
  36. hg.Start ();
  37. hg.WaitForExit ();
  38. string hash = hg.StandardOutput.ReadToEnd ().Trim ();
  39. if (hash.Length > 0)
  40. return hash;
  41. else
  42. return null;
  43. }
  44. }
  45. public override bool CheckForRemoteChanges ()
  46. {
  47. return true; // Mercurial doesn't have a way to check for the remote hash
  48. }
  49. public override bool SyncUp ()
  50. {
  51. Add ();
  52. string message = FormatCommitMessage ();
  53. Commit (message);
  54. SparkleHg hg = new SparkleHg (LocalPath, "push");
  55. hg.Start ();
  56. hg.WaitForExit ();
  57. if (hg.ExitCode == 0) {
  58. return true;
  59. } else {
  60. return false;
  61. }
  62. }
  63. public override bool SyncDown ()
  64. {
  65. SparkleHg hg = new SparkleHg (LocalPath, "pull");
  66. hg.Start ();
  67. hg.WaitForExit ();
  68. if (hg.ExitCode == 0) {
  69. Merge ();
  70. return true;
  71. } else {
  72. return false;
  73. }
  74. }
  75. public override bool AnyDifferences {
  76. get {
  77. SparkleHg hg = new SparkleHg (LocalPath, "status");
  78. hg.Start ();
  79. hg.WaitForExit ();
  80. string output = hg.StandardOutput.ReadToEnd ().TrimEnd ();
  81. string [] lines = output.Split ("\n".ToCharArray ());
  82. foreach (string line in lines) {
  83. if (line.Length > 1 && !line [1].Equals (" "))
  84. return true;
  85. }
  86. return false;
  87. }
  88. }
  89. public override bool HasUnsyncedChanges {
  90. get {
  91. string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
  92. ".hg", "has_unsynced_changes");
  93. return File.Exists (unsynced_file_path);
  94. }
  95. set {
  96. string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath,
  97. ".hg", "has_unsynced_changes");
  98. if (value) {
  99. if (!File.Exists (unsynced_file_path))
  100. File.Create (unsynced_file_path).Close ();
  101. } else {
  102. File.Delete (unsynced_file_path);
  103. }
  104. }
  105. }
  106. // Stages the made changes
  107. private void Add ()
  108. {
  109. SparkleHg hg = new SparkleHg (LocalPath, "addremove --quiet");
  110. hg.Start ();
  111. hg.WaitForExit ();
  112. SparkleHelpers.DebugInfo ("Hg", "[" + Name + "] Changes staged");
  113. }
  114. // Commits the made changes
  115. private void Commit (string message)
  116. {
  117. if (!AnyDifferences)
  118. return;
  119. SparkleHg hg = new SparkleHg (LocalPath, "commit -m '" + message + "'");
  120. hg.Start ();
  121. hg.WaitForExit ();
  122. SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message);
  123. }
  124. // Merges the fetched changes
  125. private void Merge ()
  126. {
  127. DisableWatching ();
  128. if (AnyDifferences) {
  129. Add ();
  130. string commit_message = FormatCommitMessage ();
  131. Commit (commit_message);
  132. }
  133. SparkleHg hg = new SparkleHg (LocalPath, "update");
  134. hg.Start ();
  135. hg.WaitForExit ();
  136. EnableWatching ();
  137. }
  138. // Returns a list of the latest change sets
  139. public override List<SparkleChangeSet> GetChangeSets (int count)
  140. {
  141. if (count < 1)
  142. count = 30;
  143. List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> ();
  144. SparkleHg hg_log = new SparkleHg (LocalPath, "log --limit " + count + " --style changelog --verbose --stat");
  145. Console.OutputEncoding = System.Text.Encoding.Unicode;
  146. hg_log.Start ();
  147. // Reading the standard output HAS to go before
  148. // WaitForExit, or it will hang forever on output > 4096 bytes
  149. string output = hg_log.StandardOutput.ReadToEnd ();
  150. hg_log.WaitForExit ();
  151. string [] lines = output.Split ("\n".ToCharArray ());
  152. List <string> entries = new List <string> ();
  153. int j = 0;
  154. string entry = "", last_entry = "";
  155. foreach (string line in lines) {
  156. if (line.StartsWith ("2") && line.EndsWith (")") && j > 0) {
  157. entries.Add (entry);
  158. entry = "";
  159. }
  160. entry += line + "\n";
  161. j++;
  162. last_entry = entry;
  163. }
  164. entries.Add (last_entry);
  165. Regex regex = new Regex (@"([0-9]{4})-([0-9]{2})-([0-9]{2}).*([0-9]{2}):([0-9]{2}).*.([0-9]{4})" +
  166. "(.+)<(.+)>.*.([a-z0-9]{12})", RegexOptions.Compiled);
  167. foreach (string log_entry in entries) {
  168. bool is_merge_commit = false;
  169. Match match = regex.Match (log_entry);
  170. if (!match.Success)
  171. continue;
  172. SparkleChangeSet change_set = new SparkleChangeSet () {
  173. Revision = match.Groups [9].Value,
  174. IsMagical = is_merge_commit
  175. };
  176. change_set.User.Name = match.Groups [7].Value.Trim ();
  177. change_set.User.Email = match.Groups [8].Value;
  178. change_set.Timestamp = new DateTime (int.Parse (match.Groups [1].Value),
  179. int.Parse (match.Groups [2].Value), int.Parse (match.Groups [3].Value),
  180. int.Parse (match.Groups [4].Value), int.Parse (match.Groups [5].Value), 0);
  181. string [] entry_lines = log_entry.Split ("\n".ToCharArray ());
  182. foreach (string entry_line in entry_lines) {
  183. if (!entry_line.StartsWith ("\t* "))
  184. continue;
  185. if (entry_line.EndsWith ("new file.")) {
  186. string files = entry_line.Substring (3, entry_line.Length - 13);
  187. string [] added_files = files.Split (",".ToCharArray ());
  188. foreach (string added_file in added_files) {
  189. string file = added_file.TrimEnd (": ".ToCharArray ());
  190. change_set.Added.Add (file);
  191. }
  192. } else if (entry_line.EndsWith ("deleted file.")) {
  193. string files = entry_line.Substring (3, entry_line.Length - 17);
  194. string [] deleted_files = files.Split (",".ToCharArray ());
  195. foreach (string deleted_file in deleted_files) {
  196. string file = deleted_file.TrimEnd (": ".ToCharArray ());
  197. change_set.Deleted.Add (file);
  198. }
  199. } else if (!"".Equals (entry_line.Trim ())){
  200. string files = entry_line.Substring (3);
  201. files = files.TrimEnd (":".ToCharArray());
  202. string [] edited_files = files.Split (",".ToCharArray ());
  203. foreach (string edited_file in edited_files) {
  204. if (!change_set.Added.Contains (edited_file) &&
  205. !change_set.Deleted.Contains (edited_file)) {
  206. change_set.Edited.Add (edited_file);
  207. }
  208. }
  209. }
  210. }
  211. change_sets.Add (change_set);
  212. }
  213. return change_sets;
  214. }
  215. // Creates a pretty commit message based on what has changed
  216. private string FormatCommitMessage () // TODO
  217. {
  218. return "SparkleShare Hg";
  219. }
  220. public override void CreateInitialChangeSet ()
  221. {
  222. base.CreateInitialChangeSet ();
  223. Add ();
  224. string message = FormatCommitMessage ();
  225. Commit (message);
  226. }
  227. public override bool UsesNotificationCenter
  228. {
  229. get {
  230. string file_path = SparkleHelpers.CombineMore (LocalPath, ".hg", "disable_notification_center");
  231. return !File.Exists (file_path);
  232. }
  233. }
  234. }
  235. }