You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2013/01/15 21:38:03 UTC

svn commit: r1433621 - /subversion/trunk/subversion/libsvn_client/merge.c

Author: rhuijben
Date: Tue Jan 15 20:38:03 2013
New Revision: 1433621

URL: http://svn.apache.org/viewvc?rev=1433621&view=rev
Log:
In the merge code use the obstruction information obtained from
perform_obstruction_check for the whole tree conflict detection for nodes
that are assumed to exist before an operation, instead of partially relying
on the on-disk information which we already compared.

* subversion/libsvn_client/merge.c
  (check_moved_away,
   check_moved_here): Move above the first diff callback.

  (merge_dir_props_changed): Handle moved_away reason.

  (check_moved_away,
   check_moved_here): Move above the first diff callback.

  (merge_file_deleted,
   merge_dir_deleted): Use the already available information for the
    obstruction/tree conflict check. Use local_abspath for path variable.

Modified:
    subversion/trunk/subversion/libsvn_client/merge.c

Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1433621&r1=1433620&r2=1433621&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Tue Jan 15 20:38:03 2013
@@ -1393,6 +1393,56 @@ prepare_merge_props_changed(const apr_ar
   return SVN_NO_ERROR;
 }
 
+/* Indicate in *MOVED_AWAY whether the node at LOCAL_ABSPATH was
+ * moved away locally. Do not raise an error if the node at LOCAL_ABSPATH
+ * does not exist. */
+static svn_error_t *
+check_moved_away(svn_boolean_t *moved_away,
+                 svn_wc_context_t *wc_ctx,
+                 const char *local_abspath,
+                 apr_pool_t *scratch_pool)
+{
+  const char *moved_to_abspath;
+
+  SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
+                                      wc_ctx, local_abspath,
+                                      scratch_pool, scratch_pool));
+  
+  *moved_away = (moved_to_abspath != NULL);
+
+  return SVN_NO_ERROR;
+}
+
+/* Indicate in *MOVED_HERE whether the node at LOCAL_ABSPATH was
+ * moved here locally. Do not raise an error if the node at LOCAL_ABSPATH
+ * does not exist. */
+static svn_error_t *
+check_moved_here(svn_boolean_t *moved_here,
+                 svn_wc_context_t *wc_ctx,
+                 const char *local_abspath,
+                 apr_pool_t *scratch_pool)
+{
+  const char *moved_from_abspath;
+  svn_error_t *err;
+
+  *moved_here = FALSE;
+
+  err = svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
+                                    wc_ctx, local_abspath,
+                                    scratch_pool, scratch_pool);
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+        svn_error_clear(err);
+      else
+        return svn_error_trace(err);
+    }
+  else if (moved_from_abspath)
+    *moved_here = TRUE;
+
+  return SVN_NO_ERROR;
+}
+
 /* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_dir_props_changed(svn_wc_notify_state_t *state,
@@ -1428,7 +1478,17 @@ merge_dir_props_changed(svn_wc_notify_st
       svn_wc_conflict_reason_t reason;
 
       if (is_deleted)
-        reason = svn_wc_conflict_reason_deleted;
+        {
+          svn_boolean_t moved_away;
+
+          SVN_ERR(check_moved_away(&moved_away, merge_b->ctx->wc_ctx,
+                                   local_abspath, scratch_pool));
+
+          if (moved_away)
+            reason = svn_wc_conflict_reason_moved_away;
+          else
+            reason = svn_wc_conflict_reason_deleted;
+        }
       else
         reason = svn_wc_conflict_reason_missing;
 
@@ -1547,57 +1607,6 @@ merge_file_opened(svn_boolean_t *tree_co
   return SVN_NO_ERROR;
 }
 
-
-/* Indicate in *MOVED_AWAY whether the node at LOCAL_ABSPATH was
- * moved away locally. Do not raise an error if the node at LOCAL_ABSPATH
- * does not exist. */
-static svn_error_t *
-check_moved_away(svn_boolean_t *moved_away,
-                 svn_wc_context_t *wc_ctx,
-                 const char *local_abspath,
-                 apr_pool_t *scratch_pool)
-{
-  const char *moved_to_abspath;
-
-  SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
-                                      wc_ctx, local_abspath,
-                                      scratch_pool, scratch_pool));
-  
-  *moved_away = (moved_to_abspath != NULL);
-
-  return SVN_NO_ERROR;
-}
-
-/* Indicate in *MOVED_HERE whether the node at LOCAL_ABSPATH was
- * moved here locally. Do not raise an error if the node at LOCAL_ABSPATH
- * does not exist. */
-static svn_error_t *
-check_moved_here(svn_boolean_t *moved_here,
-                 svn_wc_context_t *wc_ctx,
-                 const char *local_abspath,
-                 apr_pool_t *scratch_pool)
-{
-  const char *moved_from_abspath;
-  svn_error_t *err;
-
-  *moved_here = FALSE;
-
-  err = svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
-                                    wc_ctx, local_abspath,
-                                    scratch_pool, scratch_pool);
-  if (err)
-    {
-      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-        svn_error_clear(err);
-      else
-        return svn_error_trace(err);
-    }
-  else if (moved_from_abspath)
-    *moved_here = TRUE;
-
-  return SVN_NO_ERROR;
-}
-
 /* An svn_wc_diff_callbacks4_t function. */
 static svn_error_t *
 merge_file_changed(svn_wc_notify_state_t *content_state,
@@ -2164,17 +2173,11 @@ merge_file_deleted(svn_wc_notify_state_t
                    apr_pool_t *scratch_pool)
 {
   merge_cmd_baton_t *merge_b = baton;
-  const char *mine_abspath = svn_dirent_join(merge_b->target->abspath,
-                                             mine_relpath, scratch_pool);
+  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
+                                              mine_relpath, scratch_pool);
   svn_node_kind_t kind;
   svn_boolean_t is_deleted;
-
-  if (merge_b->dry_run)
-    {
-      const char *wcpath = apr_pstrdup(merge_b->pool, mine_abspath);
-      apr_hash_set(merge_b->dry_run_deletions, wcpath,
-                   APR_HASH_KEY_STRING, wcpath);
-    }
+  svn_boolean_t same;
 
   /* Easy out: We are only applying mergeinfo differences. */
   if (merge_b->record_only)
@@ -2187,9 +2190,8 @@ merge_file_deleted(svn_wc_notify_state_t
   {
     svn_wc_notify_state_t obstr_state;
 
-    SVN_ERR(perform_obstruction_check(&obstr_state, NULL, &is_deleted,
-                                      NULL,
-                                      merge_b, mine_abspath, svn_node_unknown,
+    SVN_ERR(perform_obstruction_check(&obstr_state, NULL, &is_deleted, &kind,
+                                      merge_b, local_abspath, svn_node_unknown,
                                       scratch_pool));
 
     if (obstr_state != svn_wc_notify_state_inapplicable)
@@ -2199,94 +2201,82 @@ merge_file_deleted(svn_wc_notify_state_t
       }
   }
 
-  SVN_ERR(svn_io_check_path(mine_abspath, &kind, scratch_pool));
-  switch (kind)
+  if (merge_b->dry_run)
     {
-    case svn_node_file:
-      {
-        svn_boolean_t same;
+      const char *wcpath = apr_pstrdup(merge_b->pool, local_abspath);
+      /* Store deletion *after* obstruction check, or the registration will be
+         noted as an obstruction */
+      apr_hash_set(merge_b->dry_run_deletions, wcpath,
+                   APR_HASH_KEY_STRING, wcpath);
+    }
 
-        /* If the files are identical, attempt deletion */
-        SVN_ERR(files_same_p(&same, older_abspath, original_props,
-                             mine_abspath, merge_b->ctx->wc_ctx,
-                             scratch_pool));
-        if (same || merge_b->force || merge_b->record_only /* ### why? */)
-          {
-            /* Passing NULL for the notify_func and notify_baton because
-               repos_diff.c:delete_entry() will do it for us. */
-            SVN_ERR(svn_client__wc_delete(mine_abspath, TRUE,
-                                          merge_b->dry_run, FALSE, NULL, NULL,
-                                          merge_b->ctx, scratch_pool));
-            *state = svn_wc_notify_state_changed;
+  if (kind != svn_node_file || is_deleted)
+    {
+      svn_wc_conflict_reason_t reason;
 
-            /* Record that we might have deleted mergeinfo */
-            if (!merge_b->paths_with_deleted_mergeinfo)
-              merge_b->paths_with_deleted_mergeinfo =
-                                                apr_hash_make(merge_b->pool);
-
-            apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
-                         apr_pstrdup(merge_b->pool, mine_abspath),
-                         APR_HASH_KEY_STRING, mine_abspath);
-          }
-        else
-          {
-            /* The files differ, so raise a conflict instead of deleting */
+      if (is_deleted)
+        {
+          svn_boolean_t moved_away;
 
-            /* This might be use case 5 described in the paper attached to issue
-             * #2282.  See also notes/tree-conflicts/detection.txt
-             */
-            SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
-                                  svn_wc_conflict_action_delete,
-                                  svn_wc_conflict_reason_edited));
-            *tree_conflicted = TRUE;
+          SVN_ERR(check_moved_away(&moved_away, merge_b->ctx->wc_ctx,
+                                   local_abspath, scratch_pool));
+
+          if (moved_away)
+            reason = svn_wc_conflict_reason_moved_away;
+          else
+            reason = svn_wc_conflict_reason_deleted;
+        }
+      else
+        reason = svn_wc_conflict_reason_missing;
+
+      SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_file,
+                            svn_wc_conflict_action_delete, reason));
 
-            *state = svn_wc_notify_state_obstructed;
-          }
-      }
-      break;
-    case svn_node_dir:
-      /* The file deletion the merge wants to carry out is obstructed by
-       * a directory, so the file the merge wants to delete is a tree
-       * conflict victim.
-       * See notes about obstructions in notes/tree-conflicts/detection.txt.
-       */
-      SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
-                            svn_wc_conflict_action_delete,
-                            svn_wc_conflict_reason_obstructed));
       *tree_conflicted = TRUE;
-      *state = svn_wc_notify_state_obstructed;
-      break;
-    case svn_node_none:
-      {
-        svn_wc_conflict_reason_t reason;
+      *state = svn_wc_notify_state_missing;
 
-        /* The file deleted in the diff does not exist at the current URL.
-         *
-         * This is use case 6 described in the paper attached to issue
-         * #2282.  See also notes/tree-conflicts/detection.txt
-         */
-        if (is_deleted)
-          {
-            svn_boolean_t moved_away;
-            SVN_ERR(check_moved_away(&moved_away, merge_b->ctx->wc_ctx,
-                                     mine_abspath, scratch_pool));
-            reason = moved_away ? svn_wc_conflict_reason_moved_away
-                                : svn_wc_conflict_reason_deleted;
-          }
-        else
-          reason = svn_wc_conflict_reason_missing;
+      return SVN_NO_ERROR;
+    }
 
-        SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
-                              svn_wc_conflict_action_delete, reason));
-        *tree_conflicted = TRUE;
-        *state = svn_wc_notify_state_missing;
-      }
-      break;
-    default:
-      *state = svn_wc_notify_state_unknown;
-      break;
+  
+
+  /* If the files are identical, attempt deletion */
+  SVN_ERR(files_same_p(&same, older_abspath, original_props,
+                       local_abspath, merge_b->ctx->wc_ctx,
+                       scratch_pool));
+  if (same || merge_b->force || merge_b->record_only /* ### why? */)
+    {
+      /* Passing NULL for the notify_func and notify_baton because
+         repos_diff.c:delete_entry() will do it for us. */
+      SVN_ERR(svn_client__wc_delete(local_abspath, TRUE,
+                                    merge_b->dry_run, FALSE, NULL, NULL,
+                                    merge_b->ctx, scratch_pool));
+      *state = svn_wc_notify_state_changed;
+
+      /* Record that we might have deleted mergeinfo */
+      if (!merge_b->paths_with_deleted_mergeinfo)
+        merge_b->paths_with_deleted_mergeinfo =
+                                          apr_hash_make(merge_b->pool);
+
+      apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
+                   apr_pstrdup(merge_b->pool, local_abspath),
+                   APR_HASH_KEY_STRING, local_abspath);
     }
+  else
+    {
+      /* The files differ, so raise a conflict instead of deleting */
+
+      /* This might be use case 5 described in the paper attached to issue
+       * #2282.  See also notes/tree-conflicts/detection.txt
+       */
+      SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_file,
+                            svn_wc_conflict_action_delete,
+                            svn_wc_conflict_reason_edited));
+      *tree_conflicted = TRUE;
 
+      *state = svn_wc_notify_state_obstructed;
+    }
+  
   return SVN_NO_ERROR;
 }
 
@@ -2524,8 +2514,8 @@ merge_dir_deleted(svn_wc_notify_state_t 
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
                                               local_relpath, scratch_pool);
   svn_node_kind_t kind;
-  svn_boolean_t is_versioned;
   svn_boolean_t is_deleted;
+  svn_error_t *err;
 
   /* Easy out: We are only applying mergeinfo differences. */
   if (merge_b->record_only)
@@ -2543,8 +2533,6 @@ merge_dir_deleted(svn_wc_notify_state_t 
                                       merge_b, local_abspath, svn_node_unknown,
                                       scratch_pool));
 
-    is_versioned = (kind == svn_node_dir) || (kind == svn_node_file);
-
     if (obstr_state != svn_wc_notify_state_inapplicable)
       {
         *state = obstr_state;
@@ -2558,116 +2546,80 @@ merge_dir_deleted(svn_wc_notify_state_t 
   if (merge_b->dry_run)
     {
       const char *wcpath = apr_pstrdup(merge_b->pool, local_abspath);
+      /* Store deletion *after* obstruction check, or the registration will be
+         noted as an obstruction */
       apr_hash_set(merge_b->dry_run_deletions, wcpath,
                    APR_HASH_KEY_STRING, wcpath);
     }
 
-
-  /* Switch on the wc state of this path */
-  switch (kind)
+  if (kind != svn_node_dir || is_deleted)
     {
-    case svn_node_dir:
-      {
-        if (is_versioned && !is_deleted)
-          {
-            svn_error_t *err;
+      svn_wc_conflict_reason_t reason;
 
-            /* ### TODO: Before deleting, we should ensure that this dir
-               tree is equal to the one we're being asked to delete.
-               If not, mark this directory as a tree conflict victim,
-               because this could be use case 5 as described in
-               notes/tree-conflicts/detection.txt.
-             */
-
-            /* Passing NULL for the notify_func and notify_baton because
-               repos_diff.c:delete_entry() will do it for us. */
-            err = svn_client__wc_delete(local_abspath, merge_b->force,
-                                        merge_b->dry_run, FALSE,
-                                        NULL, NULL,
-                                        merge_b->ctx, scratch_pool);
-            if (err)
-              {
-                svn_error_clear(err);
+      if (is_deleted)
+        {
+          svn_boolean_t moved_away;
 
-                /* If the attempt to delete an existing directory failed,
-                 * the directory has local modifications (e.g. locally added
-                 * files, or property changes). Flag a tree conflict. */
-                SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
-                                      svn_wc_conflict_action_delete,
-                                      svn_wc_conflict_reason_edited));
-                *tree_conflicted = TRUE;
-                *state = svn_wc_notify_state_conflicted;
-              }
-            else
-              {
-                *state = svn_wc_notify_state_changed;
-              }
+          SVN_ERR(check_moved_away(&moved_away, merge_b->ctx->wc_ctx,
+                                   local_abspath, scratch_pool));
 
-            /* Record that we might have deleted mergeinfo */
-            if (!merge_b->paths_with_deleted_mergeinfo)
-              merge_b->paths_with_deleted_mergeinfo =
-                                                apr_hash_make(merge_b->pool);
-
-            apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
-                         apr_pstrdup(merge_b->pool, local_abspath),
-                         APR_HASH_KEY_STRING, local_abspath);
-          }
-        else
-          {
-            svn_wc_conflict_reason_t reason;
-            /* Dir is already not under version control at this path. */
-            /* Raise a tree conflict. */
+          if (moved_away)
+            reason = svn_wc_conflict_reason_moved_away;
+          else
+            reason = svn_wc_conflict_reason_deleted;
+        }
+      else
+        reason = svn_wc_conflict_reason_missing;
 
-            if (is_deleted)
-              {
-                svn_boolean_t moved_away;
+      SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
+                            svn_wc_conflict_action_delete, reason));
 
-                SVN_ERR(check_moved_away(&moved_away, merge_b->ctx->wc_ctx,
-                                         local_abspath, scratch_pool));
-                reason = moved_away ? svn_wc_conflict_reason_moved_away
-                                    : svn_wc_conflict_reason_deleted;
-              }
-            else
-              reason = svn_wc_conflict_reason_missing;
-            SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
-                                  svn_wc_conflict_action_delete, reason));
-            *tree_conflicted = TRUE;
-          }
-      }
-      break;
-    case svn_node_file:
-      *state = svn_wc_notify_state_obstructed;
-      break;
-    case svn_node_none:
-      {
-        svn_wc_conflict_reason_t reason;
+      *tree_conflicted = TRUE;
+      *state = svn_wc_notify_state_missing;
 
-        if (is_deleted)
-          {
-            svn_boolean_t moved_away;
+      return SVN_NO_ERROR;
+    }
 
-            /* Dir is already non-existent. This is use case 6 as described in
-             * notes/tree-conflicts/detection.txt.
-             * This case was formerly treated as no-op. */
-            SVN_ERR(check_moved_away(&moved_away, merge_b->ctx->wc_ctx,
-                                     local_abspath, scratch_pool));
-            reason = moved_away ? svn_wc_conflict_reason_moved_away
-                                : svn_wc_conflict_reason_deleted;
-          }
-        else
-          reason = svn_wc_conflict_reason_missing;
+  /* ### TODO: Before deleting, we should ensure that this dir
+     tree is equal to the one we're being asked to delete.
+     If not, mark this directory as a tree conflict victim,
+     because this could be use case 5 as described in
+     notes/tree-conflicts/detection.txt.
+   */
 
-        SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
-                              svn_wc_conflict_action_delete, reason));
-        *tree_conflicted = TRUE;
-        *state = svn_wc_notify_state_missing;
-      }
-      break;
-    default:
-      *state = svn_wc_notify_state_unknown;
-      break;
+  /* Passing NULL for the notify_func and notify_baton because
+     repos_diff.c:delete_entry() will do it for us. */
+  err = svn_client__wc_delete(local_abspath, merge_b->force,
+                              merge_b->dry_run, FALSE,
+                              NULL, NULL,
+                              merge_b->ctx, scratch_pool);
+  if (err)
+    {
+      svn_error_clear(err);
+
+      /* If the attempt to delete an existing directory failed,
+       * the directory has local modifications (e.g. locally added
+       * files, or property changes). Flag a tree conflict. */
+      SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
+                            svn_wc_conflict_action_delete,
+                            svn_wc_conflict_reason_edited));
+      *tree_conflicted = TRUE;
+      *state = svn_wc_notify_state_conflicted;
+    }
+  else
+    {
+      *state = svn_wc_notify_state_changed;
     }
 
+  /* Record that we might have deleted mergeinfo */
+  if (!merge_b->paths_with_deleted_mergeinfo)
+    merge_b->paths_with_deleted_mergeinfo =
+                                      apr_hash_make(merge_b->pool);
+
+  apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
+               apr_pstrdup(merge_b->pool, local_abspath),
+               APR_HASH_KEY_STRING, local_abspath);
+
   return SVN_NO_ERROR;
 }