public bool Commit(IEnumerable <PendingChange> changes, PendingChangeCommitArgs args) { // Ok, to make a commit happen we have to take 'a few' steps IAnkhServiceEvents ci = GetService <IAnkhServiceEvents>(); if (ci != null) { ci.OnLastChanged(new LastChangedEventArgs(null, null)); } bool storeMessage = args.StoreMessageOnError; foreach (PendingCommitState state in GetCommitRoots(changes)) { if (state == null) { return(false); } try { state.KeepLocks = args.KeepLocks; state.KeepChangeLists = args.KeepChangeLists; state.LogMessage = args.LogMessage; state.IssueText = args.IssueText; if (!PreCommit_VerifySingleRoot(state)) // Verify single root 'first' { return(false); } // Verify this before verifying log message // so that issue tracker integration has precedence if (!PreCommit_VerifyIssueTracker(state)) { return(false); } if (!PreCommit_VerifyLogMessage(state)) { return(false); } if (!PreCommit_VerifyNoConflicts(state)) { return(false); } if (!PreCommit_SaveDirty(state)) { return(false); } if (!PreCommit_AddNewFiles(state)) { return(false); } if (!PreCommit_HandleMissingFiles(state)) { return(false); } state.FlushState(); if (!PreCommit_AddNeededParents(state)) { return(false); } if (!PreCommit_VerifySingleRoot(state)) // Verify single root 'again' { return(false); } if (!PreCommit_VerifyTargetsVersioned(state)) { return(false); } // if(!PreCommit_....()) // return; bool ok = false; using (DocumentLock dl = GetService <IAnkhOpenDocumentTracker>().LockDocuments(state.CommitPaths, DocumentLockType.NoReload)) using (dl.MonitorChangesForReload()) // Monitor files that are changed by keyword expansion { if (Commit_CommitToRepository(state)) { storeMessage = true; ok = true; } } if (!ok) { return(false); } } finally { string msg = state.LogMessage; state.Dispose(); if (storeMessage && msg != null && msg.Trim().Length > 0) { Config.GetRecentLogMessages().Add(msg); } } } return(true); }
/// <summary> /// Finalizes the action by committing to the repository /// </summary> /// <param name="state">The state.</param> /// <returns></returns> private bool Commit_CommitToRepository(PendingCommitState state) { bool ok = false; SvnCommitResult rslt = null; bool enableHooks = Config.Instance.EnableTortoiseSvnHooks; bool outOfDateError = false; bool otherError = false; StringBuilder outOfDateMessage = null; state.GetService <IProgressRunner>().RunModal(PccStrings.CommitTitle, delegate(object sender, ProgressWorkerArgs e) { string itemPath = null; SvnCommitArgs ca = new SvnCommitArgs(); ca.Depth = SvnDepth.Empty; ca.KeepLocks = state.KeepLocks; ca.KeepChangeLists = state.KeepChangeLists; ca.LogMessage = state.LogMessage; foreach (KeyValuePair <string, string> kv in state.CustomProperties) { ca.LogProperties.Add(kv.Key, kv.Value); } ca.AddExpectedError(SvnErrorCode.SVN_ERR_WC_NOT_UP_TO_DATE); ca.AddExpectedError(SvnErrorCode.SVN_ERR_CLIENT_FORBIDDEN_BY_SERVER); ca.AddExpectedError(SvnErrorCode.SVN_ERR_CLIENT_NO_LOCK_TOKEN); ca.AddExpectedError(SvnErrorCode.SVN_ERR_IO_INCONSISTENT_EOL); ca.AddExpectedError(SvnErrorCode.SVN_ERR_FS_TXN_OUT_OF_DATE); ca.AddExpectedError(SvnErrorCode.SVN_ERR_RA_OUT_OF_DATE); ca.AddExpectedError(SvnErrorCode.SVN_ERR_WC_FOUND_CONFLICT); ca.AddExpectedError(SvnErrorCode.SVN_ERR_WC_PATH_NOT_FOUND); ca.Notify += delegate(object notifySender, SvnNotifyEventArgs notifyE) { switch (notifyE.Action) { case SvnNotifyAction.FailedOutOfDate: if (notifyE.Error != null) { ca.AddExpectedError(notifyE.Error.SvnErrorCode); // Don't throw an exception for this error } outOfDateError = true; itemPath = itemPath ?? notifyE.FullPath; break; case SvnNotifyAction.FailedConflict: case SvnNotifyAction.FailedMissing: case SvnNotifyAction.FailedNoParent: case SvnNotifyAction.FailedLocked: case SvnNotifyAction.FailedForbiddenByServer: if (notifyE.Error != null) { ca.AddExpectedError(notifyE.Error.SvnErrorCode); // Don't throw an exception for this error } otherError = true; itemPath = itemPath ?? notifyE.FullPath; break; } }; ca.RunTortoiseHooks = enableHooks; ok = e.Client.Commit( state.CommitPaths, ca, out rslt); if (!ok && ca.LastException != null) { if (!outOfDateError && !otherError) { outOfDateError = true; // Remaining errors are handled as exception } outOfDateMessage = new StringBuilder(); Exception ex = ca.LastException; while (ex != null) { outOfDateMessage.AppendLine(ex.Message); ex = ex.InnerException; } if (!string.IsNullOrEmpty(itemPath)) { outOfDateMessage.AppendLine(); outOfDateMessage.AppendFormat(PccStrings.WhileCommittingX, itemPath); } } }); if (outOfDateMessage != null) { state.MessageBox.Show(outOfDateMessage.ToString(), outOfDateError ? PccStrings.OutOfDateCaption : PccStrings.CommitFailedCaption, MessageBoxButtons.OK, MessageBoxIcon.Error); } if (rslt != null) { IAnkhServiceEvents ci = GetService <IAnkhServiceEvents>(); if (ci != null) { ci.OnLastChanged(new LastChangedEventArgs(PccStrings.CommittedPrefix, rslt.Revision.ToString())); } if (!string.IsNullOrEmpty(rslt.PostCommitError)) { state.MessageBox.Show(rslt.PostCommitError, PccStrings.PostCommitError, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } PostCommit_IssueTracker(state, rslt); } return(ok); }
public override void OnExecute(CommandEventArgs e) { IAnkhServiceEvents ci = e.GetService <IAnkhServiceEvents>(); if (ci != null) { ci.OnLastChanged(new LastChangedEventArgs(null, null)); } SvnRevision rev; bool allowUnversionedObstructions = false; bool updateExternals = true; bool setDepthInfinity = true; IAnkhSolutionSettings settings = e.GetService <IAnkhSolutionSettings>(); ISvnStatusCache cache = e.GetService <ISvnStatusCache>(); IProjectFileMapper mapper = e.GetService <IProjectFileMapper>(); Uri reposRoot = null; if (IsHeadCommand(e.Command) || e.DontPrompt) { rev = SvnRevision.Head; } else if (IsSolutionCommand(e.Command)) { SvnItem projectItem = settings.ProjectRootSvnItem; Debug.Assert(projectItem != null, "Has item"); using (UpdateDialog ud = new UpdateDialog()) { ud.ItemToUpdate = projectItem; ud.Revision = SvnRevision.Head; if (ud.ShowDialog(e.Context) != DialogResult.OK) { return; } rev = ud.Revision; allowUnversionedObstructions = ud.AllowUnversionedObstructions; updateExternals = ud.UpdateExternals; setDepthInfinity = ud.SetDepthInfinty; } } else if (IsFolderCommand(e.Command)) { SvnItem dirItem = EnumTools.GetFirst(e.Selection.GetSelectedSvnItems(false)); Debug.Assert(dirItem != null && dirItem.IsDirectory && dirItem.IsVersioned); using (UpdateDialog ud = new UpdateDialog()) { ud.Text = CommandStrings.UpdateFolder; ud.FolderLabelText = CommandStrings.UpdateFolderLabel; ud.ItemToUpdate = dirItem; ud.Revision = SvnRevision.Head; if (ud.ShowDialog(e.Context) != DialogResult.OK) { return; } rev = ud.Revision; allowUnversionedObstructions = ud.AllowUnversionedObstructions; updateExternals = ud.UpdateExternals; setDepthInfinity = ud.SetDepthInfinty; } } else { // We checked there was only a single repository to select a revision // from in OnUpdate, so we can suffice with only calculate the path SvnItem si = null; SvnOrigin origin = null; foreach (SccProject p in GetSelectedProjects(e)) { ISccProjectInfo pi = mapper.GetProjectInfo(p); if (pi == null || pi.ProjectDirectory == null) { continue; } SvnItem item = cache[pi.ProjectDirectory]; if (!item.IsVersioned) { continue; } if (si == null && origin == null) { si = item; origin = new SvnOrigin(item); reposRoot = item.WorkingCopy.RepositoryRoot; } else { si = null; string urlPath1 = origin.Uri.AbsolutePath; string urlPath2 = item.Uri.AbsolutePath; int i = 0; while (i < urlPath1.Length && i < urlPath2.Length && urlPath1[i] == urlPath2[i]) { i++; } while (i > 0 && urlPath1[i - 1] != '/') { i--; } origin = new SvnOrigin(new Uri(origin.Uri, urlPath1.Substring(0, i)), origin.RepositoryRoot); } } Debug.Assert(origin != null); using (UpdateDialog ud = new UpdateDialog()) { ud.Text = CommandStrings.UpdateProject; if (si != null) { ud.ItemToUpdate = si; } else { ud.SvnOrigin = origin; ud.SetMultiple(true); } ud.Revision = SvnRevision.Head; if (ud.ShowDialog(e.Context) != DialogResult.OK) { return; } rev = ud.Revision; allowUnversionedObstructions = ud.AllowUnversionedObstructions; updateExternals = ud.UpdateExternals; setDepthInfinity = ud.SetDepthInfinty; } } Dictionary <string, SvnItem> itemsToUpdate = new Dictionary <string, SvnItem>(StringComparer.OrdinalIgnoreCase); SortedList <string, UpdateGroup> groups = new SortedList <string, UpdateGroup>(StringComparer.OrdinalIgnoreCase); // Get a list of all documents below the specified paths that are open in editors inside VS HybridCollection <string> lockPaths = new HybridCollection <string>(StringComparer.OrdinalIgnoreCase); IAnkhOpenDocumentTracker documentTracker = e.GetService <IAnkhOpenDocumentTracker>(); foreach (SvnItem item in GetAllUpdateRoots(e)) { // GetAllUpdateRoots can (and probably will) return duplicates! if (itemsToUpdate.ContainsKey(item.FullPath) || !item.IsVersioned) { continue; } SvnWorkingCopy wc = item.WorkingCopy; if (!IsHeadCommand(e.Command) && reposRoot != null) { // Specific revisions are only valid on a single repository! if (wc != null && wc.RepositoryRoot != reposRoot) { continue; } } UpdateGroup group; if (!groups.TryGetValue(wc.FullPath, out group)) { group = new UpdateGroup(wc.FullPath); groups.Add(wc.FullPath, group); } group.Nodes.Add(item.FullPath); itemsToUpdate.Add(item.FullPath, item); foreach (string file in documentTracker.GetDocumentsBelow(item.FullPath)) { if (!lockPaths.Contains(file)) { lockPaths.Add(file); } } } documentTracker.SaveDocuments(lockPaths); // Make sure all files are saved before updating/merging! using (DocumentLock lck = documentTracker.LockDocuments(lockPaths, DocumentLockType.NoReload)) using (lck.MonitorChangesForReload()) { SvnUpdateResult updateResult = null; ProgressRunnerArgs pa = new ProgressRunnerArgs(); pa.CreateLog = true; string title; if (IsSolutionCommand(e.Command)) { title = CommandStrings.UpdatingSolution; } else if (IsFolderCommand(e.Command)) { title = CommandStrings.UpdatingFolder; } else { title = CommandStrings.UpdatingProject; } e.GetService <IProgressRunner>().RunModal(title, pa, delegate(object sender, ProgressWorkerArgs a) { PerformUpdate(e, a, rev, allowUnversionedObstructions, updateExternals, setDepthInfinity, groups.Values, out updateResult); }); if (ci != null && updateResult != null && IsSolutionCommand(e.Command)) { ci.OnLastChanged(new LastChangedEventArgs(CommandStrings.UpdatedToTitle, updateResult.Revision.ToString())); } } }