Index: subversion/include/private/svn_fs_mergeinfo.h
===================================================================
--- subversion/include/private/svn_fs_mergeinfo.h (revision 25324)
+++ subversion/include/private/svn_fs_mergeinfo.h (working copy)
@@ -51,15 +51,18 @@
/* Get the merge info for the set of PATHS (an array of
absolute-in-the-fs paths) under ROOT and return it in *MERGEINFO,
mapping char * paths to char * strings with mergeinfo, allocated in
- POOL. If INCLUDE_PARENTS is TRUE elide for mergeinfo. If a path
- has no mergeinfo, just return no info for that path. Return an
- error if the mergeinfo store does not exist or doesn't use the
- 'mergeinfo' schema. */
+ POOL. If INCLUDE_PARENTS is TRUE look for inherited mergeinfo from a
+ path's nearest ancestor. If PARENTS_ONLY is TRUE ignore mergeinfo on
+ a path and look only for inherited mergeinfo. If PARENTS_ONLY is TRUE
+ the value of INCLUDE_PARENTS is ignored. If a path has no mergeinfo,
+ just return no info for that path. Return an error if the mergeinfo
+ store does not exist or doesn't use the 'mergeinfo' schema. */
svn_error_t *
svn_fs_mergeinfo__get_mergeinfo(apr_hash_t **mergeinfo,
svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool);
/* Get the combined mergeinfo for the tree under each one of PATHS
Index: subversion/include/svn_fs.h
===================================================================
--- subversion/include/svn_fs.h (revision 25324)
+++ subversion/include/svn_fs.h (working copy)
@@ -1205,7 +1205,9 @@
* @a paths indicate the paths you are requesting information for
*
* When @a include_parents is @c TRUE, include inherited merge info
- * from parent directories of @a paths.
+ * from parent directories of @a paths. When @a parents_only is
+ * @c TRUE, ignore @a include_parents and include only inherited
+ * merge info.
*
* Do any necessary temporary allocation in @a pool.
*
@@ -1215,6 +1217,7 @@
svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool);
/** Retrieve combined mergeinfo for multiple nodes, and their children.
Index: subversion/include/svn_ra.h
===================================================================
--- subversion/include/svn_ra.h (revision 25324)
+++ subversion/include/svn_ra.h (working copy)
@@ -791,7 +791,9 @@
* info available. Allocate the returned values in @a pool.
*
* When @a include_parents is @c TRUE, include inherited merge info
- * from parent directories of @a paths.
+ * from parent directories of @a paths. When @a parents_only is
+ * @c TRUE, ignore @a include_parents and include only inherited
+ * merge info.
*
* If @a revision is @c SVN_INVALID_REVNUM, it defaults to youngest.
*
@@ -802,6 +804,7 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool);
/**
Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h (revision 25324)
+++ subversion/include/svn_repos.h (working copy)
@@ -1238,7 +1238,9 @@
* merge info visible or available.
*
* When @a include_parents is @c TRUE, include inherited merge info
- * from parent directories of @a paths.
+ * from parent directories of @a paths. When @a parents_only is
+ * @c TRUE, ignore @a include_parents and include only inherited
+ * merge info.
*
* If @a revision is @c SVN_INVALID_REVNUM, it defaults to youngest.
*
@@ -1257,6 +1259,7 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool);
Index: subversion/libsvn_client/copy.c
===================================================================
--- subversion/libsvn_client/copy.c (revision 25324)
+++ subversion/libsvn_client/copy.c (working copy)
@@ -419,7 +419,7 @@
SVN_ERR(get_implied_mergeinfo(ra_session, target_mergeinfo,
src_rel_path, src_path, src_revnum, pool));
SVN_ERR(svn_client__get_repos_mergeinfo(ra_session, &src_mergeinfo,
- src_path, src_revnum, pool));
+ src_path, src_revnum, FALSE, pool));
/* Combine and return all merge info. */
if (src_mergeinfo)
Index: subversion/libsvn_client/merge.c
===================================================================
--- subversion/libsvn_client/merge.c (revision 25324)
+++ subversion/libsvn_client/merge.c (working copy)
@@ -1038,18 +1038,25 @@
/* Retrieve the direct merge info for the TARGET_WCPATH from the WC's
merge info prop, or that inherited from its nearest ancestor if the
- target has no info of its own. If no merge info can be obtained
- from the WC get if from repository (opening a new RA session if
- RA_SESSION is NULL). Store any merge info obtained for the target
- (reflected by *ENTRY, which is also acquired and returned by this
- function) in *TARGET_MERGEINFO, if no merge info is found
- *TARGET_MERGEINFO is NULL. If TARGET_WCPATH inherited its merge info
- from a working copy ancestor or if it was obtained from the repository,
- set *INDIRECT to TRUE, set it to FALSE *otherwise. */
+ target has no info of its own.
+
+ If no merge info can be obtained from the WC or REPOS_ONLY is TRUE,
+ get it from the repository (opening a new RA session if RA_SESSION is
+ NULL). Store any merge info obtained for the target (reflected by
+ *ENTRY, which is also acquired and returned by this function) in
+ *TARGET_MERGEINFO, if no merge info is found *TARGET_MERGEINFO is NULL.
+
+ If PARENT_ONLY is TRUE only return inherited merge info.
+
+ If TARGET_WCPATH inherited its merge info from a working copy ancestor
+ or if it was obtained from the repository, set *INDIRECT to TRUE, set it
+ to FALSE *otherwise. */
static svn_error_t *
get_wc_or_repos_mergeinfo(apr_hash_t **target_mergeinfo,
const svn_wc_entry_t **entry,
svn_boolean_t *indirect,
+ svn_boolean_t repos_only,
+ svn_boolean_t parent_only,
svn_ra_session_t *ra_session,
const char *target_wcpath,
svn_wc_adm_access_t *adm_access,
@@ -1103,9 +1110,12 @@
### differs from that of TARGET_WCPATH, and if those paths are
### directories, their children as well. */
- SVN_ERR(get_wc_mergeinfo(target_mergeinfo, indirect, FALSE, *entry,
- target_wcpath, NULL, NULL, adm_access, ctx,
- pool));
+ if (repos_only)
+ *target_mergeinfo = NULL;
+ else
+ SVN_ERR(get_wc_mergeinfo(target_mergeinfo, indirect, parent_only, *entry,
+ target_wcpath, NULL, NULL, adm_access, ctx,
+ pool));
/* If there in no WC merge info check the repository. */
if (*target_mergeinfo == NULL)
@@ -1118,7 +1128,7 @@
SVN_ERR(svn_client__get_repos_mergeinfo(ra_session,
&repos_mergeinfo,
repos_rel_path, target_rev,
- pool));
+ parent_only, pool));
if (repos_mergeinfo)
{
@@ -1133,33 +1143,112 @@
/*** Eliding merge info. ***/
-/* Helper for svn_client__elide_mergeinfo and elide_children.
+/* Helper for elide_mergeinfo().
- Compare PARENT_MERGEINFO and CHILD_MERGEINFO to see if they are
- identical (they may be NULL). If PATH_SUFFIX is not NULL append
- PATH_SUFFIX to each path in PARENT_MERGEINFO before performing the
- comparison.
-
- Set *ELIDES to whether PARENT_MERGEINFO and CHILD_MERGEINFO are
- identical (TRUE or FALSE). */
+ Find all paths in CHILD_MERGEINFO which map to empty revision ranges
+ and copy these from CHILD_MERGEINFO to *EMPTY_RANGE_MERGEINFO iff
+ PARENT_MERGEINFO is NULL or does not have merge info for the path in
+ question.
+
+ All mergeinfo in CHILD_MERGEINFO not copied to *EMPTY_RANGE_MERGEINFO
+ is copied to *NONEMPTY_RANGE_MERGEINFO.
+
+ *EMPTY_RANGE_MERGEINFO and *NONEMPTY_RANGE_MERGEINFO are set to empty
+ hashes if nothing is copied into them.
+
+ All copied hashes are deep copies allocated in POOL. */
static svn_error_t *
-mergeinfo_elides(svn_boolean_t *elides,
- apr_hash_t *parent_mergeinfo,
- apr_hash_t *child_mergeinfo,
- const char *path_suffix,
- apr_pool_t *pool)
+get_child_only_empty_revs(apr_hash_t **empty_range_mergeinfo,
+ apr_hash_t **nonempty_range_mergeinfo,
+ apr_hash_t *child_mergeinfo,
+ apr_hash_t *parent_mergeinfo,
+ apr_pool_t *pool)
{
- apr_pool_t *subpool = svn_pool_create(pool);
- apr_hash_t *mergeinfo;
+ *empty_range_mergeinfo = apr_hash_make(pool);
+ *nonempty_range_mergeinfo = apr_hash_make(pool);
- if (parent_mergeinfo == NULL || child_mergeinfo == NULL ||
- apr_hash_count(parent_mergeinfo) != apr_hash_count(child_mergeinfo))
+ if (child_mergeinfo)
{
- *elides = FALSE;
- return SVN_NO_ERROR;
+ apr_hash_index_t *hi;
+ void *child_val;
+ const void *child_key;
+ apr_array_header_t *child_range;
+ const char *child_path;
+
+ /* Iterate through CHILD_MERGEINFO looking for merge info with empty
+ revision ranges. */
+ for (hi = apr_hash_first(pool, child_mergeinfo); hi;
+ hi = apr_hash_next(hi))
+ {
+ apr_hash_this(hi, &child_key, NULL, &child_val);
+ child_path = child_key;
+ child_range = child_val;
+
+ /* Copy paths with empty revision ranges which don't exist in
+ PARENT_MERGEINFO from CHILD_MERGEINFO to *EMPTY_RANGE_MERGEINFO.
+ Copy everything else to *NONEMPTY_RANGE_MERGEINFO. */
+ if (child_range->nelts == 0
+ && (parent_mergeinfo == NULL
+ || apr_hash_get(parent_mergeinfo, child_path,
+ APR_HASH_KEY_STRING) == NULL))
+ {
+ apr_hash_set(*empty_range_mergeinfo,
+ apr_pstrdup(pool, child_path),
+ APR_HASH_KEY_STRING,
+ svn_rangelist_dup(child_range, pool));
+ }
+ else
+ {
+ apr_hash_set(*nonempty_range_mergeinfo,
+ apr_pstrdup(pool, child_path),
+ APR_HASH_KEY_STRING,
+ svn_rangelist_dup(child_range, pool));
+ }
+ }
}
+ return SVN_NO_ERROR;
+}
- if (path_suffix)
+/* Helper for svn_client__elide_mergeinfo() and elide_children().
+
+ Given a working copy PATH, its mergeinfo hash CHILD_MERGEINFO, and the
+ mergeinfo of PATH's nearest ancestor PARENT_MERGEINFO, compare
+ CHILD_MERGEINFO to PARENT_MERGEINFO to see if the former elides to
+ the latter, following the elision rules described in
+ svn_client__elide_mergeinfo()'s docstring. If elision (full or partial)
+ does occur, then update PATH's mergeinfo appropriately. If CHILD_MERGEINFO
+ is NULL, do nothing.
+
+ If PATH_SUFFIX and PARENT_MERGEINFO are not NULL append PATH_SUFFIX to each
+ path in PARENT_MERGEINFO before performing the comparison. */
+static svn_error_t *
+elide_mergeinfo(apr_hash_t *parent_mergeinfo,
+ apr_hash_t *child_mergeinfo,
+ const char *path,
+ const char *path_suffix,
+ svn_wc_adm_access_t *adm_access,
+ apr_pool_t *pool)
+{
+ apr_pool_t *subpool;
+ apr_hash_t *mergeinfo, *child_empty_mergeinfo, *child_nonempty_mergeinfo;
+ svn_boolean_t equal_mergeinfo;
+
+ /* A tri-state value describing the various types of elision possible for
+ svn:mergeinfo set on a WC path. */
+ enum wc_elision_type
+ {
+ elision_type_none, /* No elision occurs. */
+ elision_type_partial, /* Paths that exist only in CHILD_MERGEINFO and
+ map to empty revision ranges elide. */
+ elision_type_full /* All merge info in CHILD_MERGEINFO elides. */
+ } elision_type = elision_type_none;
+
+ /* Easy out: No child merge info to elide. */
+ if (child_mergeinfo == NULL)
+ return SVN_NO_ERROR;
+
+ subpool = svn_pool_create(pool);
+ if (path_suffix && parent_mergeinfo)
{
apr_hash_index_t *hi;
void *val;
@@ -1173,9 +1262,8 @@
hi = apr_hash_next(hi))
{
apr_hash_this(hi, &key, NULL, &val);
- path = svn_path_join((const char *) key, path_suffix, pool);
+ path = svn_path_join((const char *) key, path_suffix, subpool);
rangelist = val;
-
apr_hash_set(mergeinfo, path, APR_HASH_KEY_STRING, rangelist);
}
}
@@ -1184,9 +1272,76 @@
mergeinfo = parent_mergeinfo;
}
- SVN_ERR(svn_mergeinfo__equals(elides, child_mergeinfo, mergeinfo,
- subpool));
+ /* Separate any merge info with empty rev ranges for paths that exist only
+ in CHILD_MERGEINFO and store these in CHILD_EMPTY_MERGEINFO. */
+ SVN_ERR(get_child_only_empty_revs(&child_empty_mergeinfo,
+ &child_nonempty_mergeinfo,
+ child_mergeinfo, mergeinfo, subpool));
+
+ /* If *all* paths in CHILD_MERGEINFO map to empty revision ranges and none
+ of these paths exist in PARENT_MERGEINFO full elision occurs; if only
+ *some* of the paths in CHILD_MERGEINFO meet this criteria we know, at a
+ minimum, partial elision will occur. */
+ if (apr_hash_count(child_empty_mergeinfo) > 0)
+ elision_type = apr_hash_count(child_nonempty_mergeinfo) == 0
+ ? elision_type_full : elision_type_partial;
+ if (elision_type == elision_type_none && mergeinfo)
+ {
+ apr_hash_t *parent_empty_mergeinfo, *parent_nonempty_mergeinfo;
+
+ /* Full elision also occurs if MERGEINFO and TARGET_MERGEINFO are
+ equal except for paths unique to MERGEINFO that map to empty
+ revision ranges.
+
+ Separate any merge info with empty rev ranges for paths that exist
+ only in MERGEINFO and store these in PARENT_EMPTY_MERGEINFO and
+ compare that with CHILD_MERGEINFO. */
+ SVN_ERR(get_child_only_empty_revs(&parent_empty_mergeinfo,
+ &parent_nonempty_mergeinfo,
+ mergeinfo, child_mergeinfo,
+ subpool));
+ SVN_ERR(svn_mergeinfo__equals(&equal_mergeinfo,
+ parent_nonempty_mergeinfo,
+ child_mergeinfo, subpool));
+ if (equal_mergeinfo)
+ elision_type = elision_type_full;
+ }
+
+ if (elision_type != elision_type_full && mergeinfo)
+ {
+ /* If no determination of elision status has been made yet or we know
+ only that partial elision occurs, compare CHILD_NONEMPTY_MERGEINFO
+ with the PATH_SUFFIX tweaked version of PARENT_MERGEINFO for equality.
+
+ If we determined that at least partial elision occurs, full elision
+ may still yet occur if CHILD_NONEMPTY_MERGEINFO, which no longer
+ contains any paths unique to it that map to empty revision ranges,
+ is equivalent to PARENT_MERGEINFO. */
+ svn_boolean_t equal_mergeinfo;
+
+ SVN_ERR(svn_mergeinfo__equals(&equal_mergeinfo,
+ child_nonempty_mergeinfo,
+ mergeinfo, subpool));
+ if (equal_mergeinfo)
+ elision_type = elision_type_full;
+ }
+
+ switch (elision_type)
+ {
+ case elision_type_full:
+ SVN_ERR(svn_wc_prop_set2(SVN_PROP_MERGE_INFO, NULL, path, adm_access,
+ TRUE, subpool));
+ break;
+ case elision_type_partial:
+ SVN_ERR(svn_client__record_wc_mergeinfo(path,
+ child_nonempty_mergeinfo,
+ adm_access, subpool));
+ break;
+ default:
+ break; /* Leave mergeinfo on PATH as-is. */
+ }
+
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
@@ -1226,7 +1381,7 @@
{
apr_hash_t *child_mergeinfo;
const char *child_wcpath;
- svn_boolean_t elides, switched;
+ svn_boolean_t switched;
const svn_wc_entry_t *child_entry;
svn_sort__item_t *item = &APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_sort__item_t);
@@ -1283,13 +1438,9 @@
path_prefix = svn_path_dirname(path_prefix, iterpool);
}
- SVN_ERR(mergeinfo_elides(&elides, target_mergeinfo,
- child_mergeinfo, path_suffix,
- iterpool));
- if (elides)
- SVN_ERR(svn_wc_prop_set2(SVN_PROP_MERGE_INFO, NULL,
- child_wcpath, adm_access, TRUE,
- iterpool));
+ SVN_ERR(elide_mergeinfo(target_mergeinfo, child_mergeinfo,
+ child_wcpath, path_suffix, adm_access,
+ iterpool));
}
}
svn_pool_destroy(iterpool);
@@ -1300,18 +1451,19 @@
svn_error_t *
svn_client__elide_mergeinfo(const char *target_wcpath,
- const char *elision_limit_path,
+ const char *wc_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)
{
/* Check for first easy out: We are already at the limit path. */
- if (!elision_limit_path || strcmp(target_wcpath, elision_limit_path) != 0)
+ if (!wc_elision_limit_path
+ || strcmp(target_wcpath, wc_elision_limit_path) != 0)
{
apr_hash_t *target_mergeinfo;
apr_hash_t *mergeinfo = NULL;
- svn_boolean_t inherited, elides, switched;
+ svn_boolean_t inherited, switched;
const char *walk_path;
/* Check for second easy out: TARGET_WCPATH is switched. */
@@ -1321,8 +1473,8 @@
/* Get the TARGET_WCPATH's explicit mergeinfo. */
SVN_ERR(get_wc_mergeinfo(&target_mergeinfo, &inherited, FALSE,
entry, target_wcpath,
- elision_limit_path
- ? elision_limit_path : NULL,
+ wc_elision_limit_path
+ ? wc_elision_limit_path : NULL,
&walk_path, adm_access, ctx, pool));
/* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to
@@ -1330,23 +1482,33 @@
if (inherited || target_mergeinfo == NULL)
return SVN_NO_ERROR;
- /* Get TARGET_WCPATH's inherited mergeinfo. */
+ /* Get TARGET_WCPATH's inherited mergeinfo from the WC. */
SVN_ERR(get_wc_mergeinfo(&mergeinfo, &inherited, TRUE, entry,
target_wcpath,
- elision_limit_path
- ? elision_limit_path : NULL,
+ wc_elision_limit_path
+ ? wc_elision_limit_path : NULL,
&walk_path, adm_access, ctx, pool));
- /* If TARGET_WCPATH has no inherited mergeinfo, there's
- nowhere to elide to, we're done. */
- if (mergeinfo == NULL)
+ /* If TARGET_WCPATH inherited no mergeinfo from the WC and we are
+ not limiting our search to the working copy then check if it
+ inherits any from the repos. */
+ if (!mergeinfo && !wc_elision_limit_path)
+ {
+ const svn_wc_entry_t *tmp_entry;
+
+ SVN_ERR(get_wc_or_repos_mergeinfo(&mergeinfo, &tmp_entry,
+ &inherited, TRUE, TRUE,
+ NULL, target_wcpath,
+ adm_access, ctx, pool));
+ }
+
+ /* If there is nowhere to elide TARGET_WCPATH's mergeinfo to and
+ the elision is limited, then we are done.*/
+ if (!mergeinfo && wc_elision_limit_path)
return SVN_NO_ERROR;
- 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(elide_mergeinfo(mergeinfo, target_mergeinfo, target_wcpath,
+ NULL, adm_access, pool));
}
}
return SVN_NO_ERROR;
@@ -1601,8 +1763,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)
@@ -1869,7 +2029,7 @@
if (notify_b.same_urls)
{
SVN_ERR(get_wc_or_repos_mergeinfo(&target_mergeinfo, &entry,
- &indirect, ra_session,
+ &indirect, FALSE, FALSE, ra_session,
target_wcpath, adm_access,
ctx, pool));
@@ -2228,7 +2388,7 @@
return SVN_NO_ERROR;
SVN_ERR(get_wc_or_repos_mergeinfo(&target_mergeinfo, &entry,
- &indirect, ra_session1,
+ &indirect, FALSE, FALSE, ra_session1,
target_wcpath, adm_access,
ctx, pool));
@@ -2978,7 +3138,8 @@
SVN_ERR(svn_client__path_relative_to_root(&repos_rel_path, path_or_url,
NULL, ra_session, NULL, pool));
SVN_ERR(svn_client__get_repos_mergeinfo(ra_session, mergeinfo,
- repos_rel_path, rev, pool));
+ repos_rel_path, rev, FALSE,
+ pool));
}
else
{
@@ -2989,8 +3150,8 @@
SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, path_or_url, FALSE,
0, ctx->cancel_func, ctx->cancel_baton,
pool));
- SVN_ERR(get_wc_or_repos_mergeinfo(mergeinfo, &entry, &indirect,
- NULL, path_or_url, adm_access,
+ SVN_ERR(get_wc_or_repos_mergeinfo(mergeinfo, &entry, &indirect, FALSE,
+ FALSE, NULL, path_or_url, adm_access,
ctx, pool));
SVN_ERR(svn_wc_adm_close(adm_access));
}
Index: subversion/libsvn_client/mergeinfo.c
===================================================================
--- subversion/libsvn_client/mergeinfo.c (revision 25324)
+++ subversion/libsvn_client/mergeinfo.c (working copy)
@@ -38,13 +38,14 @@
apr_hash_t **target_mergeinfo,
const char *rel_path,
svn_revnum_t rev,
+ svn_boolean_t inherited_only,
apr_pool_t *pool)
{
apr_hash_t *repos_mergeinfo;
apr_array_header_t *rel_paths = apr_array_make(pool, 1, sizeof(rel_path));
APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
SVN_ERR(svn_ra_get_mergeinfo(ra_session, &repos_mergeinfo, rel_paths,
- rev, TRUE, pool));
+ rev, TRUE, inherited_only, pool));
/* Grab only the merge info provided for REL_PATH. */
if (repos_mergeinfo)
Index: subversion/libsvn_client/mergeinfo.h
===================================================================
--- subversion/libsvn_client/mergeinfo.h (revision 25324)
+++ subversion/libsvn_client/mergeinfo.h (working copy)
@@ -28,6 +28,7 @@
apr_hash_t **target_mergeinfo,
const char *rel_path,
svn_revnum_t rev,
+ svn_boolean_t inherited_only,
apr_pool_t *pool);
/* Parse any merge info from the WCPATH's ENTRY and store it in
@@ -51,14 +52,38 @@
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,
- 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. */
+ copy (or possibly repository) ancestor with equivalent mergeinfo.
+
+ If WC_ELISION_LIMIT_PATH is NULL check up to the root of the working copy
+ for an elision destination, if none is found check the repository,
+ otherwise check as far as WC_ELISION_LIMIT_PATH within the working copy.
+ TARGET_PATH and WC_ELISION_LIMIT_PATH, if it exists, must both be absolute
+ or relative to the working directory.
+
+ If TARGET_WCPATH's merge info and its nearest ancestor's merge info
+ differ by paths existing only in TARGET_PATH's merge info that map to
+ empty revision ranges, then the merge info between the two is considered
+ equivalent and elision occurs. If the merge info between the two still
+ differs then partial elision occurs: only the paths mapped to empty
+ revision ranges in TARGET_WCPATH's merge info elide.
+
+ If TARGET_WCPATH's merge info and its nearest ancestor's merge info
+ differ by paths existing only in the ancestor's merge info that map to
+ empty revision ranges, then the merge info between the two is considered
+ equivalent and elision occurs.
+
+ If TARGET_WCPATH's merge info consists only of paths mapped to empty
+ revision ranges and none of these paths exist in TARGET_WCPATH's nearest
+ ancestor, then elision occurs.
+
+ If TARGET_WCPATH's merge info consists only of paths mapped to empty
+ revision ranges and TARGET_WCPATH has no working copy or repository
+ ancestor with merge info (WC_ELISION_LIMIT_PATH must be NULL to ensure the
+ repository is checked), then elision occurs.
+ */
svn_error_t *
svn_client__elide_mergeinfo(const char *target_wcpath,
- const char *elision_limit_path,
+ const char *wc_elision_limit_path,
const svn_wc_entry_t *entry,
svn_wc_adm_access_t *adm_access,
svn_client_ctx_t *ctx,
Index: subversion/libsvn_fs/fs-loader.c
===================================================================
--- subversion/libsvn_fs/fs-loader.c (revision 25324)
+++ subversion/libsvn_fs/fs-loader.c (working copy)
@@ -811,10 +811,11 @@
svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
return root->vtable->get_mergeinfo(minfohash, root, paths, include_parents,
- pool);
+ parents_only, pool);
}
svn_error_t *
Index: subversion/libsvn_fs/fs-loader.h
===================================================================
--- subversion/libsvn_fs/fs-loader.h (revision 25324)
+++ subversion/libsvn_fs/fs-loader.h (working copy)
@@ -311,6 +311,7 @@
svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool);
svn_error_t *(*get_mergeinfo_for_tree)(apr_hash_t **mergeinfo,
svn_fs_root_t *root,
Index: subversion/libsvn_fs_util/mergeinfo-sqlite-index.c
===================================================================
--- subversion/libsvn_fs_util/mergeinfo-sqlite-index.c (revision 25324)
+++ subversion/libsvn_fs_util/mergeinfo-sqlite-index.c (working copy)
@@ -199,6 +199,7 @@
apr_hash_t *result,
apr_hash_t *cache,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool);
/* Represents "no merge info". */
@@ -232,7 +233,7 @@
apr_hash_t *cache = apr_hash_make(pool);
remove_mergeinfo = TRUE;
SVN_ERR(get_mergeinfo_for_path(db, path, new_rev, mergeinfo, cache,
- TRUE, pool));
+ TRUE, FALSE, pool));
mergeinfo = apr_hash_get(mergeinfo, path, APR_HASH_KEY_STRING);
if (mergeinfo == NULL)
/* There was previously no merge info, inherited or explicit,
@@ -511,6 +512,7 @@
apr_hash_t *result,
apr_hash_t *cache,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
apr_hash_t *path_mergeinfo;
@@ -518,50 +520,56 @@
int sqlite_result;
sqlite_int64 lastmerged_rev;
- path_mergeinfo = apr_hash_get(cache, path, APR_HASH_KEY_STRING);
- if (path_mergeinfo != NULL)
+ if (!parents_only)
{
- if (path_mergeinfo != NEGATIVE_CACHE_RESULT && result)
- apr_hash_set(result, path, APR_HASH_KEY_STRING, path_mergeinfo);
- return SVN_NO_ERROR;
- }
+ path_mergeinfo = apr_hash_get(cache, path, APR_HASH_KEY_STRING);
+ if (path_mergeinfo != NULL)
+ {
+ if (path_mergeinfo != NEGATIVE_CACHE_RESULT && result)
+ apr_hash_set(result, path, APR_HASH_KEY_STRING, path_mergeinfo);
+ return SVN_NO_ERROR;
+ }
- /* See if we have a mergeinfo_changed record for this path. If not,
- then it can't have mergeinfo. */
- SQLITE_ERR(sqlite3_prepare(db,
- "SELECT MAX(revision) FROM mergeinfo_changed "
- "WHERE path = ? AND revision <= ?;",
- -1, &stmt, NULL), db);
+ /* See if we have a mergeinfo_changed record for this path. If not,
+ then it can't have mergeinfo. */
+ SQLITE_ERR(sqlite3_prepare(db,
+ "SELECT MAX(revision) FROM mergeinfo_changed "
+ "WHERE path = ? AND revision <= ?;",
+ -1, &stmt, NULL), db);
- SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 2, rev), db);
- sqlite_result = sqlite3_step(stmt);
- if (sqlite_result != SQLITE_ROW)
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- sqlite3_errmsg(db));
+ SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 2, rev), db);
+ sqlite_result = sqlite3_step(stmt);
+ if (sqlite_result != SQLITE_ROW)
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ sqlite3_errmsg(db));
- lastmerged_rev = sqlite3_column_int64(stmt, 0);
- SQLITE_ERR(sqlite3_finalize(stmt), db);
+ lastmerged_rev = sqlite3_column_int64(stmt, 0);
+ SQLITE_ERR(sqlite3_finalize(stmt), db);
- /* If we've got mergeinfo data, transform it from the db into a
- mergeinfo hash */
- if (lastmerged_rev > 0)
- {
- SVN_ERR(parse_mergeinfo_from_db(db, path, lastmerged_rev,
- &path_mergeinfo, pool));
- if (path_mergeinfo)
+ /* If we've got mergeinfo data, transform it from the db into a
+ mergeinfo hash */
+ if (lastmerged_rev > 0)
{
- if (result)
- apr_hash_set(result, path, APR_HASH_KEY_STRING, path_mergeinfo);
- apr_hash_set(cache, path, APR_HASH_KEY_STRING, path_mergeinfo);
+ SVN_ERR(parse_mergeinfo_from_db(db, path, lastmerged_rev,
+ &path_mergeinfo, pool));
+ if (path_mergeinfo)
+ {
+ if (result)
+ apr_hash_set(result, path, APR_HASH_KEY_STRING,
+ path_mergeinfo);
+ apr_hash_set(cache, path, APR_HASH_KEY_STRING, path_mergeinfo);
+ }
+ else
+ apr_hash_set(cache, path, APR_HASH_KEY_STRING,
+ NEGATIVE_CACHE_RESULT);
+ return SVN_NO_ERROR;
}
- else
- apr_hash_set(cache, path, APR_HASH_KEY_STRING, NEGATIVE_CACHE_RESULT);
- return SVN_NO_ERROR;
- }
+ } /* !parents_only */
- /* If this path has no mergeinfo, and we are asked to, check our parent */
- if (lastmerged_rev == 0 && include_parents)
+ /* If we want only this path's parent's mergeinfo or this path has no
+ mergeinfo, and we are asked to, check our parent */
+ if ((lastmerged_rev == 0 && include_parents) || parents_only)
{
svn_stringbuf_t *parentpath;
@@ -579,7 +587,7 @@
SVN_ERR(get_mergeinfo_for_path(db, parentpath->data, rev,
NULL, cache, include_parents,
- pool));
+ FALSE, pool));
path_mergeinfo = apr_hash_get(cache, parentpath->data,
APR_HASH_KEY_STRING);
if (path_mergeinfo == NEGATIVE_CACHE_RESULT)
@@ -669,6 +677,7 @@
svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
apr_hash_t *mergeinfo_cache = apr_hash_make(pool);
@@ -686,7 +695,8 @@
{
const char *path = APR_ARRAY_IDX(paths, i, const char *);
SVN_ERR(get_mergeinfo_for_path(db, path, rev, *mergeinfo,
- mergeinfo_cache, include_parents, pool));
+ mergeinfo_cache, include_parents,
+ parents_only, pool));
}
return SVN_NO_ERROR;
@@ -698,13 +708,15 @@
svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
sqlite3 *db;
int i;
SVN_ERR(open_db(&db, root->fs->path, pool));
- SVN_ERR(get_mergeinfo(db, mergeinfo, root, paths, include_parents, pool));
+ SVN_ERR(get_mergeinfo(db, mergeinfo, root, paths, include_parents,
+ parents_only, pool));
SQLITE_ERR(sqlite3_close(db), db);
for (i = 0; i < paths->nelts; i++)
@@ -739,7 +751,7 @@
int i;
SVN_ERR(open_db(&db, root->fs->path, pool));
- SVN_ERR(get_mergeinfo(db, mergeinfo, root, paths, TRUE, pool));
+ SVN_ERR(get_mergeinfo(db, mergeinfo, root, paths, TRUE, FALSE, pool));
rev = REV_ROOT_REV(root);
Index: subversion/libsvn_ra/ra_loader.c
===================================================================
--- subversion/libsvn_ra/ra_loader.c (revision 25324)
+++ subversion/libsvn_ra/ra_loader.c (working copy)
@@ -591,10 +591,12 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
return session->vtable->get_mergeinfo(session, mergeinfo, paths,
- revision, include_parents, pool);
+ revision, include_parents,
+ parents_only, pool);
}
svn_error_t *svn_ra_do_update2(svn_ra_session_t *session,
Index: subversion/libsvn_ra/ra_loader.h
===================================================================
--- subversion/libsvn_ra/ra_loader.h (revision 25324)
+++ subversion/libsvn_ra/ra_loader.h (working copy)
@@ -109,6 +109,7 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool);
svn_error_t *(*do_update)(svn_ra_session_t *session,
const svn_ra_reporter3_t **reporter,
Index: subversion/libsvn_ra_dav/mergeinfo.c
===================================================================
--- subversion/libsvn_ra_dav/mergeinfo.c (revision 25324)
+++ subversion/libsvn_ra_dav/mergeinfo.c (working copy)
@@ -156,6 +156,7 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
svn_error_t *err;
@@ -182,6 +183,13 @@
""));
}
+ if (parents_only)
+ {
+ svn_stringbuf_appendcstr(request_body,
+ apr_psprintf(pool,
+ ""));
+ }
+
if (paths)
{
for (i = 0; i < paths->nelts; i++)
Index: subversion/libsvn_ra_dav/ra_dav.h
===================================================================
--- subversion/libsvn_ra_dav/ra_dav.h (revision 25324)
+++ subversion/libsvn_ra_dav/ra_dav.h (working copy)
@@ -246,6 +246,7 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool);
svn_error_t * svn_ra_dav__do_update(svn_ra_session_t *session,
Index: subversion/libsvn_ra_local/ra_plugin.c
===================================================================
--- subversion/libsvn_ra_local/ra_plugin.c (revision 25324)
+++ subversion/libsvn_ra_local/ra_plugin.c (working copy)
@@ -645,13 +645,14 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
svn_ra_local__session_baton_t *baton = session->priv;
apr_hash_t *tmp_mergeinfo;
SVN_ERR(svn_repos_fs_get_mergeinfo(&tmp_mergeinfo, baton->repos, paths,
- revision, include_parents,
+ revision, include_parents, parents_only,
NULL, NULL, pool));
if (tmp_mergeinfo != NULL && apr_hash_count(tmp_mergeinfo) > 0)
{
Index: subversion/libsvn_ra_serf/mergeinfo.c
===================================================================
--- subversion/libsvn_ra_serf/mergeinfo.c (revision 25324)
+++ subversion/libsvn_ra_serf/mergeinfo.c (working copy)
@@ -163,6 +163,7 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
svn_error_t *err;
@@ -201,6 +202,12 @@
NULL, session->bkt_alloc);
}
+ if (parents_only)
+ {
+ svn_ra_serf__add_tag_buckets(buckets, "S:parents-only",
+ NULL, session->bkt_alloc);
+ }
+
if (paths)
{
for (i = 0; i < paths->nelts; i++)
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c (revision 25324)
+++ subversion/libsvn_ra_svn/client.c (working copy)
@@ -1022,6 +1022,7 @@
const apr_array_header_t *paths,
svn_revnum_t revision,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
apr_pool_t *pool)
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
@@ -1044,8 +1045,8 @@
path = APR_ARRAY_IDX(paths, i, const char *);
SVN_ERR(svn_ra_svn_write_cstring(conn, pool, path));
}
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(?r)b)", revision,
- include_parents));
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(?r)bb)", revision,
+ include_parents, parents_only));
SVN_ERR(handle_auth_request(sess_baton, pool));
SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "(?l)", &mergeinfo_tuple));
Index: subversion/libsvn_repos/fs-wrap.c
===================================================================
--- subversion/libsvn_repos/fs-wrap.c (revision 25324)
+++ subversion/libsvn_repos/fs-wrap.c (working copy)
@@ -556,6 +556,7 @@
const apr_array_header_t *paths,
svn_revnum_t rev,
svn_boolean_t include_parents,
+ svn_boolean_t parents_only,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
@@ -606,7 +607,7 @@
the change itself. */
if (readable_paths->nelts > 0)
SVN_ERR(svn_fs_get_mergeinfo(mergeinfo, root, readable_paths,
- include_parents, pool));
+ include_parents, parents_only, pool));
else
*mergeinfo = NULL;
Index: subversion/mod_dav_svn/reports/mergeinfo.c
===================================================================
--- subversion/mod_dav_svn/reports/mergeinfo.c (revision 25324)
+++ subversion/mod_dav_svn/reports/mergeinfo.c (working copy)
@@ -53,6 +53,7 @@
/* These get determined from the request document. */
svn_revnum_t rev = SVN_INVALID_REVNUM;
svn_boolean_t include_parents = FALSE; /* off by default */
+ svn_boolean_t parents_only = FALSE; /* off by default */
apr_array_header_t *paths
= apr_array_make(resource->pool, 0, sizeof(const char *));
@@ -78,6 +79,8 @@
rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
else if (strcmp(child->name, "include-parents") == 0)
include_parents = 1;
+ else if (strcmp(child->name, "parents-only") == 0)
+ parents_only = 1;
else if (strcmp(child->name, "path") == 0)
{
const char *target;
@@ -100,7 +103,7 @@
serr = svn_repos_fs_get_mergeinfo(&mergeinfo, repos->repos, paths, rev,
- include_parents,
+ include_parents, parents_only,
dav_svn__authz_read_func(&arb),
&arb, resource->pool);
if (serr)
Index: subversion/svnserve/serve.c
===================================================================
--- subversion/svnserve/serve.c (revision 25324)
+++ subversion/svnserve/serve.c (working copy)
@@ -1457,10 +1457,10 @@
int i;
apr_hash_index_t *hi;
const char *path, *info;
- svn_boolean_t include_parents;
+ svn_boolean_t include_parents, parents_only;
- SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "l(?r)b", &paths, &rev,
- &include_parents));
+ SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "l(?r)bb", &paths, &rev,
+ &include_parents, &parents_only));
/* Canonicalize the paths which merge info has been requested for. */
canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
@@ -1478,7 +1478,7 @@
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repos,
canonical_paths, rev,
- include_parents,
+ include_parents, parents_only,
authz_check_access_cb_func(b), b,
pool));
if (mergeinfo != NULL && apr_hash_count(mergeinfo) > 0)
Index: subversion/tests/cmdline/merge_tests.py
===================================================================
--- subversion/tests/cmdline/merge_tests.py (revision 25324)
+++ subversion/tests/cmdline/merge_tests.py (working copy)
@@ -6833,7 +6833,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,
XFail(detect_copy_src_for_target_with_multiple_ancestors),
prop_add_to_child_with_mergeinfo,
diff_repos_does_not_update_mergeinfo,
Index: subversion/tests/libsvn_fs/fs-test.c
===================================================================
--- subversion/tests/libsvn_fs/fs-test.c (revision 25324)
+++ subversion/tests/libsvn_fs/fs-test.c (working copy)
@@ -4489,10 +4489,12 @@
paths = apr_array_make(pool, 1, sizeof (const char *));
APR_ARRAY_PUSH(paths, const char *) = "/A/E";
- SVN_ERR(svn_fs_get_mergeinfo(&result, revision_root, paths, TRUE, pool));
+ SVN_ERR(svn_fs_get_mergeinfo(&result, revision_root, paths, TRUE, FALSE,
+ pool));
paths = apr_array_make(pool, 1, sizeof (const char *));
APR_ARRAY_PUSH(paths, const char *) = "/A/B/E";
- SVN_ERR(svn_fs_get_mergeinfo(&result, revision_root, paths, TRUE, pool));
+ SVN_ERR(svn_fs_get_mergeinfo(&result, revision_root, paths, TRUE, FALSE,
+ pool));
return SVN_NO_ERROR;
}