private List<SparkleChangeSet> GetChangeSetsInternal(string path) { List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> (); SparkleGit git; if (path == null) { git = new SparkleGit (LocalPath, "log --since=1.month --raw --find-renames --date=iso " + "--format=medium --no-color -m --first-parent"); } else { path = path.Replace ("\\", "/"); git = new SparkleGit (LocalPath, "log --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges -- \"" + path + "\""); } string output = git.StartAndReadStandardOutput (); if (path == null && string.IsNullOrWhiteSpace (output)) { git = new SparkleGit (LocalPath, "log -n 75 --raw --find-renames --date=iso " + "--format=medium --no-color -m --first-parent"); output = git.StartAndReadStandardOutput (); } string [] lines = output.Split ("\n".ToCharArray ()); List<string> entries = new List <string> (); // Split up commit entries int line_number = 0; bool first_pass = true; string entry = "", last_entry = ""; foreach (string line in lines) { if (line.StartsWith ("commit") && !first_pass) { entries.Add (entry); entry = ""; line_number = 0; } else { first_pass = false; } // Only parse first 250 files to prevent memory issues if (line_number < 250) { entry += line + "\n"; line_number++; } last_entry = entry; } entries.Add (last_entry); // Parse commit entries foreach (string log_entry in entries) { Match match = this.log_regex.Match (log_entry); if (!match.Success) { match = this.merge_regex.Match (log_entry); if (!match.Success) continue; } SparkleChangeSet change_set = new SparkleChangeSet (); change_set.Folder = new SparkleFolder (Name); change_set.Revision = match.Groups [1].Value; change_set.User = new SparkleUser (match.Groups [2].Value, match.Groups [3].Value); change_set.RemoteUrl = RemoteUrl; change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value), int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value), int.Parse (match.Groups [7].Value), int.Parse (match.Groups [8].Value), int.Parse (match.Groups [9].Value)); string time_zone = match.Groups [10].Value; int our_offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Hours; int their_offset = int.Parse (time_zone.Substring (0, 3)); change_set.Timestamp = change_set.Timestamp.AddHours (their_offset * -1); change_set.Timestamp = change_set.Timestamp.AddHours (our_offset); string [] entry_lines = log_entry.Split ("\n".ToCharArray ()); // Parse file list. Lines containing file changes start with ":" foreach (string entry_line in entry_lines) { // Skip lines containing backspace characters if (!entry_line.StartsWith (":") || entry_line.Contains ("\\177")) continue; string file_path = entry_line.Substring (39); if (file_path.Equals (".sparkleshare")) continue; string type_letter = entry_line [37].ToString (); bool change_is_folder = false; if (file_path.EndsWith (".empty")) { file_path = file_path.Substring (0, file_path.Length - ".empty".Length); change_is_folder = true; } try { file_path = EnsureSpecialCharacters (file_path); } catch (Exception e) { SparkleLogger.LogInfo ("Local", "Error parsing file name '" + file_path + "'", e); continue; } file_path = file_path.Replace ("\\\"", "\""); SparkleChange change = new SparkleChange () { Path = file_path, IsFolder = change_is_folder, Timestamp = change_set.Timestamp, Type = SparkleChangeType.Added }; if (type_letter.Equals ("R")) { int tab_pos = entry_line.LastIndexOf ("\t"); file_path = entry_line.Substring (42, tab_pos - 42); string to_file_path = entry_line.Substring (tab_pos + 1); try { file_path = EnsureSpecialCharacters (file_path); } catch (Exception e) { SparkleLogger.LogInfo ("Local", "Error parsing file name '" + file_path + "'", e); continue; } try { to_file_path = EnsureSpecialCharacters (to_file_path); } catch (Exception e) { SparkleLogger.LogInfo ("Local", "Error parsing file name '" + to_file_path + "'", e); continue; } file_path = file_path.Replace ("\\\"", "\""); to_file_path = to_file_path.Replace ("\\\"", "\""); if (file_path.EndsWith (".empty")) { file_path = file_path.Substring (0, file_path.Length - 6); change_is_folder = true; } if (to_file_path.EndsWith (".empty")) { to_file_path = to_file_path.Substring (0, to_file_path.Length - 6); change_is_folder = true; } change.Path = file_path; change.MovedToPath = to_file_path; change.Type = SparkleChangeType.Moved; } else if (type_letter.Equals ("M")) { change.Type = SparkleChangeType.Edited; } else if (type_letter.Equals ("D")) { change.Type = SparkleChangeType.Deleted; } change_set.Changes.Add (change); } // Group commits per user, per day if (change_sets.Count > 0 && path == null) { SparkleChangeSet last_change_set = change_sets [change_sets.Count - 1]; if (change_set.Timestamp.Year == last_change_set.Timestamp.Year && change_set.Timestamp.Month == last_change_set.Timestamp.Month && change_set.Timestamp.Day == last_change_set.Timestamp.Day && change_set.User.Name.Equals (last_change_set.User.Name)) { last_change_set.Changes.AddRange (change_set.Changes); if (DateTime.Compare (last_change_set.Timestamp, change_set.Timestamp) < 1) { last_change_set.FirstTimestamp = last_change_set.Timestamp; last_change_set.Timestamp = change_set.Timestamp; last_change_set.Revision = change_set.Revision; } else { last_change_set.FirstTimestamp = change_set.Timestamp; } } else { change_sets.Add (change_set); } } else { // Don't show removals or moves in the revision list of a file if (path != null) { List<SparkleChange> changes_to_skip = new List<SparkleChange> (); foreach (SparkleChange change in change_set.Changes) { if ((change.Type == SparkleChangeType.Deleted || change.Type == SparkleChangeType.Moved) && change.Path.Equals (path)) { changes_to_skip.Add (change); } } foreach (SparkleChange change_to_skip in changes_to_skip) change_set.Changes.Remove (change_to_skip); } change_sets.Add (change_set); } } return change_sets; }
private List<SparkleChange> ParseStatus () { List<SparkleChange> changes = new List<SparkleChange> (); SparkleGit git_status = new SparkleGit (LocalPath, "status --porcelain"); git_status.Start (); while (!git_status.StandardOutput.EndOfStream) { string line = git_status.StandardOutput.ReadLine (); line = line.Trim (); if (line.EndsWith (".empty") || line.EndsWith (".empty\"")) line = line.Replace (".empty", ""); SparkleChange change; if (line.StartsWith ("R")) { string path = line.Substring (3, line.IndexOf (" -> ") - 3).Trim ("\" ".ToCharArray ()); string moved_to_path = line.Substring (line.IndexOf (" -> ") + 4).Trim ("\" ".ToCharArray ()); change = new SparkleChange () { Type = SparkleChangeType.Moved, Path = EnsureSpecialCharacters (path), MovedToPath = EnsureSpecialCharacters (moved_to_path) }; } else { string path = line.Substring (2).Trim ("\" ".ToCharArray ()); change = new SparkleChange () { Path = EnsureSpecialCharacters (path) }; change.Type = SparkleChangeType.Added; if (line.StartsWith ("M")) { change.Type = SparkleChangeType.Edited; } else if (line.StartsWith ("D")) { change.Type = SparkleChangeType.Deleted; } } changes.Add (change); } git_status.StandardOutput.ReadToEnd (); git_status.WaitForExit (); return changes; }