// 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();
        }
Example #2
0
        void InstallConfiguration()
        {
            string [] settings =
            {
                "core.autocrlf input",
                "core.quotepath false",        // For commands to output Unicode characters "as is". e.g. '"h\303\251"' becomes 'hé'.
                "core.precomposeunicode true", // Use the same Unicode form on all filesystems
                "core.ignorecase false",       // Be case sensitive explicitly to work on Mac
                "core.filemode false",         // Ignore permission changes
                "core.safecrlf false",
                "core.excludesfile \"\"",
                "core.packedGitLimit 128m", // Some memory limiting options
                "core.packedGitWindowSize 128m",
                "pack.deltaCacheSize 128m",
                "pack.packSizeLimit 128m",
                "pack.windowMemory 128m",
                "push.default matching"
            };

            if (InstallationInfo.OperatingSystem == OS.Windows)
            {
                settings [0] = "core.autocrlf true";
            }

            foreach (string setting in settings)
            {
                var git_config = new GitCommand(TargetFolder, "config " + setting);
                git_config.StartAndWaitForExit();
            }
        }
Example #3
0
        void InstallGitLFS()
        {
            var git_config_required = new GitCommand(TargetFolder, "config filter.lfs.required true");

            string GIT_SSH_COMMAND = GitCommand.FormatGitSSHCommand(auth_info);
            string smudge_command;
            string clean_command;

            if (InstallationInfo.OperatingSystem == OS.macOS || InstallationInfo.OperatingSystem == OS.Windows)
            {
                smudge_command = "env GIT_SSH_COMMAND='" + GIT_SSH_COMMAND + "' " +
                                 Path.Combine(Configuration.DefaultConfiguration.BinPath, "git-lfs").Replace("\\", "/") + " smudge %f";

                clean_command = Path.Combine(Configuration.DefaultConfiguration.BinPath, "git-lfs").Replace("\\", "/") + " clean %f";
            }
            else
            {
                smudge_command = "env GIT_SSH_COMMAND='" + GIT_SSH_COMMAND + "' git-lfs smudge %f";
                clean_command  = "git-lfs clean %f";
            }

            var git_config_smudge = new GitCommand(TargetFolder,
                                                   string.Format("config filter.lfs.smudge \"{0}\"", smudge_command));

            var git_config_clean = new GitCommand(TargetFolder,
                                                  string.Format("config filter.lfs.clean '{0}'", clean_command));

            git_config_required.StartAndWaitForExit();
            git_config_clean.StartAndWaitForExit();
            git_config_smudge.StartAndWaitForExit();
        }
        // Stages the made changes
        bool Add()
        {
            var git = new GitCommand(LocalPath, "add --all");

            git.StartAndWaitForExit();

            return(git.ExitCode == 0);
        }
Example #5
0
        public GitRepository(string path, Configuration config, SSHAuthenticationInfo auth_info)
            : base(path, config)
        {
            this.auth_info = auth_info;

            var git_config = new GitCommand (LocalPath, "config core.ignorecase false");
            git_config.StartAndWaitForExit ();

            git_config = new GitCommand (LocalPath, "config remote.origin.url \"" + RemoteUrl + "\"");
            git_config.StartAndWaitForExit ();
        }
        public GitRepository(string path, Configuration config, SSHAuthenticationInfo auth_info) : base(path, config)
        {
            this.auth_info = auth_info;

            var git_config = new GitCommand(LocalPath, "config core.ignorecase false");

            git_config.StartAndWaitForExit();

            git_config = new GitCommand(LocalPath, "config remote.origin.url \"" + RemoteUrl + "\"");
            git_config.StartAndWaitForExit();
        }
        public override bool SyncDown()
        {
            string lfs_is_behind_file_path = Path.Combine(LocalPath, ".git", "lfs", "is_behind");

            if (StorageType == StorageType.LargeFiles)
            {
                File.Create(lfs_is_behind_file_path);
            }

            var git_fetch = new GitCommand(LocalPath, "fetch --progress origin " + branch, auth_info);

            git_fetch.StartInfo.RedirectStandardError = true;
            git_fetch.Start();

            if (!ReadStream(git_fetch))
            {
                return(false);
            }

            git_fetch.WaitForExit();

            if (git_fetch.ExitCode != 0)
            {
                Error = ErrorStatus.HostUnreachable;
                return(false);
            }

            if (Merge())
            {
                if (StorageType == StorageType.LargeFiles)
                {
                    // Pull LFS files manually to benefit from concurrency
                    var git_lfs_pull = new GitCommand(LocalPath, "lfs pull origin", auth_info);
                    git_lfs_pull.StartAndWaitForExit();

                    if (git_lfs_pull.ExitCode != 0)
                    {
                        Error = ErrorStatus.HostUnreachable;
                        return(false);
                    }

                    if (File.Exists(lfs_is_behind_file_path))
                    {
                        File.Delete(lfs_is_behind_file_path);
                    }
                }

                UpdateSizes();
                return(true);
            }

            return(false);
        }
Example #8
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);
        }
Example #9
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();
        }
Example #10
0
        public override void EnableFetchedRepoCrypto(string password)
        {
            string password_file       = ".git/info/encryption_password";
            var    git_config_required = new GitCommand(TargetFolder, "config filter.encryption.required true");

            var git_config_smudge = new GitCommand(TargetFolder, "config filter.encryption.smudge " +
                                                   string.Format("\"openssl enc -d -aes-256-cbc -base64 -S {0} -pass file:{1}\"", password_salt, password_file));

            var git_config_clean = new GitCommand(TargetFolder, "config filter.encryption.clean " +
                                                  string.Format("\"openssl enc -e -aes-256-cbc -base64 -S {0} -pass file:{1}\"", password_salt, password_file));

            git_config_required.StartAndWaitForExit();
            git_config_smudge.StartAndWaitForExit();
            git_config_clean.StartAndWaitForExit();

            // Store the password, TODO: 600 permissions
            string password_file_path = Path.Combine(TargetFolder, ".git", "info", "encryption_password");

            File.WriteAllText(password_file_path, password.SHA256(password_salt));
        }
Example #11
0
        public override void RestoreFile(string path, string revision, string target_file_path)
        {
            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            if (revision == null)
            {
                throw new ArgumentNullException("revision");
            }

            Logger.LogInfo("Git", Name + " | Restoring \"" + path + "\" (revision " + revision + ")");

            // Restore the older file...
            var git = new GitCommand(LocalPath, "checkout " + revision + " \"" + path + "\"");

            git.StartAndWaitForExit();

            string local_file_path = Path.Combine(LocalPath, path);

            // ...move it...
            try {
                File.Move(local_file_path, target_file_path);
            } catch {
                Logger.LogInfo("Git",
                               Name + " | Could not move \"" + local_file_path + "\" to \"" + target_file_path + "\"");
            }

            // ...and restore the most recent revision
            git = new GitCommand(LocalPath, "checkout " + CurrentRevision + " \"" + path + "\"");
            git.StartAndWaitForExit();


            if (target_file_path.StartsWith(LocalPath))
            {
                new Thread(() => OnFileActivity(null)).Start();
            }
        }
Example #12
0
        public GitRepository(string path, Configuration config, SSHAuthenticationInfo auth_info) : base(path, config)
        {
            this.auth_info = auth_info;

            var git_config = new GitCommand(LocalPath, "config core.ignorecase false");

            git_config.StartAndWaitForExit();

            git_config = new GitCommand(LocalPath, "config remote.origin.url \"" + RemoteUrl + "\"");
            git_config.StartAndWaitForExit();

            git_config = new GitCommand(LocalPath, "config core.sshCommand " + GitCommand.FormatGitSSHCommand(auth_info));
            git_config.StartAndWaitForExit();

            if (InstallationInfo.OperatingSystem != OS.Windows)
            {
                string pre_push_hook_path = Path.Combine(LocalPath, ".git", "hooks", "pre-push");

                // TODO: Use proper API
                var chmod = new Command("chmod", "700 " + pre_push_hook_path);
                chmod.StartAndWaitForExit();
            }
        }
        void InstallConfiguration()
        {
            string [] settings = {
                "core.autocrlf input",
                "core.quotepath false", // Don't quote "unusual" characters in path names
                "core.ignorecase false", // Be case sensitive explicitly to work on Mac
                "core.filemode false", // Ignore permission changes
                "core.precomposeunicode true", // Use the same Unicode form on all filesystems
                "core.safecrlf false",
                "core.excludesfile \"\"",
                "core.packedGitLimit 128m", // Some memory limiting options
                "core.packedGitWindowSize 128m",
                "pack.deltaCacheSize 128m",
                "pack.packSizeLimit 128m",
                "pack.windowMemory 128m",
                "push.default matching"
            };

            if (InstallationInfo.OperatingSystem == OS.Windows)
                settings [0] = "core.autocrlf true";

            foreach (string setting in settings) {
                var git_config = new GitCommand (TargetFolder, "config " + setting);
                git_config.StartAndWaitForExit ();
            }
        }
Example #14
0
        public override string Complete(StorageType selected_storage_type)
        {
            string identifier      = base.Complete(selected_storage_type);
            string identifier_path = Path.Combine(TargetFolder, ".sparkleshare");

            InstallConfiguration();
            InstallGitLFS();

            InstallAttributeRules();
            InstallExcludeRules();

            if (IsFetchedRepoEmpty)
            {
                File.WriteAllText(identifier_path, identifier);
                File.SetAttributes(identifier_path, FileAttributes.Hidden);

                // We can't do the "commit --all" shortcut because it doesn't add untracked files
                var git_add    = new GitCommand(TargetFolder, "add .sparkleshare");
                var git_commit = new GitCommand(TargetFolder,
                                                string.Format("commit --message=\"{0}\" --author=\"{1}\"",
                                                              "Set up SparkleShare project",
                                                              "SparkleShare <*****@*****.**>"));

                git_add.StartAndWaitForExit();
                git_commit.StartAndWaitForExit();

                // These branches will be pushed later by "git push --all"
                if (selected_storage_type == StorageType.LargeFiles)
                {
                    var git_branch = new GitCommand(TargetFolder, "branch x-sparkleshare-lfs", auth_info);
                    git_branch.StartAndWaitForExit();
                }

                if (selected_storage_type == StorageType.Encrypted)
                {
                    var git_branch = new GitCommand(TargetFolder,
                                                    string.Format("branch x-sparkleshare-encrypted-{0}", password_salt), auth_info);

                    git_branch.StartAndWaitForExit();
                }
            }
            else
            {
                string branch          = "HEAD";
                string prefered_branch = "SparkleShare";

                // Prefer the "SparkleShare" branch if it exists
                var git_show_ref = new GitCommand(TargetFolder,
                                                  "show-ref --verify --quiet refs/heads/" + prefered_branch);

                git_show_ref.StartAndWaitForExit();

                if (git_show_ref.ExitCode == 0)
                {
                    branch = prefered_branch;
                }

                var git_checkout = new GitCommand(TargetFolder, string.Format("checkout --quiet --force {0}", branch));
                git_checkout.StartAndWaitForExit();

                if (File.Exists(identifier_path))
                {
                    File.SetAttributes(identifier_path, FileAttributes.Hidden);
                    identifier = File.ReadAllText(identifier_path).Trim();
                }
            }

            // git-lfs may leave junk behind
            string git_lfs_tmp_path = Path.Combine(Configuration.DefaultConfiguration.TmpPath, "lfs");

            if (Directory.Exists(git_lfs_tmp_path))
            {
                Directory.Delete(git_lfs_tmp_path, recursive: true);
            }

            return(identifier);
        }
Example #15
0
        public override void RestoreFile(string path, string revision, string target_file_path)
        {
            if (path == null)
                throw new ArgumentNullException ("path");

            if (revision == null)
                throw new ArgumentNullException ("revision");

            Logger.LogInfo ("Git", Name + " | Restoring \"" + path + "\" (revision " + revision + ")");

            // Restore the older file...
            var git = new GitCommand (LocalPath, "checkout " + revision + " \"" + path + "\"");
            git.StartAndWaitForExit ();

            string local_file_path = Path.Combine (LocalPath, path);

            // ...move it...
            try {
                File.Move (local_file_path, target_file_path);

            } catch {
                Logger.LogInfo ("Git",
                    Name + " | Could not move \"" + local_file_path + "\" to \"" + target_file_path + "\"");
            }

            // ...and restore the most recent revision
            git = new GitCommand (LocalPath, "checkout " + CurrentRevision + " \"" + path + "\"");
            git.StartAndWaitForExit ();

            if (target_file_path.StartsWith (LocalPath))
                new Thread (() => OnFileActivity (null)).Start ();
        }
Example #16
0
        // Stages the made changes
        bool Add()
        {
            var git = new GitCommand (LocalPath, "add --all");
            git.StartAndWaitForExit ();

            return (git.ExitCode == 0);
        }
Example #17
0
        public override bool SyncDown()
        {
            string lfs_is_behind_file_path = Path.Combine (LocalPath, ".git", "lfs", "is_behind");

            if (StorageType == StorageType.LargeFiles)
                File.Create (lfs_is_behind_file_path);

            var git_fetch = new GitCommand (LocalPath, "fetch --progress origin " + branch, auth_info);

            git_fetch.StartInfo.RedirectStandardError = true;
            git_fetch.Start ();

            if (!ReadStream (git_fetch))
                return false;

            git_fetch.WaitForExit ();

            if (git_fetch.ExitCode != 0) {
                Error = ErrorStatus.HostUnreachable;
                return false;
            }

            if (Merge ()) {
                if (StorageType == StorageType.LargeFiles) {
                    // Pull LFS files manually to benefit from concurrency
                    var git_lfs_pull = new GitCommand (LocalPath, "lfs pull origin", auth_info);
                    git_lfs_pull.StartAndWaitForExit ();

                    if (git_lfs_pull.ExitCode != 0) {
                        Error = ErrorStatus.HostUnreachable;
                        return false;
                    }

                    if (File.Exists (lfs_is_behind_file_path))
                        File.Delete (lfs_is_behind_file_path);
                }

                UpdateSizes ();
                return true;
            }

            return false;
        }
Example #18
0
        // Merges the fetched changes
        bool Merge()
        {
            string message = FormatCommitMessage();

            if (message != null)
            {
                Add();
                Commit(message);
            }

            GitCommand git;

            // Stop if we're already in a merge because something went wrong
            if (this.in_merge)
            {
                git = new GitCommand(LocalPath, "merge --abort");
                git.StartAndWaitForExit();

                return(false);
            }

            // Temporarily change the ignorecase setting to true to avoid
            // conflicts in file names due to letter case changes
            git = new GitCommand(LocalPath, "config core.ignorecase true");
            git.StartAndWaitForExit();

            git = new GitCommand(LocalPath, "merge FETCH_HEAD");
            git.StartInfo.RedirectStandardOutput = false;

            string error_output = git.StartAndReadStandardError();

            if (git.ExitCode != 0)
            {
                // Stop when we can't merge due to locked local files
                // error: cannot stat 'filename': Permission denied
                if (error_output.Contains("error: cannot stat"))
                {
                    Error = ErrorStatus.UnreadableFiles;
                    Logger.LogInfo("Git", Name + " | Error status changed to " + Error);

                    git = new GitCommand(LocalPath, "merge --abort");
                    git.StartAndWaitForExit();

                    git = new GitCommand(LocalPath, "config core.ignorecase false");
                    git.StartAndWaitForExit();

                    return(false);
                }
                else
                {
                    Logger.LogInfo("Git", error_output);
                    Logger.LogInfo("Git", Name + " | Conflict detected, trying to get out...");

                    while (this.in_merge && HasLocalChanges)
                    {
                        try {
                            ResolveConflict();
                        } catch (Exception e) {
                            Logger.LogInfo("Git", Name + " | Failed to resolve conflict, trying again...", e);
                        }
                    }

                    Logger.LogInfo("Git", Name + " | Conflict resolved");
                }
            }

            git = new GitCommand(LocalPath, "config core.ignorecase false");
            git.StartAndWaitForExit();

            return(true);
        }
Example #19
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();
            }
        }
Example #20
0
        // Merges the fetched changes
        bool Merge()
        {
            string message = FormatCommitMessage ();

            if (message != null) {
                Add ();
                Commit (message);
            }

            GitCommand git;

            // Stop if we're already in a merge because something went wrong
            if (this.in_merge) {
                 git = new GitCommand (LocalPath, "merge --abort");
                 git.StartAndWaitForExit ();

                 return false;
            }

            // Temporarily change the ignorecase setting to true to avoid
            // conflicts in file names due to letter case changes
            git = new GitCommand (LocalPath, "config core.ignorecase true");
            git.StartAndWaitForExit ();

            git = new GitCommand (LocalPath, "merge FETCH_HEAD");
            git.StartInfo.RedirectStandardOutput = false;

            string error_output = git.StartAndReadStandardError ();

            if (git.ExitCode != 0) {
                // Stop when we can't merge due to locked local files
                // error: cannot stat 'filename': Permission denied
                if (error_output.Contains ("error: cannot stat")) {
                    Error = ErrorStatus.UnreadableFiles;
                    Logger.LogInfo ("Git", Name + " | Error status changed to " + Error);

                    git = new GitCommand (LocalPath, "merge --abort");
                    git.StartAndWaitForExit ();

                    git = new GitCommand (LocalPath, "config core.ignorecase false");
                    git.StartAndWaitForExit ();

                    return false;

                } else {
                    Logger.LogInfo ("Git", error_output);
                    Logger.LogInfo ("Git", Name + " | Conflict detected, trying to get out...");

                    while (this.in_merge && HasLocalChanges) {
                        try {
                            ResolveConflict ();

                        } catch (Exception e) {
                            Logger.LogInfo ("Git", Name + " | Failed to resolve conflict, trying again...", e);
                        }
                    }

                    Logger.LogInfo ("Git", Name + " | Conflict resolved");
                }
            }

            git = new GitCommand (LocalPath, "config core.ignorecase false");
            git.StartAndWaitForExit ();

            return true;
        }
Example #21
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 ();
        }
Example #22
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 ();
        }
        public override string Complete(StorageType selected_storage_type)
        {
            string identifier = base.Complete (selected_storage_type);
            string identifier_path = Path.Combine (TargetFolder, ".sparkleshare");

            InstallConfiguration ();
            InstallGitLFS ();

            InstallAttributeRules ();
            InstallExcludeRules ();

            if (IsFetchedRepoEmpty) {
                File.WriteAllText (identifier_path, identifier);

                var git_add    = new GitCommand (TargetFolder, "add .sparkleshare");
                var git_commit = new GitCommand (TargetFolder, "commit --message=\"Initial commit by SparkleShare\"");

                // We can't do the "commit --all" shortcut because it doesn't add untracked files
                git_add.StartAndWaitForExit ();
                git_commit.StartAndWaitForExit ();

                // These branches will be pushed  later by "git push --all"
                if (selected_storage_type == StorageType.LargeFiles) {
                    var git_branch = new GitCommand (TargetFolder, "branch x-sparkleshare-lfs", auth_info);
                    git_branch.StartAndWaitForExit ();
                }

                if (selected_storage_type == StorageType.Encrypted) {
                    var git_branch = new GitCommand (TargetFolder,
                        string.Format ("branch x-sparkleshare-encrypted-{0}", password_salt), auth_info);

                    git_branch.StartAndWaitForExit ();
                }

            } else {
                if (File.Exists (identifier_path))
                    identifier = File.ReadAllText (identifier_path).Trim ();

                string branch = "HEAD";
                string prefered_branch = "SparkleShare";

                // Prefer the "SparkleShare" branch if it exists
                var git_show_ref = new GitCommand (TargetFolder,
                   "show-ref --verify --quiet refs/heads/" + prefered_branch);

                git_show_ref.StartAndWaitForExit ();

                if (git_show_ref.ExitCode == 0)
                    branch = prefered_branch;

                var git_checkout = new GitCommand (TargetFolder, string.Format ("checkout --quiet --force {0}", branch));
                git_checkout.StartAndWaitForExit ();
            }

            // git-lfs may leave junk behind
            string git_lfs_tmp_path = Path.Combine (Configuration.DefaultConfiguration.TmpPath, "lfs");

            if (Directory.Exists (git_lfs_tmp_path))
                Directory.Delete (git_lfs_tmp_path, true);

            File.SetAttributes (identifier_path, FileAttributes.Hidden);
            return identifier;
        }
        void InstallGitLFS()
        {
            var git_config_required = new GitCommand (TargetFolder, "config filter.lfs.required true");

            string GIT_SSH_COMMAND = GitCommand.FormatGitSSHCommand (auth_info);
            string smudge_command;
            string clean_command;

            if (InstallationInfo.OperatingSystem == OS.Mac) {
                smudge_command = "env GIT_SSH_COMMAND='" + GIT_SSH_COMMAND + "' " +
                    Path.Combine (Configuration.DefaultConfiguration.BinPath, "git-lfs") + " smudge %f";

                clean_command = Path.Combine (Configuration.DefaultConfiguration.BinPath, "git-lfs") + " clean %f";

            } else {
                smudge_command = "env GIT_SSH_COMMAND='" + GIT_SSH_COMMAND + "' git-lfs smudge %f";
                clean_command = "git-lfs clean %f";
            }

            var git_config_smudge = new GitCommand (TargetFolder,
                string.Format ("config filter.lfs.smudge \"{0}\"", smudge_command));

            var git_config_clean = new GitCommand (TargetFolder,
                string.Format ("config filter.lfs.clean '{0}'", clean_command));

            git_config_required.StartAndWaitForExit ();
            git_config_clean.StartAndWaitForExit ();
            git_config_smudge.StartAndWaitForExit ();
        }
Example #25
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();
            }
        }
        public override void EnableFetchedRepoCrypto(string password)
        {
            string password_file = ".git/info/encryption_password";
            var git_config_required = new GitCommand (TargetFolder, "config filter.encryption.required true");

            var git_config_smudge = new GitCommand (TargetFolder, "config filter.encryption.smudge " +
                string.Format ("\"openssl enc -d -aes-256-cbc -base64 -S {0} -pass file:{1}\"", password_salt, password_file));

            var git_config_clean = new GitCommand (TargetFolder, "config filter.encryption.clean " +
                string.Format ("\"openssl enc -e -aes-256-cbc -base64 -S {0} -pass file:{1}\"", password_salt, password_file));

            git_config_required.StartAndWaitForExit ();
            git_config_smudge.StartAndWaitForExit ();
            git_config_clean.StartAndWaitForExit ();

            // Store the password, TODO: 600 permissions
            string password_file_path = Path.Combine (TargetFolder, ".git", "info", "encryption_password");
            File.WriteAllText (password_file_path, password.SHA256 (password_salt));
        }