// 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(); }
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(); } }
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); }
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); }
// 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); }
// 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(); }
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)); }
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(); } }
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 (); } }
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); }
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 (); }
// Stages the made changes bool Add() { var git = new GitCommand (LocalPath, "add --all"); git.StartAndWaitForExit (); return (git.ExitCode == 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; }
// 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); }
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(); } }
// 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; }
// 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 (); }
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 (); }
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)); }