Index: subversion/include/svn_mergeinfo.h =================================================================== --- subversion/include/svn_mergeinfo.h (revision 37605) +++ subversion/include/svn_mergeinfo.h (working copy) @@ -202,15 +202,30 @@ svn_mergeinfo_merge(svn_mergeinfo_t mergeinfo, svn_mergeinfo_t changes, apr_pool_t *pool); -/** Removes @a eraser (the subtrahend) from @a whiteboard (the - * minuend), and places the resulting difference in @a *mergeinfo. +/** Like svn_mergeinfo_remove2, but always considers inheritance. * - * @since New in 1.5. + * @deprecated Provided for backward compatibility with the 1.5 API. */ svn_error_t * svn_mergeinfo_remove(svn_mergeinfo_t *mergeinfo, svn_mergeinfo_t eraser, svn_mergeinfo_t whiteboard, apr_pool_t *pool); +/** Removes @a eraser (the subtrahend) from @a whiteboard (the + * minuend), and places the resulting difference in @a *mergeinfo. + * + * @a consider_inheritance determines how to account for the inheritability + * of the two mergeinfo's ranges when calculating the range equivalence, + * as described for svn_mergeinfo_diff(). + * + * @since New in 1.7. + */ +svn_error_t * +svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo, + svn_mergeinfo_t eraser, + svn_mergeinfo_t whiteboard, + svn_boolean_t consider_inheritance, + apr_pool_t *pool); + /** Calculate the delta between two rangelists consisting of @c * svn_merge_range_t * elements (sorted in ascending order), @a from * and @a to, and place the result in @a *deleted and @a *added @@ -267,11 +282,9 @@ svn_boolean_t consider_inheritance, apr_pool_t *pool); -/** Find the intersection of two mergeinfos, @a mergeinfo1 and @a - * mergeinfo2, and place the result in @a *mergeinfo, which is (deeply) - * allocated in @a pool. +/** Like svn_mergeinfo_intersect2, but always considers inheritance. * - * @since New in 1.5. + * @deprecated Provided for backward compatibility with the 1.5 API. */ svn_error_t * svn_mergeinfo_intersect(svn_mergeinfo_t *mergeinfo, @@ -279,6 +292,23 @@ svn_mergeinfo_t mergeinfo2, apr_pool_t *pool); +/** Find the intersection of two mergeinfos, @a mergeinfo1 and @a + * mergeinfo2, and place the result in @a *mergeinfo, which is (deeply) + * allocated in @a pool. + * + * @a consider_inheritance determines how to account for the inheritability + * of the two mergeinfo's ranges when calculating the range equivalence, + * as described for svn_mergeinfo_diff(). + * + * @since New in 1.5. + */ +svn_error_t * +svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo, + svn_mergeinfo_t mergeinfo1, + svn_mergeinfo_t mergeinfo2, + svn_boolean_t consider_inheritance, + apr_pool_t *pool); + /** Find the intersection of two rangelists consisting of @c * svn_merge_range_t * elements, @a rangelist1 and @a rangelist2, and * place the result in @a *rangelist (which is never @c NULL). Index: subversion/libsvn_client/mergeinfo.c =================================================================== --- subversion/libsvn_client/mergeinfo.c (revision 37605) +++ subversion/libsvn_client/mergeinfo.c (working copy) @@ -1272,8 +1272,8 @@ SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, NULL, NULL, ctx, pool)); - SVN_ERR(svn_mergeinfo_intersect(&mergeinfo, tgt_mergeinfo, - source_history, pool)); + SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo, tgt_mergeinfo, + source_history, FALSE, pool)); /* Step 3: Now, we iterate over the eligible paths/rangelists to find the youngest revision (and its associated path). Because @@ -1419,7 +1419,8 @@ /* Now, we want to remove from the possible mergeinfo (SOURCE_HISTORY) the merges already present in our PATH_OR_URL. */ - SVN_ERR(svn_mergeinfo_remove(&available, mergeinfo, source_history, pool)); + SVN_ERR(svn_mergeinfo_remove2(&available, mergeinfo, source_history, + FALSE, pool)); /* Step 4: Now, we iterate over the eligible paths/rangelists to find the youngest revision (and its associated path). Because Index: subversion/libsvn_subr/mergeinfo.c =================================================================== --- subversion/libsvn_subr/mergeinfo.c (revision 37605) +++ subversion/libsvn_subr/mergeinfo.c (working copy) @@ -1130,6 +1130,17 @@ svn_mergeinfo_t mergeinfo2, apr_pool_t *pool) { + return svn_mergeinfo_intersect2(mergeinfo, mergeinfo1, mergeinfo2, + TRUE, pool); +} + +svn_error_t * +svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo, + svn_mergeinfo_t mergeinfo1, + svn_mergeinfo_t mergeinfo2, + svn_boolean_t consider_ineheritance, + apr_pool_t *pool) +{ apr_hash_index_t *hi; *mergeinfo = apr_hash_make(pool); @@ -1152,7 +1163,8 @@ { SVN_ERR(svn_rangelist_intersect(&rangelist, (apr_array_header_t *) val, - rangelist, TRUE, pool)); + rangelist, consider_ineheritance, + pool)); if (rangelist->nelts > 0) apr_hash_set(*mergeinfo, apr_pstrdup(pool, path), @@ -1166,9 +1178,19 @@ svn_mergeinfo_remove(svn_mergeinfo_t *mergeinfo, svn_mergeinfo_t eraser, svn_mergeinfo_t whiteboard, apr_pool_t *pool) { + return svn_mergeinfo_remove2(mergeinfo, eraser, whiteboard, TRUE, pool); +} + +svn_error_t * +svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo, + svn_mergeinfo_t eraser, + svn_mergeinfo_t whiteboard, + svn_boolean_t consider_ineritance, + apr_pool_t *pool) +{ *mergeinfo = apr_hash_make(pool); return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL, - TRUE, pool); + consider_ineritance, pool); } svn_error_t * Index: subversion/tests/cmdline/mergeinfo_tests.py =================================================================== --- subversion/tests/cmdline/mergeinfo_tests.py (revision 37605) +++ subversion/tests/cmdline/mergeinfo_tests.py (working copy) @@ -32,6 +32,11 @@ from svntest.main import SVN_PROP_MERGEINFO from svntest.main import server_has_mergeinfo +# Get a couple merge helpers from merge_tests.py +import merge_tests +from merge_tests import set_up_branch +from merge_tests import expected_merge_output + def adjust_error_for_server_version(expected_err): "Return the expected error regexp appropriate for the server version." if server_has_mergeinfo(): @@ -139,6 +144,73 @@ "mergeinfo", "--show-revs", "eligible", url, wc_dir) +# Test for issue #3126 'svn mergeinfo shows too few or too many +# eligible revisions'. Specifically +# http://subversion.tigris.org/issues/show_bug.cgi?id=3126#desc5. +def non_inheritable_mergeinfo(sbox): + "non-inheritable mergeinfo shows as merged" + + sbox.build() + wc_dir = sbox.wc_dir + expected_disk, expected_status = set_up_branch(sbox) + + # Some paths we'll care about + A_COPY_path = os.path.join(wc_dir, "A_COPY") + D_COPY_path = os.path.join(wc_dir, "A_COPY", "D") + rho_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "G", "rho") + + # Update the WC, then merge r4 from A to A_COPY and r6 from A to A_COPY + # at --depth empty and commit the merges as r7. + svntest.actions.run_and_verify_svn(None, ["At revision 6.\n"], [], 'up', + wc_dir) + expected_status.tweak(wc_rev=6) + svntest.actions.run_and_verify_svn(None, + expected_merge_output([[4]], 'U ' + + rho_COPY_path + + '\n'), + [], 'merge', '-c4', + sbox.repo_url + '/A', + A_COPY_path) + svntest.actions.run_and_verify_svn(None, + [], # Noop due to shallow depth + [], 'merge', '-c6', + sbox.repo_url + '/A', + A_COPY_path, '--depth', 'empty') + expected_output = wc.State(wc_dir, { + 'A_COPY' : Item(verb='Sending'), + 'A_COPY/D/G/rho' : Item(verb='Sending'), + }) + expected_status.tweak('A_COPY', 'A_COPY/D/G/rho', wc_rev=7) + svntest.actions.run_and_verify_commit(wc_dir, expected_output, + expected_status, None, wc_dir) + + # Update the WC a last time to ensure full inheritance. + svntest.actions.run_and_verify_svn(None, ["At revision 7.\n"], [], 'up', + wc_dir) + + # Despite being non-inheritable, r6 should still show as merged to A_COPY + # and not eligible for merging. + svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), + [4,6], + sbox.repo_url + '/A', + A_COPY_path) + svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), + [3,5], + sbox.repo_url + '/A', + A_COPY_path, + '--show-revs', 'eligible') + # But if we drop down to A_COPY/D, r6 should show as eligible because it + # was only merged into A_COPY, no deeper. + svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), + [4], + sbox.repo_url + '/A/D', + D_COPY_path) + svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""), + [3,6], + sbox.repo_url + '/A/D', + D_COPY_path, + '--show-revs', 'eligible') + ######################################################################## # Run the tests @@ -150,6 +222,7 @@ SkipUnless(explicit_mergeinfo_source, server_has_mergeinfo), XFail(mergeinfo_non_source, server_has_mergeinfo), mergeinfo_on_unknown_url, + non_inheritable_mergeinfo, ] if __name__ == '__main__':