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 2013/02/02 22:11:02 UTC

svn commit: r1441810 - in /subversion/trunk/subversion: libsvn_client/merge.c tests/cmdline/merge_tests.py

Author: julianfoad
Date: Sat Feb  2 21:11:02 2013
New Revision: 1441810

URL: http://svn.apache.org/viewvc?rev=1441810&view=rev
Log:
Fix two errors in how a single-file merge stops if there conflicts.  (1) It
didn't abort if there were conflicts on the last sub-range of a non-last
requested range.  (2) When aborting with conflicts it recorded mergeinfo
describing only the current sub-range, not the sub-ranges merged before the
conflict.

* subversion/libsvn_client/merge.c
  (do_file_merge): Take an 'abort_on-conflicts' flag and use it so that we
    abort on conflicts in all but the last sub-range of the last specified
    range, just like in do_directory_merge(). Record the right mergeinfo
    when aborting.
  (do_merge): Pass that flag.

* subversion/tests/cmdline/merge_tests.py
  (conflict_aborted_mergeinfo_described_partial_merge): Test more cases.

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=1441810&r1=1441809&r2=1441810&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Sat Feb  2 21:11:02 2013
@@ -7083,6 +7083,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
               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 *scratch_pool)
 {
@@ -7316,10 +7317,13 @@ do_file_merge(svn_mergeinfo_catalog_t re
                                               processor,
                                               iterpool));
             }
-          if ((i < (ranges_to_merge->nelts - 1))
+          if ((i < (ranges_to_merge->nelts - 1) || abort_on_conflicts)
               && is_path_conflicted_by_merge(merge_b))
             {
               conflicted_range = svn_merge_range_dup(r, scratch_pool);
+              /* Only record partial mergeinfo if only a partial merge was
+                 performed before a conflict was encountered. */
+              range.end = r->end;
               break;
             }
         }
@@ -7345,9 +7349,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
         &filtered_rangelist,
         mergeinfo_path,
         merge_target->implicit_mergeinfo,
-        /* Only record partial mergeinfo if only a partial merge was
-           performed before a conflict was encountered. */
-        conflicted_range ? conflicted_range : &range,
+        &range,
         iterpool));
 
       /* Only record mergeinfo if there is something other than
@@ -9463,6 +9465,11 @@ 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_pool_clear(iterpool);
 
@@ -9512,16 +9519,11 @@ do_merge(apr_hash_t **modified_subtrees,
                                 processor,
                                 sources_related,
                                 squelch_mergeinfo_notifications,
+                                abort_on_conflicts,
                                 &merge_cmd_baton, iterpool));
         }
       else /* Directory */
         {
-          /* 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_ERR(do_directory_merge(result_catalog,
                                      source, target->abspath,
                                      processor,

Modified: subversion/trunk/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/merge_tests.py?rev=1441810&r1=1441809&r2=1441810&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/merge_tests.py Sat Feb  2 21:11:02 2013
@@ -18191,46 +18191,108 @@ def conflict_aborted_mergeinfo_described
 
   sbox.build()
 
-  iota_copy_path = sbox.ospath('iota-copy')
-
-  # r2
-  sbox.simple_copy('iota', 'iota-copy')
+  trunk = 'A'
+  branch = 'A2'
+  file = 'mu'
+  trunk_file = 'A/mu'
+
+  # r2: initial state
+  for rev in range(4, 11):
+    sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev),
+                        trunk_file)
   sbox.simple_commit()
 
-  # r3
-  sbox.simple_append('iota', 'new line in r3\n')
+  # r3: branch
+  sbox.simple_copy(trunk, branch)
   sbox.simple_commit()
 
-  # r4
-  sbox.simple_append('iota', 'new line in r4\n')
-  sbox.simple_commit()
+  zero_rev = 3
 
-  # r5
-  sbox.simple_append('iota', 'new line in r5\n')
-  sbox.simple_commit()
+  def edit_file(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 r13: simple edits
+  for rev in range(4, 11):
+    edit_file(trunk_file, rev, 'Edited')
+    sbox.simple_commit()
 
-  # r6 Merge r4 from iota to iota-moved
-  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/iota',
-                                     '-c', '4', iota_copy_path, '--accept',
-                                     'theirs-conflict')
+  # r14: merge some changes to the branch so that later merges will be split
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c5,9',
+                                     '^/' + trunk, sbox.ospath(branch),
+                                     '--accept', 'theirs-conflict')
   sbox.simple_commit()
 
-  # Merge everything (i.e. r2 and r5) from iota to iota-moved.
-  # This is split into to merges, first of r2 and then of r5.
-  # But since we are postponing conflict resolution, the merge
-  # should stop after r2 is merged, allowing us to resolve and
-  # repeat the merge at which point r5 can be merged.  The mergeinfo
-  # on iota-copy then should only reflect that r2 and r3 have been
-  # merged from ^/iota; r5 should not be present.
-  svntest.actions.run_and_verify_svn(None, None, '.*', 'merge', '^/iota',
-                                     iota_copy_path, '--accept', 'postpone')
-
-  # Previously this test failed because the merge failed after merging
-  # only r2 (as it should) but mergeinfo for r5-6 was recorded, preventing
-  # subsequent repeat merges from applying the operative r5.
-  svntest.actions.run_and_verify_svn(
-    "Incorrect mergeinfo set during conflict aborted merge",
-    ['/iota:2-4\n'], [], 'pg', SVN_PROP_MERGEINFO, iota_copy_path)
+  def try_merge(target, conflict_rev, rev_ranges, mergeinfo, expect_error=True):
+    """Revert TARGET_PATH in the branch; merge TARGET_PATH in the trunk
+       to TARGET_PATH in the branch; expect to find MERGEINFO.
+    """
+    src_url = '^/' + trunk + '/' + target
+    src_path = trunk + '/' + target
+    tgt_path = branch + '/' + target
+    svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R',
+                                       sbox.ospath(tgt_path))
+    edit_file(tgt_path, conflict_rev, 'Conflict')
+    if expect_error:
+      expected_error = ('^svn: E155015: .* conflicts were produced .* into$'
+                        "|^'.*" + sbox.ospath(tgt_path) + "' --$"
+                        '|^resolve all conflicts .* remaining$'
+                        '|^unmerged revisions$')
+    else:
+      expected_error = []
+    svntest.actions.run_and_verify_svn(None, None, expected_error,
+                                       'merge',
+                                       src_url, sbox.ospath(tgt_path),
+                                       '--accept', 'postpone',
+                                       *rev_ranges)
+    expected_out = ['/' + src_path + ':' + mergeinfo + '\n']
+    svntest.actions.run_and_verify_svn(
+      "Incorrect mergeinfo set during conflict aborted merge",
+      expected_out, [], 'pg', SVN_PROP_MERGEINFO, sbox.ospath(tgt_path))
+
+  # In a mergeinfo-aware merge, each specified revision range is split
+  # internally into sub-ranges, to avoid any already-merged revisions.
+  #
+  # From white-box inspection, we see there are code paths that treat
+  # the last specified range and the last sub-range specially.  The
+  # first specified range or sub-range is not treated specially in terms
+  # of the code paths, although it might be in terms of data flow.
+  #
+  # We test merges that raise a conflict in the first and last sub-range
+  # of the first and last specified range.
+
+  # 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.
+  try_merge(file, 4, [], '3-5,9')
+
+  # Try a multiple-range merge that raises a conflict in the
+  # first sub-range in the first specified range.
+  try_merge(file, 4, ['-r1:6', '-r7:10'], '3-5,9')
+
+  # Try a multiple-range merge that raises a conflict in the
+  # last sub-range in the first specified range.
+  try_merge(file, 6, ['-r1:6', '-r7:10'], '3-6,9')
+
+  # Try a multiple-range merge that raises a conflict in the
+  # first sub-range in the last specified range.
+  try_merge(file, 8, ['-r1:6', '-r7:10'], '3-6,8-9')
+
+  # Try a multiple-range merge that raises a conflict in the
+  # 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.)
+  try_merge(file, 10, ['-r1:6', '-r7:10'], '3-6,8-10', expect_error=False)
 
 @SkipUnless(server_has_mergeinfo)
 @Issue(4310)