Hi Greg,

I committed a wc-ng patch weeeks ago, where there was a helper function that
you said was duplicate functionality to svn_wc__internal_node_get_url().
Because I was busy with a customer at the time I reverted the commit.

Looking at it again, I think it's not a duplicate but a misnomer. I'd like
to get your approval on this patch since you complained about it initially.

A helper function in the original patch was called get_node_uri(). I renamed
that to get_node_base_rev_and_url_components(), because that's what it
really does. svn_wc__internal_node_get_url() is not suitable because
 - It returns the URL in one string, but it's needed in separate
   repos-root and path-in-repos strings.
 - It does not return the base revision, which is also needed in that
   context, and which can be obtained in the same call to wc_db.
 - It uses svn_wc__db_read_info(), but it looks to me like
   svn_wc__db_base_get_info() is the better choice, since that
   context explicitly wants the BASE information.

Here is the entire patch again based on current trunk.

Thanks,
~Neels
wc-ng: Remove use of svn_wc_entry_t from tree-conflict detection during update.
Keep the outcome identical, avoid fixing.

Changes in behavior are pending and discussed on dev@:
http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2415071
Subject: Re: wc-ng patch review
Date: Fri, 06 Nov 2009 11:43:05 +0000
Message-ID: <1257507785.8865.46.ca...@edith.foad.me.uk>

* subversion/libsvn_wc/update_editor.c
  (SVN_WC_CONFLICT_REASON_NONE): New local #define.
  (get_node_working_state, get_node_base_rev_and_url_components):
    New static functions, for check_tree_conflict().
  (check_tree_conflict): Replace svn_wc_entry_t use with calls to the WC DB
    via the new helper functions. Remove obsolete check for duplicate tree
    conflict involving an add of a file external (cannot reproduce).
--This line, and those below, will be ignored--
Index: subversion/libsvn_wc/update_editor.c
===================================================================
--- subversion/libsvn_wc/update_editor.c        (revision 899602)
+++ subversion/libsvn_wc/update_editor.c        (working copy)
@@ -1537,6 +1537,175 @@ tree_has_local_mods(svn_boolean_t *modif
 }
 
 
+#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
+
+/* Query a node's local status found in DB at LOCAL_ABSPATH.
+ *
+ * Return POSSIBLE_CONFLICT_REASON, which is the current status / schedule /
+ * difference-between-BASE-and-WORKING of the node, paraphrased into an
+ * svn_wc_conflict_reason_t. For example, if the node is replaced by
+ * WORKING, this returns svn_wc_conflict_reason_replaced. If this node is
+ * not changed by WORKING, this returns SVN_WC_CONFLICT_REASON_NONE. If it's
+ * not SVN_WC_CONFLICT_REASON_NONE, POSSIBLE_CONFLICT_REASON can be plugged
+ * directly into a tree-conflict descriptor struct (i.e.
+ * svn_wc_conflict_description2_t) once this reason is found to conflict
+ * with an incoming action.
+ *
+ * Return LOCAL_ABSPATH's KIND in the BASE tree, which is
+ * - svn_node_file for a file or symlink,
+ * - svn_node_dir for a directory and
+ * - svn_node_none if not found in BASE;
+ * - svn_node_unknown in all other cases. ### TODO: check each of those
+ *   "other cases" (e.g. DB reports "unknown", ...) and make this
+ *   svn_node_none if possible.
+ */
+static svn_error_t*
+get_node_working_state(svn_wc_conflict_reason_t *possible_conflict_reason,
+                       svn_node_kind_t *kind,
+                       svn_wc__db_t *db,
+                       const char *local_abspath,
+                       apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+  svn_wc__db_status_t status;
+  svn_wc__db_kind_t db_kind;
+  svn_boolean_t base_shadowed;
+
+  *possible_conflict_reason = SVN_WC_CONFLICT_REASON_NONE;
+  *kind = svn_node_unknown;
+
+  err = svn_wc__db_read_info(&status,
+                             &db_kind,
+                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL,
+                             &base_shadowed,
+                             NULL, NULL,
+                             db,
+                             local_abspath,
+                             scratch_pool,
+                             scratch_pool);
+
+  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      *possible_conflict_reason = svn_wc_conflict_reason_missing;
+      *kind = svn_node_none;
+    }
+  else
+    {
+      SVN_ERR(err);
+
+      /* Find the "reason" (local schedule) of the potential conflict. */
+      if (status == svn_wc__db_status_added
+          || status == svn_wc__db_status_obstructed_add)
+        {
+          *possible_conflict_reason = svn_wc_conflict_reason_added;
+          /* Is it a replace? */
+          if (base_shadowed)
+            {
+              svn_wc__db_status_t base_status;
+              SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, NULL,
+                                               NULL, NULL, NULL, NULL, NULL,
+                                               NULL, NULL, NULL, NULL, NULL,
+                                               NULL, NULL,
+                                               db, local_abspath,
+                                               scratch_pool,
+                                               scratch_pool));
+              if (base_status != svn_wc__db_status_not_present)
+                *possible_conflict_reason = svn_wc_conflict_reason_replaced;
+            }
+        }
+      else
+      if (status == svn_wc__db_status_deleted
+          || status == svn_wc__db_status_obstructed_delete)
+        *possible_conflict_reason = svn_wc_conflict_reason_deleted;
+
+      /* Translate the node kind. */
+      if (db_kind == svn_wc__db_kind_file
+          || db_kind == svn_wc__db_kind_symlink)
+        *kind = svn_node_file;
+      else
+      if (db_kind == svn_wc__db_kind_dir
+          || db_kind == svn_wc__db_kind_subdir)
+        *kind = svn_node_dir;
+      else
+        *kind = svn_node_unknown;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Find the source-left REVISON, REPOS_RELPATH and REPOS_ROOT_URL for
+ * recording a tree-conflict on node LOCAL_ABSPATH in DB.
+ */
+static svn_error_t*
+get_node_base_rev_and_url_components(svn_revnum_t *base_revision,
+                                     const char **repos_relpath,
+                                     const char **repos_root_url,
+                                     svn_wc__db_t *db,
+                                     const char *local_abspath,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+  svn_boolean_t do_scan = FALSE;
+  *base_revision = SVN_INVALID_REVNUM;
+
+  /* First cover all the cases that have a base present.
+   * The only ones that lack a base are added, copied, moved_here.
+   */
+  err = svn_wc__db_base_get_info(NULL, NULL,
+                                 base_revision,
+                                 repos_relpath,
+                                 repos_root_url,
+                                 NULL, NULL, NULL, NULL, NULL, NULL,
+                                 NULL, NULL, NULL, NULL,
+                                 db,
+                                 local_abspath,
+                                 result_pool,
+                                 scratch_pool);
+
+  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      do_scan = TRUE;
+    }
+  else
+    SVN_ERR(err);
+
+  if (do_scan || !SVN_IS_VALID_REVNUM(*base_revision) || !*repos_root_url)
+    {
+      /* No base found. Assume this is an 'add'. */
+      svn_wc__db_status_t added_status;
+
+      SVN_ERR(svn_wc__db_scan_addition(&added_status, NULL,
+                                       repos_relpath,
+                                       repos_root_url,
+                                       NULL, NULL, NULL, NULL,
+                                       base_revision,
+                                       db,
+                                       local_abspath,
+                                       result_pool,
+                                       scratch_pool));
+
+      /* If we ever hit a non-add here, we need to query the actual status
+       * of the node and handle other cases accordingly. */
+      SVN_ERR_ASSERT(added_status == svn_wc__db_status_added
+                     || added_status == svn_wc__db_status_obstructed_add
+                     || added_status == svn_wc__db_status_copied
+                     || added_status == svn_wc__db_status_moved_here);
+    }
+
+  /* Legacy behaviour from svn_wc__get_entry() use.
+   * ### TODO check if this is still needed, and, if yes, why. */
+  if (*repos_root_url && !*repos_relpath)
+    *repos_relpath = "";
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Check whether the incoming change ACTION on FULL_PATH would conflict with
  * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
  * LOCAL_ABSPATH as the victim.
@@ -1572,86 +1741,64 @@ check_tree_conflict(svn_wc_conflict_desc
                     svn_boolean_t inside_deleted_subtree,
                     apr_pool_t *pool)
 {
-  svn_wc_conflict_reason_t reason = (svn_wc_conflict_reason_t)(-1);
+  svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
   svn_boolean_t all_mods_are_deletes = FALSE;
-  const svn_wc_entry_t *entry;
-  svn_error_t *err;
-
-  err = svn_wc__get_entry(&entry, eb->db, local_abspath, TRUE,
-                          svn_node_unknown, FALSE, pool, pool);
-
-  if (err && err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND)
-    svn_error_clear(err);
-  else
-    SVN_ERR(err);
-
-  if (entry)
-    {
-      svn_boolean_t hidden;
-      SVN_ERR(svn_wc__db_node_hidden(&hidden, eb->db, local_abspath, pool));
+  svn_wc_conflict_reason_t possible_conflict_reason;
+  svn_node_kind_t kind;
 
-      if (hidden)
-        entry = NULL;
-    }
+  SVN_ERR(get_node_working_state(&possible_conflict_reason, &kind,
+                                 eb->db, local_abspath, pool));
 
   switch (action)
     {
+
     case svn_wc_conflict_action_edit:
       /* Use case 1: Modifying a locally-deleted item.
          If LOCAL_ABSPATH is an incoming leaf edit within a local
          tree deletion then we will already have recorded a tree
          conflict on the locally deleted parent tree.  No need
          to record a conflict within the conflict. */
-      if ((entry->schedule == svn_wc_schedule_delete
-           || entry->schedule == svn_wc_schedule_replace)
-          && !inside_deleted_subtree)
-        reason = entry->schedule == svn_wc_schedule_delete
-                                    ? svn_wc_conflict_reason_deleted
-                                    : svn_wc_conflict_reason_replaced;
+      if (!inside_deleted_subtree
+          && (possible_conflict_reason == svn_wc_conflict_reason_deleted
+              || possible_conflict_reason == svn_wc_conflict_reason_replaced))
+        reason = possible_conflict_reason;
       break;
 
     case svn_wc_conflict_action_add:
-      /* Use case "3.5": Adding a locally-added item.
-       *
-       * When checking out a file-external, add_file() is called twice:
-       * 1.) In the main update, a minimal entry is created.
-       * 2.) In the external update, the file is added properly.
-       * Don't raise a tree conflict the second time! */
-      if (entry && !entry->file_external_path)
-        reason = svn_wc_conflict_reason_added;
+      /* Use case "3.5": Adding a locally-added item. */
+      if (possible_conflict_reason == svn_wc_conflict_reason_added)
+        reason = possible_conflict_reason;
       break;
 
     case svn_wc_conflict_action_delete:
     case svn_wc_conflict_action_replace:
       /* Use case 3: Deleting a locally-deleted item. */
-      if (entry->schedule == svn_wc_schedule_delete
-          || entry->schedule == svn_wc_schedule_replace)
+      if (possible_conflict_reason == svn_wc_conflict_reason_deleted
+          || possible_conflict_reason == svn_wc_conflict_reason_replaced)
         {
           /* If LOCAL_ABSPATH is an incoming leaf deletion within a local
              tree deletion then we will already have recorded a tree
              conflict on the locally deleted parent tree.  No need
              to record a conflict within the conflict. */
           if (!inside_deleted_subtree)
-            reason = entry->schedule == svn_wc_schedule_delete
-                                        ? svn_wc_conflict_reason_deleted
-                                        : svn_wc_conflict_reason_replaced;
+            reason = possible_conflict_reason;
         }
       else
         {
           svn_boolean_t modified = FALSE;
 
           /* Use case 2: Deleting a locally-modified item. */
-          if (entry->kind == svn_node_file)
+          if (kind == svn_node_file)
             {
-              if (entry->schedule != svn_wc_schedule_normal)
+              if (possible_conflict_reason != SVN_WC_CONFLICT_REASON_NONE)
                 modified = TRUE;
               else
                 SVN_ERR(entry_has_local_mods(&modified, eb->db, local_abspath,
-                                             entry->kind, pool));
-              if (entry->schedule == svn_wc_schedule_delete)
+                                             kind, pool));
+              if (possible_conflict_reason == svn_wc_conflict_reason_deleted)
                 all_mods_are_deletes = TRUE;
             }
-          else if (entry->kind == svn_node_dir)
+          else if (kind == svn_node_dir)
             {
               /* We must detect deep modifications in a directory tree,
                * but the update editor will not visit the subdirectories
@@ -1681,35 +1828,37 @@ check_tree_conflict(svn_wc_conflict_desc
 
   /* If a conflict was detected, append log commands to the log accumulator
    * to record it. */
-  if (reason != (svn_wc_conflict_reason_t)(-1))
+  if (reason != SVN_WC_CONFLICT_REASON_NONE)
     {
       svn_wc_conflict_description2_t *conflict;
       const svn_wc_conflict_version_t *src_left_version;
       const svn_wc_conflict_version_t *src_right_version;
+      svn_revnum_t base_revision;
       const char *repos_url = NULL;
       const char *path_in_repos = NULL;
-      svn_node_kind_t left_kind = (entry->schedule == svn_wc_schedule_add)
+      svn_node_kind_t left_kind = (reason == svn_wc_conflict_reason_added)
                                   ? svn_node_none
-                                  : (entry->schedule == svn_wc_schedule_delete)
+                                  : (reason == svn_wc_conflict_reason_deleted)
                                     ? svn_node_unknown
-                                    : entry->kind;
+                                    : kind;
 
       /* Source-left repository root URL and path in repository.
        * The Source-right ones will be the same for update.
        * For switch, only the path in repository will differ, because
        * a cross-repository switch is not possible. */
-      repos_url = entry->repos;
-      path_in_repos = svn_uri_is_child(repos_url, entry->url, pool);
-      if (path_in_repos == NULL)
-        path_in_repos = "";
+      SVN_ERR(get_node_base_rev_and_url_components(&base_revision,
+                                                   &path_in_repos,
+                                                   &repos_url, eb->db,
+                                                   local_abspath, pool,
+                                                   pool));
 
       src_left_version = svn_wc_conflict_version_create(repos_url,
                                                         path_in_repos,
-                                                        entry->revision,
+                                                        base_revision,
                                                         left_kind,
                                                         pool);
 
-      /* entry->kind is both base kind and working kind, because schedule
+      /* kind is both base kind and working kind, because schedule
        * replace-by-different-kind is not supported. */
       /* ### TODO: but in case the entry is locally removed, entry->kind
        * is svn_node_none and doesn't reflect the older kind. Then we
@@ -1744,7 +1893,7 @@ check_tree_conflict(svn_wc_conflict_desc
                                                          pool);
 
       conflict = svn_wc_conflict_description_create_tree2(
-        local_abspath, entry->kind,
+        local_abspath, kind,
         eb->switch_url ? svn_wc_operation_switch : svn_wc_operation_update,
         src_left_version, src_right_version, pool);
       conflict->action = action;

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to