public override bool CheckForRemoteChanges() { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Checking for remote changes..."); SparkleGit git = new SparkleGit(LocalPath, "ls-remote origin master"); git.Start(); git.WaitForExit(); if (git.ExitCode != 0) { return(false); } string remote_revision = git.StandardOutput.ReadToEnd().TrimEnd(); if (!remote_revision.StartsWith(CurrentRevision)) { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Remote changes found. (" + remote_revision + ")"); return(true); } else { return(false); } }
private void CheckForRemoteChanges() { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Checking for remote changes..."); SparkleGit git = new SparkleGit(LocalPath, "ls-remote origin master"); git.Exited += delegate { if (git.ExitCode != 0) { return; } string remote_hash = git.StandardOutput.ReadToEnd(); if (!remote_hash.StartsWith(_CurrentHash)) { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Remote changes found. (" + remote_hash + ")"); Fetch(); Watcher.EnableRaisingEvents = false; Rebase(); Watcher.EnableRaisingEvents = true; } }; git.Start(); git.WaitForExit(); }
// Commits the made changes public void Commit(string message) { if (!AnyDifferences) { return; } SparkleGit git = new SparkleGit(LocalPath, "commit -m '" + message + "'"); git.Start(); git.WaitForExit(); _CurrentHash = GetCurrentHash(); SparkleHelpers.DebugInfo("Commit", "[" + Name + "] " + message + " (" + _CurrentHash + ")"); SparkleEventArgs args = new SparkleEventArgs("Commited") { Message = message }; if (Commited != null) { Commited(this, args); } // Collect garbage pseudo-randomly if (DateTime.Now.Second % 10 == 0) { CollectGarbage(); } }
// Merges the fetched changes private void Rebase() { DisableWatching(); if (AnyDifferences) { Add(); string commit_message = FormatCommitMessage(); Commit(commit_message); } SparkleGit git = new SparkleGit(LocalPath, "rebase -v FETCH_HEAD"); git.Start(); git.WaitForExit(); if (git.ExitCode != 0) { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Conflict detected. Trying to get out..."); while (AnyDifferences) { ResolveConflict(); } SparkleHelpers.DebugInfo("Git", "[" + Name + "] Conflict resolved."); OnConflictResolved(); } EnableWatching(); }
public override bool SyncUp() { if (AnyDifferences) { Add(); string message = FormatCommitMessage(); Commit(message); } SparkleGit git = new SparkleGit(LocalPath, "push origin master"); git.Start(); git.StandardOutput.ReadToEnd(); git.WaitForExit(); if (git.ExitCode == 0) { return(true); } else { return(false); } }
// Fetches changes from the remote repository public void Fetch() { _IsSyncing = true; _IsFetching = true; RemoteTimer.Stop(); SparkleHelpers.DebugInfo("Git", "[" + Name + "] Fetching changes..."); SparkleGit git = new SparkleGit(LocalPath, "fetch -v origin master"); SparkleEventArgs args; args = new SparkleEventArgs("FetchingStarted"); if (FetchingStarted != null) { FetchingStarted(this, args); } git.Exited += delegate { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes fetched."); _IsSyncing = false; _IsFetching = false; _CurrentHash = Head.CurrentCommit.Hash; if (git.ExitCode != 0) { _ServerOnline = false; args = new SparkleEventArgs("FetchingFailed"); if (FetchingFailed != null) { FetchingFailed(this, args); } } else { _ServerOnline = true; args = new SparkleEventArgs("FetchingFinished"); if (FetchingFinished != null) { FetchingFinished(this, args); } } RemoteTimer.Start(); }; git.Start(); git.WaitForExit(); }
// Stages the made changes private void Add() { SparkleGit git = new SparkleGit(LocalPath, "add --all"); git.Start(); git.WaitForExit(); SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes staged"); }
// Removes unneeded objects private void CollectGarbage() { SparkleGit git = new SparkleGit(LocalPath, "gc"); git.Start(); git.WaitForExit(); SparkleHelpers.DebugInfo("Git", "[" + Name + "] Garbage collected."); }
private string GetCurrentHash() { SparkleGit git = new SparkleGit(LocalPath, "log -1 --format=%H"); git.Start(); git.WaitForExit(); string output = git.StandardOutput.ReadToEnd(); string hash = output.Trim(); return(hash); }
private string GetUserName() { SparkleGit git = new SparkleGit(LocalPath, "config --get user.name"); git.Start(); git.WaitForExit(); string output = git.StandardOutput.ReadToEnd(); string user_name = output.Trim(); return(user_name); }
private string GetUserEmail() { SparkleGit git = new SparkleGit(LocalPath, "config --get user.email"); git.Start(); git.WaitForExit(); string output = git.StandardOutput.ReadToEnd(); string user_email = output.Trim(); return(user_email); }
private string GetRemoteOriginUrl() { SparkleGit git = new SparkleGit(LocalPath, "config --get remote.origin.url"); git.Start(); git.WaitForExit(); string output = git.StandardOutput.ReadToEnd(); string url = output.Trim(); return(url); }
// Merges the fetched changes public void Rebase() { if (AnyDifferences) { Add(); string commit_message = FormatCommitMessage(); Commit(commit_message); } SparkleHelpers.DebugInfo("Git", "[" + Name + "] Rebasing changes..."); SparkleGit git = new SparkleGit(LocalPath, "rebase -v FETCH_HEAD"); git.Exited += delegate { if (git.ExitCode != 0) { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Conflict detected. Trying to get out..."); Watcher.EnableRaisingEvents = false; while (AnyDifferences) { ResolveConflict(); } SparkleHelpers.DebugInfo("Git", "[" + Name + "] Conflict resolved."); Watcher.EnableRaisingEvents = true; SparkleEventArgs args = new SparkleEventArgs("ConflictDetected"); if (ConflictDetected != null) { ConflictDetected(this, args); } } _CurrentHash = GetCurrentHash(); }; git.Start(); git.WaitForExit(); _CurrentHash = GetCurrentHash(); if (NewCommit != null) { NewCommit(GetCommits(1) [0], LocalPath); } SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes rebased."); }
// Stages the made changes private void Add() { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Staging changes..."); SparkleGit git = new SparkleGit(LocalPath, "add --all"); git.Start(); git.WaitForExit(); SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes staged."); SparkleEventArgs args = new SparkleEventArgs("Added"); if (Added != null) { Added(this, args); } }
public override bool SyncDown() { SparkleGit git = new SparkleGit(LocalPath, "fetch -v"); git.Start(); git.WaitForExit(); if (git.ExitCode == 0) { Rebase(); return(true); } else { return(false); } }
// Removes unneeded objects /* private void CollectGarbage () * { * SparkleGit git = new SparkleGit (LocalPath, "gc"); * git.Start (); * git.WaitForExit (); * * SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Garbage collected."); * } */ // Commits the made changes private void Commit(string message) { SparkleGit git = new SparkleGit(LocalPath, "commit -m \"" + message + "\" " + "--author=\"" + SparkleConfig.DefaultConfig.User.Name + " <" + SparkleConfig.DefaultConfig.User.Email + ">\""); git.Start(); git.StandardOutput.ReadToEnd(); git.WaitForExit(); SparkleHelpers.DebugInfo("Commit", "[" + Name + "] " + message); // Collect garbage pseudo-randomly. Turn off for // now: too resource heavy. // if (DateTime.Now.Second % 10 == 0) // CollectGarbage (); }
public override bool Fetch() { SparkleGit git = new SparkleGit (SparklePaths.SparkleTmpPath, "clone \"" + base.remote_url + "\" " + "\"" + base.target_folder + "\""); git.Start (); git.WaitForExit (); SparkleHelpers.DebugInfo ("Git", "Exit code " + git.ExitCode.ToString ()); if (git.ExitCode != 0) { return false; } else { InstallConfiguration (); InstallExcludeRules (); return true; } }
// Stages the made changes private void Add() { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Staging changes..."); // FIXME: this GitSharp method seems to block... // Index.AddAll (); SparkleGit git = new SparkleGit(LocalPath, "add --all"); git.Start(); git.WaitForExit(); SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes staged."); SparkleEventArgs args = new SparkleEventArgs("Added"); if (Added != null) { Added(this, args); } }
private string GetCurrentHash() { // Remove stale rebase-apply files because it // makes the method return the wrong hashes. string rebase_apply_file = SparkleHelpers.CombineMore(LocalPath, ".git", "rebase-apply"); if (File.Exists(rebase_apply_file)) { File.Delete(rebase_apply_file); } SparkleGit git = new SparkleGit(LocalPath, "log -1 --format=%H"); git.Start(); git.WaitForExit(); string output = git.StandardOutput.ReadToEnd(); string hash = output.Trim(); return(hash); }
// Returns a list of latest commits // TODO: Method needs to be made a lot faster public List <SparkleCommit> GetCommits(int count) { if (count < 1) { count = 30; } List <SparkleCommit> commits = new List <SparkleCommit> (); SparkleGit git_log = new SparkleGit(LocalPath, "log -" + count + " --raw -M --date=iso"); Console.OutputEncoding = System.Text.Encoding.Unicode; git_log.Start(); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_log.StandardOutput.ReadToEnd(); git_log.WaitForExit(); string [] lines = output.Split("\n".ToCharArray()); List <string> entries = new List <string> (); int j = 0; string entry = "", last_entry = ""; foreach (string line in lines) { if (line.StartsWith("commit") && j > 0) { entries.Add(entry); entry = ""; } entry += line + "\n"; j++; last_entry = entry; } entries.Add(last_entry); // TODO: Need to optimise for speed foreach (string log_entry in entries) { Regex regex; bool is_merge_commit = false; if (log_entry.Contains("\nMerge: ")) { regex = new Regex(@"commit ([a-z0-9]{40})\n" + "Merge: .+ .+\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) .([0-9]{4})\n" + "*"); is_merge_commit = true; } else { regex = new Regex(@"commit ([a-z0-9]{40})\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) .([0-9]{4})\n" + "*"); } Match match = regex.Match(log_entry); if (match.Success) { SparkleCommit commit = new SparkleCommit(); commit.Hash = match.Groups [1].Value; commit.UserName = match.Groups [2].Value; commit.UserEmail = match.Groups [3].Value; commit.IsMerge = is_merge_commit; commit.DateTime = 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 [] entry_lines = log_entry.Split("\n".ToCharArray()); foreach (string entry_line in entry_lines) { if (entry_line.StartsWith(":")) { string change_type = entry_line [37].ToString(); string file_path = entry_line.Substring(39); string to_file_path; if (change_type.Equals("A")) { commit.Added.Add(file_path); } else if (change_type.Equals("M")) { commit.Edited.Add(file_path); } else if (change_type.Equals("D")) { commit.Deleted.Add(file_path); } else if (change_type.Equals("R")) { int tab_pos = entry_line.LastIndexOf("\t"); file_path = entry_line.Substring(42, tab_pos - 42); to_file_path = entry_line.Substring(tab_pos + 1); commit.MovedFrom.Add(file_path); commit.MovedTo.Add(to_file_path); } } } commits.Add(commit); } } return(commits); }
private void ResolveConflict() { // This is al 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 theirs, save ours as a timestamped copy // UD unmerged, deleted by them -> Use ours // UA unmerged, added by them -> Use theirs, save ours as a timestamped copy // DU unmerged, deleted by us -> Use theirs // AA unmerged, both added -> Use theirs, save ours as a timestamped copy // UU unmerged, both modified -> Use theirs, save ours as a timestamped copy // ?? unmerged, new files -> Stage the new files // // Note that a rebase merge works by replaying each commit from the working branch on // top of the upstream branch. Because of this, when a merge conflict happens the // side reported as 'ours' is the so-far rebased series, starting with upstream, // and 'theirs' is the working branch. In other words, the sides are swapped. // // So: 'ours' means the 'server's version' and 'theirs' means the 'local version' SparkleGit git_status = new SparkleGit (LocalPath, "status --porcelain"); git_status.Start (); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_status.StandardOutput.ReadToEnd ().TrimEnd (); git_status.WaitForExit (); string [] lines = output.Split ("\n".ToCharArray ()); foreach (string line in lines) { string conflicting_path = line.Substring (3); conflicting_path = conflicting_path.Trim ("\"".ToCharArray ()); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict type: " + line); // 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 SparkleGit git_theirs = new SparkleGit (LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.Start (); git_theirs.WaitForExit (); // 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 ("HH\\hmm MMM d"); string their_path = conflicting_path + " (" + SparkleConfig.DefaultConfig.User.Name + ", " + timestamp + ")"; string abs_conflicting_path = Path.Combine (LocalPath, conflicting_path); string abs_their_path = Path.Combine (LocalPath, their_path); File.Move (abs_conflicting_path, abs_their_path); // Recover server version SparkleGit git_ours = new SparkleGit (LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.Start (); git_ours.WaitForExit (); Add (); SparkleGit git_rebase_continue = new SparkleGit (LocalPath, "rebase --continue"); git_rebase_continue.Start (); git_rebase_continue.WaitForExit (); } // The local version has been modified, but the server version was removed 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 SparkleGit git_add = new SparkleGit (LocalPath, "add \"" + conflicting_path + "\""); git_add.Start (); git_add.WaitForExit (); SparkleGit git_rebase_continue = new SparkleGit (LocalPath, "rebase --continue"); git_rebase_continue.Start (); git_rebase_continue.WaitForExit (); } // The server version has been modified, but the local version was removed if (line.StartsWith ("UD")) { // We can just skip here, the server version is // already in the checkout SparkleGit git_rebase_skip = new SparkleGit (LocalPath, "rebase --skip"); git_rebase_skip.Start (); git_rebase_skip.WaitForExit (); } // New local files if (line.StartsWith ("??")) { Add (); SparkleGit git_rebase_continue = new SparkleGit (LocalPath, "rebase --continue"); git_rebase_continue.Start (); git_rebase_continue.WaitForExit (); } } }
// Merges the fetched changes private void Rebase() { DisableWatching (); if (AnyDifferences) { Add (); string commit_message = FormatCommitMessage (); Commit (commit_message); } SparkleGit git = new SparkleGit (LocalPath, "rebase -v FETCH_HEAD"); git.Start (); git.WaitForExit (); if (git.ExitCode != 0) { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict detected. Trying to get out..."); while (AnyDifferences) ResolveConflict (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved."); OnConflictResolved (); } EnableWatching (); }
// Creates a pretty commit message based on what has changed private string FormatCommitMessage() { List<string> Added = new List<string> (); List<string> Modified = new List<string> (); List<string> Removed = new List<string> (); string file_name = ""; string message = ""; SparkleGit git_status = new SparkleGit (LocalPath, "status --porcelain"); git_status.Start (); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_status.StandardOutput.ReadToEnd ().Trim ("\n".ToCharArray ()); git_status.WaitForExit (); string [] lines = output.Split ("\n".ToCharArray ()); foreach (string line in lines) { if (line.StartsWith ("A")) Added.Add (line.Substring (3)); else if (line.StartsWith ("M")) Modified.Add (line.Substring (3)); else if (line.StartsWith ("D")) Removed.Add (line.Substring (3)); else if (line.StartsWith ("R")) { Removed.Add (line.Substring (3, (line.IndexOf (" -> ") - 3))); Added.Add (line.Substring (line.IndexOf (" -> ") + 4)); } } int count = 0; int max_count = 20; string n = Environment.NewLine; foreach (string added in Added) { file_name = added.Trim ("\"".ToCharArray ()); if (file_name.EndsWith (".empty")) file_name = file_name.Substring (0, file_name.Length - 6); message += "+ ‘" + file_name + "’" + n; count++; if (count == max_count) return message + "..."; } foreach (string modified in Modified) { file_name = modified.Trim ("\"".ToCharArray ()); if (file_name.EndsWith (".empty")) file_name = file_name.Substring (0, file_name.Length - 6); message += "/ ‘" + file_name + "’" + n; count++; if (count == max_count) return message + "..."; } foreach (string removed in Removed) { file_name = removed.Trim ("\"".ToCharArray ()); if (file_name.EndsWith (".empty")) file_name = file_name.Substring (0, file_name.Length - 6); message += "- ‘" + file_name + "’" + n; count++; if (count == max_count) return message + "..." + n; } message = message.Replace ("\"", ""); return message.TrimEnd (); }
// Merges the fetched changes public void Rebase() { if (AnyDifferences) { Add (); string commit_message = FormatCommitMessage (); Commit (commit_message); } SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Rebasing changes..."); SparkleGit git = new SparkleGit (LocalPath, "rebase -v FETCH_HEAD"); git.Exited += delegate { if (git.ExitCode != 0) { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict detected. Trying to get out..."); Watcher.EnableRaisingEvents = false; while (AnyDifferences) ResolveConflict (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved."); Watcher.EnableRaisingEvents = true; SparkleEventArgs args = new SparkleEventArgs ("ConflictDetected"); if (ConflictDetected != null) ConflictDetected (this, args); } _CurrentHash = GetCurrentHash (); }; git.Start (); git.WaitForExit (); _CurrentHash = GetCurrentHash (); if (NewCommit != null) NewCommit (GetCommits (1) [0], LocalPath); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes rebased."); }
private string GetUserEmail() { SparkleGit git = new SparkleGit (LocalPath, "config --get user.email"); git.Start (); git.WaitForExit (); string output = git.StandardOutput.ReadToEnd (); string user_email = output.Trim (); return user_email; }
// Creates a pretty commit message based on what has changed private string FormatCommitMessage() { List <string> Added = new List <string> (); List <string> Modified = new List <string> (); List <string> Removed = new List <string> (); string file_name = ""; string message = ""; SparkleGit git_status = new SparkleGit(LocalPath, "status --porcelain"); git_status.Start(); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_status.StandardOutput.ReadToEnd().Trim("\n".ToCharArray()); git_status.WaitForExit(); string [] lines = output.Split("\n".ToCharArray()); foreach (string line in lines) { if (line.StartsWith("A")) { Added.Add(line.Substring(3)); } else if (line.StartsWith("M")) { Modified.Add(line.Substring(3)); } else if (line.StartsWith("D")) { Removed.Add(line.Substring(3)); } else if (line.StartsWith("R")) { Removed.Add(line.Substring(3, (line.IndexOf(" -> ") - 3))); Added.Add(line.Substring(line.IndexOf(" -> ") + 4)); } } int count = 0; int max_count = 20; string n = Environment.NewLine; foreach (string added in Added) { file_name = added.Trim("\"".ToCharArray()); if (file_name.EndsWith(".empty")) { file_name = file_name.Substring(0, file_name.Length - 6); } message += "+ ‘" + file_name + "’" + n; count++; if (count == max_count) { return(message + "..."); } } foreach (string modified in Modified) { file_name = modified.Trim("\"".ToCharArray()); if (file_name.EndsWith(".empty")) { file_name = file_name.Substring(0, file_name.Length - 6); } message += "/ ‘" + file_name + "’" + n; count++; if (count == max_count) { return(message + "..."); } } foreach (string removed in Removed) { file_name = removed.Trim("\"".ToCharArray()); if (file_name.EndsWith(".empty")) { file_name = file_name.Substring(0, file_name.Length - 6); } message += "- ‘" + file_name + "’" + n; count++; if (count == max_count) { return(message + "..." + n); } } message = message.Replace("\"", ""); return(message.TrimEnd()); }
// Pushes the changes to the remote repo public void Push() { _IsSyncing = true; _IsPushing = true; SparkleGit git = new SparkleGit(LocalPath, "push origin master"); SparkleHelpers.DebugInfo("Git", "[" + Name + "] Pushing changes..."); SparkleEventArgs args = new SparkleEventArgs("PushingStarted"); if (PushingStarted != null) { PushingStarted(this, args); } git.Exited += delegate { _IsSyncing = false; _IsPushing = false; if (git.ExitCode != 0) { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Pushing failed."); string unsynced_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "has_unsynced_changes"); if (!File.Exists(unsynced_file_path)) { File.Create(unsynced_file_path); } _HasUnsyncedChanges = true; args = new SparkleEventArgs("PushingFailed"); if (PushingFailed != null) { PushingFailed(this, args); } CheckForRemoteChanges(); Push(); } else { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes pushed."); args = new SparkleEventArgs("PushingFinished"); string unsynced_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "has_unsynced_changes"); if (File.Exists(unsynced_file_path)) { File.Delete(unsynced_file_path); } _HasUnsyncedChanges = false; if (PushingFinished != null) { PushingFinished(this, args); } if (!_IsPolling) { Listener.Announce(_CurrentHash); } } }; git.Start(); git.WaitForExit(); }
// Creates a pretty commit message based on what has changed private string FormatCommitMessage() { List <string> Added = new List <string> (); List <string> Modified = new List <string> (); List <string> Removed = new List <string> (); string file_name = ""; string message = null; SparkleGit git_status = new SparkleGit(LocalPath, "status --porcelain"); git_status.Start(); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_status.StandardOutput.ReadToEnd().Trim("\n".ToCharArray()); git_status.WaitForExit(); string [] lines = output.Split("\n".ToCharArray()); foreach (string line in lines) { if (line.StartsWith("A")) { Added.Add(line.Substring(2)); } else if (line.StartsWith("M")) { Modified.Add(line.Substring(2)); } else if (line.StartsWith("D")) { Removed.Add(line.Substring(2)); } else if (line.StartsWith("R")) { Removed.Add(line.Substring(3, (line.IndexOf(" -> ") - 3))); Added.Add(line.Substring(line.IndexOf(" -> ") + 4)); } } if (Added.Count > 0) { foreach (string added in Added) { file_name = added.Trim("\"".ToCharArray()); break; } message = "+ ‘" + file_name + "’"; } if (Modified.Count > 0) { foreach (string modified in Modified) { file_name = modified.Trim("\"".ToCharArray()); break; } message = "/ ‘" + file_name + "’"; } if (Removed.Count > 0) { foreach (string removed in Removed) { file_name = removed.Trim("\"".ToCharArray()); break; } message = "- ‘" + file_name + "’"; } int changes_count = (Added.Count + Modified.Count + Removed.Count); if (changes_count > 1) { message += " + " + (changes_count - 1); } return(message); }
// Fetches changes from the remote repository public void Fetch() { _IsSyncing = true; _IsFetching = true; RemoteTimer.Stop (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Fetching changes..."); SparkleGit git = new SparkleGit (LocalPath, "fetch -v origin master"); SparkleEventArgs args = new SparkleEventArgs ("FetchingStarted"); if (FetchingStarted != null) FetchingStarted (this, args); git.Exited += delegate { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes fetched."); _IsSyncing = false; _IsFetching = false; _CurrentHash = GetCurrentHash (); if (git.ExitCode != 0) { _ServerOnline = false; args = new SparkleEventArgs ("FetchingFailed"); if (FetchingFailed != null) FetchingFailed (this, args); } else { _ServerOnline = true; args = new SparkleEventArgs ("FetchingFinished"); if (FetchingFinished != null) FetchingFinished (this, args); } RemoteTimer.Start (); }; git.Start (); git.WaitForExit (); }
private string GetCurrentHash() { // Remove stale rebase-apply files because it // makes the method return the wrong hashes. string rebase_apply_file = SparkleHelpers.CombineMore (LocalPath, ".git", "rebase-apply"); if (File.Exists (rebase_apply_file)) File.Delete (rebase_apply_file); SparkleGit git = new SparkleGit (LocalPath, "log -1 --format=%H"); git.Start (); git.WaitForExit (); string output = git.StandardOutput.ReadToEnd (); string hash = output.Trim (); return hash; }
public override void SyncUpNotes() { while (Status != SyncStatus.Idle) { System.Threading.Thread.Sleep (5 * 20); } SparkleGit git_push = new SparkleGit (LocalPath, "push origin refs/notes/*"); git_push.Start (); git_push.WaitForExit (); if (git_push.ExitCode == 0) { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Notes pushed"); } else { HasUnsyncedChanges = true; SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Pushing notes failed, trying again later"); } SparkleAnnouncement announcement = new SparkleAnnouncement (Identifier, SHA1 (DateTime.Now.ToString ())); base.listener.Announce (announcement); }
// Returns a list of the latest change sets public override List<SparkleChangeSet> GetChangeSets(int count) { if (count < 1) count = 30; List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> (); SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso --show-notes=*"); Console.OutputEncoding = System.Text.Encoding.Unicode; git_log.Start (); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_log.StandardOutput.ReadToEnd (); git_log.WaitForExit (); string [] lines = output.Split ("\n".ToCharArray ()); List <string> entries = new List <string> (); int j = 0; string entry = "", last_entry = ""; foreach (string line in lines) { if (line.StartsWith ("commit") && j > 0) { entries.Add (entry); entry = ""; } entry += line + "\n"; j++; last_entry = entry; } entries.Add (last_entry); Regex merge_regex = new Regex (@"commit ([a-z0-9]{40})\n" + "Merge: .+ .+\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) .([0-9]{4})\n" + "*", RegexOptions.Compiled); Regex non_merge_regex = new Regex (@"commit ([a-z0-9]{40})\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) (.[0-9]{4})\n" + "*", RegexOptions.Compiled); foreach (string log_entry in entries) { Regex regex; bool is_merge_commit = false; if (log_entry.Contains ("\nMerge: ")) { regex = merge_regex; is_merge_commit = true; } else { regex = non_merge_regex; } Match match = regex.Match (log_entry); if (match.Success) { SparkleChangeSet change_set = new SparkleChangeSet (); change_set.Folder = Name; change_set.Revision = match.Groups [1].Value; change_set.UserName = match.Groups [2].Value; change_set.UserEmail = match.Groups [3].Value; change_set.IsMerge = is_merge_commit; change_set.SupportsNotes = true; 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 ()); foreach (string entry_line in entry_lines) { if (entry_line.StartsWith (":")) { string change_type = entry_line [37].ToString (); string file_path = entry_line.Substring (39); string to_file_path; if (change_type.Equals ("A")) { change_set.Added.Add (file_path); } else if (change_type.Equals ("M")) { change_set.Edited.Add (file_path); } else if (change_type.Equals ("D")) { change_set.Deleted.Add (file_path); } else if (change_type.Equals ("R")) { int tab_pos = entry_line.LastIndexOf ("\t"); file_path = entry_line.Substring (42, tab_pos - 42); to_file_path = entry_line.Substring (tab_pos + 1); change_set.MovedFrom.Add (file_path); change_set.MovedTo.Add (to_file_path); } } else if (entry_line.StartsWith (" <note>")) { Regex regex_notes = new Regex (@"<name>(.+)</name>.*" + "<email>(.+)</email>.*" + "<timestamp>([0-9]+)</timestamp>.*" + "<body>(.+)</body>", RegexOptions.Compiled); Match match_notes = regex_notes.Match (entry_line); if (match_notes.Success) { SparkleNote note = new SparkleNote () { UserName = match_notes.Groups [1].Value, UserEmail = match_notes.Groups [2].Value, Timestamp = new DateTime (1970, 1, 1).AddSeconds (int.Parse (match_notes.Groups [3].Value)), Body = match_notes.Groups [4].Value }; change_set.Notes.Add (note); } } } change_sets.Add (change_set); } } return change_sets; }
public override void AddNote(string revision, string note) { string url = SparkleConfig.DefaultConfig.GetUrlForFolder (Name); if (url.StartsWith ("git") || url.StartsWith ("http")) return; int timestamp = (int) (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalSeconds; // Create the note in one line for easier merging note = "<note>" + " <user>" + " <name>" + SparkleConfig.DefaultConfig.UserName + "</name>" + " <email>" + SparkleConfig.DefaultConfig.UserEmail + "</email>" + " </user>" + " <timestamp>" + timestamp + "</timestamp>" + " <body>" + note + "</body>" + "</note>"; string note_namespace = SHA1 (timestamp.ToString () + note); SparkleGit git_notes = new SparkleGit (LocalPath, "notes --ref=" + note_namespace + " append -m \"" + note + "\" " + revision); git_notes.Start (); git_notes.WaitForExit (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Added note to " + revision); SyncUpNotes (); }
public override bool SyncDown() { SparkleGit git = new SparkleGit(LocalPath, "fetch --progress"); git.StartInfo.RedirectStandardError = true; git.Start(); double percentage = 1.0; Regex progress_regex = new Regex(@"([0-9]+)%", RegexOptions.Compiled); DateTime last_change = DateTime.Now; TimeSpan change_interval = new TimeSpan(0, 0, 0, 1); while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine(); Match match = progress_regex.Match(line); string speed = ""; double number = 0.0; if (match.Success) { number = double.Parse(match.Groups [1].Value); // The fetching progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Receiving objects" stage which we count as the last 80% if (line.StartsWith("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { // "Writing objects" stage number = (number / 100 * 80 + 20); if (line.Contains("|")) { speed = line.Substring(line.IndexOf("|") + 1).Trim(); speed = speed.Replace(", done.", "").Trim(); speed = speed.Replace("i", ""); speed = speed.Replace("KB/s", "ᴋʙ/s"); speed = speed.Replace("MB/s", "ᴍʙ/s"); } } } if (number >= percentage) { percentage = number; if (percentage == 100.0) { percentage = 99.0; } if (DateTime.Compare(last_change, DateTime.Now.Subtract(change_interval)) < 0) { base.OnSyncProgressChanged(percentage, speed); last_change = DateTime.Now; } } } git.WaitForExit(); if (git.ExitCode == 0) { Rebase(); return(true); } else { return(false); } }
private void ResolveConflict() { // This is al 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 theirs, save ours as a timestamped copy // UD unmerged, deleted by them -> Use ours // UA unmerged, added by them -> Use theirs, save ours as a timestamped copy // DU unmerged, deleted by us -> Use theirs // AA unmerged, both added -> Use theirs, save ours as a timestamped copy // UU unmerged, both modified -> Use theirs, save ours as a timestamped copy // // Note that a rebase merge works by replaying each commit from the working branch on // top of the upstream branch. Because of this, when a merge conflict happens the // side reported as 'ours' is the so-far rebased series, starting with upstream, // and 'theirs' is the working branch. In other words, the sides are swapped. // // So: 'ours' means the 'server's version' and 'theirs' means the 'local version' SparkleGit git_status = new SparkleGit(LocalPath, "status --porcelain"); git_status.Start(); git_status.WaitForExit(); string output = git_status.StandardOutput.ReadToEnd().TrimEnd(); string [] lines = output.Split("\n".ToCharArray()); foreach (string line in lines) { string conflicting_path = line.Substring(3); conflicting_path = conflicting_path.Trim("\"".ToCharArray()); // 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 SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.Start(); git_theirs.WaitForExit(); // Append a timestamp to local version string timestamp = DateTime.Now.ToString("HH:mm MMM d"); string their_path = conflicting_path + " (" + UserName + ", " + timestamp + ")"; string abs_conflicting_path = Path.Combine(LocalPath, conflicting_path); string abs_their_path = Path.Combine(LocalPath, their_path); File.Move(abs_conflicting_path, abs_their_path); // Recover server version SparkleGit git_ours = new SparkleGit(LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.Start(); git_ours.WaitForExit(); Add(); SparkleGit git_rebase_continue = new SparkleGit(LocalPath, "rebase --continue"); git_rebase_continue.Start(); git_rebase_continue.WaitForExit(); } // The local version has been modified, but the server version was removed 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 SparkleGit git_add = new SparkleGit(LocalPath, "add " + conflicting_path); git_add.Start(); git_add.WaitForExit(); SparkleGit git_rebase_continue = new SparkleGit(LocalPath, "rebase --continue"); git_rebase_continue.Start(); git_rebase_continue.WaitForExit(); } // The server version has been modified, but the local version was removed if (line.StartsWith("UD")) { // We can just skip here, the server version is // already in the checkout SparkleGit git_rebase_skip = new SparkleGit(LocalPath, "rebase --skip"); git_rebase_skip.Start(); git_rebase_skip.WaitForExit(); } } }
// Pushes the changes to the remote repo public void Push() { _IsSyncing = true; _IsPushing = true; SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Pushing changes..."); SparkleGit git = new SparkleGit (LocalPath, "push origin master"); SparkleEventArgs args = new SparkleEventArgs ("PushingStarted"); if (PushingStarted != null) PushingStarted (this, args); git.Exited += delegate { _IsSyncing = false; _IsPushing = false; if (git.ExitCode != 0) { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Pushing failed."); string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath , ".git", "has_unsynced_changes"); if (!File.Exists (unsynced_file_path)) File.Create (unsynced_file_path); _HasUnsyncedChanges = true; args = new SparkleEventArgs ("PushingFailed"); if (PushingFailed != null) PushingFailed (this, args); FetchRebaseAndPush (); } else { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes pushed."); args = new SparkleEventArgs ("PushingFinished"); string unsynced_file_path = SparkleHelpers.CombineMore (LocalPath , ".git", "has_unsynced_changes"); if (File.Exists (unsynced_file_path)) File.Delete (unsynced_file_path); _HasUnsyncedChanges = false; if (PushingFinished != null) PushingFinished (this, args); if (Listener.Client.IsConnected) { Listener.Announce (_CurrentHash); } else { AnnounceQueue++; SparkleHelpers.DebugInfo ("Irc", "[" + Name + "] Could not deliver notification, added it to the queue"); } } }; git.Start (); git.WaitForExit (); }
// Merges the fetched changes public void Rebase() { if (AnyDifferences) { Add(); string commit_message = FormatCommitMessage(); Commit(commit_message); } SparkleHelpers.DebugInfo("Git", "[" + Name + "] Rebasing changes..."); SparkleGit git = new SparkleGit(LocalPath, "rebase -v FETCH_HEAD"); git.Exited += delegate { if (Status.MergeConflict.Count > 0) { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Conflict detected..."); foreach (string problem_file_name in Status.MergeConflict) { SparkleGit git_ours = new SparkleGit(LocalPath, "checkout --ours " + problem_file_name); git_ours.Start(); git_ours.WaitForExit(); string timestamp = DateTime.Now.ToString("H:mm d MMM"); string new_file_name = problem_file_name + " (" + UserName + ", " + timestamp + ")"; File.Move(problem_file_name, new_file_name); SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs " + problem_file_name); git_theirs.Start(); git_theirs.WaitForExit(); SparkleEventArgs args = new SparkleEventArgs("ConflictDetected"); if (ConflictDetected != null) { ConflictDetected(this, args); } } Add(); SparkleGit git_continue = new SparkleGit(LocalPath, "rebase --continue"); git_continue.Start(); git_continue.WaitForExit(); SparkleHelpers.DebugInfo("Git", "[" + Name + "] Conflict resolved."); Push(); } }; git.Start(); git.WaitForExit(); _CurrentHash = Head.CurrentCommit.Hash; if (NewCommit != null) { NewCommit(GetCommits(1) [0], LocalPath); } SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes rebased."); }
// Stages the made changes private void Add() { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Staging changes..."); SparkleGit git = new SparkleGit (LocalPath, "add --all"); git.Start (); git.WaitForExit (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes staged."); SparkleEventArgs args = new SparkleEventArgs ("Added"); if (Added != null) Added (this, args); }
// Removes unneeded objects private void CollectGarbage() { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Collecting garbage..."); SparkleGit git = new SparkleGit (LocalPath, "gc"); git.Start (); git.WaitForExit (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Garbage collected."); }
// Pushes the changes to the remote repo public void Push() { _IsSyncing = true; _IsPushing = true; SparkleHelpers.DebugInfo("Git", "[" + Name + "] Pushing changes..."); SparkleGit git = new SparkleGit(LocalPath, "push origin master"); SparkleEventArgs args = new SparkleEventArgs("PushingStarted"); if (PushingStarted != null) { PushingStarted(this, args); } git.Exited += delegate { _IsSyncing = false; _IsPushing = false; if (git.ExitCode != 0) { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Pushing failed."); string unsynced_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "has_unsynced_changes"); if (!File.Exists(unsynced_file_path)) { File.Create(unsynced_file_path); } _HasUnsyncedChanges = true; args = new SparkleEventArgs("PushingFailed"); if (PushingFailed != null) { PushingFailed(this, args); } FetchRebaseAndPush(); } else { SparkleHelpers.DebugInfo("Git", "[" + Name + "] Changes pushed."); args = new SparkleEventArgs("PushingFinished"); string unsynced_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "has_unsynced_changes"); if (File.Exists(unsynced_file_path)) { File.Delete(unsynced_file_path); } _HasUnsyncedChanges = false; if (PushingFinished != null) { PushingFinished(this, args); } if (Listener.Client.IsConnected) { Listener.Announce(_CurrentHash); } else { AnnounceQueue++; SparkleHelpers.DebugInfo("Irc", "[" + Name + "] Could not deliver notification, added it to the queue"); } } }; git.Start(); git.WaitForExit(); }
private string GetCurrentHash() { SparkleGit git = new SparkleGit (LocalPath, "log -1 --format=%H"); git.Start (); git.WaitForExit (); string output = git.StandardOutput.ReadToEnd (); string hash = output.Trim (); return hash; }
public override bool SyncDown() { SparkleGit git = new SparkleGit (LocalPath, "fetch -v origin master"); git.Start (); git.WaitForExit (); if (git.ExitCode == 0) { Rebase (); return true; } else { return false; } }
// Returns a list of the latest change sets public override List <SparkleChangeSet> GetChangeSets(int count) { if (count < 1) { count = 30; } List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> (); // Console.InputEncoding = System.Text.Encoding.Unicode; Console.OutputEncoding = System.Text.Encoding.Unicode; SparkleGit git_log = new SparkleGit(LocalPath, "log -" + count + " --raw -M --date=iso"); git_log.Start(); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_log.StandardOutput.ReadToEnd(); git_log.WaitForExit(); string [] lines = output.Split("\n".ToCharArray()); List <string> entries = new List <string> (); int j = 0; string entry = "", last_entry = ""; foreach (string line in lines) { if (line.StartsWith("commit") && j > 0) { entries.Add(entry); entry = ""; } entry += line + "\n"; j++; last_entry = entry; } entries.Add(last_entry); Regex merge_regex = new Regex(@"commit ([a-z0-9]{40})\n" + "Merge: .+ .+\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) .([0-9]{4})\n" + "*", RegexOptions.Compiled); Regex non_merge_regex = new Regex(@"commit ([a-z0-9]{40})\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) (.[0-9]{4})\n" + "*", RegexOptions.Compiled); foreach (string log_entry in entries) { Regex regex; bool is_merge_commit = false; if (log_entry.Contains("\nMerge: ")) { regex = merge_regex; is_merge_commit = true; } else { regex = non_merge_regex; } Match match = regex.Match(log_entry); if (match.Success) { SparkleChangeSet change_set = new SparkleChangeSet(); change_set.Folder = Name; change_set.Revision = match.Groups [1].Value; change_set.User.Name = match.Groups [2].Value; change_set.User.Email = match.Groups [3].Value; change_set.IsMagical = is_merge_commit; 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()); foreach (string entry_line in entry_lines) { if (entry_line.StartsWith(":")) { string change_type = entry_line [37].ToString(); string file_path = entry_line.Substring(39); string to_file_path; if (file_path.EndsWith(".empty")) { file_path = file_path.Substring(0, file_path.Length - ".empty".Length); } if (change_type.Equals("A") && !file_path.Contains(".notes")) { change_set.Added.Add(file_path); } else if (change_type.Equals("M")) { change_set.Edited.Add(file_path); } else if (change_type.Equals("D")) { change_set.Deleted.Add(file_path); } else if (change_type.Equals("R")) { int tab_pos = entry_line.LastIndexOf("\t"); file_path = entry_line.Substring(42, tab_pos - 42); to_file_path = entry_line.Substring(tab_pos + 1); change_set.MovedFrom.Add(file_path); change_set.MovedTo.Add(to_file_path); } } } if ((change_set.Added.Count + change_set.Edited.Count + change_set.Deleted.Count + change_set.MovedFrom.Count) > 0) { change_set.Notes.AddRange(GetNotes(change_set.Revision)); change_sets.Add(change_set); } } } return(change_sets); }
// Commits the made changes public void Commit(string message) { if (!AnyDifferences) return; SparkleGit git = new SparkleGit (LocalPath, "commit -m '" + message + "'"); git.Start (); git.WaitForExit (); _CurrentHash = GetCurrentHash (); SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message + " (" + _CurrentHash + ")"); SparkleEventArgs args = new SparkleEventArgs ("Commited") { Message = message }; if (Commited != null) Commited (this, args); // Collect garbage pseudo-randomly if (DateTime.Now.Second % 10 == 0) CollectGarbage (); }
public override bool SyncUp() { Add (); string message = FormatCommitMessage (); Commit (message); SparkleGit git = new SparkleGit (LocalPath, "push origin master"); git.Start (); git.WaitForExit (); if (git.ExitCode == 0) return true; else return false; }
// Returns a list of latest commits // TODO: Method needs to be made a lot faster public List<SparkleCommit> GetCommits(int count) { if (count < 1) count = 30; List <SparkleCommit> commits = new List <SparkleCommit> (); SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso"); Console.OutputEncoding = System.Text.Encoding.Unicode; git_log.Start (); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_log.StandardOutput.ReadToEnd (); git_log.WaitForExit (); string [] lines = output.Split ("\n".ToCharArray ()); List <string> entries = new List <string> (); int j = 0; string entry = "", last_entry = ""; foreach (string line in lines) { if (line.StartsWith ("commit") && j > 0) { entries.Add (entry); entry = ""; } entry += line + "\n"; j++; last_entry = entry; } entries.Add (last_entry); // TODO: Need to optimise for speed foreach (string log_entry in entries) { Regex regex; bool is_merge_commit = false; if (log_entry.Contains ("\nMerge: ")) { regex = new Regex (@"commit ([a-z0-9]{40})\n" + "Merge: .+ .+\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) .([0-9]{4})\n" + "*"); is_merge_commit = true; } else { regex = new Regex (@"commit ([a-z0-9]{40})\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) .([0-9]{4})\n" + "*"); } Match match = regex.Match (log_entry); if (match.Success) { SparkleCommit commit = new SparkleCommit (); commit.Hash = match.Groups [1].Value; commit.UserName = match.Groups [2].Value; commit.UserEmail = match.Groups [3].Value; commit.IsMerge = is_merge_commit; commit.DateTime = 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 [] entry_lines = log_entry.Split ("\n".ToCharArray ()); foreach (string entry_line in entry_lines) { if (entry_line.StartsWith (":")) { string change_type = entry_line [37].ToString (); string file_path = entry_line.Substring (39); string to_file_path; if (change_type.Equals ("A")) { commit.Added.Add (file_path); } else if (change_type.Equals ("M")) { commit.Edited.Add (file_path); } else if (change_type.Equals ("D")) { commit.Deleted.Add (file_path); } else if (change_type.Equals ("R")) { int tab_pos = entry_line.LastIndexOf ("\t"); file_path = entry_line.Substring (42, tab_pos - 42); to_file_path = entry_line.Substring (tab_pos + 1); commit.MovedFrom.Add (file_path); commit.MovedTo.Add (to_file_path); } } } commits.Add (commit); } } return commits; }
// Commits the made changes private void Commit(string message) { if (!AnyDifferences) return; SparkleGit git = new SparkleGit (LocalPath, "commit -m \"" + message + "\""); git.Start (); git.WaitForExit (); SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message); // Collect garbage pseudo-randomly. Turn off for // now: too resource heavy. // if (DateTime.Now.Second % 10 == 0) // CollectGarbage (); }
// Merges the fetched changes public void Rebase() { if (AnyDifferences) { Add (); string commit_message = FormatCommitMessage (); Commit (commit_message); } SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Rebasing changes..."); SparkleGit git = new SparkleGit (LocalPath, "rebase -v FETCH_HEAD"); git.Exited += delegate { /* if (Status.MergeConflict.Count > 0) { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict detected..."); foreach (string problem_file_name in Status.MergeConflict) { SparkleGit git_ours = new SparkleGit (LocalPath, "checkout --ours " + problem_file_name); git_ours.Start (); git_ours.WaitForExit (); string timestamp = DateTime.Now.ToString ("H:mm d MMM"); string new_file_name = problem_file_name + " (" + UserName + ", " + timestamp + ")"; File.Move (problem_file_name, new_file_name); SparkleGit git_theirs = new SparkleGit (LocalPath, "checkout --theirs " + problem_file_name); git_theirs.Start (); git_theirs.WaitForExit (); SparkleEventArgs args = new SparkleEventArgs ("ConflictDetected"); if (ConflictDetected != null) ConflictDetected (this, args); } Add (); SparkleGit git_continue = new SparkleGit (LocalPath, "rebase --continue"); git_continue.Start (); git_continue.WaitForExit (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved."); */ _CurrentHash = GetCurrentHash (); // Push (); // } }; git.Start (); git.WaitForExit (); _CurrentHash = GetCurrentHash (); if (NewCommit != null) NewCommit (GetCommits (1) [0], LocalPath); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes rebased."); }
public override bool CheckForRemoteChanges() { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Checking for remote changes..."); SparkleGit git = new SparkleGit (LocalPath, "ls-remote origin master"); git.Start (); git.WaitForExit (); if (git.ExitCode != 0) return false; string remote_revision = git.StandardOutput.ReadToEnd ().TrimEnd (); if (!remote_revision.StartsWith (CurrentRevision)) { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Remote changes found. (" + remote_revision + ")"); return true; } else { return false; } }
private void CheckForRemoteChanges() { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Checking for remote changes..."); SparkleGit git = new SparkleGit (LocalPath, "ls-remote origin master"); git.Exited += delegate { if (git.ExitCode != 0) return; string remote_hash = git.StandardOutput.ReadToEnd (); if (!remote_hash.StartsWith (_CurrentHash)) { SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Remote changes found."); Fetch (); Watcher.EnableRaisingEvents = false; Rebase (); Watcher.EnableRaisingEvents = true; } }; git.Start (); git.WaitForExit (); }
// Returns a list of the latest change sets public override List<SparkleChangeSet> GetChangeSets(int count) { if (count < 1) count = 30; List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> (); // Console.InputEncoding = System.Text.Encoding.Unicode; Console.OutputEncoding = System.Text.Encoding.Unicode; SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso"); git_log.Start (); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_log.StandardOutput.ReadToEnd (); git_log.WaitForExit (); string [] lines = output.Split ("\n".ToCharArray ()); List <string> entries = new List <string> (); 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 250 files to prevent memory issues if (line_number < 254) { entry += line + "\n"; line_number++; } last_entry = entry; } entries.Add (last_entry); Regex merge_regex = new Regex (@"commit ([a-z0-9]{40})\n" + "Merge: .+ .+\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) .([0-9]{4})\n" + "*", RegexOptions.Compiled); Regex non_merge_regex = new Regex (@"commit ([a-z0-9]{40})\n" + "Author: (.+) <(.+)>\n" + "Date: ([0-9]{4})-([0-9]{2})-([0-9]{2}) " + "([0-9]{2}):([0-9]{2}):([0-9]{2}) (.[0-9]{4})\n" + "*", RegexOptions.Compiled); foreach (string log_entry in entries) { Regex regex; bool is_merge_commit = false; if (log_entry.Contains ("\nMerge: ")) { regex = merge_regex; is_merge_commit = true; } else { regex = non_merge_regex; } Match match = regex.Match (log_entry); if (match.Success) { SparkleChangeSet change_set = new SparkleChangeSet (); change_set.Folder = Name; change_set.Revision = match.Groups [1].Value; change_set.User.Name = match.Groups [2].Value; change_set.User.Email = match.Groups [3].Value; change_set.IsMagical = is_merge_commit; 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 ()); foreach (string entry_line in entry_lines) { if (entry_line.StartsWith (":")) { string change_type = entry_line [37].ToString (); string file_path = entry_line.Substring (39); string to_file_path; if (file_path.EndsWith (".empty")) file_path = file_path.Substring (0, file_path.Length - ".empty".Length); if (change_type.Equals ("A") && !file_path.Contains (".notes")) { change_set.Added.Add (file_path); } else if (change_type.Equals ("M")) { change_set.Edited.Add (file_path); } else if (change_type.Equals ("D")) { change_set.Deleted.Add (file_path); } else if (change_type.Equals ("R")) { int tab_pos = entry_line.LastIndexOf ("\t"); file_path = entry_line.Substring (42, tab_pos - 42); to_file_path = entry_line.Substring (tab_pos + 1); change_set.MovedFrom.Add (file_path); change_set.MovedTo.Add (to_file_path); } } } if ((change_set.Added.Count + change_set.Edited.Count + change_set.Deleted.Count + change_set.MovedFrom.Count) > 0) { change_set.Notes.AddRange (GetNotes (change_set.Revision)); change_sets.Add (change_set); } } } return change_sets; }
// Creates a pretty commit message based on what has changed private string FormatCommitMessage() { List<string> Added = new List<string> (); List<string> Modified = new List<string> (); List<string> Removed = new List<string> (); string file_name = ""; string message = null; SparkleGit git_status = new SparkleGit (LocalPath, "status --porcelain"); git_status.Start (); // Reading the standard output HAS to go before // WaitForExit, or it will hang forever on output > 4096 bytes string output = git_status.StandardOutput.ReadToEnd ().Trim ("\n".ToCharArray ()); git_status.WaitForExit (); string [] lines = output.Split ("\n".ToCharArray ()); foreach (string line in lines) { if (line.StartsWith ("A")) Added.Add (line.Substring (2)); else if (line.StartsWith ("M")) Modified.Add (line.Substring (2)); else if (line.StartsWith ("D")) Removed.Add (line.Substring (2)); else if (line.StartsWith ("R")) { Removed.Add (line.Substring (3, (line.IndexOf (" -> ") - 3))); Added.Add (line.Substring (line.IndexOf (" -> ") + 4)); } } if (Added.Count > 0) { foreach (string added in Added) { file_name = added.Trim ("\"".ToCharArray ()); break; } message = "+ ‘" + file_name + "’"; } if (Modified.Count > 0) { foreach (string modified in Modified) { file_name = modified.Trim ("\"".ToCharArray ()); break; } message = "/ ‘" + file_name + "’"; } if (Removed.Count > 0) { foreach (string removed in Removed) { file_name = removed.Trim ("\"".ToCharArray ()); break; } message = "- ‘" + file_name + "’"; } int changes_count = (Added.Count + Modified.Count + Removed.Count); if (changes_count > 1) message += " + " + (changes_count - 1); return message; }
public override bool SyncUp() { if (AnyDifferences) { Add (); string message = FormatCommitMessage (); Commit (message); } SparkleGit git = new SparkleGit (LocalPath, "push --progress " + // Redirects progress stats to standarderror "origin master"); git.StartInfo.RedirectStandardError = true; git.Start (); double percentage = 1.0; Regex progress_regex = new Regex (@"([0-9]+)%", RegexOptions.Compiled); while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine (); Match match = progress_regex.Match (line); string speed = ""; double number = 0.0; if (match.Success) { number = double.Parse (match.Groups [1].Value); // The pushing progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Writing objects" stage which we count as the last 80% if (line.StartsWith ("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { // "Writing objects" stage number = (number / 100 * 80 + 20); if (line.Contains ("|")) { speed = line.Substring (line.IndexOf ("|") + 1).Trim (); speed = speed.Replace (", done.", "").Trim (); speed = speed.Replace ("i", ""); speed = speed.Replace ("KB/s", "ᴋʙ/s"); speed = speed.Replace ("MB/s", "ᴍʙ/s"); } } } if (number >= percentage) { percentage = number; base.OnSyncProgressChanged (percentage, speed); } } git.WaitForExit (); CalculateSizes (); if (git.ExitCode == 0) return true; else return false; }
private string GetRemoteOriginUrl() { SparkleGit git = new SparkleGit (LocalPath, "config --get remote.origin.url"); git.Start (); git.WaitForExit (); string output = git.StandardOutput.ReadToEnd (); string url = output.Trim (); return url; }
// Stages the made changes private void Add() { SparkleGit git = new SparkleGit (LocalPath, "add --all"); git.Start (); git.WaitForExit (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Changes staged"); }
private string GetUserName() { SparkleGit git = new SparkleGit (LocalPath, "config --get user.name"); git.Start (); git.WaitForExit (); string output = git.StandardOutput.ReadToEnd (); string user_name = output.Trim (); return user_name; }
// Removes unneeded objects /* private void CollectGarbage () { SparkleGit git = new SparkleGit (LocalPath, "gc"); git.Start (); git.WaitForExit (); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Garbage collected."); } */ // Commits the made changes private void Commit(string message) { SparkleGit git = new SparkleGit (LocalPath, "commit -m \"" + message + "\" " + "--author=\"" + SparkleConfig.DefaultConfig.User.Name + " <" + SparkleConfig.DefaultConfig.User.Email + ">\""); git.Start (); git.StandardOutput.ReadToEnd (); git.WaitForExit (); SparkleHelpers.DebugInfo ("Commit", "[" + Name + "] " + message); // Collect garbage pseudo-randomly. Turn off for // now: too resource heavy. // if (DateTime.Now.Second % 10 == 0) // CollectGarbage (); }