// Merges the fetched changes private void Rebase() { if (HasLocalChanges) { Add(); string commit_message = FormatCommitMessage(); Commit(commit_message); } SparkleGit git = new SparkleGit(LocalPath, "rebase FETCH_HEAD"); git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit(); if (git.ExitCode != 0) { SparkleLogger.LogInfo("Git", Name + " | Conflict detected, trying to get out..."); while (HasLocalChanges) { try { ResolveConflict(); } catch (IOException e) { SparkleLogger.LogInfo("Git", Name + " | Failed to resolve conflict, trying again... (" + e.Message + ")"); } } SparkleLogger.LogInfo("Git", Name + " | Conflict resolved"); OnConflictResolved(); } }
public override void RevertFile(string path, string revision) { if (path == null) { throw new ArgumentNullException("path"); } if (revision == null) { throw new ArgumentNullException("revision"); } path = path.Replace("\\", "/"); SparkleGit git = new SparkleGit(LocalPath, "checkout " + revision + " \"" + path + "\""); git.StartAndWaitForExit(); if (git.ExitCode == 0) { SparkleLogger.LogInfo("Git", Name + " | Checked out \"" + path + "\" (" + revision + ")"); } else { SparkleLogger.LogInfo("Git", Name + " | Failed to check out \"" + path + "\" (" + revision + ")"); } }
public override void EnableFetchedRepoCrypto(string password) { // Set up the encryption filter SparkleGit git_config_smudge = new SparkleGit(TargetFolder, "config filter.encryption.smudge \"openssl enc -d -aes-256-cbc -base64 -S " + this.crypto_salt + " -pass file:.git/info/encryption_password\""); SparkleGit git_config_clean = new SparkleGit(TargetFolder, "config filter.encryption.clean \"openssl enc -e -aes-256-cbc -base64 -S " + this.crypto_salt + " -pass file:.git/info/encryption_password\""); git_config_smudge.StartAndWaitForExit(); git_config_clean.StartAndWaitForExit(); // Pass all files through the encryption filter string git_attributes_file_path = new string [] { TargetFolder, ".git", "info", "attributes" }.Combine(); File.WriteAllText(git_attributes_file_path, "\n* filter=encryption"); // Store the password string password_file_path = new string [] { TargetFolder, ".git", "info", "encryption_password" }.Combine(); if (this.crypto_password_is_hashed) { File.WriteAllText(password_file_path, password.SHA256(this.crypto_salt)); } else { File.WriteAllText(password_file_path, password); } }
private void InstallConfiguration() { string [] settings = new string [] { "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.autocrlf false", // Don't change file line endings "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" }; foreach (string setting in settings) { SparkleGit git_config = new SparkleGit(TargetFolder, "config " + setting); git_config.StartAndWaitForExit(); } if (this.use_git_bin) { InstallGitBinConfiguration(); } }
public SparkleRepo(string path, SparkleConfig config) : base(path, config) { SparkleGit git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); // Check if we should use git-bin git = new SparkleGit(LocalPath, "config --get filter.bin.clean"); git.StartAndWaitForExit(); this.use_git_bin = (git.ExitCode == 0); if (this.use_git_bin) { ConfigureGitBin(); } git = new SparkleGit(LocalPath, "config remote.origin.url \"" + RemoteUrl + "\""); git.StartAndWaitForExit(); string password_file_path = Path.Combine(LocalPath, ".git", "password"); if (File.Exists(password_file_path)) { this.is_encrypted = true; } }
// Stages the made changes private bool Add() { SparkleGit git = new SparkleGit(LocalPath, "add --all"); git.StartAndWaitForExit(); return(git.ExitCode == 0); }
// Stages the made changes private void Add() { SparkleGit git = new SparkleGit(LocalPath, "add --all"); git.StartAndWaitForExit(); SparkleLogger.LogInfo("Git", Name + " | Changes staged"); }
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"); } SparkleLogger.LogInfo("Git", Name + " | Restoring \"" + path + "\" (revision " + revision + ")"); // FIXME: git-show doesn't decrypt objects, so we can't use it to retrieve // files from the index. This is a suboptimal workaround but it does the job if (this.is_encrypted) { // Restore the older file... SparkleGit git = new SparkleGit(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 { SparkleLogger.LogInfo("Git", Name + " | Could not move \"" + local_file_path + "\" to \"" + target_file_path + "\""); } // ...and restore the most recent revision git = new SparkleGit(LocalPath, "checkout " + CurrentRevision + " \"" + path + "\""); git.StartAndWaitForExit(); // The correct way } else { path = path.Replace("\"", "\\\""); SparkleGit git = new SparkleGit(LocalPath, "show " + revision + ":\"" + path + "\""); git.Start(); FileStream stream = File.OpenWrite(target_file_path); git.StandardOutput.BaseStream.CopyTo(stream); stream.Close(); git.WaitForExit(); } if (target_file_path.StartsWith(LocalPath)) { new Thread(() => OnFileActivity(null)).Start(); } }
public override void Complete() { if (!IsFetchedRepoEmpty) { SparkleGit git = new SparkleGit(TargetFolder, "checkout --quiet HEAD"); git.StartAndWaitForExit(); } base.Complete(); }
public void InstallGitBinConfiguration() { string [] settings = new string [] { "core.bigFileThreshold 1024g", "filter.bin.clean \"git bin clean %f\"", "filter.bin.smudge \"git bin smudge\"" }; foreach (string setting in settings) { SparkleGit git_config = new SparkleGit(TargetFolder, "config " + setting); git_config.StartAndWaitForExit(); } }
private void ConfigureGitBin() { SparkleGit git = new SparkleGit(LocalPath, "config filter.bin.clean \"git bin clean %f\""); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config filter.bin.smudge \"git bin smudge\""); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config git-bin.sftpUrl \"" + RemoteUrl + "\""); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config git-bin.sftpPrivateKeyFile \"" + base.local_config.User.PrivateKeyFilePath + "\""); git.StartAndWaitForExit(); }
public SparkleRepo(string path, SparkleConfig config) : base(path, config) { SparkleGit git = new SparkleGit(LocalPath, "config --get filter.bin.clean"); git.Start(); git.WaitForExit(); this.use_git_bin = (git.ExitCode == 0); string rebase_apply_path = new string [] { LocalPath, ".git", "rebase-apply" }.Combine(); if (Directory.Exists(rebase_apply_path)) { git = new SparkleGit(LocalPath, "rebase --abort"); git.StartAndWaitForExit(); } }
public void InstallGitBinConfiguration() { string [] settings = new string [] { "core.bigFileThreshold 8g", "filter.bin.clean \"git bin clean %f\"", "filter.bin.smudge \"git bin smudge\"", "git-bin.chunkSize 1m", "git-bin.s3bucket \"your bucket name\"", "git-bin.s3key \"your key\"", "git-bin.s3secretKey \"your secret key\"" }; foreach (string setting in settings) { SparkleGit git_config = new SparkleGit(TargetFolder, "config " + setting); git_config.StartAndWaitForExit(); } }
// Commits the made changes private void Commit(string message) { SparkleGit git; if (!this.user_is_set) { git = new SparkleGit(LocalPath, "config user.name \"" + base.local_config.User.Name + "\""); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config user.email \"" + base.local_config.User.Email + "\""); git.StartAndWaitForExit(); this.user_is_set = true; } git = new SparkleGit(LocalPath, "commit --all --message=\"" + message + "\" " + "--author=\"" + base.local_config.User.Name + " <" + base.local_config.User.Email + ">\""); git.StartAndReadStandardOutput(); }
public SparkleRepo(string path, SparkleConfig config) : base(path, config) { // TODO: Set git locale to en-US SparkleGit git = new SparkleGit(LocalPath, "config --get filter.bin.clean"); git.StartAndWaitForExit(); this.use_git_bin = (git.ExitCode == 0); git = new SparkleGit(LocalPath, "config remote.origin.url \"" + RemoteUrl + "\""); git.StartAndWaitForExit(); string rebase_apply_path = new string [] { LocalPath, ".git", "rebase-apply" }.Combine(); if (Directory.Exists(rebase_apply_path)) { git = new SparkleGit(LocalPath, "rebase --abort"); git.StartAndWaitForExit(); } }
// Merges the fetched changes private bool Merge() { string message = FormatCommitMessage(); if (message != null) { Add(); Commit(message); } SparkleGit git; // Stop if we're already in a merge because something went wrong if (this.in_merge) { git = new SparkleGit(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 SparkleGit(LocalPath, "config core.ignorecase true"); git.StartAndWaitForExit(); git = new SparkleGit(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; SparkleLogger.LogInfo("Git", Name + " | Error status changed to " + Error); git = new SparkleGit(LocalPath, "merge --abort"); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(false); } else { SparkleLogger.LogInfo("Git", error_output); SparkleLogger.LogInfo("Git", Name + " | Conflict detected, trying to get out..."); while (this.in_merge && HasLocalChanges) { try { ResolveConflict(); } catch (Exception e) { SparkleLogger.LogInfo("Git", Name + " | Failed to resolve conflict, trying again...", e); } } SparkleLogger.LogInfo("Git", Name + " | Conflict resolved"); } } git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(true); }
private 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 // // Note that a rebase merge works by replaying each commit from the working branch on // top of the upstream branch. Because of this, when a merge conflict happens the // side reported as 'ours' is the so-far rebased series, starting with upstream, // and 'theirs' is the working branch. In other words, the sides are swapped. // // So: 'ours' means the 'server's version' and 'theirs' means the 'local version' after this comment SparkleGit git_status = new SparkleGit(LocalPath, "status --porcelain"); string output = git_status.StartAndReadStandardOutput(); string [] lines = output.Split("\n".ToCharArray()); bool changes_added = false; foreach (string line in lines) { string conflicting_path = line.Substring(3); conflicting_path = EnsureSpecialCharacters(conflicting_path); conflicting_path = conflicting_path.Replace("\"", "\\\""); SparkleLogger.LogInfo("Git", Name + " | Conflict type: " + line); // Ignore conflicts in the .sparkleshare file and use the local version if (conflicting_path.EndsWith(".sparkleshare") || conflicting_path.EndsWith(".empty")) { // Recover local version SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); File.SetAttributes(Path.Combine(LocalPath, conflicting_path), FileAttributes.Hidden); changes_added = true; continue; } // Both the local and server version have been modified if (line.StartsWith("UU") || line.StartsWith("AA") || line.StartsWith("AU") || line.StartsWith("UA")) { // Recover local version SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.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 their_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_their_path = Path.Combine(LocalPath, their_path); File.Move(abs_conflicting_path, abs_their_path); // Recover server version SparkleGit git_ours = new SparkleGit(LocalPath, "checkout --ours \"" + conflicting_path + "\""); git_ours.StartAndWaitForExit(); changes_added = true; // The local version has been modified, but the server version was removed } else if (line.StartsWith("DU")) { // The modified local version is already in the checkout, so it just needs to be added. // We need to specifically mention the file, so we can't reuse the Add () method SparkleGit git_add = new SparkleGit(LocalPath, "add \"" + conflicting_path + "\""); git_add.StartAndWaitForExit(); changes_added = true; } } Add(); SparkleGit git; if (changes_added) { git = new SparkleGit(LocalPath, "rebase --continue"); } else { git = new SparkleGit(LocalPath, "rebase --skip"); } git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit(); }
// Merges the fetched changes private bool Rebase() { if (HasLocalChanges) { Add(); string commit_message = FormatCommitMessage(); Commit(commit_message); } // Temporarily change the ignorecase setting to true to avoid // conflicts in file names due to case changes SparkleGit git = new SparkleGit(LocalPath, "config core.ignorecase true"); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "rebase FETCH_HEAD"); git.StartInfo.RedirectStandardOutput = false; string error_output = git.StartAndReadStandardError(); if (git.ExitCode != 0) { // Stop when we can't rebase due to locked local files // error: cannot stat 'filename': Permission denied if (error_output.Contains("error: cannot stat")) { Error = ErrorStatus.LockedFiles; SparkleLogger.LogInfo("Git", Name + " | Error status changed to " + Error); git = new SparkleGit(LocalPath, "rebase --abort"); git.StartAndWaitForExit(); git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(false); } else { SparkleLogger.LogInfo("Git", Name + " | Conflict detected, trying to get out..."); string rebase_apply_path = new string [] { LocalPath, ".git", "rebase-apply" }.Combine(); while (Directory.Exists(rebase_apply_path) && HasLocalChanges) { try { ResolveConflict(); } catch (IOException e) { SparkleLogger.LogInfo("Git", Name + " | Failed to resolve conflict, trying again... (" + e.Message + ")"); } } SparkleLogger.LogInfo("Git", Name + " | Conflict resolved"); OnConflictResolved(); } } git = new SparkleGit(LocalPath, "config core.ignorecase false"); git.StartAndWaitForExit(); return(true); }
// Stages the made changes private void Add() { SparkleGit git = new SparkleGit(LocalPath, "add --all"); git.StartAndWaitForExit(); }
public override bool SyncUp() { if (HasLocalChanges) { Add(); string message = FormatCommitMessage(); Commit(message); } SparkleGit git; if (this.use_git_bin) { SparkleGitBin git_bin = new SparkleGitBin(LocalPath, "push"); git_bin.StartAndWaitForExit(); // TODO: Progress } git = new SparkleGit(LocalPath, "push --progress \"" + RemoteUrl + "\" " + this.branch); git.StartInfo.RedirectStandardError = true; git.Start(); double percentage = 1.0; while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine(); Match match = this.progress_regex.Match(line); double speed = 0.0; double number = 0.0; if (match.Success) { number = double.Parse(match.Groups [1].Value); // The pushing progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Writing objects" stage which we count as the last 80% if (line.StartsWith("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { // "Writing objects" stage number = (number / 100 * 80 + 20); Match speed_match = this.speed_regex.Match(line); if (speed_match.Success) { speed = double.Parse(speed_match.Groups [1].Value) * 1024; if (speed_match.Groups [2].Value.Equals("M")) { speed = speed * 1024; } } } } else { SparkleLogger.LogInfo("Git", Name + " | " + line); if (FindError(line)) { return(false); } } if (number >= percentage) { percentage = number; base.OnProgressChanged(percentage, speed); } } git.WaitForExit(); UpdateSizes(); if (git.ExitCode == 0) { ClearCache(); string salt_file_path = new string [] { LocalPath, ".git", "salt" }.Combine(); // If the repo is encrypted, create a branch to // store the salt in and push it to the host if (File.Exists(salt_file_path)) { string salt = File.ReadAllText(salt_file_path).Trim(); SparkleGit git_salt = new SparkleGit(LocalPath, "branch salt-" + salt); git_salt.StartAndWaitForExit(); git_salt = new SparkleGit(LocalPath, "push origin salt-" + salt); git_salt.StartAndWaitForExit(); File.Delete(salt_file_path); } return(true); } else { Error = ErrorStatus.HostUnreachable; return(false); } }
public override bool SyncUp() { if (HasLocalChanges) { Add(); string message = FormatCommitMessage(); Commit(message); } SparkleGit git; if (this.use_git_bin) { if (this.remote_url_is_set) { git = new SparkleGit(LocalPath, "config remote.origin.url \"" + RemoteUrl + "\""); git.StartAndWaitForExit(); this.remote_url_is_set = true; } SparkleGitBin git_bin = new SparkleGitBin(LocalPath, "push"); git_bin.StartAndWaitForExit(); // TODO: Progress } git = new SparkleGit(LocalPath, "push --progress " + // Redirects progress stats to standarderror "\"" + RemoteUrl + "\" master"); git.StartInfo.RedirectStandardError = true; git.Start(); double percentage = 1.0; Regex progress_regex = new Regex(@"([0-9]+)%", RegexOptions.Compiled); while (!git.StandardError.EndOfStream) { string line = git.StandardError.ReadLine(); Match match = progress_regex.Match(line); string speed = ""; double number = 0.0; if (match.Success) { number = double.Parse(match.Groups [1].Value); // The pushing progress consists of two stages: the "Compressing // objects" stage which we count as 20% of the total progress, and // the "Writing objects" stage which we count as the last 80% if (line.StartsWith("Compressing")) { // "Compressing objects" stage number = (number / 100 * 20); } else { if (line.StartsWith("ERROR: QUOTA EXCEEDED")) { int quota_limit = int.Parse(line.Substring(21).Trim()); throw new QuotaExceededException("Quota exceeded", quota_limit); } // "Writing objects" stage number = (number / 100 * 80 + 20); if (line.Contains("|")) { speed = line.Substring(line.IndexOf("|") + 1).Trim(); speed = speed.Replace(", done.", "").Trim(); speed = speed.Replace("i", ""); speed = speed.Replace("KB/s", "ᴋʙ/s"); speed = speed.Replace("MB/s", "ᴍʙ/s"); } } } else { SparkleLogger.LogInfo("Git", Name + " | " + line); } if (number >= percentage) { percentage = number; base.OnProgressChanged(percentage, speed); } } git.WaitForExit(); UpdateSizes(); if (git.ExitCode == 0) { ClearCache(); return(true); } else { return(false); } }
private 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 SparkleGit git_status = new SparkleGit(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); } } SparkleLogger.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")) { SparkleLogger.LogInfo("Git", Name + " | Ignoring conflict in special file: " + conflicting_path); // Recover local version SparkleGit git_ours = new SparkleGit(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; } SparkleLogger.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 SparkleGit git_ours = new SparkleGit(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 SparkleGit git_theirs = new SparkleGit(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 SparkleGit git_add = new SparkleGit(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 SparkleGit git_theirs = new SparkleGit(LocalPath, "checkout --theirs \"" + conflicting_path + "\""); git_theirs.StartAndWaitForExit(); // Server and local versions were removed } else if (line.StartsWith("DD")) { SparkleLogger.LogInfo("Git", Name + " | No need to resolve: " + line); // New local files } else if (line.StartsWith("??")) { SparkleLogger.LogInfo("Git", Name + " | Found new file, no need to resolve: " + line); } else { SparkleLogger.LogInfo("Git", Name + " | Don't know what to do with: " + line); } } Add(); SparkleGit git = new SparkleGit(LocalPath, "commit --message \"Conflict resolution by SparkleShare\""); git.StartInfo.RedirectStandardOutput = false; git.StartAndWaitForExit(); if (trigger_conflict_event) { OnConflictResolved(); } }