Hello! Starting from Subversion 1.10, the command-line client automatically resolves tree conflicts using the recommended resolution option when it's available. An example of such recommended resolution is the "Move and merge" action that can be available for tree conflicts caused by renames.
The relevant links are: https://svn.apache.org/r1783500 https://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?view=markup#l1801 TortoiseSVN commands currently have the "old" behavior where the user has to choose the conflict resolution option manually. I would like to propose a patch that makes the TortoiseSVN commands (merge, update, switch, resolve) automatically apply the recommended tree conflict resolution when it's available. With the patch, TortoiseSVN commands have the same behavior as the command-line client, with an improved user experience in multiple cases, especially when an operation such as merge causes multiple tree conflicts. Best Regards, Denis Kovalchuk -- You received this message because you are subscribed to the Google Groups "TortoiseSVN-dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to tortoisesvn-dev+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/tortoisesvn-dev/76b81997-6081-4759-8bb6-2f7e3ad8ec3dn%40googlegroups.com.
Automatically resolve tree conflicts using the recommended resolution option when it's available. The change applies to the TortoiseSVN Merge, Switch, Update and Resolve commands. Subversion behaves this way starting from version 1.10. The relevant links are: https://svn.apache.org/r1783500 https://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?view=markup#l1801 src/SVN/SVN.cpp: src/SVN/SVN.h: Implement SVN::ResolveTreeConflictById() method. src/TortoiseProc/Commands/ConflictEditorCommand.cpp: src/TortoiseProc/Commands/ConflictEditorCommand.h: First try to automatically resolve a tree conflict. If it fails, fall back to manual resolution. src/TortoiseProc/SVNProgressDlg.cpp: src/TortoiseProc/SVNProgressDlg.h: For the Merge command, try to automatically resolve tree conflicts. If it fails, fall back to manual resolution. For the Switch and Update commands, only try the auto resolution. Index: src/SVN/SVN.cpp =================================================================== --- src/SVN/SVN.cpp (revision 29394) +++ src/SVN/SVN.cpp (working copy) @@ -1201,6 +1201,20 @@ return (m_err == nullptr); } +bool SVN::ResolveTreeConflictById(svn_client_conflict_t* conflict, svn_client_conflict_option_id_t optionId) +{ + SVNPool scratchPool(m_pool); + Prepare(); + + const char* svnPath = svn_client_conflict_get_local_abspath(conflict); + + SVNTRACE( + m_err = svn_client_conflict_tree_resolve_by_id(conflict, optionId, m_pCtx, scratchPool), + svnPath); + + return (m_err == nullptr); +} + bool SVN::ResolveTextConflict(svn_client_conflict_t* conflict, svn_client_conflict_option_t* option) { SVNPool scratchPool(m_pool); Index: src/SVN/SVN.h =================================================================== --- src/SVN/SVN.h (revision 29394) +++ src/SVN/SVN.h (working copy) @@ -1005,6 +1005,7 @@ * In case there's no preferred move target, set those values to -1 */ bool ResolveTreeConflict(svn_client_conflict_t* conflict, svn_client_conflict_option_t* option, int preferredMovedTargetIdx, int preferredMovedReltargetIdx); + bool ResolveTreeConflictById(svn_client_conflict_t* conflict, svn_client_conflict_option_id_t optionId); /** * Resolves text conflict. */ Index: src/TortoiseProc/Commands/ConflictEditorCommand.cpp =================================================================== --- src/TortoiseProc/Commands/ConflictEditorCommand.cpp (revision 29394) +++ src/TortoiseProc/Commands/ConflictEditorCommand.cpp (working copy) @@ -30,6 +30,7 @@ CTSVNPath directory = merge.GetDirectory(); bool bRet = false; bool bAlternativeTool = !!parser.HasKey(L"alternative"); + SVN svn; // Use Subversion 1.10 API to resolve possible tree conlifcts. SVNConflictInfo conflict; @@ -58,16 +59,46 @@ progressDlg.Stop(); conflict.SetProgressDlg(nullptr); - CTreeConflictEditorDlg dlg; - dlg.SetConflictInfo(&conflict); + svn_client_conflict_option_id_t recommendedOptionId = conflict.GetRecommendedOptionId(); + bool bAutoResolved = false; - dlg.DoModal(GetExplorerHWND()); - if (dlg.IsCancelled()) - return false; + if (recommendedOptionId != svn_client_conflict_option_unspecified) + { + if (svn.ResolveTreeConflictById(conflict, recommendedOptionId)) + { + bAutoResolved = true; + } + else + { + apr_status_t rootCause = svn_error_root_cause(const_cast<svn_error_t*>(svn.GetSVNError()))->apr_err; + if (rootCause == SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE || + rootCause == SVN_ERR_WC_OBSTRUCTED_UPDATE || + rootCause == SVN_ERR_WC_FOUND_CONFLICT) + { + svn.ClearSVNError(); + } + else + { + svn.ShowErrorDialog(GetExplorerHWND()); + return false; + } + } + } - if (dlg.GetResult() == svn_client_conflict_option_postpone) - return false; + // If auto resolution is not available, try to resolve manually. + if (!bAutoResolved) + { + CTreeConflictEditorDlg dlg; + dlg.SetConflictInfo(&conflict); + dlg.SetSVNContext(&svn); + dlg.DoModal(GetExplorerHWND()); + if (dlg.IsCancelled()) + return false; + if (dlg.GetResult() == svn_client_conflict_option_postpone) + return false; + } + // Send notififcation that status may be changed. We cannot use // '/resolvemsghwnd' here because satus of multiple files may be changed // during tree conflict resolution. @@ -107,7 +138,7 @@ { CPropConflictEditorDlg dlg; dlg.SetConflictInfo(&conflict); - + dlg.SetSVNContext(&svn); dlg.DoModal(GetExplorerHWND(), i); if (dlg.IsCancelled()) return false; Index: src/TortoiseProc/SVNProgressDlg.cpp =================================================================== --- src/TortoiseProc/SVNProgressDlg.cpp (revision 29394) +++ src/TortoiseProc/SVNProgressDlg.cpp (working copy) @@ -4493,18 +4493,48 @@ progressDlg.Stop(); conflict.SetProgressDlg(nullptr); - CTreeConflictEditorDlg dlg; - dlg.SetConflictInfo(&conflict); - dlg.SetSVNContext(this); - dlg.DoModal(m_hWnd); - if (dlg.IsCancelled()) + svn_client_conflict_option_id_t recommendedOptionId = conflict.GetRecommendedOptionId(); + bool bAutoResolved = false; + + if (recommendedOptionId != svn_client_conflict_option_unspecified) { - return; + if (ResolveTreeConflictById(conflict, recommendedOptionId)) + { + bAutoResolved = true; + } + else + { + apr_status_t rootCause = svn_error_root_cause(const_cast<svn_error_t*>(GetSVNError()))->apr_err; + if (rootCause == SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE || + rootCause == SVN_ERR_WC_OBSTRUCTED_UPDATE || + rootCause == SVN_ERR_WC_FOUND_CONFLICT) + { + ClearSVNError(); + } + else + { + ShowErrorDialog(m_hWnd); + continue; + } + } } - if (dlg.GetResult() == svn_client_conflict_option_postpone) - continue; + // If auto resolution is not available, try to resolve manually. + if (!bAutoResolved) + { + CTreeConflictEditorDlg dlg; + dlg.SetConflictInfo(&conflict); + dlg.SetSVNContext(this); + dlg.DoModal(m_hWnd); + if (dlg.IsCancelled()) + { + return; + } + if (dlg.GetResult() == svn_client_conflict_option_postpone) + continue; + } + // Update conflict information. if (!conflict.Get(path)) { @@ -4571,6 +4601,79 @@ GetDlgItem(IDC_RETRYMERGE)->ShowWindow(SW_HIDE); } } + // Try to automatically resolve tree conflicts for Switch and Update commands. + else if ((m_command == SVNProgress_Switch) || (m_command == SVNProgress_SwitchBackToParent) || + (m_command == SVNProgress_Update)) + { + for (int i = 0; i < static_cast<int>(m_arData.size()); ++i) + { + CString info = BuildInfoString(); + SetDlgItemText(IDC_INFOTEXT, info); + + NotificationData* data = m_arData[i]; + if (data->bConflictedActionItem) + { + // Make a copy of NotificationData::path because it data pointer + // may become invalid during processing conflict resolution callbacks. + CTSVNPath path = data->path; + + SVNConflictInfo conflict; + if (!conflict.Get(path)) + { + conflict.ShowErrorDialog(m_hWnd); + return; + } + + if (conflict.HasTreeConflict()) + { + m_progList.SetItemState(-1, 0, LVIS_SELECTED); + m_progList.SetItemState(i, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + m_progList.EnsureVisible(i, FALSE); + m_progList.SetFocus(); + m_progList.SetSelectionMark(i); + + CProgressDlg progressDlg; + progressDlg.SetTitle(IDS_PROC_EDIT_TREE_CONFLICTS); + CString sProgressLine; + sProgressLine.LoadString(IDS_PROGRS_FETCHING_TREE_CONFLICT_INFO); + progressDlg.SetLine(1, sProgressLine); + progressDlg.SetShowProgressBar(false); + progressDlg.ShowModal(m_hWnd, FALSE); + conflict.SetProgressDlg(&progressDlg); + if (!conflict.FetchTreeDetails()) + { + conflict.ShowErrorDialog(m_hWnd); + progressDlg.Stop(); + continue; + } + progressDlg.Stop(); + conflict.SetProgressDlg(nullptr); + + svn_client_conflict_option_id_t recommendedOptionId = conflict.GetRecommendedOptionId(); + + if (recommendedOptionId != svn_client_conflict_option_unspecified) + { + if (!ResolveTreeConflictById(conflict, recommendedOptionId)) + { + apr_status_t rootCause = svn_error_root_cause(const_cast<svn_error_t*>(GetSVNError()))->apr_err; + if (rootCause == SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE || + rootCause == SVN_ERR_WC_OBSTRUCTED_UPDATE || + rootCause == SVN_ERR_WC_FOUND_CONFLICT) + { + ClearSVNError(); + } + else + { + ShowErrorDialog(m_hWnd); + continue; + } + } + } + } + } + } + } + CString info = BuildInfoString(); SetDlgItemText(IDC_INFOTEXT, info); }