Index: subversion/libsvn_client/merge.c =================================================================== --- subversion/libsvn_client/merge.c (revision 25101) +++ subversion/libsvn_client/merge.c (working copy) @@ -1298,13 +1298,16 @@ } svn_error_t * -svn_client__elide_mergeinfo(const char *target_wcpath, +svn_client__elide_mergeinfo(svn_boolean_t *elided, + const char *target_wcpath, const char *elision_limit_path, const svn_wc_entry_t *entry, svn_wc_adm_access_t *adm_access, svn_client_ctx_t *ctx, apr_pool_t *pool) { + if (elided) + *elided = FALSE; /* Check for first easy out: We are already at the limit path. */ if (!elision_limit_path || strcmp(target_wcpath, elision_limit_path) != 0) { @@ -1344,8 +1347,13 @@ SVN_ERR(mergeinfo_elides(&elides, mergeinfo, target_mergeinfo, NULL, pool)); if (elides) - SVN_ERR(svn_wc_prop_set2(SVN_PROP_MERGE_INFO, NULL, - target_wcpath, adm_access, TRUE, pool)); + { + SVN_ERR(svn_wc_prop_set2(SVN_PROP_MERGE_INFO, NULL, + target_wcpath, adm_access, TRUE, + pool)); + if (elided) + *elided = TRUE; + } } } return SVN_NO_ERROR; @@ -1600,8 +1608,6 @@ SVN_ERR(svn_rangelist_merge(&rangelist, ranges, subpool)); } /* Update the merge info by adjusting the path's rangelist. */ - if (rangelist->nelts == 0) - rangelist = NULL; apr_hash_set(mergeinfo, rel_path, APR_HASH_KEY_STRING, rangelist); if (is_revert && apr_hash_count(mergeinfo) == 0) @@ -2055,7 +2061,7 @@ svn_stringbuf_create(target_wcpath, pool); svn_path_remove_components(elision_limit_path, target_count - merge_target_count - 1); - SVN_ERR(svn_client__elide_mergeinfo(target_wcpath, + SVN_ERR(svn_client__elide_mergeinfo(NULL, target_wcpath, elision_limit_path->data, entry, adm_access, ctx, pool)); } @@ -2374,7 +2380,7 @@ svn_stringbuf_create(target_wcpath, pool); svn_path_remove_components(elision_limit_path, target_count - merge_target_count - 1); - SVN_ERR(svn_client__elide_mergeinfo(target_wcpath, + SVN_ERR(svn_client__elide_mergeinfo(NULL, target_wcpath, elision_limit_path->data, entry, adm_access, ctx, pool)); } @@ -2475,6 +2481,62 @@ return SVN_NO_ERROR; } +/* Post-process helper for svn_client_merge_peg3() and svn_client_merge3() + + If TARGET_WCPATH has merge info made up only of merge sources mapped to + empty revision ranges and this merge info is not overriding any ancestor + path's mergeinfo then remove the merge info property altogether on + TARGET_WCPATH. + */ +static svn_error_t * +clear_empty_range_mergeinfo(const char *target_wcpath, + const svn_wc_entry_t *entry, + svn_wc_adm_access_t *adm_access, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + apr_hash_t *inherited_mergeinfo, *target_mergeinfo; + svn_boolean_t inherited; + + SVN_ERR(get_wc_merge_info(&target_mergeinfo, &inherited, FALSE, entry, + target_wcpath, NULL, NULL, adm_access, ctx, + pool)); + if (target_mergeinfo) + { + /* Get TARGET_WCPATH's inherited merge info if any. */ + SVN_ERR(get_wc_merge_info(&inherited_mergeinfo, &inherited, TRUE, entry, + target_wcpath, NULL, NULL, adm_access, ctx, + pool)); + if (!inherited_mergeinfo) + { + apr_hash_index_t *hi; + const void *key; + void *val; + const char *path; + apr_array_header_t *rangelist; + svn_boolean_t empty_ranges = TRUE; + + /* Check if TARGET_MERGEINFO has only empty revision ranges. */ + for (hi = apr_hash_first(pool, target_mergeinfo); + hi; hi = apr_hash_next(hi)) + { + apr_hash_this(hi, &key, NULL, &val); + path = key; + rangelist = val; + if (rangelist->nelts != 0) + { + empty_ranges = FALSE; + break; + } + } + if (empty_ranges) + SVN_ERR(svn_wc_prop_set2(SVN_PROP_MERGE_INFO, NULL, target_wcpath, + adm_access, TRUE, pool)); + } + } + return SVN_NO_ERROR; +} + /*-----------------------------------------------------------------------*/ /*** Public APIs. ***/ @@ -2501,6 +2563,7 @@ const char *path1, *path2; svn_opt_revision_t peg_revision; apr_array_header_t *children_with_mergeinfo; + svn_boolean_t elided; /* This is not a pegged merge. */ peg_revision.kind = svn_opt_revision_unspecified; @@ -2634,9 +2697,16 @@ } /* The final mergeinfo on TARGET_WCPATH may itself elide. */ - SVN_ERR(svn_client__elide_mergeinfo(target_wcpath, NULL, entry, + SVN_ERR(svn_client__elide_mergeinfo(&elided, target_wcpath, NULL, entry, adm_access, ctx, pool)); + /* If no elision occurred the final mergeinfo may still disappear if + if is made up of nothing but empty ranges and overrides no ancestor + mergeinfo. */ + if (!elided) + SVN_ERR(clear_empty_range_mergeinfo(target_wcpath, entry, adm_access, + ctx, pool)); + SVN_ERR(svn_wc_adm_close(adm_access)); return SVN_NO_ERROR; @@ -2701,6 +2771,7 @@ const char *URL; const char *path; apr_array_header_t *children_with_mergeinfo; + svn_boolean_t elided; if (source) { @@ -2856,9 +2927,15 @@ } /* The final mergeinfo on TARGET_WCPATH may itself elide. */ - SVN_ERR(svn_client__elide_mergeinfo(target_wcpath, NULL, entry, + SVN_ERR(svn_client__elide_mergeinfo(&elided, target_wcpath, NULL, entry, adm_access, ctx, pool)); + /* If no elision occurred the final mergeinfo may still disappear if + if is made up of nothing but empty ranges and overrides no ancestor + mergeinfo. */ + if (!elided) + SVN_ERR(clear_empty_range_mergeinfo(target_wcpath, entry, adm_access, + ctx, pool)); SVN_ERR(svn_wc_adm_close(adm_access)); return SVN_NO_ERROR; Index: subversion/libsvn_client/mergeinfo.h =================================================================== --- subversion/libsvn_client/mergeinfo.h (revision 25101) +++ subversion/libsvn_client/mergeinfo.h (working copy) @@ -50,14 +50,16 @@ svn_wc_adm_access_t *adm_access, apr_pool_t *pool); -/* Elide any svn:mergeinfo set on TARGET_PATH to its nearest working - copy ancestor with equivalent mergeinfo. If ELISION_LIMIT_PATH is NULL - check up to the root of the working copy for elidable mergeinfo, +/* Attempt to elide any svn:mergeinfo set on TARGET_PATH to its nearest + working copy ancestor with equivalent mergeinfo. If ELISION_LIMIT_PATH + is NULL check up to the root of the working copy for elidable mergeinfo, otherwise check as far as ELISION_LIMIT_PATH. TARGET_PATH and ELISION_LIMIT_PATH, if it exists, must both be absolute or relative to - the working directory. */ + the working directory. If ELIDED is not NULL, set *ELIDED to whether + elision occurred (TRUE/FALSE). */ svn_error_t * -svn_client__elide_mergeinfo(const char *target_wcpath, +svn_client__elide_mergeinfo(svn_boolean_t *elided, + const char *target_wcpath, const char *elision_limit_path, const svn_wc_entry_t *entry, svn_wc_adm_access_t *adm_access, Index: subversion/libsvn_client/switch.c =================================================================== --- subversion/libsvn_client/switch.c (revision 25101) +++ subversion/libsvn_client/switch.c (working copy) @@ -239,7 +239,7 @@ child_wcpath = item->key; SVN_ERR(svn_wc__entry_versioned(&child_entry, child_wcpath, adm_access, FALSE, pool)); - SVN_ERR(svn_client__elide_mergeinfo(child_wcpath, NULL, + SVN_ERR(svn_client__elide_mergeinfo(NULL, child_wcpath, NULL, child_entry, adm_access, ctx, pool)); } Index: subversion/libsvn_client/update.c =================================================================== --- subversion/libsvn_client/update.c (revision 25101) +++ subversion/libsvn_client/update.c (working copy) @@ -296,8 +296,9 @@ child_wcpath = item->key; SVN_ERR(svn_wc__entry_versioned(&child_entry, child_wcpath, path_adm_access, FALSE, pool)); - SVN_ERR(svn_client__elide_mergeinfo(child_wcpath, NULL, child_entry, - path_adm_access, ctx, pool)); + SVN_ERR(svn_client__elide_mergeinfo(NULL, child_wcpath, NULL, + child_entry, path_adm_access, + ctx, pool)); } } Index: subversion/tests/cmdline/merge_tests.py =================================================================== --- subversion/tests/cmdline/merge_tests.py (revision 25103) +++ subversion/tests/cmdline/merge_tests.py (working copy) @@ -6058,7 +6058,7 @@ merge_to_switched_path, XFail(merge_to_path_with_switched_children), merge_with_implicit_target_file, - XFail(empty_rev_range_mergeinfo), + empty_rev_range_mergeinfo, ] if __name__ == '__main__':