private void AddWarnings()
        {
            OrangeGit git = new OrangeGit (TargetFolder,
                "config --global core.excludesfile");

            git.Start ();

            // Reading the standard output HAS to go before
            // WaitForExit, or it will hang forever on output > 4096 bytes
            string output = git.StandardOutput.ReadToEnd ().Trim ();
            git.WaitForExit ();

            if (string.IsNullOrEmpty (output))
                return;
            else
                this.warnings.Add ("You seem to have a system wide ‘gitignore’ file, this may affect OrangeShare files.");
        }
        public override void Complete()
        {
            OrangeGit git = new OrangeGit (TargetFolder, "checkout HEAD");
            git.Start ();

            // Reading the standard output HAS to go before
            // WaitForExit, or it will hang forever on output > 4096 bytes
            git.StandardOutput.ReadToEnd ();
            git.WaitForExit ();
        }
        public override bool IsFetchedRepoPasswordCorrect(string password)
        {
            string password_check_file_path = Path.Combine (TargetFolder, ".sparkleshare");

            if (!File.Exists (password_check_file_path)) {
                OrangeGit git = new OrangeGit (TargetFolder, "show HEAD:.sparkleshare");
                git.Start ();

                // Reading the standard output HAS to go before
                // WaitForExit, or it will hang forever on output > 4096 bytes
                string output = git.StandardOutput.ReadToEnd ();
                git.WaitForExit ();

                if (git.ExitCode != 0) {
                    return false;

                } else {
                    File.WriteAllText (password_check_file_path, output);
                }
            }

            Process process = new Process () {
                EnableRaisingEvents = true
            };

            process.StartInfo.WorkingDirectory       = TargetFolder;
            process.StartInfo.UseShellExecute        = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.CreateNoWindow         = true;

            process.StartInfo.FileName  = "openssl";
            process.StartInfo.Arguments = "enc -d -aes-256-cbc -base64 -S " + this.crypto_salt +
                " -pass pass:\"" + password + "\" -in " + password_check_file_path;

            process.Start ();

            // Reading the standard output HAS to go before
            // WaitForExit, or it will hang forever on output > 4096 bytes
            process.StandardOutput.ReadToEnd ();
            process.WaitForExit ();

            if (process.ExitCode == 0) {
                File.Delete (password_check_file_path);
                return true;

            } else {
                return false;
            }
        }
        // Merges the fetched changes
        private void Rebase()
        {
            DisableWatching ();

            if (HasLocalChanges) {
                Add ();

                string commit_message = FormatCommitMessage ();
                Commit (commit_message);
            }

            OrangeGit git = new OrangeGit (LocalPath, "rebase FETCH_HEAD");
            git.StartInfo.RedirectStandardOutput = false;

            git.Start ();
            git.WaitForExit ();

            if (git.ExitCode != 0) {
                OrangeHelpers.DebugInfo ("Git", "[" + Name + "] Conflict detected. Trying to get out...");

                while (HasLocalChanges)
                    ResolveConflict ();

                OrangeHelpers.DebugInfo ("Git", "[" + Name + "] Conflict resolved.");
                OnConflictResolved ();
            }

            EnableWatching ();
        }
        private void ResolveConflict()
        {
            // This is a list of conflict status codes that Git uses, their
            // meaning, and how OrangeShare 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'

            OrangeGit git_status = new OrangeGit (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 ());

                OrangeHelpers.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
                    OrangeGit git_theirs = new OrangeGit (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 ("MMM d H\\hmm");
                    string their_path           = Path.GetFileNameWithoutExtension (conflicting_path) +
                        " (" + OrangeConfig.DefaultConfig.User.Name + ", " + timestamp + ")" + Path.GetExtension (conflicting_path);

                    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
                    OrangeGit git_ours = new OrangeGit (LocalPath,
                        "checkout --ours \"" + conflicting_path + "\"");
                    git_ours.Start ();
                    git_ours.WaitForExit ();

                    Add ();

                    OrangeGit git_rebase_continue = new OrangeGit (LocalPath, "rebase --continue");
                    git_rebase_continue.Start ();
                    git_rebase_continue.WaitForExit ();

                // The local version has been modified, but the server 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
                    OrangeGit git_add = new OrangeGit (LocalPath,
                        "add \"" + conflicting_path + "\"");
                    git_add.Start ();
                    git_add.WaitForExit ();

                    OrangeGit git_rebase_continue = new OrangeGit (LocalPath, "rebase --continue");
                    git_rebase_continue.Start ();
                    git_rebase_continue.WaitForExit ();

                // The server version has been modified, but the local version was removed
                } else if (line.StartsWith ("UD")) {

                    // We can just skip here, the server version is
                    // already in the checkout
                    OrangeGit git_rebase_skip = new OrangeGit (LocalPath, "rebase --skip");
                    git_rebase_skip.Start ();
                    git_rebase_skip.WaitForExit ();

                // New local files
                } else {
                    Add ();

                    OrangeGit git_rebase_continue = new OrangeGit (LocalPath, "rebase --continue");
                    git_rebase_continue.Start ();
                    git_rebase_continue.WaitForExit ();
                }
            }
        }
        // Removes unneeded objects
        /*        private void CollectGarbage ()
        {
            OrangeGit git = new OrangeGit (LocalPath, "gc");
            git.Start ();
            git.WaitForExit ();

            OrangeHelpers.DebugInfo ("Git", "[" + Name + "] Garbage collected.");
        } */
        // Commits the made changes
        private void Commit(string message)
        {
            OrangeGit git = new OrangeGit (LocalPath,
                "commit -m \"" + message + "\" " +
                "--author=\"" + OrangeConfig.DefaultConfig.User.Name +
                " <" + OrangeConfig.DefaultConfig.User.Email + ">\"");

            git.Start ();
            git.StandardOutput.ReadToEnd ();
            git.WaitForExit ();

            OrangeHelpers.DebugInfo ("Commit", "[" + Name + "] " + message);
        }
        // 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        = "";

            OrangeGit git_status = new OrangeGit (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"))
                    continue;

                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 ();
        }
        // Stages the made changes
        private void Add()
        {
            OrangeGit git = new OrangeGit (LocalPath, "add --all");
            git.Start ();
            git.WaitForExit ();

            OrangeHelpers.DebugInfo ("Git", "[" + Name + "] Changes staged");
        }
        public override bool SyncUp()
        {
            if (HasLocalChanges) {
                Add ();

                string message = FormatCommitMessage ();
                Commit (message);
            }

            OrangeGit git = new OrangeGit (LocalPath,
                "push --progress " + // Redirects progress stats to standarderror
                "\"" + RemoteUrl + "\" 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 {
                        if (line.StartsWith ("ERROR: QUOTA EXCEEDED")) {
                            int quota_limit = int.Parse (line.Substring (21).Trim ());
                            throw new QuotaExceededException ("Quota exceeded", quota_limit);
                        }

                        // "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");
                        }
                    }

                } else {
                    OrangeHelpers.DebugInfo ("Git", "[" + Name + "] " + line);
                }

                if (number >= percentage) {
                    percentage = number;
                    base.OnProgressChanged (percentage, speed);
                }
            }

            git.WaitForExit ();

            UpdateSizes ();
            ChangeSets = GetChangeSets ();

            if (git.ExitCode == 0)
                return true;
            else
                return false;
        }
        public override bool SyncDown()
        {
            OrangeGit git = new OrangeGit (LocalPath, "fetch --progress \"" + RemoteUrl + "\" 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 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");
                        }
                    }

                } else {
                    OrangeHelpers.DebugInfo ("Git", "[" + Name + "] " + line);
                }

                if (number >= percentage) {
                    percentage = number;
                    base.OnProgressChanged (percentage, speed);
                }
            }

            git.WaitForExit ();

            UpdateSizes ();

            if (git.ExitCode == 0) {
                Rebase ();

                File.SetAttributes (
                    Path.Combine (LocalPath, ".sparkleshare"),
                    FileAttributes.Hidden
                );

                ChangeSets = GetChangeSets ();

                return true;

            } else {
                ChangeSets = GetChangeSets ();
                return false;
            }
        }
        // Returns a list of the latest change sets
        public override List<OrangeChangeSet> GetChangeSets(int count)
        {
            if (count < 1)
                count = 30;

            List <OrangeChangeSet> change_sets = new List <OrangeChangeSet> ();

            // Console.InputEncoding  = System.Text.Encoding.Unicode;
            // Console.OutputEncoding = System.Text.Encoding.Unicode;

            OrangeGit git_log = new OrangeGit (LocalPath,
                "log -" + count + " --raw -M --date=iso --format=medium --no-color");
            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) {
                    OrangeChangeSet change_set = new OrangeChangeSet ();

                    change_set.Folder    = new OrangeFolder (Name);
                    change_set.Revision  = match.Groups [1].Value;
                    change_set.User      = new OrangeUser (match.Groups [2].Value, match.Groups [3].Value);
                    change_set.IsMagical = is_merge_commit;
                    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 ());

                    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 (file_path.Equals (".sparkleshare"))
                                continue;

                            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);

                                if (file_path.EndsWith (".empty"))
                                    file_path = file_path.Substring (0,
                                        file_path.Length - ".empty".Length);

                                if (to_file_path.EndsWith (".empty"))
                                    to_file_path = to_file_path.Substring (0,
                                        to_file_path.Length - ".empty".Length);

                                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_sets.Add (change_set);
                    }
                }
            }

            return change_sets;
        }
        public override string ComputeIdentifier()
        {
            // Because git computes a hash based on content,
            // author, and timestamp; it is unique enough to
            // use the hash of the first commit as an identifier
            // for our folder
            OrangeGit git = new OrangeGit (LocalPath, "rev-list --reverse HEAD");
            git.Start ();

            // Reading the standard output HAS to go before
            // WaitForExit, or it will hang forever on output > 4096 bytes
            string output = git.StandardOutput.ReadToEnd ();
            git.WaitForExit ();

            if (output.Length < 40)
                return null;

            return output.Substring (0, 40);
        }