Пример #1
0
        // 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();
        }
Пример #2
0
        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);
        }
Пример #3
0
        // 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);
        }
Пример #4
0
        // 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();
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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();
            }
        }
Пример #8
0
        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;
        }
Пример #9
0
        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;
        }
Пример #10
0
        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);
        }
Пример #11
0
        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();
            }
        }
Пример #12
0
        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 ();
        }
Пример #13
0
        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;
        }
Пример #14
0
        // 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 ();
        }