// Commits the made changes void Commit(string message) { GitCommand git; string user_name = base.local_config.User.Name; string user_email = base.local_config.User.Email; if (!this.user_is_set) { git = new GitCommand(LocalPath, "config user.name \"" + user_name + "\""); git.StartAndWaitForExit(); git = new GitCommand(LocalPath, "config user.email \"" + user_email + "\""); git.StartAndWaitForExit(); this.user_is_set = true; } if (StorageType == StorageType.Encrypted) { string password_file_path = Path.Combine(LocalPath, ".git", "info", "encryption_password"); string password = File.ReadAllText(password_file_path); user_name = user_name.AESEncrypt(password); user_email = user_email.AESEncrypt(password); } git = new GitCommand(LocalPath, string.Format("commit --all --message=\"{0}\" --author=\"{1} <{2}>\"", message, user_name, user_email)); git.StartAndReadStandardOutput(); }
public override bool IsFetchedRepoPasswordCorrect(string password) { string password_check_file_path = Path.Combine(TargetFolder, ".sparkleshare"); if (!File.Exists(password_check_file_path)) { var git_show = new GitCommand(TargetFolder, "show HEAD:.sparkleshare"); string output = git_show.StartAndReadStandardOutput(); if (git_show.ExitCode == 0) { File.WriteAllText(password_check_file_path, output); } else { return(false); } } string args = string.Format("enc -d -aes-256-cbc -base64 -S {0} -pass pass:{1} -in \"{2}\" -md sha256", password_salt, password.SHA256(password_salt), password_check_file_path); var process = new Command("openssl", args); process.StartInfo.WorkingDirectory = TargetFolder; process.StartAndWaitForExit(); if (process.ExitCode == 0) { File.Delete(password_check_file_path); return(true); } return(false); }
// Commits the made changes void Commit(string message) { GitCommand git_config; string user_name = base.local_config.User.Name; string user_email = base.local_config.User.Email; if (!this.user_is_set) { git_config = new GitCommand(LocalPath, "config user.name \"" + user_name + "\""); git_config.StartAndWaitForExit(); git_config = new GitCommand(LocalPath, "config user.email \"" + user_email + "\""); git_config.StartAndWaitForExit(); this.user_is_set = true; } if (StorageType == StorageType.Encrypted) { string password_file_path = Path.Combine(LocalPath, ".git", "info", "encryption_password"); string password = File.ReadAllText(password_file_path); user_name = user_name.AESEncrypt(password); user_email = user_email.AESEncrypt(password); } GitCommand git_commit; string message_file_path = Path.Combine(LocalPath, ".git", "info", "commit_message"); try { File.WriteAllText(message_file_path, message); // Commit from message stored in temporary file to avoid special character conflicts on the command line git_commit = new GitCommand(LocalPath, string.Format("commit --all --file=\"{0}\" --author=\"{1} <{2}>\"", message_file_path, user_name, user_email)); } catch (IOException e) { Logger.LogInfo("Git", Name + " | Could not create commit message file: " + message_file_path, e); // If committing with a temporary file fails, use a simple static commit message git_commit = new GitCommand(LocalPath, string.Format("commit --all --message=\"{0}\" --author=\"{1} <{2}>\"", "Changes by SparkleShare", user_name, user_email)); } git_commit.StartAndReadStandardOutput(); File.Delete(message_file_path); }
// Commits the made changes void Commit(string message) { GitCommand git; if (!this.user_is_set) { git = new GitCommand(LocalPath, "config user.name \"" + base.local_config.User.Name + "\""); git.StartAndWaitForExit(); git = new GitCommand(LocalPath, "config user.email \"" + base.local_config.User.Email + "\""); git.StartAndWaitForExit(); this.user_is_set = true; } git = new GitCommand(LocalPath, "commit --all --message=\"" + message + "\" " + "--author=\"" + base.local_config.User.Name + " <" + base.local_config.User.Email + ">\""); git.StartAndReadStandardOutput(); }
StorageType?DetermineStorageType() { var git_ls_remote = new GitCommand(Configuration.DefaultConfiguration.TmpPath, string.Format("ls-remote --heads \"{0}\"", RemoteUrl), auth_info); string output = git_ls_remote.StartAndReadStandardOutput(); if (git_ls_remote.ExitCode != 0) { return(null); } if (string.IsNullOrWhiteSpace(output)) { return(StorageType.Unknown); } string encrypted_storage_prefix = "x-sparkleshare-encrypted-"; string large_file_storage_prefix = "x-sparkleshare-lfs"; foreach (string line in output.Split("\n".ToCharArray())) { // Remote branches are outputed as "remote/branch", we need the second part string [] line_parts = line.Split('/'); string branch = line_parts [line_parts.Length - 1]; if (branch == large_file_storage_prefix) { return(StorageType.LargeFiles); } if (branch.StartsWith(encrypted_storage_prefix)) { password_salt = branch.Replace(encrypted_storage_prefix, ""); return(StorageType.Encrypted); } } return(StorageType.Plain); }
List <ChangeSet> GetChangeSetsInternal(string path) { var change_sets = new List <ChangeSet> (); GitCommand git; if (path == null) { git = new GitCommand(LocalPath, "--no-pager log --since=1.month --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges"); } else { path = path.Replace("\\", "/"); git = new GitCommand(LocalPath, "--no-pager 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 GitCommand(LocalPath, "--no-pager log -n 75 --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges"); 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; } } ChangeSet change_set = new ChangeSet(); change_set.Folder = new SparkleFolder(Name); change_set.Revision = match.Groups [1].Value; change_set.User = new User(match.Groups [2].Value, match.Groups [3].Value); if (change_set.User.Name == "SparkleShare") { continue; } change_set.RemoteUrl = RemoteUrl; if (StorageType == StorageType.Encrypted) { string password_file_path = Path.Combine(LocalPath, ".git", "info", "encryption_password"); string password = File.ReadAllText(password_file_path); try { change_set.User = new User( change_set.User.Name.AESDecrypt(password), change_set.User.Email.AESDecrypt(password)); } catch (Exception e) { Console.WriteLine(e.StackTrace); change_set.User = new User(match.Groups [2].Value, match.Groups [3].Value); } } 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) { Logger.LogInfo("Local", "Error parsing file name '" + file_path + "'", e); continue; } file_path = file_path.Replace("\\\"", "\""); Change change = new Change() { Path = file_path, IsFolder = change_is_folder, Timestamp = change_set.Timestamp, Type = ChangeType.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) { Logger.LogInfo("Local", "Error parsing file name '" + file_path + "'", e); continue; } try { to_file_path = EnsureSpecialCharacters(to_file_path); } catch (Exception e) { Logger.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 = ChangeType.Moved; } else if (type_letter.Equals("M")) { change.Type = ChangeType.Edited; } else if (type_letter.Equals("D")) { change.Type = ChangeType.Deleted; } change_set.Changes.Add(change); } // Group commits per user, per day if (change_sets.Count > 0 && path == null) { ChangeSet 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 <Change> changes_to_skip = new List <Change> (); foreach (Change change in change_set.Changes) { if ((change.Type == ChangeType.Deleted || change.Type == ChangeType.Moved) && change.Path.Equals(path)) { changes_to_skip.Add(change); } } foreach (Change change_to_skip in changes_to_skip) { change_set.Changes.Remove(change_to_skip); } } change_sets.Add(change_set); } } return(change_sets); }
void ResolveConflict() { // This is a list of conflict status codes that Git uses, their // meaning, and how SparkleShare should handle them. // // DD unmerged, both deleted -> Do nothing // AU unmerged, added by us -> Use server's, save ours as a timestamped copy // UD unmerged, deleted by them -> Use ours // UA unmerged, added by them -> Use server's, save ours as a timestamped copy // DU unmerged, deleted by us -> Use server's // AA unmerged, both added -> Use server's, save ours as a timestamped copy // UU unmerged, both modified -> Use server's, save ours as a timestamped copy // ?? unmerged, new files -> Stage the new files var git_status = new GitCommand(LocalPath, "status --porcelain"); string output = git_status.StartAndReadStandardOutput(); string [] lines = output.Split("\n".ToCharArray()); bool trigger_conflict_event = false; foreach (string line in lines) { string conflicting_path = line.Substring(3); conflicting_path = EnsureSpecialCharacters(conflicting_path); conflicting_path = conflicting_path.Trim("\"".ToCharArray()); // Remove possible rename indicators string [] separators = { " -> \"", " -> " }; foreach (string separator in separators) { if (conflicting_path.Contains(separator)) { conflicting_path = conflicting_path.Substring( conflicting_path.IndexOf(separator) + separator.Length); } } Logger.LogInfo("Git", Name + " | Conflict type: " + line); // Ignore conflicts in hidden files and use the local versions if (conflicting_path.EndsWith(".sparkleshare") || conflicting_path.EndsWith(".empty")) { Logger.LogInfo("Git", Name + " | Ignoring conflict in special file: " + conflicting_path); // Recover local version var git_ours = new GitCommand(LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit(); string abs_conflicting_path = Path.Combine(LocalPath, conflicting_path); if (File.Exists(abs_conflicting_path)) { File.SetAttributes(abs_conflicting_path, FileAttributes.Hidden); } continue; } Logger.LogInfo("Git", Name + " | Resolving: " + conflicting_path); // Both the local and server version have been modified if (line.StartsWith("UU") || line.StartsWith("AA") || line.StartsWith("AU") || line.StartsWith("UA")) { // Recover local version var git_ours = new GitCommand(LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit(); // Append a timestamp to local version. // Windows doesn't allow colons in the file name, so // we use "h" between the hours and minutes instead. string timestamp = DateTime.Now.ToString("MMM d H\\hmm"); string our_path = Path.GetFileNameWithoutExtension(conflicting_path) + " (" + base.local_config.User.Name + ", " + timestamp + ")" + Path.GetExtension(conflicting_path); string abs_conflicting_path = Path.Combine(LocalPath, conflicting_path); string abs_our_path = Path.Combine(LocalPath, our_path); if (File.Exists(abs_conflicting_path) && !File.Exists(abs_our_path)) { File.Move(abs_conflicting_path, abs_our_path); } // Recover server version var git_theirs = new GitCommand(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); trigger_conflict_event = true; // The server version has been modified, but the local version was removed } else if (line.StartsWith("DU")) { // The modified local version is already in the checkout, so it just needs to be added. // We need to specifically mention the file, so we can't reuse the Add () method var git_add = new GitCommand(LocalPath, "add \"" + conflicting_path + "\""); git_add.StartAndWaitForExit(); // The local version has been modified, but the server version was removed } else if (line.StartsWith("UD")) { // Recover server version var git_theirs = new GitCommand(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); // Server and local versions were removed } else if (line.StartsWith("DD")) { Logger.LogInfo("Git", Name + " | No need to resolve: " + line); // New local files } else if (line.StartsWith("??")) { Logger.LogInfo("Git", Name + " | Found new file, no need to resolve: " + line); } else { Logger.LogInfo("Git", Name + " | Don't know what to do with: " + line); } } Add(); var git = new GitCommand(LocalPath, "commit --message=\"Conflict resolution\" --author=\"SparkleShare <*****@*****.**>\""); git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit(); if (trigger_conflict_event) { OnConflictResolved(); } }
StorageType? DetermineStorageType() { var git_ls_remote = new GitCommand (Configuration.DefaultConfiguration.TmpPath, string.Format ("ls-remote --heads \"{0}\"", RemoteUrl), auth_info); string output = git_ls_remote.StartAndReadStandardOutput (); if (git_ls_remote.ExitCode != 0) return null; if (string.IsNullOrWhiteSpace (output)) return StorageType.Unknown; foreach (string line in output.Split ("\n".ToCharArray ())) { string [] line_parts = line.Split ('/'); string branch = line_parts [line_parts.Length - 1]; if (branch == "x-sparkleshare-lfs") return StorageType.LargeFiles; string encrypted_storage_prefix = "x-sparkleshare-encrypted-"; if (branch.StartsWith (encrypted_storage_prefix)) { password_salt = branch.Replace (encrypted_storage_prefix, ""); return StorageType.Encrypted; } } return StorageType.Plain; }
public override bool IsFetchedRepoPasswordCorrect(string password) { string password_check_file_path = Path.Combine (TargetFolder, ".sparkleshare"); if (!File.Exists (password_check_file_path)) { var git_show = new GitCommand (TargetFolder, "show HEAD:.sparkleshare"); string output = git_show.StartAndReadStandardOutput (); if (git_show.ExitCode == 0) File.WriteAllText (password_check_file_path, output); else return false; } string args = string.Format ("enc -d -aes-256-cbc -base64 -S {0} -pass pass:{1} -in \"{2}\"", password_salt, password.SHA256 (password_salt), password_check_file_path); var process = new Command ("openssl", args); process.StartInfo.WorkingDirectory = TargetFolder; process.StartAndWaitForExit (); if (process.ExitCode == 0) { File.Delete (password_check_file_path); return true; } return false; }
List <ChangeSet> GetChangeSetsInternal(string path) { var change_sets = new List <ChangeSet> (); GitCommand git; string log_args = "--since=1.month --name-status --date=iso --find-renames --no-merges --no-color"; if (path == null) { git = new GitCommand(LocalPath, "--no-pager log " + log_args); } else { path = path.Replace("\\", "/"); git = new GitCommand(LocalPath, "--no-pager log " + log_args + " -- \"" + path + "\""); } string output = git.StartAndReadStandardOutput(); if (path == null && string.IsNullOrWhiteSpace(output)) { git = new GitCommand(LocalPath, "--no-pager log -n 75 " + log_args); output = git.StartAndReadStandardOutput(); } // Offset the output so our log_regex can be simpler string commit_sep = "commit "; if (output.StartsWith(commit_sep)) { output = output.Substring(commit_sep.Length) + "\n\n" + commit_sep; } MatchCollection matches = this.log_regex.Matches(output); foreach (Match match in matches) { ChangeSet change_set = ParseChangeSet(match); if (change_set == null) { continue; } int count = 0; foreach (string line in match.Groups["files"].Value.Split("\n".ToCharArray())) { if (count++ == 256) { break; } Change change = ParseChange(line); if (change == null) { continue; } change.Timestamp = change_set.Timestamp; change_set.Changes.Add(change); } if (path == null && change_sets.Count > 0) { ChangeSet last_change_set = change_sets [change_sets.Count - 1]; // If a change set set already exists for this user and day, group into that one 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 if (path != null) { // Don't show removals or moves in the history list of a file var changes = new Change [change_set.Changes.Count]; change_set.Changes.CopyTo(changes); foreach (Change change in changes) { if (!change.Path.Equals(path)) { continue; } if (change.Type == ChangeType.Deleted || change.Type == ChangeType.Moved) { change_set.Changes.Remove(change); } } change_sets.Add(change_set); } else { change_sets.Add(change_set); } } return(change_sets); }
void ResolveConflict() { // This is a list of conflict status codes that Git uses, their // meaning, and how SparkleShare should handle them. // // DD unmerged, both deleted -> Do nothing // AU unmerged, added by us -> Use server's, save ours as a timestamped copy // UD unmerged, deleted by them -> Use ours // UA unmerged, added by them -> Use server's, save ours as a timestamped copy // DU unmerged, deleted by us -> Use server's // AA unmerged, both added -> Use server's, save ours as a timestamped copy // UU unmerged, both modified -> Use server's, save ours as a timestamped copy // ?? unmerged, new files -> Stage the new files var git_status = new GitCommand(LocalPath, "status --porcelain"); string output = git_status.StartAndReadStandardOutput(); string [] lines = output.Split("\n".ToCharArray()); bool trigger_conflict_event = false; foreach (string line in lines) { string conflicting_file_path = line.Substring(3); conflicting_file_path = EnsureSpecialChars(conflicting_file_path); conflicting_file_path = conflicting_file_path.Trim("\"".ToCharArray()); // Remove possible rename indicators string [] separators = { " -> \"", " -> " }; foreach (string separator in separators) { if (conflicting_file_path.Contains(separator)) { conflicting_file_path = conflicting_file_path.Substring(conflicting_file_path.IndexOf(separator) + separator.Length); } } Logger.LogInfo("Git", Name + " | Conflict type: " + line); // Ignore conflicts in hidden files and use the local versions if (conflicting_file_path.EndsWith(".sparkleshare") || conflicting_file_path.EndsWith(".empty")) { Logger.LogInfo("Git", Name + " | Ignoring conflict in special file: " + conflicting_file_path); // Recover local version var git_ours = new GitCommand(LocalPath, "checkout --ours \"" + conflicting_file_path + "\""); git_ours.StartAndWaitForExit(); string abs_conflicting_path = Path.Combine(LocalPath, conflicting_file_path); if (File.Exists(abs_conflicting_path)) { File.SetAttributes(abs_conflicting_path, FileAttributes.Hidden); } continue; } Logger.LogInfo("Git", Name + " | Resolving: " + conflicting_file_path); // Both the local and server version have been modified if (line.StartsWith("UU") || line.StartsWith("AA") || line.StartsWith("AU") || line.StartsWith("UA")) { // Get the author name of the conflicting version var git_log = new GitCommand(LocalPath, "log -n 1 FETCH_HEAD --pretty=format:%an " + conflicting_file_path); string other_author_name = git_log.StartAndReadStandardOutput(); // Generate distinguishing names for both versions of the file string clue_A = string.Format(" (by {0})", base.local_config.User.Name); string clue_B = string.Format(" (by {0})", other_author_name); if (base.local_config.User.Name == other_author_name) { clue_A = " (A)"; clue_B = " (B)"; } string file_name_A = Path.GetFileNameWithoutExtension(conflicting_file_path) + clue_A + Path.GetExtension(conflicting_file_path); string file_name_B = Path.GetFileNameWithoutExtension(conflicting_file_path) + clue_B + Path.GetExtension(conflicting_file_path); string abs_conflicting_file_path = Path.Combine(LocalPath, conflicting_file_path); string abs_file_path_A = Path.Combine(Path.GetDirectoryName(abs_conflicting_file_path), file_name_A); string abs_file_path_B = Path.Combine(Path.GetDirectoryName(abs_conflicting_file_path), file_name_B); // Recover local version var git_checkout_A = new GitCommand(LocalPath, "checkout --ours \"" + conflicting_file_path + "\""); git_checkout_A.StartAndWaitForExit(); if (File.Exists(abs_conflicting_file_path) && !File.Exists(abs_file_path_A)) { File.Move(abs_conflicting_file_path, abs_file_path_A); } // Recover server version var git_checkout_B = new GitCommand(LocalPath, "checkout --theirs \"" + conflicting_file_path + "\""); git_checkout_B.StartAndWaitForExit(); if (File.Exists(abs_conflicting_file_path) && !File.Exists(abs_file_path_B)) { File.Move(abs_conflicting_file_path, abs_file_path_B); } // Recover original (before both versions diverged) var git_checkout = new GitCommand(LocalPath, "checkout ORIG_HEAD^ \"" + conflicting_file_path + "\""); git_checkout.StartAndWaitForExit(); trigger_conflict_event = true; // The server version has been modified, but the local version was removed } else if (line.StartsWith("DU")) { // The modified local version is already in the checkout, so it just needs to be added. // We need to specifically mention the file, so we can't reuse the Add () method var git_add = new GitCommand(LocalPath, "add \"" + conflicting_file_path + "\""); git_add.StartAndWaitForExit(); // The local version has been modified, but the server version was removed } else if (line.StartsWith("UD")) { // Recover our version var git_theirs = new GitCommand(LocalPath, "checkout --ours \"" + conflicting_file_path + "\""); git_theirs.StartAndWaitForExit(); // Server and local versions were removed } else if (line.StartsWith("DD")) { Logger.LogInfo("Git", Name + " | No need to resolve: " + line); // New local files } else if (line.StartsWith("??")) { Logger.LogInfo("Git", Name + " | Found new file, no need to resolve: " + line); } else { Logger.LogInfo("Git", Name + " | Don't know what to do with: " + line); } } Add(); var git = new GitCommand(LocalPath, "commit --message=\"Conflict resolution\" --author=\"SparkleShare <*****@*****.**>\""); git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit(); HasUnsyncedChanges = true; if (trigger_conflict_event) { OnConflictResolved(); } }
void ResolveConflict() { // This is a list of conflict status codes that Git uses, their // meaning, and how SparkleShare should handle them. // // DD unmerged, both deleted -> Do nothing // AU unmerged, added by us -> Use server's, save ours as a timestamped copy // UD unmerged, deleted by them -> Use ours // UA unmerged, added by them -> Use server's, save ours as a timestamped copy // DU unmerged, deleted by us -> Use server's // AA unmerged, both added -> Use server's, save ours as a timestamped copy // UU unmerged, both modified -> Use server's, save ours as a timestamped copy // ?? unmerged, new files -> Stage the new files var git_status = new GitCommand (LocalPath, "status --porcelain"); string output = git_status.StartAndReadStandardOutput (); string [] lines = output.Split ("\n".ToCharArray ()); bool trigger_conflict_event = false; foreach (string line in lines) { string conflicting_path = line.Substring (3); conflicting_path = EnsureSpecialCharacters (conflicting_path); conflicting_path = conflicting_path.Trim ("\"".ToCharArray ()); // Remove possible rename indicators string [] separators = {" -> \"", " -> "}; foreach (string separator in separators) { if (conflicting_path.Contains (separator)) { conflicting_path = conflicting_path.Substring ( conflicting_path.IndexOf (separator) + separator.Length); } } Logger.LogInfo ("Git", Name + " | Conflict type: " + line); // Ignore conflicts in hidden files and use the local versions if (conflicting_path.EndsWith (".sparkleshare") || conflicting_path.EndsWith (".empty")) { Logger.LogInfo ("Git", Name + " | Ignoring conflict in special file: " + conflicting_path); // Recover local version var git_ours = new GitCommand (LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit (); string abs_conflicting_path = Path.Combine (LocalPath, conflicting_path); if (File.Exists (abs_conflicting_path)) File.SetAttributes (abs_conflicting_path, FileAttributes.Hidden); continue; } Logger.LogInfo ("Git", Name + " | Resolving: " + conflicting_path); // Both the local and server version have been modified if (line.StartsWith ("UU") || line.StartsWith ("AA") || line.StartsWith ("AU") || line.StartsWith ("UA")) { // Recover local version var git_ours = new GitCommand (LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit (); // Append a timestamp to local version. // Windows doesn't allow colons in the file name, so // we use "h" between the hours and minutes instead. string timestamp = DateTime.Now.ToString ("MMM d H\\hmm"); string our_path = Path.GetFileNameWithoutExtension (conflicting_path) + " (" + base.local_config.User.Name + ", " + timestamp + ")" + Path.GetExtension (conflicting_path); string abs_conflicting_path = Path.Combine (LocalPath, conflicting_path); string abs_our_path = Path.Combine (LocalPath, our_path); if (File.Exists (abs_conflicting_path) && !File.Exists (abs_our_path)) File.Move (abs_conflicting_path, abs_our_path); // Recover server version var git_theirs = new GitCommand (LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit (); trigger_conflict_event = true; // The server version has been modified, but the local version was removed } else if (line.StartsWith ("DU")) { // The modified local version is already in the checkout, so it just needs to be added. // We need to specifically mention the file, so we can't reuse the Add () method var git_add = new GitCommand (LocalPath, "add \"" + conflicting_path + "\""); git_add.StartAndWaitForExit (); // The local version has been modified, but the server version was removed } else if (line.StartsWith ("UD")) { // Recover server version var git_theirs = new GitCommand (LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit (); // Server and local versions were removed } else if (line.StartsWith ("DD")) { Logger.LogInfo ("Git", Name + " | No need to resolve: " + line); // New local files } else if (line.StartsWith ("??")) { Logger.LogInfo ("Git", Name + " | Found new file, no need to resolve: " + line); } else { Logger.LogInfo ("Git", Name + " | Don't know what to do with: " + line); } } Add (); var git = new GitCommand (LocalPath, "commit --message \"Conflict resolution by SparkleShare\""); git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit (); if (trigger_conflict_event) OnConflictResolved (); }
List<ChangeSet> GetChangeSetsInternal(string path) { var change_sets = new List <ChangeSet> (); GitCommand git; if (path == null) { git = new GitCommand (LocalPath, "--no-pager log --since=1.month --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges"); } else { path = path.Replace ("\\", "/"); git = new GitCommand (LocalPath, "--no-pager 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 GitCommand (LocalPath, "--no-pager log -n 75 --raw --find-renames --date=iso " + "--format=medium --no-color --no-merges"); 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; } ChangeSet change_set = new ChangeSet (); change_set.Folder = new SparkleFolder (Name); change_set.Revision = match.Groups [1].Value; change_set.User = new User (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) { Logger.LogInfo ("Local", "Error parsing file name '" + file_path + "'", e); continue; } file_path = file_path.Replace ("\\\"", "\""); Change change = new Change () { Path = file_path, IsFolder = change_is_folder, Timestamp = change_set.Timestamp, Type = ChangeType.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) { Logger.LogInfo ("Local", "Error parsing file name '" + file_path + "'", e); continue; } try { to_file_path = EnsureSpecialCharacters (to_file_path); } catch (Exception e) { Logger.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 = ChangeType.Moved; } else if (type_letter.Equals ("M")) { change.Type = ChangeType.Edited; } else if (type_letter.Equals ("D")) { change.Type = ChangeType.Deleted; } change_set.Changes.Add (change); } // Group commits per user, per day if (change_sets.Count > 0 && path == null) { ChangeSet 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<Change> changes_to_skip = new List<Change> (); foreach (Change change in change_set.Changes) { if ((change.Type == ChangeType.Deleted || change.Type == ChangeType.Moved) && change.Path.Equals (path)) { changes_to_skip.Add (change); } } foreach (Change change_to_skip in changes_to_skip) change_set.Changes.Remove (change_to_skip); } change_sets.Add (change_set); } } return change_sets; }
// Commits the made changes void Commit(string message) { GitCommand git; if (!this.user_is_set) { git = new GitCommand (LocalPath, "config user.name \"" + base.local_config.User.Name + "\""); git.StartAndWaitForExit (); git = new GitCommand (LocalPath, "config user.email \"" + base.local_config.User.Email + "\""); git.StartAndWaitForExit (); this.user_is_set = true; } git = new GitCommand (LocalPath, "commit --all --message=\"" + message + "\" " + "--author=\"" + base.local_config.User.Name + " <" + base.local_config.User.Email + ">\""); git.StartAndReadStandardOutput (); }