subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1444453 - in /subversion/trunk/subversion: libsvn_client/merge.c tests/cmdline/merge_tests.py
Date Sat, 09 Feb 2013 22:12:14 GMT
Author: julianfoad
Date: Sat Feb  9 22:12:13 2013
New Revision: 1444453

URL: http://svn.apache.org/r1444453
Log:
When a merge aborts due to conflicts, don't raise an error at such a low
level as we were doing, but in a single place at a slightly higher level.
A small step torwards being able to keep going if the user resolves all the
conflicts, for issue #4316 "Merge errors out after resolving conflicts".

* subversion/libsvn_client/merge.c
  (do_file_merge,
   do_mergeinfo_unaware_dir_merge,
   do_mergeinfo_aware_dir_merge,
   do_directory_merge): If we raise a conflict, return the revision range
    during which that occurred instead of an error describing it.
  (do_merge): Raise the error here instead -- consistently.

* subversion/tests/cmdline/merge_tests.py
  (conflict_aborted_mergeinfo_described): Test directory merges as well as
    single-file merges.

Modified:
    subversion/trunk/subversion/libsvn_client/merge.c
    subversion/trunk/subversion/tests/cmdline/merge_tests.py

Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1444453&r1=1444452&r2=1444453&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Sat Feb  9 22:12:13 2013
@@ -7060,25 +7060,27 @@ subrange_source(const merge_source_t *so
    and the value is the new mergeinfo for that path.  Allocate additions
    to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
 
+   CONFLICTED_RANGE is as documented for do_directory_merge().
+
    Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
    MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
 */
 static svn_error_t *
 do_file_merge(svn_mergeinfo_catalog_t result_catalog,
+              svn_merge_range_t **conflicted_range,
               const merge_source_t *source,
               const char *target_abspath,
               const svn_diff_tree_processor_t *processor,
               svn_boolean_t sources_related,
               svn_boolean_t squelch_mergeinfo_notifications,
-              svn_boolean_t abort_on_conflicts,
               merge_cmd_baton_t *merge_b,
+              apr_pool_t *result_pool,
               apr_pool_t *scratch_pool)
 {
   svn_rangelist_t *remaining_ranges;
   svn_client_ctx_t *ctx = merge_b->ctx;
   svn_merge_range_t range;
   svn_mergeinfo_t target_mergeinfo;
-  svn_merge_range_t *conflicted_range = NULL;
   svn_boolean_t inherited = FALSE;
   svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
   const svn_client__pathrev_t *primary_src
@@ -7089,6 +7091,8 @@ do_file_merge(svn_mergeinfo_catalog_t re
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
 
+  *conflicted_range = NULL;
+
   /* Note that this is a single-file merge. */
   range.start = source->loc1->rev;
   range.end = source->loc2->rev;
@@ -7327,10 +7331,10 @@ do_file_merge(svn_mergeinfo_catalog_t re
                                               processor,
                                               iterpool));
             }
-          if (((ranges_to_merge->nelts > 1) || abort_on_conflicts)
-              && is_path_conflicted_by_merge(merge_b))
+
+          if (is_path_conflicted_by_merge(merge_b))
             {
-              conflicted_range = svn_merge_range_dup(r, scratch_pool);
+              *conflicted_range = svn_merge_range_dup(r, result_pool);
               /* Only record partial mergeinfo if only a partial merge was
                  performed before a conflict was encountered. */
               range.end = r->end;
@@ -7406,12 +7410,6 @@ do_file_merge(svn_mergeinfo_catalog_t re
 
   svn_pool_destroy(iterpool);
 
-  /* If our multi-pass merge terminated early due to conflicts, return
-     that fact as an error. */
-  if (conflicted_range)
-    return make_merge_conflict_error(target_abspath, conflicted_range,
-                                     scratch_pool);
-
   return SVN_NO_ERROR;
 }
 
@@ -7593,34 +7591,48 @@ subtree_touched_by_merge(const char *loc
    SOURCE, DEPTH, NOTIFY_B, and MERGE_B
    are all cascaded from do_directory_merge's arguments of the same names.
 
+   CONFLICTED_RANGE is as documented for do_directory_merge().
+
    NOTE: This is a very thin wrapper around drive_merge_report_editor() and
    exists only to populate CHILDREN_WITH_MERGEINFO with the single element
    expected during mergeinfo unaware merges.
 */
 static svn_error_t *
-do_mergeinfo_unaware_dir_merge(const merge_source_t *source,
+do_mergeinfo_unaware_dir_merge(svn_merge_range_t **conflicted_range,
+                               const merge_source_t *source,
                                const char *target_dir_wcpath,
                                apr_array_header_t *children_with_mergeinfo,
                                const svn_diff_tree_processor_t *processor,
                                svn_depth_t depth,
                                merge_cmd_baton_t *merge_b,
-                               apr_pool_t *pool)
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool)
 {
   /* Initialize CHILDREN_WITH_MERGEINFO and populate it with
      one element describing the merge of SOURCE->rev1:rev2 to
      TARGET_DIR_WCPATH. */
   svn_client__merge_path_t *item
-    = svn_client__merge_path_create(target_dir_wcpath, pool);
+    = svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
 
+  *conflicted_range = NULL;
   item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
                                                      source->loc2->rev,
-                                                     TRUE, pool);
+                                                     TRUE, scratch_pool);
   APR_ARRAY_PUSH(children_with_mergeinfo,
                  svn_client__merge_path_t *) = item;
-  return drive_merge_report_editor(target_dir_wcpath,
-                                   source,
-                                   NULL, processor, depth,
-                                   merge_b, pool);
+  SVN_ERR(drive_merge_report_editor(target_dir_wcpath,
+                                    source,
+                                    NULL, processor, depth,
+                                    merge_b, scratch_pool));
+  if (is_path_conflicted_by_merge(merge_b))
+    {
+      svn_merge_range_t *r = apr_palloc(result_pool, sizeof(*r));
+      r->start = source->loc1->rev;
+      r->end = source->loc2->rev;
+      r->inheritable = TRUE;
+      *conflicted_range = r;
+    }
+  return SVN_NO_ERROR;
 }
 
 /* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */
@@ -8866,8 +8878,7 @@ remove_noop_subtree_ranges(const merge_s
 
    Handle DEPTH as documented for svn_client_merge5().
 
-   If ABORT_ON_CONFLICTS is TRUE raise an SVN_ERR_WC_FOUND_CONFLICT error
-   if any merge conflicts occur.
+   CONFLICTED_RANGE is as documented for do_directory_merge().
 
    Perform any temporary allocations in SCRATCH_POOL.
 
@@ -8878,19 +8889,17 @@ remove_noop_subtree_ranges(const merge_s
 */
 static svn_error_t *
 do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
+                             svn_merge_range_t **conflicted_range,
                              const merge_source_t *source,
                              const char *target_abspath,
                              apr_array_header_t *children_with_mergeinfo,
                              const svn_diff_tree_processor_t *processor,
                              svn_depth_t depth,
                              svn_boolean_t squelch_mergeinfo_notifications,
-                             svn_boolean_t abort_on_conflicts,
                              merge_cmd_baton_t *merge_b,
+                             apr_pool_t *result_pool,
                              apr_pool_t *scratch_pool)
 {
-  svn_error_t *err = SVN_NO_ERROR;
-  svn_error_t *merge_conflict_err = SVN_NO_ERROR;
-
   /* The range defining the mergeinfo we will record to describe the merge
      (assuming we are recording mergeinfo
 
@@ -8912,6 +8921,8 @@ do_mergeinfo_aware_dir_merge(svn_mergein
        same repository as the target -- merge tracking might be
        happenin'! ***/
 
+  *conflicted_range = NULL;
+
   /* Point our RA_SESSION to the URL of our youngest merge source side. */
   ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
 
@@ -9025,7 +9036,6 @@ do_mergeinfo_aware_dir_merge(svn_mergein
 
           while (end_rev != SVN_INVALID_REVNUM)
             {
-              svn_revnum_t next_end_rev;
               merge_source_t *real_source;
               svn_merge_range_t *first_target_range
                 = (target_merge_path->remaining_ranges->nelts == 0 ? NULL
@@ -9095,26 +9105,26 @@ do_mergeinfo_aware_dir_merge(svn_mergein
               /* Prepare for the next iteration (if any). */
               remove_first_range_from_remaining_ranges(
                 end_rev, children_with_mergeinfo, scratch_pool);
-              next_end_rev =
-                get_most_inclusive_rev(children_with_mergeinfo,
-                                       is_rollback, FALSE);
-              if ((next_end_rev != SVN_INVALID_REVNUM || abort_on_conflicts)
-                  && is_path_conflicted_by_merge(merge_b))
+
+              /* If we raised any conflicts, break out and report how much
+                 we have merged. */
+              if (is_path_conflicted_by_merge(merge_b))
                 {
-                  svn_merge_range_t conflicted_range;
-                  conflicted_range.start = start_rev;
-                  conflicted_range.end = end_rev;
-                  merge_conflict_err = make_merge_conflict_error(
-                                         merge_b->target->abspath,
-                                         &conflicted_range,
-                                         scratch_pool);
+                  svn_merge_range_t *r = apr_palloc(result_pool, sizeof(*r));
+                  r->start = start_rev;
+                  r->end = end_rev;
+                  r->inheritable = TRUE;
+                  *conflicted_range = r;
                   range.end = end_rev;
                   break;
                 }
+
               start_rev =
                 get_most_inclusive_rev(children_with_mergeinfo,
                                        is_rollback, TRUE);
-              end_rev = next_end_rev;
+              end_rev =
+                get_most_inclusive_rev(children_with_mergeinfo,
+                                       is_rollback, FALSE);
             }
         }
       svn_pool_destroy(iterpool);
@@ -9146,14 +9156,14 @@ do_mergeinfo_aware_dir_merge(svn_mergein
       const char *mergeinfo_path
         = svn_client__pathrev_fspath(primary_src, scratch_pool);
 
-      err = record_mergeinfo_for_dir_merge(result_catalog,
-                                           &range,
-                                           mergeinfo_path,
-                                           children_with_mergeinfo,
-                                           depth,
-                                           squelch_mergeinfo_notifications,
-                                           merge_b,
-                                           scratch_pool);
+      SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog,
+                                             &range,
+                                             mergeinfo_path,
+                                             children_with_mergeinfo,
+                                             depth,
+                                             squelch_mergeinfo_notifications,
+                                             merge_b,
+                                             scratch_pool));
 
       /* If a path has an immediate parent with non-inheritable mergeinfo at
          this point, then it meets criteria 3 or 5 described in
@@ -9169,36 +9179,40 @@ do_mergeinfo_aware_dir_merge(svn_mergein
          So here we look at the root path of each subtree added during the
          merge and set explicit mergeinfo on it if it meets the aforementioned
          conditions. */
-      if (err == SVN_NO_ERROR
-          && (range.start < range.end)) /* Nothing to record on added subtrees
-                                           resulting from reverse merges. */
+      if (range.start < range.end) /* Nothing to record on added subtrees
+                                      resulting from reverse merges. */
         {
-          err = record_mergeinfo_for_added_subtrees(
-                  &range, mergeinfo_path, depth,
-                  squelch_mergeinfo_notifications,
-                  merge_b->added_abspaths, merge_b, scratch_pool);
+          SVN_ERR(record_mergeinfo_for_added_subtrees(
+                    &range, mergeinfo_path, depth,
+                    squelch_mergeinfo_notifications,
+                    merge_b->added_abspaths, merge_b, scratch_pool));
         }
     }
 
-  return svn_error_compose_create(err, merge_conflict_err);
+  return SVN_NO_ERROR;
 }
 
 /* Helper for do_merge() when the merge target is a directory.
-
-*/
+ *
+ * If any conflict is raised during the merge, set *CONFLICTED_RANGE to
+ * the revision sub-range that raised the conflict.  In this case, the
+ * merge will have ended at revision CONFLICTED_RANGE and mergeinfo will
+ * have been recorded for all revision sub-ranges up to and including
+ * CONFLICTED_RANGE.  Otherwise, set *CONFLICTED_RANGE to NULL.
+ */
 static svn_error_t *
 do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
+                   svn_merge_range_t **conflicted_range,
                    const merge_source_t *source,
                    const char *target_abspath,
                    const svn_diff_tree_processor_t *processor,
                    svn_depth_t depth,
                    svn_boolean_t squelch_mergeinfo_notifications,
-                   svn_boolean_t abort_on_conflicts,
                    merge_cmd_baton_t *merge_b,
+                   apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
   apr_array_header_t *children_with_mergeinfo;
-  svn_error_t *err;
 
   /* Initialize CHILDREN_WITH_MERGEINFO. See the comment
      'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
@@ -9211,23 +9225,22 @@ do_directory_merge(svn_mergeinfo_catalog
   /* If we are not honoring mergeinfo we can skip right to the
      business of merging changes! */
   if (HONOR_MERGEINFO(merge_b))
-    err = do_mergeinfo_aware_dir_merge(result_catalog,
-                                       source, target_abspath,
-                                       children_with_mergeinfo,
-                                       processor, depth,
-                                       squelch_mergeinfo_notifications,
-                                       abort_on_conflicts,
-                                       merge_b, scratch_pool);
-  else
-    err = do_mergeinfo_unaware_dir_merge(source,
-                                         target_abspath,
+    SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflicted_range,
+                                         source, target_abspath,
                                          children_with_mergeinfo,
                                          processor, depth,
-                                         merge_b, scratch_pool);
+                                         squelch_mergeinfo_notifications,
+                                         merge_b, result_pool, scratch_pool));
+  else
+    SVN_ERR(do_mergeinfo_unaware_dir_merge(conflicted_range,
+                                           source, target_abspath,
+                                           children_with_mergeinfo,
+                                           processor, depth,
+                                           merge_b, result_pool, scratch_pool));
 
   merge_b->notify_begin.nodes_with_mergeinfo = NULL;
 
-  return svn_error_trace(err);
+  return SVN_NO_ERROR;
 }
 
 /** Ensure that *RA_SESSION is opened to URL, either by reusing
@@ -9472,11 +9485,8 @@ do_merge(apr_hash_t **modified_subtrees,
       svn_node_kind_t src1_kind;
       merge_source_t *source =
         APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
-      /* If conflicts occur while merging any but the very last
-       * revision range we want an error to be raised that aborts
-       * the merge operation. The user will be asked to resolve conflicts
-       * before merging subsequent revision ranges. */
-      svn_boolean_t abort_on_conflicts = (i < merge_sources->nelts - 1);
+      svn_merge_range_t *conflicted_range;
+      svn_error_t *err;
 
       svn_pool_clear(iterpool);
 
@@ -9521,24 +9531,34 @@ do_merge(apr_hash_t **modified_subtrees,
       /* Call our merge helpers based on SRC1's kind. */
       if (src1_kind != svn_node_dir)
         {
-          SVN_ERR(do_file_merge(result_catalog,
-                                source, target->abspath,
-                                processor,
-                                sources_related,
-                                squelch_mergeinfo_notifications,
-                                abort_on_conflicts,
-                                &merge_cmd_baton, iterpool));
+          err = do_file_merge(result_catalog, &conflicted_range,
+                              source, target->abspath,
+                              processor,
+                              sources_related,
+                              squelch_mergeinfo_notifications,
+                              &merge_cmd_baton, iterpool, iterpool);
         }
       else /* Directory */
         {
-          SVN_ERR(do_directory_merge(result_catalog,
-                                     source, target->abspath,
-                                     processor,
-                                     depth, squelch_mergeinfo_notifications,
-                                     abort_on_conflicts,
-                                     &merge_cmd_baton,
-                                     iterpool));
+          err = do_directory_merge(result_catalog, &conflicted_range,
+                                   source, target->abspath,
+                                   processor,
+                                   depth, squelch_mergeinfo_notifications,
+                                   &merge_cmd_baton, iterpool, iterpool);
+        }
+      /* If conflicts occurred while merging any but the very last
+       * range of a multi-pass merge, we raise an error that aborts
+       * the merge. The user will be asked to resolve conflicts
+       * before merging subsequent revision ranges. */
+      if (conflicted_range
+          && (i < merge_sources->nelts - 1
+              || conflicted_range->end != source->loc2->rev))
+        {
+          err = svn_error_compose_create(
+                  err, make_merge_conflict_error(
+                         target->abspath, conflicted_range, scratch_pool));
         }
+      SVN_ERR(err);
 
       /* The final mergeinfo on TARGET_WCPATH may itself elide. */
       if (! dry_run)

Modified: subversion/trunk/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/merge_tests.py?rev=1444453&r1=1444452&r2=1444453&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/merge_tests.py Sat Feb  9 22:12:13 2013
@@ -18351,12 +18351,14 @@ def conflict_aborted_mergeinfo_described
   trunk = 'A'
   branch = 'A2'
   file = 'mu'
+  dir = 'B'
   trunk_file = 'A/mu'
+  trunk_dir = 'A/B'
 
   # r2: initial state
   for rev in range(4, 11):
     sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev),
-                        trunk_file)
+                        trunk_file, trunk_dir)
   sbox.simple_commit()
 
   # r3: branch
@@ -18365,13 +18367,14 @@ def conflict_aborted_mergeinfo_described
 
   zero_rev = 3
 
-  def edit_file(path, rev, val):
+  def edit_file_or_dir(path, rev, val):
     """Make a local edit to the file at PATH."""
     sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path)
 
   # r4 through r10: simple edits
   for rev in range(4, 11):
-    edit_file(trunk_file, rev, 'Edited')
+    edit_file_or_dir(trunk_file, rev, 'Edited')
+    edit_file_or_dir(trunk_dir, rev, 'Edited')
     sbox.simple_commit()
 
   # r14: merge some changes to the branch so that later merges will be split
@@ -18379,6 +18382,7 @@ def conflict_aborted_mergeinfo_described
                                      '^/' + trunk, sbox.ospath(branch),
                                      '--accept', 'theirs-conflict')
   sbox.simple_commit()
+  sbox.simple_update()
 
   def revert_branch():
     svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R',
@@ -18408,7 +18412,7 @@ def conflict_aborted_mergeinfo_described
 
     # Arrange for the merge to conflict at CONFLICT_REV.
     if conflict_rev:
-      edit_file(tgt_path, conflict_rev, 'Conflict')
+      edit_file_or_dir(tgt_path, conflict_rev, 'Conflict')
 
     src_url = '^/' + src_path
     svntest.actions.run_and_verify_svn(
@@ -18431,96 +18435,99 @@ def conflict_aborted_mergeinfo_described
   # We test merges that raise a conflict in the first and last sub-range
   # of the first and last specified range.
 
-  tgt_ospath = sbox.ospath(branch + '/' + file)
+  for target in [file, dir]:
 
-  # First test: Merge "everything" to the branch.
-  #
-  # This merge is split into three sub-ranges: r3-4, r6-8, r10-head.
-  # We have arranged that the merge will raise a conflict in the first
-  # sub-range.  Since we are postponing conflict resolution, the merge
-  # should stop after the first sub-range, allowing us to resolve and
-  # repeat the merge at which point the next sub-range(s) can be merged.
-  # The mergeinfo on the target then should only reflect that the first
-  # sub-range (r3-4) has been merged.
-  #
-  # Previously the merge failed after merging only r3-4 (as it should)
-  # but mergeinfo for the whole range was recorded, preventing subsequent
-  # repeat merges from applying the rest of the source changes.
-  expect = expected_out_and_err(tgt_ospath,
-                                '3-4', ['3-4'],
-                                prop_conflicts=1)
-  try_merge(file, 4, [], expect, '3-5,9')
-
-  # Try a multiple-range merge that raises a conflict in the
-  # first sub-range in the first specified range;
-  expect = expected_out_and_err(tgt_ospath,
-                                '4', ['4'],
-                                prop_conflicts=1)
-  try_merge(file, 4, ['-c4-6,8-10'], expect, '4-5,9')
-  # last sub-range in the first specified range;
-  expect = expected_out_and_err(tgt_ospath,
-                                '4-6', ['4,6'],
-                                prop_conflicts=1)
-  try_merge(file, 6, ['-c4-6,8-10'], expect, '4-6,9')
-  # first sub-range in the last specified range;
-  expect = expected_out_and_err(tgt_ospath,
-                                '4-6,8', ['4,6', '8'],
-                                prop_conflicts=1)
-  try_merge(file, 8, ['-c4-6,8-10'], expect, '4-6,8-9')
-  # last sub-range in the last specified range.
-  # (Expect no error, because 'svn merge' does not throw an error if
-  # there is no more merging to do when a conflict occurs.)
-  expect = expected_out_and_err(tgt_ospath,
-                                '4-6,8-10', ['4,6', '8,10'],
-                                prop_conflicts=1, expect_error=False)
-  try_merge(file, 10, ['-c4-6,8-10'], expect, '4-6,8-10')
-
-  # Try similar merges but involving ranges in reverse order.
-  expect = expected_out_and_err(tgt_ospath,
-                                '8', ['8'],
-                                prop_conflicts=1)
-  try_merge(file, 8,  ['-c8-10,4-6'], expect, '5,8-9')
-  expect = expected_out_and_err(tgt_ospath,
-                                '8-10', ['8,10'],
-                                prop_conflicts=1)
-  try_merge(file, 10, ['-c8-10,4-6'], expect, '5,8-10')
-  expect = expected_out_and_err(tgt_ospath,
-                                '8-10,4', ['8,10', '4'],
-                                prop_conflicts=1)
-  try_merge(file, 4,  ['-c8-10,4-6'], expect, '4-5,8-10')
-  expect = expected_out_and_err(tgt_ospath,
-                                '8-10,4-6', ['8,10', '4,6'],
-                                prop_conflicts=1, expect_error=False)
-  try_merge(file, 6,  ['-c8-10,4-6'], expect, '4-6,8-10')
+    tgt_ospath = sbox.ospath(branch + '/' + target)
 
-  # Try some reverse merges, with ranges in forward and reverse order.
-  #
-  # Reverse merges start with all source changes merged except 5 and 9.
-  revert_branch()
-  simple_merge(trunk_file, sbox.ospath(branch + '/' + file),
-               ['-c-5,-9,4,6-8,10'])
-  sbox.simple_commit(branch + '/' + file)
-
-  expect = expected_out_and_err(tgt_ospath,
-                                '6-4,10-8', ['-6,-4', '-10,-8'],
-                                expect_error=False)
-  try_merge(file, None, ['-r6:3', '-r10:7'], expect, '7')
-  expect = expected_out_and_err(tgt_ospath,
-                                '-6', ['-6'],
-                                prop_conflicts=1)
-  try_merge(file, 6,  ['-r6:3', '-r10:7'], expect, '4,7-8,10')
-  expect = expected_out_and_err(tgt_ospath,
-                                '6-4', ['-6,-4'],
-                                prop_conflicts=1)
-  try_merge(file, 4,  ['-r6:3', '-r10:7'], expect, '7-8,10')
-  expect = expected_out_and_err(tgt_ospath,
-                                '6-4,-10', ['-6,-4', '-10'],
-                                prop_conflicts=1)
-  try_merge(file, 10, ['-r6:3', '-r10:7'], expect, '7-8')
-  expect = expected_out_and_err(tgt_ospath,
-                                '6-4,10-8', ['-6,-4', '-10,-8'],
-                                prop_conflicts=1, expect_error=False)
-  try_merge(file, 8,  ['-r6:3', '-r10:7'], expect, '7')
+    # First test: Merge "everything" to the branch.
+    #
+    # This merge is split into three sub-ranges: r3-4, r6-8, r10-head.
+    # We have arranged that the merge will raise a conflict in the first
+    # sub-range.  Since we are postponing conflict resolution, the merge
+    # should stop after the first sub-range, allowing us to resolve and
+    # repeat the merge at which point the next sub-range(s) can be merged.
+    # The mergeinfo on the target then should only reflect that the first
+    # sub-range (r3-4) has been merged.
+    #
+    # Previously the merge failed after merging only r3-4 (as it should)
+    # but mergeinfo for the whole range was recorded, preventing subsequent
+    # repeat merges from applying the rest of the source changes.
+    expect = expected_out_and_err(tgt_ospath,
+                                  '3-4', ['3-4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4, [], expect, '3-5,9')
+
+    # Try a multiple-range merge that raises a conflict in the
+    # first sub-range in the first specified range;
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4', ['4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4, ['-c4-6,8-10'], expect, '4-5,9')
+    # last sub-range in the first specified range;
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4-6', ['4,6'],
+                                  prop_conflicts=1)
+    try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,9')
+    # first sub-range in the last specified range;
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4-6,8', ['4,6', '8'],
+                                  prop_conflicts=1)
+    try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-9')
+    # last sub-range in the last specified range.
+    # (Expect no error, because 'svn merge' does not throw an error if
+    # there is no more merging to do when a conflict occurs.)
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4-6,8-10', ['4,6', '8,10'],
+                                  prop_conflicts=1, expect_error=False)
+    try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10')
+
+    # Try similar merges but involving ranges in reverse order.
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8', ['8'],
+                                  prop_conflicts=1)
+    try_merge(target, 8,  ['-c8-10,4-6'], expect, '5,8-9')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8-10', ['8,10'],
+                                  prop_conflicts=1)
+    try_merge(target, 10, ['-c8-10,4-6'], expect, '5,8-10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8-10,4', ['8,10', '4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4,  ['-c8-10,4-6'], expect, '4-5,8-10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8-10,4-6', ['8,10', '4,6'],
+                                  prop_conflicts=1, expect_error=False)
+    try_merge(target, 6,  ['-c8-10,4-6'], expect, '4-6,8-10')
+
+    # Try some reverse merges, with ranges in forward and reverse order.
+    #
+    # Reverse merges start with all source changes merged except 5 and 9.
+    revert_branch()
+    simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target),
+                 ['-c-5,-9,4,6-8,10'])
+    sbox.simple_commit()
+    sbox.simple_update()
+
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4,10-8', ['-6,-4', '-10,-8'],
+                                  expect_error=False)
+    try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '-6', ['-6'],
+                                  prop_conflicts=1)
+    try_merge(target, 6,  ['-r6:3', '-r10:7'], expect, '4,7-8,10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4', ['-6,-4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4,  ['-r6:3', '-r10:7'], expect, '7-8,10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4,-10', ['-6,-4', '-10'],
+                                  prop_conflicts=1)
+    try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7-8')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4,10-8', ['-6,-4', '-10,-8'],
+                                  prop_conflicts=1, expect_error=False)
+    try_merge(target, 8,  ['-r6:3', '-r10:7'], expect, '7')
 
 @SkipUnless(server_has_mergeinfo)
 @Issue(4310)



Mime
View raw message