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);
 }

Reply via email to