You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2015/10/23 18:58:03 UTC

svn commit: r1710269 - /subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c

Author: julianfoad
Date: Fri Oct 23 16:58:03 2015
New Revision: 1710269

URL: http://svn.apache.org/viewvc?rev=1710269&view=rev
Log:
On the 'move-tracking-2' branch: Start making 'svnmover merge' (and
'svnmover switch') report conflicts more programatically and thoroughly.

* subversion/svnmover/svnmover.c
  (element_merge3_conflict_t,
   element_merge3_conflict_create,
   name_clash_conflict_t,
   name_clash_conflict_create,
   orphan_conflict_t,
   orphan_conflict_create,
   conflict_storage_t,
   conflict_storage_create,
   brief_eid_and_name_or_nil,
   display_conflicts): New.
  (element_merge): Return a conflict object instead of just a boolean.
  (merge_subbranch): Ignore, for now, any conflicts from merging a
    subbranch.
  (detect_clashes): Store clash-conflict objects instead of just some
    elements.
  (detect_orphans): New.
  (branch_merge_subtree_r): Collect and return the conflicts instead
    of just raising an error.
  (svn_branch_merge): Return the conflicts.
  (do_switch,
   execute): If the merge returns any conflicts, display them and then
    raise an error.  

Modified:
    subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c

Modified: subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c?rev=1710269&r1=1710268&r2=1710269&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c Fri Oct 23 16:58:03 2015
@@ -1240,6 +1240,156 @@ typedef struct merge_conflict_policy_t
   /* merge (props, text) independently or as a group */
 } merge_conflict_policy_t;
 
+/* An element-merge conflict description.
+ */
+typedef struct element_merge3_conflict_t
+{
+  svn_element_content_t *yca;
+  svn_element_content_t *side1;
+  svn_element_content_t *side2;
+} element_merge3_conflict_t;
+
+static element_merge3_conflict_t *
+element_merge3_conflict_create(svn_element_content_t *yca,
+                               svn_element_content_t *side1,
+                               svn_element_content_t *side2,
+                               apr_pool_t *result_pool)
+{
+  element_merge3_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
+
+  c->yca = yca;
+  c->side1 = side1;
+  c->side2 = side2;
+  return c;
+}
+
+/* A name-clash conflict description.
+ */
+typedef struct name_clash_conflict_t
+{
+  int parent_eid;
+  const char *name;
+  /* All EIDs that conflict with each other: hash of (eid -> irrelevant). */
+  apr_hash_t *elements;
+} name_clash_conflict_t;
+
+static name_clash_conflict_t *
+name_clash_conflict_create(int parent_eid,
+                           const char *name,
+                           apr_pool_t *result_pool)
+{
+  name_clash_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
+
+  c->parent_eid = parent_eid;
+  c->name = apr_pstrdup(result_pool, name);
+  c->elements = apr_hash_make(result_pool);
+  return c;
+}
+
+/* An orphan conflict description.
+ */
+typedef struct orphan_conflict_t
+{
+  svn_element_content_t *element;
+} orphan_conflict_t;
+
+static orphan_conflict_t *
+orphan_conflict_create(svn_element_content_t *element,
+                       apr_pool_t *result_pool)
+{
+  orphan_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
+
+  c->element = element;
+  return c;
+}
+
+typedef struct conflict_storage_t
+{
+  /* Single-element conflicts */
+  /* (eid -> element_merge3_conflict_t) */
+  apr_hash_t *single_element_conflicts;
+
+  /* Name-clash conflicts */
+  /* ("%{parent_eid}d/%{name}s" -> name_clash_conflict_t) */
+  apr_hash_t *name_clash_conflicts;
+
+  /* Orphan conflicts */
+  /* (eid -> orphan_conflict_t) */
+  apr_hash_t *orphan_conflicts;
+} conflict_storage_t;
+
+/*  */
+static conflict_storage_t *
+conflict_storage_create(apr_pool_t *result_pool)
+{
+  conflict_storage_t *c = apr_pcalloc(result_pool, sizeof(*c));
+
+  return c;
+}
+
+/*  */
+static const char *
+brief_eid_and_name_or_nil(svn_element_content_t *e,
+                          apr_pool_t *result_pool)
+{
+  return e ? apr_psprintf(result_pool, "%d/%s", e->parent_eid, e->name)
+           : "<nil>";
+
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+display_conflicts(conflict_storage_t *conflict_storage,
+                  const char *prefix,
+                  apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(scratch_pool,
+                           conflict_storage->single_element_conflicts);
+       hi; hi = apr_hash_next(hi))
+    {
+      int eid = svn_int_hash_this_key(hi);
+      element_merge3_conflict_t *c = apr_hash_this_val(hi);
+
+      printf("%ssingle-element conflict: e%d: yca=%s, side1=%s, side2=%s\n",
+             prefix, eid,
+             brief_eid_and_name_or_nil(c->yca, scratch_pool),
+             brief_eid_and_name_or_nil(c->side1, scratch_pool),
+             brief_eid_and_name_or_nil(c->side2, scratch_pool));
+    }
+  for (hi = apr_hash_first(scratch_pool,
+                           conflict_storage->name_clash_conflicts);
+       hi; hi = apr_hash_next(hi))
+    {
+      /*const char *key = apr_hash_this_key(hi);*/
+      name_clash_conflict_t *c = apr_hash_this_val(hi);
+      apr_hash_index_t *hi2;
+
+      printf("%sname-clash conflict: peid %d, name '%s', %d elements\n",
+             prefix, c->parent_eid, c->name, apr_hash_count(c->elements));
+      for (hi2 = apr_hash_first(scratch_pool, c->elements);
+           hi2; hi2 = apr_hash_next(hi2))
+        {
+          int eid = svn_int_hash_this_key(hi2);
+
+          printf("%s  element %d\n", prefix, eid);
+        }
+    }
+  for (hi = apr_hash_first(scratch_pool,
+                           conflict_storage->orphan_conflicts);
+       hi; hi = apr_hash_next(hi))
+    {
+      int eid = svn_int_hash_this_key(hi);
+      orphan_conflict_t *c = apr_hash_this_val(hi);
+
+      printf("%sorphan conflict: element %d/%s: peid %d does not exist\n",
+             prefix, eid, c->element->name, c->element->parent_eid);
+    }
+  return SVN_NO_ERROR;
+}
+
 /* Merge the payload for one element.
  *
  * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
@@ -1307,7 +1457,7 @@ payload_merge(svn_element_payload_t **re
  */
 static void
 element_merge(svn_element_content_t **result_p,
-              svn_boolean_t *conflict_p,
+              element_merge3_conflict_t **conflict_p,
               int eid,
               svn_element_content_t *side1,
               svn_element_content_t *side2,
@@ -1438,14 +1588,18 @@ element_merge(svn_element_content_t **re
     }
 
   *result_p = result;
-  *conflict_p = conflict;
+  *conflict_p
+    = conflict ? element_merge3_conflict_create(yca, side1, side2,
+                                                result_pool) : NULL;
 }
 
 static svn_error_t *
 branch_merge_subtree_r(svn_branch_txn_t *edit_txn,
+                       conflict_storage_t **conflict_storage_p,
                        const svn_branch_el_rev_id_t *src,
                        const svn_branch_el_rev_id_t *tgt,
                        const svn_branch_el_rev_id_t *yca,
+                       apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool);
 
 /* Merge the subbranch of {SRC, TGT, YCA} found at EID.
@@ -1483,9 +1637,14 @@ merge_subbranch(svn_branch_txn_t *edit_t
 
   if (subbr_src && subbr_tgt && subbr_yca)  /* ?edit vs. ?edit */
     {
+      conflict_storage_t *conflict_storage;
+
       /* subbranch possibly changed in source => merge */
-      SVN_ERR(branch_merge_subtree_r(edit_txn, subbr_src, subbr_tgt, subbr_yca,
-                                     scratch_pool));
+      SVN_ERR(branch_merge_subtree_r(edit_txn,
+                                     &conflict_storage,
+                                     subbr_src, subbr_tgt, subbr_yca,
+                                     scratch_pool, scratch_pool));
+      /* ### store this branch's conflict_storage somewhere ... */
     }
   else if (subbr_src && subbr_yca)  /* ?edit vs. delete */
     {
@@ -1537,15 +1696,15 @@ sort_compare_items_by_peid_and_name(cons
   return strcmp(element_a->name, element_b->name);
 }
 
-/* Return an array of (what type?) clashes...
+/* Return all (key -> name_clash_conflict_t) name clash conflicts in BRANCH.
  */
 static svn_error_t *
-detect_clashes(apr_array_header_t **clashes_p,
+detect_clashes(apr_hash_t **clashes_p,
                svn_branch_state_t *branch,
                apr_pool_t *result_pool,
                apr_pool_t *scratch_pool)
 {
-  apr_array_header_t *clashes = apr_array_make(result_pool, 0, sizeof(void *));
+  apr_hash_t *clashes = apr_hash_make(result_pool);
   SVN_ITER_T(svn_element_content_t) *pi;
   int prev_eid = -1;
   svn_element_content_t *prev_element = NULL;
@@ -1560,9 +1719,20 @@ detect_clashes(apr_array_header_t **clas
           && element->parent_eid == prev_element->parent_eid
           && strcmp(element->name, prev_element->name) == 0)
         {
-          notify("  clash: e%d, e%d: %s",
-                 prev_eid, eid, peid_name(element, scratch_pool));
-          APR_ARRAY_PUSH(clashes, void *) = element;
+          const char *key = apr_psprintf(result_pool, "%d/%s",
+                                         element->parent_eid, element->name);
+          name_clash_conflict_t *c;
+
+          c = svn_hash_gets(clashes, key);
+          if (!c)
+            {
+              c = name_clash_conflict_create(
+                    element->parent_eid, element->name,
+                    result_pool);
+              svn_hash_sets(clashes, key, c);
+            }
+          svn_int_hash_set(c->elements, eid, &c);
+          svn_int_hash_set(c->elements, prev_eid, &c);
         }
       prev_eid = eid;
       prev_element = element;
@@ -1572,21 +1742,57 @@ detect_clashes(apr_array_header_t **clas
   return SVN_NO_ERROR;
 }
 
+/* Return all (eid -> orphan_conflict_t) orphan conflicts in BRANCH.
+ */
+static svn_error_t *
+detect_orphans(apr_hash_t **orphans_p,
+               svn_branch_state_t *branch,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
+{
+  apr_hash_t *orphans = apr_hash_make(result_pool);
+  SVN_ITER_T(svn_element_content_t) *pi;
+  const svn_element_tree_t *elements = svn_branch_get_element_tree(branch);
+
+  for (SVN_HASH_ITER(pi, scratch_pool, elements->e_map))
+    {
+      int eid = *(const int *)(pi->key);
+      svn_element_content_t *element = pi->val;
+
+      if (eid != elements->root_eid
+          && ! svn_element_tree_get(elements, element->parent_eid))
+        {
+          orphan_conflict_t *c;
+
+          c = orphan_conflict_create(element, result_pool);
+          svn_int_hash_set(orphans, eid, c);
+
+          notify("  orphan: %d/%s: peid %d does not exist",
+                 eid, element->name, element->parent_eid);
+        }
+    }
+
+  *orphans_p = orphans;
+  return SVN_NO_ERROR;
+}
+
 /* Merge ...
  *
  * Merge any sub-branches in the same way, recursively.
  */
 static svn_error_t *
 branch_merge_subtree_r(svn_branch_txn_t *edit_txn,
+                       conflict_storage_t **conflict_storage_p,
                        const svn_branch_el_rev_id_t *src,
                        const svn_branch_el_rev_id_t *tgt,
                        const svn_branch_el_rev_id_t *yca,
+                       apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool)
 {
   svn_branch_subtree_t *s_src, *s_tgt, *s_yca;
   apr_hash_t *diff_yca_src, *diff_yca_tgt;
-  int single_element_conflicts = 0;
-  apr_array_header_t *clashes;
+  apr_hash_t *e_conflicts = apr_hash_make(scratch_pool);
+  conflict_storage_t *conflict_storage = conflict_storage_create(result_pool);
   SVN_ITER_T(svn_element_content_t *) *pi;
   apr_hash_t *all_elements;
   const merge_conflict_policy_t policy = { TRUE, TRUE, TRUE, TRUE, TRUE };
@@ -1646,7 +1852,7 @@ branch_merge_subtree_r(svn_branch_txn_t
       svn_element_content_t *e_src;
       svn_element_content_t *e_tgt;
       svn_element_content_t *result;
-      svn_boolean_t conflict;
+      element_merge3_conflict_t *conflict;
 
       svn_pool_clear(iterpool);
 
@@ -1676,7 +1882,7 @@ branch_merge_subtree_r(svn_branch_txn_t
       if (conflict)
         {
           notify_v("!    e%d <conflict>", eid);
-          ++single_element_conflicts;
+          svn_int_hash_set(e_conflicts, eid, conflict);
         }
       else if (e_tgt && result)
         {
@@ -1728,26 +1934,18 @@ branch_merge_subtree_r(svn_branch_txn_t
                info (including the relevant incoming changes) for each
                kind of conflict. If there are no conflicts, flatten the
                merge result into a tree. */
-  SVN_ERR(detect_clashes(&clashes,
+  conflict_storage->single_element_conflicts = e_conflicts;
+  SVN_ERR(detect_clashes(&conflict_storage->name_clash_conflicts,
+                         tgt->branch,
+                         result_pool, scratch_pool));
+  SVN_ERR(detect_orphans(&conflict_storage->orphan_conflicts,
                          tgt->branch,
-                         scratch_pool, scratch_pool));
+                         result_pool, scratch_pool));
 
   notify_v("merging into branch %s -- finished",
            svn_branch_get_id(tgt->branch, scratch_pool));
 
-  if (single_element_conflicts || clashes->nelts /* || ... */)
-    {
-      return svn_error_createf(SVN_ERR_BRANCHING, NULL,
-                               _("Merge failed: %d single-element conflicts and %d clashes occurred"),
-                               single_element_conflicts, clashes->nelts);
-      /* TODO: Return all conflicts to the caller;
-               have the caller display them. */
-    }
-  else
-    {
-      SVN_DBG(("merge completed: no conflicts"));
-    }
-
+  *conflict_storage_p = conflict_storage;
   return SVN_NO_ERROR;
 }
 
@@ -1766,6 +1964,7 @@ branch_merge_subtree_r(svn_branch_txn_t
  */
 static svn_error_t *
 svn_branch_merge(svn_branch_txn_t *edit_txn,
+                 conflict_storage_t **conflict_storage_p,
                  svn_branch_el_rev_id_t *src,
                  svn_branch_el_rev_id_t *tgt,
                  svn_branch_el_rev_id_t *yca,
@@ -1783,7 +1982,10 @@ svn_branch_merge(svn_branch_txn_t *edit_
   /*SVN_ERR(verify_not_subbranch_root(to, scratch_pool));*/
   /*SVN_ERR(verify_not_subbranch_root(yca, scratch_pool));*/
 
-  SVN_ERR(branch_merge_subtree_r(edit_txn, src, tgt, yca, scratch_pool));
+  SVN_ERR(branch_merge_subtree_r(edit_txn,
+                                 conflict_storage_p,
+                                 src, tgt, yca,
+                                 scratch_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -1828,6 +2030,7 @@ do_switch(svnmover_wc_t *wc,
   if (has_local_changes)
     {
       svn_branch_el_rev_id_t *yca, *src, *tgt;
+      conflict_storage_t *conflicts;
 
       /* Merge changes from the old into the new WC */
       yca = svn_branch_el_rev_id_create(previous_base_br,
@@ -1840,8 +2043,29 @@ do_switch(svnmover_wc_t *wc,
       tgt = svn_branch_el_rev_id_create(wc->working->branch,
                                         svn_branch_root_eid(wc->working->branch),
                                         SVN_INVALID_REVNUM, scratch_pool);
-      SVN_ERR(svn_branch_merge(wc->edit_txn, src, tgt, yca,
-                               scratch_pool));
+      SVN_ERR(svn_branch_merge(wc->edit_txn, &conflicts,
+                               src, tgt, yca, scratch_pool));
+
+      if (apr_hash_count(conflicts->single_element_conflicts)
+          || apr_hash_count(conflicts->name_clash_conflicts)
+          || apr_hash_count(conflicts->orphan_conflicts))
+        {
+          SVN_ERR(display_conflicts(conflicts, "switch: ", scratch_pool));
+          return svn_error_createf(
+                   SVN_ERR_BRANCHING, NULL,
+                   _("Switch failed because of conflicts: "
+                     "%d single-element conflicts, "
+                     "%d name-clash conflicts, "
+                     "%d orphan conflicts"),
+                   apr_hash_count(conflicts->single_element_conflicts),
+                   apr_hash_count(conflicts->name_clash_conflicts),
+                   apr_hash_count(conflicts->orphan_conflicts));
+        }
+      else
+        {
+          SVN_DBG(("Switch completed: no conflicts"));
+        }
+
       /* ### TODO: If the merge raises conflicts, either revert to the
              pre-update state or store and handle the conflicts. Currently
              this just leaves the merge partially done and raises an error. */
@@ -3337,14 +3561,38 @@ execute(svnmover_wc_t *wc,
 
         case ACTION_MERGE:
           {
+            conflict_storage_t *conflicts;
+
             VERIFY_EID_EXISTS("merge", 0);
             VERIFY_EID_EXISTS("merge", 1);
             VERIFY_EID_EXISTS("merge", 2);
             SVN_ERR(svn_branch_merge(wc->edit_txn,
+                                     &conflicts,
                                      arg[0]->el_rev /*from*/,
                                      arg[1]->el_rev /*to*/,
                                      arg[2]->el_rev /*yca*/,
                                      iterpool));
+
+            if (apr_hash_count(conflicts->single_element_conflicts)
+                || apr_hash_count(conflicts->name_clash_conflicts)
+                || apr_hash_count(conflicts->orphan_conflicts))
+              {
+                SVN_ERR(display_conflicts(conflicts, "merge: ", iterpool));
+                return svn_error_createf(
+                         SVN_ERR_BRANCHING, NULL,
+                         _("Merge failed because of conflicts: "
+                           "%d single-element conflicts, "
+                           "%d name-clash conflicts, "
+                           "%d orphan conflicts"),
+                         apr_hash_count(conflicts->single_element_conflicts),
+                         apr_hash_count(conflicts->name_clash_conflicts),
+                         apr_hash_count(conflicts->orphan_conflicts));
+              }
+            else
+              {
+                SVN_DBG(("Merge completed: no conflicts"));
+              }
+
           }
           break;