private bool AutoUpdateProject(Project p, bool notify = true) { LockProject(p.id); string dir = GetProjectDirectory(p); bool possibleCommit = false, rebaseStarted = false, success = false; ProcessReturn ret; try { if (!Directory.Exists(dir)) { if (!InitializeProject(p)) { return false; } return true; } if (GitWrapper.RebaseInProgress(dir)) { GitWrapper.Rebase(dir, "--abort"); } ret = GitWrapper.Fetch(dir); if (ret.ReturnValue != 0) { if (ret.Output.Contains("Not a git")) { MessageBoxResult res = MessageBox.Show("Project " + p.name + " seems to be corrupted. Do you want SciGit to repair it?\r\n" + "You may want to back up your files first.", "Project corrupted", MessageBoxButton.YesNo); if (res == MessageBoxResult.Yes) { string gitDir = Path.Combine(dir, ".git"); if (Directory.Exists(gitDir)) { Directory.Delete(gitDir, true); } if (!InitializeProject(p)) { return false; } ret = GitWrapper.Fetch(dir); } else { DisableAutoUpdates(p); } } if (ret.ReturnValue != 0) { return false; } } while (true) { // Reset commits until we get to something in common with FETCH_HEAD. ret = CheckReturn("merge-base", GitWrapper.MergeBase(dir, "HEAD", "FETCH_HEAD")); string baseCommit = ret.Stdout.Trim(); GitWrapper.Reset(dir, baseCommit); // Make a temporary commit to facilitate merging. GitWrapper.AddAll(dir); ret = GitWrapper.Commit(dir, "tempCommit " + DateTime.Now); possibleCommit = true; ret = GitWrapper.Rebase(dir, "FETCH_HEAD"); if (ret.Output.Contains("Permission denied")) { // One of the files is open. // If the return value isn't 0, there was a merge conflict on top of this (so abort the rebase) if (ret.ReturnValue == 0) { GitWrapper.Reset(dir, baseCommit); } else { rebaseStarted = true; } var match = Regex.Match(ret.Output, "error: unable to unlink old '(.*)' \\(Permission denied\\)"); string file = ""; if (match.Success) { file = " (" + match.Groups[1].Value + ")"; } var resp = Util.ShowMessageBox("Project " + p.name + " cannot be updated, as one of the project files is currently open" + file + ".\n" + "Please save and close your changes before continuing.", "File Locked", MessageBoxButtons.RetryCancel); if (resp == DialogResult.Cancel) { DisableAutoUpdates(p); return false; } if (rebaseStarted) GitWrapper.Rebase(dir, "--abort"); } else { break; } } if (ret.ReturnValue != 0) { rebaseStarted = true; if (ret.Output.Contains("CONFLICT")) { var resp = Util.ShowMessageBox("Merge conflict(s) were detected in project " + p.name + ". Would you like to resolve them now using the SciGit editor?\r\n" + "Please save any changes to open files before continuing.", "Auto-update: merge conflict", MessageBoxButtons.OKCancel); MergeResolver mr = null; Exception exception = null; if (resp == DialogResult.OK) { parent.Invoke(new Action(() => { try { mr = new MergeResolver(p); mr.ShowDialog(); } catch (Exception e) { if (mr != null) mr.Hide(); exception = e; } })); } if (exception != null) throw new Exception("", exception); if (mr == null || !mr.Saved) { DisableAutoUpdates(p); return false; } else { GitWrapper.AddAll(dir); ret = GitWrapper.Rebase(dir, "--continue"); if (ret.ReturnValue != 0) { if (ret.Output.Contains("No changes")) { // The temp commit was effectively ignored. Just skip it. CheckReturn("rebase", GitWrapper.Rebase(dir, "--skip")); } else { throw new Exception("rebase: " + ret.Output); } } success = true; rebaseStarted = false; } } else { throw new Exception("rebase: " + ret.Output); } } else { success = true; if (!ret.Output.Contains("up to date") && notify) { DispatchCallbacks(projectAutoUpdatedCallbacks, p); } } } catch (Exception e) { return false; } finally { if (rebaseStarted) GitWrapper.Rebase(dir, "--abort"); if (possibleCommit) { // Reset commits until we get to something in common with FETCH_HEAD. ret = CheckReturn("merge-base", GitWrapper.MergeBase(dir, "HEAD", "FETCH_HEAD")); CheckReturn("reset", GitWrapper.Reset(dir, ret.Stdout.Trim())); } if (success) { lock (updatedProjects) { updatedProjects.RemoveAll(pr => pr.id == p.id); } } UnlockProject(p.id); } return success; }
public bool UpdateProject(Project p, Window window, BackgroundWorker worker, bool progress = true) { LockProject(p.id); string dir = GetProjectDirectory(p); bool possibleCommit = false, rebaseStarted = false, success = false; ProcessReturn ret; try { if (worker.CancellationPending) return false; if (!Directory.Exists(dir)) { worker.ReportProgress(progress ? 25 : -1, "Repairing project..."); MessageBoxResult result = MessageBox.Show("Project " + p.name + " does not exist. Would you like to re-create it?", "Project does not exist", MessageBoxButton.YesNo); if (result == MessageBoxResult.Yes) { if (!InitializeProject(p)) { ShowError(window, "Could not obtain project from the SciGit servers. Please try again later."); } else { worker.ReportProgress(progress ? 100 : -1, "Repair and update successful."); return true; } } return false; } if (GitWrapper.RebaseInProgress(dir)) { GitWrapper.Rebase(dir, "--abort"); } if (worker.CancellationPending) return false; worker.ReportProgress(progress ? 25 : -1, "Checking for updates..."); ret = GitWrapper.Fetch(dir); worker.ReportProgress(-1, ret.Output); if (ret.ReturnValue != 0) { if (ret.Output.Contains("Not a git")) { MessageBoxResult res = MessageBox.Show("Project " + p.name + " seems to be corrupted. Do you want SciGit to repair it?\r\n" + "You may want to back up your files first.", "Project corrupted", MessageBoxButton.YesNo); if (res == MessageBoxResult.Yes) { string gitDir = Path.Combine(dir, ".git"); if (Directory.Exists(gitDir)) { Directory.Delete(gitDir, true); } worker.ReportProgress(progress ? 30 : -1, "Repairing project..."); if (!InitializeProject(p)) { ShowError(window, "Could not obtain project from the SciGit servers. Please try again later."); return false; } worker.ReportProgress(progress ? 35 : -1, "Checking for updates..."); ret = GitWrapper.Fetch(dir); worker.ReportProgress(-1, ret.Output); } else { return false; } } if (ret.ReturnValue != 0) { ShowError(window, "Could not connect to the SciGit servers. Please try again later."); return false; } } while (true) { if (worker.CancellationPending) return false; // Reset commits until we get to something in common with FETCH_HEAD. ret = CheckReturn("merge-base", GitWrapper.MergeBase(dir, "HEAD", "FETCH_HEAD"), worker); string baseCommit = ret.Stdout.Trim(); GitWrapper.Reset(dir, baseCommit); if (worker.CancellationPending) return false; // Make a temporary commit to facilitate merging. GitWrapper.AddAll(dir); worker.ReportProgress(-1, "Creating temporary commit..."); ret = GitWrapper.Commit(dir, "tempCommit " + DateTime.Now); possibleCommit = true; worker.ReportProgress(-1, ret.Output); if (worker.CancellationPending) return false; worker.ReportProgress(progress ? 50 : -1, "Merging..."); ret = GitWrapper.Rebase(dir, "FETCH_HEAD"); worker.ReportProgress(-1, ret.Output); if (ret.Output.Contains("Permission denied")) { // One of the files is open. // If the return value isn't 0, there was a merge conflict on top of this (so abort the rebase) if (ret.ReturnValue == 0) { GitWrapper.Reset(dir, baseCommit); } else { rebaseStarted = true; } var match = Regex.Match(ret.Output, "error: unable to unlink old '(.*)' \\(Permission denied\\)"); string file = ""; if (match.Success) { file = "(" + match.Groups[1].Value + ") "; } var resp = MessageBox.Show("One of the project files is currently open " + file + "and cannot be edited. " + "Please save and close your changes before continuing.", "File Locked", MessageBoxButton.OKCancel); if (resp == MessageBoxResult.Cancel) { return false; } if (rebaseStarted) GitWrapper.Rebase(dir, "--abort"); } else { break; } } if (ret.ReturnValue != 0) { rebaseStarted = true; if (worker.CancellationPending) return false; if (ret.Output.Contains("CONFLICT")) { MessageBoxResult resp = MessageBoxResult.Cancel; window.Dispatcher.Invoke( new Action(() => resp = MessageBox.Show(window, "Merge conflict(s) were detected. Would you like to resolve them now using the SciGit editor?\r\n" + "Please save any changes to open files before continuing.", "Merge Conflict", MessageBoxButton.OKCancel))); MergeResolver mr = null; Exception exception = null; if (resp == MessageBoxResult.OK) { window.Dispatcher.Invoke(new Action(() => { try { mr = new MergeResolver(p); mr.ShowDialog(); } catch (Exception e) { if (mr != null) mr.Hide(); exception = e; } })); } if (exception != null) throw new Exception("", exception); if (resp != MessageBoxResult.No && (mr == null || !mr.Saved)) { // Cancel the process here. return false; } else { GitWrapper.AddAll(dir); worker.ReportProgress(progress ? 75 : -1, "Continuing merge..."); ret = GitWrapper.Rebase(dir, "--continue"); worker.ReportProgress(-1, ret.Output); if (ret.ReturnValue != 0) { if (ret.Output.Contains("No changes")) { // The temp commit was effectively ignored. Just skip it. CheckReturn("rebase", GitWrapper.Rebase(dir, "--skip"), worker); } else { throw new Exception("rebase: " + ret.Output); } } worker.ReportProgress(progress ? 100 : -1, "Merge successful."); success = true; rebaseStarted = false; } } else { throw new Exception("rebase: " + ret.Output); } } else { worker.ReportProgress(progress ? 100 : -1, ret.Output.Contains("up to date") ? "No changes." : "Changes merged without conflict."); success = true; if (progress && !ret.Output.Contains("up to date")) { var result = MessageBox.Show("Project " + p.name + " was successfully updated. Would you like to view the changes?", "Project updated", MessageBoxButton.YesNo); if (result == MessageBoxResult.Yes) { window.Dispatcher.Invoke(new Action(() => { var ph = new ProjectHistory(p, "HEAD"); ph.Show(); })); } } } } catch (Exception e) { throw new Exception("", e); } finally { if (rebaseStarted) GitWrapper.Rebase(dir, "--abort"); if (possibleCommit) { // Reset commits until we get to something in common with FETCH_HEAD. ret = CheckReturn("merge-base", GitWrapper.MergeBase(dir, "HEAD", "FETCH_HEAD"), worker); CheckReturn("reset", GitWrapper.Reset(dir, ret.Stdout.Trim()), worker); } if (success) { lock (noAutoUpdateProjects) { noAutoUpdateProjects.Remove(p.id); } lock (updatedProjects) { updatedProjects.RemoveAll(pr => pr.id == p.id); } } UnlockProject(p.id); } return success; }