You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2011/12/19 19:49:43 UTC
svn commit: r1220893 [6/19] - in /subversion/branches/fs-py: ./ build/
build/ac-macros/ build/generator/ build/generator/templates/ build/win32/
contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/ notes/
subversion/bindings/javahl/native/ su...
Modified: subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.c Mon Dec 19 18:49:34 2011
@@ -38,6 +38,7 @@
#include "svn_client.h"
#include "svn_hash.h"
+#include "private/svn_opt_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_ra_private.h"
@@ -67,6 +68,16 @@ svn_client__merge_path_dup(const svn_cli
return new;
}
+svn_client__merge_path_t *
+svn_client__merge_path_create(const char *abspath,
+ apr_pool_t *pool)
+{
+ svn_client__merge_path_t *result = apr_pcalloc(pool, sizeof(*result));
+
+ result->abspath = apr_pstrdup(pool, abspath);
+ return result;
+}
+
svn_error_t *
svn_client__parse_mergeinfo(svn_mergeinfo_t *mergeinfo,
svn_wc_context_t *wc_ctx,
@@ -136,42 +147,56 @@ svn_client__record_wc_mergeinfo(const ch
return SVN_NO_ERROR;
}
-/*-----------------------------------------------------------------------*/
-
-/*** Retrieving mergeinfo. ***/
-
svn_error_t *
-svn_client__adjust_mergeinfo_source_paths(svn_mergeinfo_t adjusted_mergeinfo,
- const char *rel_path,
- svn_mergeinfo_t mergeinfo,
- apr_pool_t *pool)
+svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- apr_hash_index_t *hi;
- const char *path;
- apr_array_header_t *copied_rangelist;
-
- SVN_ERR_ASSERT(adjusted_mergeinfo);
- SVN_ERR_ASSERT(mergeinfo);
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
+ if (apr_hash_count(result_catalog))
{
- const char *merge_source = svn__apr_hash_index_key(hi);
- apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
-
- /* Copy inherited mergeinfo into our output hash, adjusting the
- merge source as appropriate. */
- path = svn_fspath__join(merge_source, rel_path, pool);
- copied_rangelist = svn_rangelist_dup(rangelist, pool);
- apr_hash_set(adjusted_mergeinfo, path, APR_HASH_KEY_STRING,
- copied_rangelist);
+ int i;
+ apr_array_header_t *sorted_cat =
+ svn_sort__hash(result_catalog, svn_sort_compare_items_as_paths,
+ scratch_pool);
+ for (i = 0; i < sorted_cat->nelts; i++)
+ {
+ svn_sort__item_t elt = APR_ARRAY_IDX(sorted_cat, i,
+ svn_sort__item_t);
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+ err = svn_client__record_wc_mergeinfo(elt.key, elt.value, TRUE,
+ ctx, iterpool);
+
+ if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
+ {
+ /* PATH isn't just missing, it's not even versioned as far
+ as this working copy knows. But it was included in
+ MERGES, which means that the server knows about it.
+ Likely we don't have access to the source due to authz
+ restrictions. For now just clear the error and
+ continue... */
+ svn_error_clear(err);
+ }
+ else
+ {
+ SVN_ERR(err);
+ }
+ }
}
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
+/*-----------------------------------------------------------------------*/
+
+/*** Retrieving mergeinfo. ***/
svn_error_t *
svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo,
- svn_boolean_t *inherited,
+ svn_boolean_t *inherited_p,
svn_mergeinfo_inheritance_t inherit,
const char *local_abspath,
const char *limit_abspath,
@@ -185,6 +210,7 @@ svn_client__get_wc_mergeinfo(svn_mergein
svn_mergeinfo_t wc_mergeinfo;
svn_revnum_t base_revision;
apr_pool_t *iterpool;
+ svn_boolean_t inherited;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (limit_abspath)
@@ -287,7 +313,7 @@ svn_client__get_wc_mergeinfo(svn_mergein
if (svn_path_is_empty(walk_relpath))
{
/* Mergeinfo is explicit. */
- *inherited = FALSE;
+ inherited = FALSE;
*mergeinfo = wc_mergeinfo;
}
else
@@ -295,16 +321,16 @@ svn_client__get_wc_mergeinfo(svn_mergein
/* Mergeinfo may be inherited. */
if (wc_mergeinfo)
{
- *inherited = TRUE;
- *mergeinfo = apr_hash_make(result_pool);
- SVN_ERR(svn_client__adjust_mergeinfo_source_paths(*mergeinfo,
- walk_relpath,
- wc_mergeinfo,
- result_pool));
+ inherited = TRUE;
+ SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(mergeinfo,
+ wc_mergeinfo,
+ walk_relpath,
+ result_pool,
+ scratch_pool));
}
else
{
- *inherited = FALSE;
+ inherited = FALSE;
*mergeinfo = NULL;
}
}
@@ -314,7 +340,7 @@ svn_client__get_wc_mergeinfo(svn_mergein
/* Remove non-inheritable mergeinfo and paths mapped to empty ranges
which may occur if WCPATH's mergeinfo is not explicit. */
- if (*inherited
+ if (inherited
&& apr_hash_count(*mergeinfo)) /* Nothing to do for empty mergeinfo. */
{
SVN_ERR(svn_mergeinfo_inheritable2(mergeinfo, *mergeinfo, NULL,
@@ -323,6 +349,9 @@ svn_client__get_wc_mergeinfo(svn_mergein
svn_mergeinfo__remove_empty_rangelists(*mergeinfo, result_pool);
}
+ if (inherited_p)
+ *inherited_p = inherited;
+
return SVN_NO_ERROR;
}
@@ -353,14 +382,15 @@ svn_client__get_wc_mergeinfo_catalog(svn
{
if (walked_path)
*walked_path = "";
- *inherited = FALSE;
+ if (inherited)
+ *inherited = FALSE;
return SVN_NO_ERROR;
}
SVN_ERR(svn_client__path_relative_to_root(&target_repos_rel_path,
ctx->wc_ctx,
local_abspath,
- repos_root, FALSE,
+ NULL, FALSE,
NULL, scratch_pool,
scratch_pool));
@@ -411,7 +441,7 @@ svn_client__get_wc_mergeinfo_catalog(svn
SVN_ERR(svn_client__path_relative_to_root(&key_path, ctx->wc_ctx,
key_path,
- repos_root, FALSE,
+ NULL, FALSE,
NULL, result_pool,
scratch_pool));
SVN_ERR(svn_mergeinfo_parse(&subtree_mergeinfo, propval->data,
@@ -432,8 +462,8 @@ svn_client__get_wc_mergeinfo_catalog(svn
}
svn_error_t *
-svn_client__get_repos_mergeinfo(svn_ra_session_t *ra_session,
- svn_mergeinfo_t *target_mergeinfo,
+svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
+ svn_ra_session_t *ra_session,
const char *rel_path,
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
@@ -490,6 +520,7 @@ svn_client__get_repos_mergeinfo_catalog(
{
svn_error_clear(err);
*mergeinfo_cat = NULL;
+ return SVN_NO_ERROR;
}
else
return svn_error_trace(err);
@@ -501,38 +532,21 @@ svn_client__get_repos_mergeinfo_catalog(
}
else
{
- const char *repos_root;
+ const char *session_relpath;
const char *session_url;
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, scratch_pool));
SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
+ SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &session_relpath,
+ session_url, scratch_pool));
- if (strcmp(repos_root, session_url) == 0)
- {
- *mergeinfo_cat = repos_mergeinfo_cat;
- }
+ if (session_relpath[0] == '\0')
+ *mergeinfo_cat = repos_mergeinfo_cat;
else
- {
- apr_hash_index_t *hi;
- svn_mergeinfo_catalog_t rekeyed_mergeinfo_cat =
- apr_hash_make(result_pool);
-
- for (hi = apr_hash_first(scratch_pool, repos_mergeinfo_cat);
- hi;
- hi = apr_hash_next(hi))
- {
- const char *path =
- svn_path_url_add_component2(session_url,
- svn__apr_hash_index_key(hi),
- scratch_pool);
- SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &path,
- path,
- result_pool));
- apr_hash_set(rekeyed_mergeinfo_cat, path, APR_HASH_KEY_STRING,
- svn__apr_hash_index_val(hi));
- }
- *mergeinfo_cat = rekeyed_mergeinfo_cat;
- }
+ SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(mergeinfo_cat,
+ repos_mergeinfo_cat,
+ session_relpath,
+ result_pool,
+ scratch_pool));
}
return SVN_NO_ERROR;
}
@@ -579,7 +593,7 @@ svn_client__get_wc_or_repos_mergeinfo(sv
svn_error_t *
svn_client__get_wc_or_repos_mergeinfo_catalog(
svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
- svn_boolean_t *inherited,
+ svn_boolean_t *inherited_p,
svn_boolean_t *from_repos,
svn_boolean_t include_descendants,
svn_boolean_t repos_only,
@@ -621,8 +635,9 @@ svn_client__get_wc_or_repos_mergeinfo_ca
if (!repos_only)
{
+ svn_boolean_t inherited;
SVN_ERR(svn_client__get_wc_mergeinfo_catalog(&target_mergeinfo_cat_wc,
- inherited,
+ &inherited,
include_descendants,
inherit,
local_abspath,
@@ -631,11 +646,13 @@ svn_client__get_wc_or_repos_mergeinfo_ca
ctx,
result_pool,
scratch_pool));
+ if (inherited_p)
+ *inherited_p = inherited;
/* If we want LOCAL_ABSPATH's inherited mergeinfo, were we able to
get it from the working copy? If not, then we must ask the
repository. */
- if (! ((*inherited)
+ if (! (inherited
|| (inherit == svn_mergeinfo_explicit)
|| (repos_relpath
&& target_mergeinfo_cat_wc
@@ -693,7 +710,8 @@ svn_client__get_wc_or_repos_mergeinfo_ca
repos_relpath,
APR_HASH_KEY_STRING))
{
- *inherited = TRUE;
+ if (inherited_p)
+ *inherited_p = TRUE;
if (from_repos)
*from_repos = TRUE;
}
@@ -801,7 +819,7 @@ should_elide_mergeinfo(svn_boolean_t *el
svn_mergeinfo_t parent_mergeinfo,
svn_mergeinfo_t child_mergeinfo,
const char *path_suffix,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
/* Easy out: No child mergeinfo to elide. */
if (child_mergeinfo == NULL)
@@ -826,22 +844,18 @@ should_elide_mergeinfo(svn_boolean_t *el
/* Both CHILD_MERGEINFO and PARENT_MERGEINFO are non-NULL and
non-empty. */
svn_mergeinfo_t path_tweaked_parent_mergeinfo;
- apr_pool_t *subpool = svn_pool_create(pool);
-
- path_tweaked_parent_mergeinfo = apr_hash_make(subpool);
/* If we need to adjust the paths in PARENT_MERGEINFO do it now. */
if (path_suffix)
- SVN_ERR(svn_client__adjust_mergeinfo_source_paths(
- path_tweaked_parent_mergeinfo,
- path_suffix, parent_mergeinfo, subpool));
+ SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
+ &path_tweaked_parent_mergeinfo, parent_mergeinfo,
+ path_suffix, scratch_pool, scratch_pool));
else
path_tweaked_parent_mergeinfo = parent_mergeinfo;
SVN_ERR(svn_mergeinfo__equals(elides,
path_tweaked_parent_mergeinfo,
- child_mergeinfo, TRUE, subpool));
- svn_pool_destroy(subpool);
+ child_mergeinfo, TRUE, scratch_pool));
}
return SVN_NO_ERROR;
@@ -958,7 +972,7 @@ svn_client__elide_mergeinfo(const char *
return SVN_NO_ERROR;
/* Get TARGET_WCPATH's inherited mergeinfo from the WC. */
- err = svn_client__get_wc_mergeinfo(&mergeinfo, &inherited,
+ err = svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
svn_mergeinfo_nearest_ancestor,
target_abspath,
limit_abspath,
@@ -983,7 +997,7 @@ svn_client__elide_mergeinfo(const char *
if (!mergeinfo && !wc_elision_limit_path)
{
err = svn_client__get_wc_or_repos_mergeinfo(
- &mergeinfo, &inherited, NULL, TRUE,
+ &mergeinfo, NULL, NULL, TRUE,
svn_mergeinfo_nearest_ancestor,
NULL, target_wcpath, ctx, pool);
if (err)
@@ -1047,12 +1061,10 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
const char *url;
svn_boolean_t use_url = svn_path_is_url(path_or_url);
svn_revnum_t peg_rev;
- svn_opt_revision_t opt_rev;
- opt_rev.kind = svn_opt_revision_unspecified;
SVN_ERR(svn_client__ra_session_from_path(&ra_session, &peg_rev, &url,
path_or_url, NULL, peg_revision,
- &opt_rev, ctx, scratch_pool));
+ peg_revision, ctx, scratch_pool));
/* If PATH_OR_URL is as working copy path determine if we will need to
contact the repository for the requested PEG_REVISION. */
@@ -1097,11 +1109,9 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
}
else /* ! svn_path_is_url() */
{
- svn_boolean_t inherited;
-
/* Acquire return values. */
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(
- mergeinfo_catalog, &inherited, NULL, include_descendants, FALSE,
+ mergeinfo_catalog, NULL, NULL, include_descendants, FALSE,
ignore_invalid_mergeinfo, svn_mergeinfo_inherited,
ra_session, path_or_url, ctx,
result_pool, scratch_pool));
@@ -1111,141 +1121,74 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
}
/*** In-memory mergeinfo elision ***/
-
-/* TODO(reint): Document. */
-struct elide_mergeinfo_catalog_dir_baton {
- const char *inherited_mergeinfo_path;
- svn_mergeinfo_t mergeinfo_catalog;
-};
-
-/* The root doesn't have mergeinfo (unless it is actually one of the
- paths passed to svn_delta_path_driver, in which case the callback
- is called directly instead of this). */
-static svn_error_t *
-elide_mergeinfo_catalog_open_root(void *eb,
- svn_revnum_t base_revision,
- apr_pool_t *dir_pool,
- void **root_baton)
-{
- struct elide_mergeinfo_catalog_dir_baton *b = apr_pcalloc(dir_pool,
- sizeof(*b));
- b->mergeinfo_catalog = eb;
- *root_baton = b;
- return SVN_NO_ERROR;
-}
-
-/* Make a directory baton for PATH. It should have the same
- inherited_mergeinfo_path as its parent... unless we just called
- elide_mergeinfo_catalog_cb on its parent with its path. */
-static svn_error_t *
-elide_mergeinfo_catalog_open_directory(const char *path,
- void *parent_baton,
- svn_revnum_t base_revision,
- apr_pool_t *dir_pool,
- void **child_baton)
-{
- struct elide_mergeinfo_catalog_dir_baton *b, *pb = parent_baton;
-
- b = apr_pcalloc(dir_pool, sizeof(*b));
- b->mergeinfo_catalog = pb->mergeinfo_catalog;
-
- if (apr_hash_get(b->mergeinfo_catalog, path, APR_HASH_KEY_STRING))
- b->inherited_mergeinfo_path = apr_pstrdup(dir_pool, path);
- else
- b->inherited_mergeinfo_path = pb->inherited_mergeinfo_path;
-
- *child_baton = b;
- return SVN_NO_ERROR;
-}
-
-/* TODO(reint): Document. */
-struct elide_mergeinfo_catalog_cb_baton {
- apr_array_header_t *elidable_paths;
- svn_mergeinfo_t mergeinfo_catalog;
- apr_pool_t *result_pool;
-};
-
-/* Implements svn_delta_path_driver_cb_func_t. */
-static svn_error_t *
-elide_mergeinfo_catalog_cb(void **dir_baton,
- void *parent_baton,
- void *callback_baton,
- const char *path,
- apr_pool_t *pool)
+svn_error_t *
+svn_client__elide_mergeinfo_catalog(svn_mergeinfo_catalog_t mergeinfo_catalog,
+ apr_pool_t *scratch_pool)
{
- struct elide_mergeinfo_catalog_cb_baton *cb = callback_baton;
- struct elide_mergeinfo_catalog_dir_baton *pb = parent_baton;
- const char *path_suffix;
- svn_boolean_t elides;
-
- /* pb == NULL would imply that there was an *empty* path in the
- paths given to the driver (which is different from "/"). */
- SVN_ERR_ASSERT(pb != NULL);
-
- /* We'll just act like everything is a file. */
- *dir_baton = NULL;
-
- /* Is there even any inherited mergeinfo to elide? */
- /* (Note that svn_delta_path_driver will call open_directory before
- the callback for the root (only).) */
- if (!pb->inherited_mergeinfo_path
- || strcmp(path, "/") == 0)
- return SVN_NO_ERROR;
+ apr_array_header_t *sorted_hash;
+ apr_array_header_t *elidable_paths = apr_array_make(scratch_pool, 1,
+ sizeof(const char *));
+ apr_array_header_t *dir_stack = apr_array_make(scratch_pool, 1,
+ sizeof(const char *));
+ apr_pool_t *iterpool;
+ int i;
- path_suffix = svn_dirent_is_child(pb->inherited_mergeinfo_path,
- path, NULL);
- SVN_ERR_ASSERT(path_suffix != NULL);
+ /* Here's the general algorithm:
+ Walk through the paths sorted in tree order. For each path, pop
+ the dir_stack until it is either empty or the top item contains a parent
+ of the current path. Check to see if that mergeinfo is then elidable,
+ and build the list of elidable mergeinfo based upon that determination.
+ Finally, push the path of interest onto the stack, and continue. */
+ sorted_hash = svn_sort__hash(mergeinfo_catalog,
+ svn_sort_compare_items_as_paths,
+ scratch_pool);
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < sorted_hash->nelts; i++)
+ {
+ svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i,
+ svn_sort__item_t);
+ const char *path = item->key;
+
+ if (dir_stack->nelts > 0)
+ {
+ const char *top;
+ const char *path_suffix;
+ svn_boolean_t elides = FALSE;
+
+ svn_pool_clear(iterpool);
- SVN_ERR(should_elide_mergeinfo(&elides,
- apr_hash_get(cb->mergeinfo_catalog,
- pb->inherited_mergeinfo_path,
- APR_HASH_KEY_STRING),
- apr_hash_get(cb->mergeinfo_catalog,
- path,
- APR_HASH_KEY_STRING),
- path_suffix,
- pool));
+ /* Pop off any paths which are not ancestors of PATH. */
+ do
+ {
+ top = APR_ARRAY_IDX(dir_stack, dir_stack->nelts - 1,
+ const char *);
+ path_suffix = svn_dirent_is_child(top, path, NULL);
- if (elides)
- APR_ARRAY_PUSH(cb->elidable_paths, const char *) =
- apr_pstrdup(cb->result_pool, path);
+ if (!path_suffix)
+ apr_array_pop(dir_stack);
+ }
+ while (dir_stack->nelts > 0 && !path_suffix);
- return SVN_NO_ERROR;
-}
+ /* If we have a path suffix, it means we haven't popped the stack
+ clean. */
+ if (path_suffix)
+ {
+ SVN_ERR(should_elide_mergeinfo(&elides,
+ apr_hash_get(mergeinfo_catalog, top,
+ APR_HASH_KEY_STRING),
+ apr_hash_get(mergeinfo_catalog, path,
+ APR_HASH_KEY_STRING),
+ path_suffix,
+ iterpool));
-svn_error_t *
-svn_client__elide_mergeinfo_catalog(svn_mergeinfo_t mergeinfo_catalog,
- apr_pool_t *pool)
-{
- apr_array_header_t *paths;
- apr_array_header_t *elidable_paths = apr_array_make(pool, 1,
- sizeof(const char *));
- svn_delta_editor_t *editor = svn_delta_default_editor(pool);
- struct elide_mergeinfo_catalog_cb_baton cb = { 0 };
- void *eb;
- int i;
+ if (elides)
+ APR_ARRAY_PUSH(elidable_paths, const char *) = path;
+ }
+ }
- cb.elidable_paths = elidable_paths;
- cb.mergeinfo_catalog = mergeinfo_catalog;
- cb.result_pool = pool;
-
- editor->open_root = elide_mergeinfo_catalog_open_root;
- editor->open_directory = elide_mergeinfo_catalog_open_directory;
-
- eb = mergeinfo_catalog;
- SVN_ERR(svn_editor__insert_shims((const svn_delta_editor_t **)&editor, &eb,
- editor, eb, NULL, NULL, NULL, NULL,
- pool, pool));
-
- /* Walk over the paths, and build up a list of elidable ones. */
- SVN_ERR(svn_hash_keys(&paths, mergeinfo_catalog, pool));
- SVN_ERR(svn_delta_path_driver(editor,
- eb,
- SVN_INVALID_REVNUM,
- paths,
- elide_mergeinfo_catalog_cb,
- &cb,
- pool));
+ APR_ARRAY_PUSH(dir_stack, const char *) = path;
+ }
+ svn_pool_destroy(iterpool);
/* Now remove the elidable paths from the catalog. */
for (i = 0; i < elidable_paths->nelts; i++)
@@ -1265,8 +1208,8 @@ svn_client__elide_mergeinfo_catalog(svn_
each path.
Return a pointer to the mergeinfo value of the nearest path-wise ancestor
- of ABS_REPOS_PATH in DEPTH_FIRST_CATALOG_INDEX. A path is considered its
- own ancestor, so if a key exactly matches ABS_REPOS_PATH, return that
+ of FSPATH in DEPTH_FIRST_CATALOG_INDEX. A path is considered its
+ own ancestor, so if a key exactly matches FSPATH, return that
key's mergeinfo and set *ANCESTOR_IS_SELF to true (set it to false in all
other cases).
@@ -1275,7 +1218,7 @@ svn_client__elide_mergeinfo_catalog(svn_
static svn_mergeinfo_t
find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
svn_boolean_t *ancestor_is_self,
- const char *abs_repos_path)
+ const char *fspath)
{
int ancestor_index = -1;
@@ -1289,12 +1232,12 @@ find_nearest_ancestor(const apr_array_he
{
svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
svn_sort__item_t);
- if (svn_fspath__is_ancestor(item.key, abs_repos_path))
+ if (svn_fspath__skip_ancestor(item.key, fspath))
{
ancestor_index = i;
- /* There's no nearer ancestor than ABS_REPOS_PATH itself. */
- if (strcmp(item.key, abs_repos_path) == 0)
+ /* There's no nearer ancestor than FSPATH itself. */
+ if (strcmp(item.key, fspath) == 0)
{
*ancestor_is_self = TRUE;
break;
@@ -1322,12 +1265,12 @@ struct filter_log_entry_baton_t
/* Unsorted array of repository relative paths representing the merge
sources. There will be more than one source */
- const apr_array_header_t *merge_source_paths;
+ const apr_array_header_t *merge_source_fspaths;
/* The repository-absolute path we are calling svn_client_log5() on. */
- const char *abs_repos_target_path;
+ const char *target_fspath;
- /* Mergeinfo catalog for the tree rooted at ABS_REPOS_TARGET_PATH.
+ /* Mergeinfo catalog for the tree rooted at TARGET_FSPATH.
The path keys must be repository-absolute. */
svn_mergeinfo_catalog_t target_mergeinfo_catalog;
@@ -1353,16 +1296,16 @@ struct filter_log_entry_baton_t
Call the wrapped log receiver BATON->log_receiver (with
BATON->log_receiver_baton) if:
-
+
BATON->FILTERING_MERGED is FALSE and the changes represented by LOG_ENTRY
- have been fully merged from BATON->MERGE_SOURCE_PATHS to the WC target
+ have been fully merged from BATON->merge_source_fspaths to the WC target
based on the mergeinfo for the WC contained in BATON->TARGET_MERGEINFO_CATALOG.
Or
BATON->FILTERING_MERGED is TRUE and the changes represented by LOG_ENTRY
have not been merged, or only partially merged, from
- BATON->MERGE_SOURCE_PATHS to the WC target based on the mergeinfo for the
+ BATON->merge_source_fspaths to the WC target based on the mergeinfo for the
WC contained in BATON->TARGET_MERGEINFO_CATALOG. */
static svn_error_t *
filter_log_entry_with_rangelist(void *baton,
@@ -1403,11 +1346,12 @@ filter_log_entry_with_rangelist(void *ba
/* If the paths changed by LOG_ENTRY->REVISION are provided we can determine
if LOG_ENTRY->REVISION, while only partially represented in
- BATON->RANGELIST, is in fact completely applied to all affected paths. */
+ BATON->RANGELIST, is in fact completely applied to all affected paths.
+ ### And ... what if it is, or if it isn't? What do we do with the answer?
+ And how do we cope if the changed paths are not provided? */
if ((log_entry->non_inheritable || !fleb->filtering_merged)
&& log_entry->changed_paths2)
{
- int i;
apr_hash_index_t *hi;
svn_boolean_t all_subtrees_have_this_rev = TRUE;
apr_array_header_t *this_rev_rangelist =
@@ -1419,27 +1363,27 @@ filter_log_entry_with_rangelist(void *ba
hi;
hi = apr_hash_next(hi))
{
+ int i;
const char *path = svn__apr_hash_index_key(hi);
svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
- const char *target_path_affected;
+ const char *target_fspath_affected;
svn_mergeinfo_t nearest_ancestor_mergeinfo;
- apr_hash_index_t *hi2;
svn_boolean_t found_this_revision = FALSE;
const char *merge_source_rel_target;
- const char *merge_source_path;
+ const char *merge_source_fspath;
svn_boolean_t ancestor_is_self;
svn_pool_clear(iterpool);
/* Check that PATH is a subtree of at least one of the
merge sources. If not then ignore this path. */
- for (i = 0; i < fleb->merge_source_paths->nelts; i++)
+ for (i = 0; i < fleb->merge_source_fspaths->nelts; i++)
{
- merge_source_path = APR_ARRAY_IDX(fleb->merge_source_paths,
- i, const char *);
+ merge_source_fspath = APR_ARRAY_IDX(fleb->merge_source_fspaths,
+ i, const char *);
merge_source_rel_target
- = svn_fspath__skip_ancestor(merge_source_path, path);
+ = svn_fspath__skip_ancestor(merge_source_fspath, path);
if (merge_source_rel_target)
{
/* If MERGE_SOURCE was itself deleted, replaced, or added
@@ -1447,24 +1391,24 @@ filter_log_entry_with_rangelist(void *ba
can't merge a addition or deletion of yourself. */
if (merge_source_rel_target[0] == '\0'
&& (change->action != 'M'))
- i = fleb->merge_source_paths->nelts;
+ i = fleb->merge_source_fspaths->nelts;
break;
}
}
/* If we examined every merge source path and PATH is a child of
none of them then we can ignore this PATH. */
- if (i == fleb->merge_source_paths->nelts)
+ if (i == fleb->merge_source_fspaths->nelts)
continue;
/* Calculate the target path which PATH would affect if merged. */
- target_path_affected = svn_fspath__join(fleb->abs_repos_target_path,
- merge_source_rel_target,
- iterpool);
+ target_fspath_affected = svn_fspath__join(fleb->target_fspath,
+ merge_source_rel_target,
+ iterpool);
nearest_ancestor_mergeinfo =
find_nearest_ancestor(fleb->depth_first_catalog_index,
&ancestor_is_self,
- target_path_affected);
+ target_fspath_affected);
/* Issue #3791: A path should never have explicit mergeinfo
describing its own addition (that's self-referential). Nor will
@@ -1495,6 +1439,8 @@ filter_log_entry_with_rangelist(void *ba
if (nearest_ancestor_mergeinfo)
{
+ apr_hash_index_t *hi2;
+
for (hi2 = apr_hash_first(iterpool, nearest_ancestor_mergeinfo);
hi2;
hi2 = apr_hash_next(hi2))
@@ -1504,11 +1450,11 @@ filter_log_entry_with_rangelist(void *ba
/* Does the mergeinfo for PATH reflect if
LOG_ENTRY->REVISION was previously merged
- from MERGE_SOURCE_PATH? */
- if (svn_fspath__is_ancestor(merge_source_path,
- mergeinfo_path))
+ from MERGE_SOURCE_FSPATH? */
+ if (svn_fspath__skip_ancestor(merge_source_fspath,
+ mergeinfo_path))
{
- /* Something was merged from MERGE_SOURCE_PATH, does
+ /* Something was merged from MERGE_SOURCE_FSPATH, does
it include LOG_ENTRY->REVISION? */
SVN_ERR(svn_rangelist_intersect(&intersection,
rangelist,
@@ -1517,15 +1463,30 @@ filter_log_entry_with_rangelist(void *ba
iterpool));
if (intersection->nelts)
{
- SVN_ERR(svn_rangelist_intersect(&intersection,
- rangelist,
- this_rev_rangelist,
- TRUE, iterpool));
- if (intersection->nelts)
+ if (ancestor_is_self)
{
+ /* TARGET_PATH_AFFECTED has explicit mergeinfo,
+ so we don't need to worry if that mergeinfo
+ is inheritable or not. */
found_this_revision = TRUE;
break;
}
+ else
+ {
+ /* TARGET_PATH_AFFECTED inherited its mergeinfo,
+ se we have to ignore non-inheritable
+ ranges. */
+ SVN_ERR(svn_rangelist_intersect(
+ &intersection,
+ rangelist,
+ this_rev_rangelist,
+ TRUE, iterpool));
+ if (intersection->nelts)
+ {
+ found_this_revision = TRUE;
+ break;
+ }
+ }
}
}
}
@@ -1557,11 +1518,11 @@ filter_log_entry_with_rangelist(void *ba
static svn_error_t *
logs_for_mergeinfo_rangelist(const char *source_url,
- const apr_array_header_t *merge_source_paths,
+ const apr_array_header_t *merge_source_fspaths,
svn_boolean_t filtering_merged,
const apr_array_header_t *rangelist,
svn_mergeinfo_t target_mergeinfo_catalog,
- const char *abs_repos_target_path,
+ const char *target_fspath,
svn_boolean_t discover_changed_paths,
const apr_array_header_t *revprops,
svn_log_entry_receiver_t log_receiver,
@@ -1573,7 +1534,6 @@ logs_for_mergeinfo_rangelist(const char
svn_merge_range_t *oldest_range, *youngest_range;
apr_array_header_t *revision_ranges;
svn_opt_revision_t oldest_rev, youngest_rev;
- svn_opt_revision_range_t *range;
struct filter_log_entry_baton_t fleb;
if (! rangelist->nelts)
@@ -1601,35 +1561,19 @@ logs_for_mergeinfo_rangelist(const char
/* FILTER_LOG_ENTRY_BATON_T->TARGET_MERGEINFO_CATALOG's keys are required
to be repository-absolute. */
- if (apr_hash_count(target_mergeinfo_catalog))
- {
- apr_hash_index_t *hi;
- svn_mergeinfo_catalog_t rekeyed_catalog = apr_hash_make(scratch_pool);
-
- for (hi = apr_hash_first(scratch_pool, target_mergeinfo_catalog);
- hi;
- hi = apr_hash_next(hi))
- {
- const char *path = svn__apr_hash_index_key(hi);
-
- if (!svn_dirent_is_absolute(path))
- apr_hash_set(rekeyed_catalog,
- svn_dirent_join("/", path, scratch_pool),
- APR_HASH_KEY_STRING,
- svn__apr_hash_index_val(hi));
- }
- target_mergeinfo_catalog = rekeyed_catalog;
- }
+ SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&target_mergeinfo_catalog,
+ target_mergeinfo_catalog, "/",
+ scratch_pool, scratch_pool));
/* Build the log filtering callback baton. */
fleb.filtering_merged = filtering_merged;
- fleb.merge_source_paths = merge_source_paths;
+ fleb.merge_source_fspaths = merge_source_fspaths;
fleb.target_mergeinfo_catalog = target_mergeinfo_catalog;
fleb.depth_first_catalog_index =
svn_sort__hash(target_mergeinfo_catalog,
svn_sort_compare_items_as_paths,
scratch_pool);
- fleb.abs_repos_target_path = abs_repos_target_path;
+ fleb.target_fspath = target_fspath;
fleb.rangelist = rangelist;
fleb.log_receiver = log_receiver;
fleb.log_receiver_baton = log_receiver_baton;
@@ -1638,10 +1582,8 @@ logs_for_mergeinfo_rangelist(const char
/* Drive the log. */
revision_ranges = apr_array_make(scratch_pool, 1,
sizeof(svn_opt_revision_range_t *));
- range = apr_pcalloc(scratch_pool, sizeof(*range));
- range->end = youngest_rev;
- range->start = oldest_rev;
- APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *) = range;
+ APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
+ = svn_opt__revision_range_create(&oldest_rev, &youngest_rev, scratch_pool);
SVN_ERR(svn_client_log5(target, &youngest_rev, revision_ranges,
0, discover_changed_paths, FALSE, FALSE, revprops,
filter_log_entry_with_rangelist, &fleb, ctx,
@@ -1664,7 +1606,6 @@ svn_client_mergeinfo_get_merged(apr_hash
apr_pool_t *pool)
{
const char *repos_root;
- apr_hash_t *full_path_mergeinfo;
svn_mergeinfo_catalog_t mergeinfo_cat;
svn_mergeinfo_t mergeinfo;
@@ -1690,26 +1631,8 @@ svn_client_mergeinfo_get_merged(apr_hash
mergeinfo = NULL;
}
- /* Copy the MERGEINFO hash items into another hash, but change
- the relative paths into full URLs. */
- *mergeinfo_p = NULL;
- if (mergeinfo)
- {
- apr_hash_index_t *hi;
-
- full_path_mergeinfo = apr_hash_make(pool);
- for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
- {
- const char *key = svn__apr_hash_index_key(hi);
- void *val = svn__apr_hash_index_val(hi);
-
- apr_hash_set(full_path_mergeinfo,
- svn_path_url_add_component2(repos_root, key + 1, pool),
- APR_HASH_KEY_STRING, val);
- }
- *mergeinfo_p = full_path_mergeinfo;
- }
-
+ SVN_ERR(svn_mergeinfo__relpaths_to_urls(mergeinfo_p, mergeinfo,
+ repos_root, pool, pool));
return SVN_NO_ERROR;
}
@@ -1743,7 +1666,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
svn_mergeinfo_t target_history;
apr_array_header_t *master_noninheritable_rangelist;
apr_array_header_t *master_inheritable_rangelist;
- apr_array_header_t *merge_source_paths =
+ apr_array_header_t *merge_source_fspaths =
apr_array_make(scratch_pool, 1, sizeof(const char *));
apr_hash_index_t *hi_catalog;
apr_hash_index_t *hi;
@@ -1774,7 +1697,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
ctx->wc_ctx,
target_path_or_url,
repos_root,
- FALSE, NULL,
+ FALSE /* leading_slash */, NULL,
scratch_pool,
scratch_pool));
@@ -1803,19 +1726,17 @@ svn_client_mergeinfo_log(svn_boolean_t f
/* Open RA sessions to the repository for the source and target.
* ### TODO: As the source and target must be in the same repository, we
* should share a single session, tracking the two URLs separately. */
-
if (!finding_merged)
{
svn_revnum_t target_peg_revnum;
- const char *url;
SVN_ERR(svn_client__ra_session_from_path(&target_session,
- &target_peg_revnum, &url,
+ &target_peg_revnum, NULL,
target_path_or_url, NULL,
target_peg_revision,
target_peg_revision,
- ctx, scratch_pool));
-
+ ctx, sesspool));
+
SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
target_peg_revnum,
SVN_INVALID_REVNUM,
@@ -1823,17 +1744,15 @@ svn_client_mergeinfo_log(svn_boolean_t f
target_session, ctx,
scratch_pool));
}
-
{
svn_revnum_t source_peg_revnum;
- const char *url;
SVN_ERR(svn_client__ra_session_from_path(&source_session,
- &source_peg_revnum, &url,
+ &source_peg_revnum, NULL,
source_path_or_url, NULL,
source_peg_revision,
source_peg_revision,
- ctx, scratch_pool));
+ ctx, sesspool));
SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
source_peg_revnum,
@@ -1842,7 +1761,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
source_session, ctx,
scratch_pool));
}
-
+ /* Close the source and target sessions. */
svn_pool_destroy(sesspool);
/* Separate the explicit or inherited mergeinfo on TARGET_PATH_OR_URL, and possibly
@@ -1905,9 +1824,9 @@ svn_client_mergeinfo_log(svn_boolean_t f
subtree_history,
subtree_source_history, TRUE,
scratch_pool, iterpool));
- SVN_ERR(svn_mergeinfo_merge(subtree_mergeinfo,
- merged_via_history,
- scratch_pool));
+ SVN_ERR(svn_mergeinfo_merge2(subtree_mergeinfo,
+ merged_via_history,
+ scratch_pool, scratch_pool));
}
SVN_ERR(svn_mergeinfo_inheritable2(&subtree_inheritable_mergeinfo,
@@ -1962,8 +1881,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
SVN_ERR(svn_rangelist__merge_many(subtree_merged_rangelist,
merged, scratch_pool, iterpool));
- apr_hash_set(inheritable_subtree_merges,
- apr_pstrdup(scratch_pool, subtree_path),
+ apr_hash_set(inheritable_subtree_merges, subtree_path,
APR_HASH_KEY_STRING, subtree_merged_rangelist);
}
else
@@ -1971,8 +1889,7 @@ svn_client_mergeinfo_log(svn_boolean_t f
/* Map SUBTREE_PATH to an empty rangelist if there was nothing
fully merged. e.g. Only empty or non-inheritable mergienfo
on the subtree or mergeinfo unrelated to the source. */
- apr_hash_set(inheritable_subtree_merges,
- apr_pstrdup(scratch_pool, subtree_path),
+ apr_hash_set(inheritable_subtree_merges, subtree_path,
APR_HASH_KEY_STRING,
apr_array_make(scratch_pool, 0,
sizeof(svn_merge_range_t *)));
@@ -2064,10 +1981,10 @@ svn_client_mergeinfo_log(svn_boolean_t f
else
{
/* Determine the correct (youngest) target for 'svn log'. */
- svn_merge_range_t *youngest_range = svn_merge_range_dup(
- APR_ARRAY_IDX(master_inheritable_rangelist,
- master_inheritable_rangelist->nelts - 1,
- svn_merge_range_t *), scratch_pool);
+ svn_merge_range_t *youngest_range
+ = APR_ARRAY_IDX(master_inheritable_rangelist,
+ master_inheritable_rangelist->nelts - 1,
+ svn_merge_range_t *);
apr_array_header_t *youngest_rangelist =
svn_rangelist__initialize(youngest_range->end - 1,
youngest_range->end,
@@ -2082,17 +1999,17 @@ svn_client_mergeinfo_log(svn_boolean_t f
apr_array_header_t *subtree_merged_rangelist =
svn__apr_hash_index_val(hi);
apr_array_header_t *intersecting_rangelist;
+
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_intersect(&intersecting_rangelist,
youngest_rangelist,
subtree_merged_rangelist,
FALSE, iterpool));
- APR_ARRAY_PUSH(merge_source_paths, const char *) =
- apr_pstrdup(scratch_pool, key);
+ APR_ARRAY_PUSH(merge_source_fspaths, const char *) = key;
if (intersecting_rangelist->nelts)
- log_target = apr_pstrdup(scratch_pool, key);
+ log_target = key;
}
}
@@ -2104,13 +2021,12 @@ svn_client_mergeinfo_log(svn_boolean_t f
log_target = svn_path_url_add_component2(repos_root, log_target + 1,
scratch_pool);
- SVN_ERR(logs_for_mergeinfo_rangelist(log_target, merge_source_paths,
+ SVN_ERR(logs_for_mergeinfo_rangelist(log_target, merge_source_fspaths,
finding_merged,
master_inheritable_rangelist,
target_mergeinfo_cat,
- svn_dirent_join("/",
- target_repos_rel,
- scratch_pool),
+ svn_fspath__join("/", target_repos_rel,
+ scratch_pool),
discover_changed_paths,
revprops,
log_receiver, log_receiver_baton,
Modified: subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.h?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/fs-py/subversion/libsvn_client/mergeinfo.h Mon Dec 19 18:49:34 2011
@@ -40,7 +40,9 @@ typedef struct svn_client__merge_path_t
{
const char *abspath; /* Absolute working copy path. */
svn_boolean_t missing_child; /* ABSPATH has an immediate child which
- is missing. */
+ is missing, but is not switched. */
+ svn_boolean_t switched_child; /* ABSPATH has an immediate child which
+ is switched. */
svn_boolean_t switched; /* ABSPATH is switched. */
svn_boolean_t has_noninheritable; /* ABSPATH has svn:mergeinfo set on it
which includes non-inheritable
@@ -79,6 +81,13 @@ typedef struct svn_client__merge_path_t
to the merge, and the operational
depth of the merge is
svn_depth_immediates. */
+ svn_boolean_t record_mergeinfo; /* Mergeinfo needs to be recorded
+ on ABSPATH to describe the
+ merge. */
+ svn_boolean_t record_noninheritable; /* Non-inheritable mergeinfo needs to
+ be recorded on ABSPATH to describe
+ the merge. Implies RECORD_MERGEINFO
+ is true. */
} svn_client__merge_path_t;
/* Return a deep copy of the merge-path structure OLD, allocated in POOL. */
@@ -86,13 +95,21 @@ svn_client__merge_path_t *
svn_client__merge_path_dup(const svn_client__merge_path_t *old,
apr_pool_t *pool);
+/* Create a new merge path structure, allocated in POOL. Initialize the
+ * 'abspath' member to a deep copy of ABSPATH and all other fields to zero
+ * bytes. */
+svn_client__merge_path_t *
+svn_client__merge_path_create(const char *abspath,
+ apr_pool_t *pool);
+
/*** Functions ***/
/* Find explicit or inherited WC mergeinfo for LOCAL_ABSPATH, and return it
in *MERGEINFO (NULL if no mergeinfo is set). Set *INHERITED to
- whether the mergeinfo was inherited (TRUE or FALSE).
+ whether the mergeinfo was inherited (TRUE or FALSE), if INHERITED is
+ non-null.
This function will search for inherited mergeinfo in the parents of
LOCAL_ABSPATH only if the base revision of LOCAL_ABSPATH falls within
@@ -166,10 +183,12 @@ svn_client__get_wc_mergeinfo_catalog(svn
If there is no mergeinfo available for REL_PATH, or if the server
doesn't support a mergeinfo capability and SQUELCH_INCAPABLE is
- TRUE, set *TARGET_MERGEINFO to NULL. */
+ TRUE, set *TARGET_MERGEINFO to NULL. If the server doesn't support
+ a mergeinfo capability and SQUELCH_INCAPABLE is FALSE, return an
+ SVN_ERR_UNSUPPORTED_FEATURE error. */
svn_error_t *
-svn_client__get_repos_mergeinfo(svn_ra_session_t *ra_session,
- svn_mergeinfo_t *target_mergeinfo,
+svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
+ svn_ra_session_t *ra_session,
const char *rel_path,
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
@@ -224,7 +243,7 @@ svn_client__get_repos_mergeinfo_catalog(
If TARGET_WCPATH inherited its mergeinfo from a working copy ancestor
or if it was obtained from the repository, set *INHERITED to TRUE, set it
- to FALSE otherwise. */
+ to FALSE otherwise, if INHERITED is non-null. */
svn_error_t *
svn_client__get_wc_or_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
svn_boolean_t *inherited,
@@ -287,7 +306,7 @@ svn_client__get_history_as_mergeinfo(svn
apr_pool_t *pool);
/* Parse any explicit mergeinfo on LOCAL_ABSPATH and store it in
- MERGEINFO. If no record of any mergeinfo exists, set MERGEINFO to NULL.
+ *MERGEINFO. If no record of any mergeinfo exists, set *MERGEINFO to NULL.
Does not acount for inherited mergeinfo. */
svn_error_t *
svn_client__parse_mergeinfo(svn_mergeinfo_t *mergeinfo,
@@ -311,6 +330,13 @@ svn_client__record_wc_mergeinfo(const ch
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
+/* Write mergeinfo into the WC. RESULT_CATALOG maps (const char *) WC paths
+ * to (svn_mergeinfo_t) mergeinfo. */
+svn_error_t *
+svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
/* Elide any svn:mergeinfo set on TARGET_WCPATH to its nearest working
copy (or possibly repository) ancestor with equivalent mergeinfo.
@@ -350,21 +376,18 @@ svn_client__elide_mergeinfo(const char *
svn_client_ctx_t *ctx,
apr_pool_t *pool);
-/* TODO(reint): Document. */
-svn_error_t *
-svn_client__elide_mergeinfo_catalog(svn_mergeinfo_t mergeinfo_catalog,
- apr_pool_t *pool);
+/* Simplify a mergeinfo catalog, if possible, via elision.
-/* For each source path : rangelist pair in MERGEINFO, append REL_PATH to
- the source path and add the new source path : rangelist pair to
- ADJUSTED_MERGEINFO. The new source path and rangelist are both deep
- copies allocated in POOL. Neither ADJUSTED_MERGEINFO
- nor MERGEINFO should be NULL. */
-svn_error_t *
-svn_client__adjust_mergeinfo_source_paths(svn_mergeinfo_t adjusted_mergeinfo,
- const char *rel_path,
- svn_mergeinfo_t mergeinfo,
- apr_pool_t *pool);
+ For each path in MERGEINFO_CATALOG, check if the path's mergeinfo can
+ elide to the path's nearest path-wise parent in MERGEINFO_CATALOG. If
+ so, remove that path from MERGEINFO_CATALOG. Elidability is determined
+ as per svn_client__elide_mergeinfo except that elision to the repository
+ is not considered.
+
+ SCRATCH_POOL is used for temporary allocations. */
+svn_error_t *
+svn_client__elide_mergeinfo_catalog(svn_mergeinfo_catalog_t mergeinfo_catalog,
+ apr_pool_t *scratch_pool);
/* Set *MERGEINFO_CHANGES to TRUE if LOCAL_ABSPATH has locally modified
mergeinfo, set *MERGEINFO_CHANGES to FALSE otherwise. */
Modified: subversion/branches/fs-py/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_client/patch.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_client/patch.c Mon Dec 19 18:49:34 2011
@@ -670,7 +670,7 @@ init_prop_target(prop_patch_target_t **p
}
content->existed = (value != NULL);
new_prop_target->value = value;
- new_prop_target->patched_value = svn_stringbuf_create("", result_pool);
+ new_prop_target->patched_value = svn_stringbuf_create_empty(result_pool);
/* Wire up the read and write callbacks. */
@@ -811,13 +811,20 @@ write_file(void *baton, const char *buf,
* with the fewest path components, the shortest basename, and the shortest
* total file name length (in that order). In case of a tie, return the new
* filename. This heuristic is also used by Larry Wall's UNIX patch (except
- * that it prompts for a filename in case of a tie). */
+ * that it prompts for a filename in case of a tie).
+ * Additionally, for compatibility with git, if one of the filenames
+ * is "/dev/null", use the other filename. */
static const char *
choose_target_filename(const svn_patch_t *patch)
{
apr_size_t old;
apr_size_t new;
+ if (strcmp(patch->old_filename, "/dev/null") == 0)
+ return patch->new_filename;
+ if (strcmp(patch->new_filename, "/dev/null") == 0)
+ return patch->old_filename;
+
old = svn_path_component_count(patch->old_filename);
new = svn_path_component_count(patch->new_filename);
@@ -1692,7 +1699,7 @@ apply_hunk(patch_target_t *target, targe
&eol_str, &eof,
iterpool, iterpool));
lines_read++;
- if (! eof && lines_read > hi->fuzz &&
+ if (lines_read > hi->fuzz &&
lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - hi->fuzz)
{
apr_size_t len;
@@ -2723,6 +2730,7 @@ delete_empty_dirs(apr_array_header_t *ta
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
target_info = APR_ARRAY_IDX(targets_info, i, patch_target_info_t *);
+
parent = svn_dirent_dirname(target_info->local_abspath, iterpool);
if (apr_hash_get(non_empty_dirs, parent, APR_HASH_KEY_STRING))
@@ -2825,54 +2833,36 @@ delete_empty_dirs(apr_array_header_t *ta
return SVN_NO_ERROR;
}
-/* Baton for apply_patches(). */
-typedef struct apply_patches_baton_t {
- /* The path to the patch file. */
- const char *patch_abspath;
-
- /* The abspath to the working copy the patch should be applied to. */
- const char *abs_wc_path;
-
- /* Indicates whether we're doing a dry run. */
- svn_boolean_t dry_run;
-
- /* Number of leading components to strip from patch target paths. */
- int strip_count;
-
- /* Whether to apply the patch in reverse. */
- svn_boolean_t reverse;
-
- /* Indicates whether we should ignore whitespace when matching context
- * lines */
- svn_boolean_t ignore_whitespace;
-
- /* As in svn_client_patch(). */
- svn_boolean_t remove_tempfiles;
-
- /* As in svn_client_patch(). */
- svn_client_patch_func_t patch_func;
- void *patch_baton;
-
- /* The client context. */
- svn_client_ctx_t *ctx;
-} apply_patches_baton_t;
-
-/* Callback for use with svn_wc__call_with_write_lock().
- * This function is the main entry point into the patch code. */
+/* This function is the main entry point into the patch code. */
static svn_error_t *
-apply_patches(void *baton,
- apr_pool_t *result_pool,
+apply_patches(/* The path to the patch file. */
+ const char *patch_abspath,
+ /* The abspath to the working copy the patch should be applied to. */
+ const char *abs_wc_path,
+ /* Indicates whether we're doing a dry run. */
+ svn_boolean_t dry_run,
+ /* Number of leading components to strip from patch target paths. */
+ int strip_count,
+ /* Whether to apply the patch in reverse. */
+ svn_boolean_t reverse,
+ /* Whether to ignore whitespace when matching context lines. */
+ svn_boolean_t ignore_whitespace,
+ /* As in svn_client_patch(). */
+ svn_boolean_t remove_tempfiles,
+ /* As in svn_client_patch(). */
+ svn_client_patch_func_t patch_func,
+ void *patch_baton,
+ /* The client context. */
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_patch_t *patch;
apr_pool_t *iterpool;
svn_patch_file_t *patch_file;
apr_array_header_t *targets_info;
- apply_patches_baton_t *btn = baton;
/* Try to open the patch file. */
- SVN_ERR(svn_diff_open_patch_file(&patch_file, btn->patch_abspath,
- scratch_pool));
+ SVN_ERR(svn_diff_open_patch_file(&patch_file, patch_abspath, scratch_pool));
/* Apply patches. */
targets_info = apr_array_make(scratch_pool, 0,
@@ -2882,23 +2872,21 @@ apply_patches(void *baton,
{
svn_pool_clear(iterpool);
- if (btn->ctx->cancel_func)
- SVN_ERR(btn->ctx->cancel_func(btn->ctx->cancel_baton));
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
- btn->reverse, btn->ignore_whitespace,
+ reverse, ignore_whitespace,
iterpool, iterpool));
if (patch)
{
patch_target_t *target;
- SVN_ERR(apply_one_patch(&target, patch, btn->abs_wc_path,
- btn->ctx->wc_ctx, btn->strip_count,
- btn->ignore_whitespace,
- btn->remove_tempfiles,
- btn->patch_func, btn->patch_baton,
- btn->ctx->cancel_func,
- btn->ctx->cancel_baton,
+ SVN_ERR(apply_one_patch(&target, patch, abs_wc_path,
+ ctx->wc_ctx, strip_count,
+ ignore_whitespace, remove_tempfiles,
+ patch_func, patch_baton,
+ ctx->cancel_func, ctx->cancel_baton,
iterpool, iterpool));
if (! target->filtered)
{
@@ -2908,35 +2896,32 @@ apply_patches(void *baton,
target_info->local_abspath = apr_pstrdup(scratch_pool,
target->local_abspath);
target_info->deleted = target->deleted;
- APR_ARRAY_PUSH(targets_info,
- patch_target_info_t *) = target_info;
if (! target->skipped)
{
+ APR_ARRAY_PUSH(targets_info,
+ patch_target_info_t *) = target_info;
+
if (target->has_text_changes
|| target->added
|| target->deleted)
- SVN_ERR(install_patched_target(target, btn->abs_wc_path,
- btn->ctx, btn->dry_run,
- iterpool));
+ SVN_ERR(install_patched_target(target, abs_wc_path,
+ ctx, dry_run, iterpool));
if (target->has_prop_changes && (!target->deleted))
- SVN_ERR(install_patched_prop_targets(target, btn->ctx,
- btn->dry_run,
- iterpool));
+ SVN_ERR(install_patched_prop_targets(target, ctx,
+ dry_run, iterpool));
- SVN_ERR(write_out_rejected_hunks(target, btn->dry_run,
- iterpool));
+ SVN_ERR(write_out_rejected_hunks(target, dry_run, iterpool));
}
- SVN_ERR(send_patch_notification(target, btn->ctx, iterpool));
+ SVN_ERR(send_patch_notification(target, ctx, iterpool));
}
}
}
while (patch);
/* Delete directories which are empty after patching, if any. */
- SVN_ERR(delete_empty_dirs(targets_info, btn->ctx, btn->dry_run,
- scratch_pool));
+ SVN_ERR(delete_empty_dirs(targets_info, ctx, dry_run, scratch_pool));
SVN_ERR(svn_diff_close_patch_file(patch_file, iterpool));
svn_pool_destroy(iterpool);
@@ -2957,7 +2942,6 @@ svn_client_patch(const char *patch_abspa
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
- apply_patches_baton_t baton;
svn_node_kind_t kind;
if (strip_count < 0)
@@ -2994,19 +2978,10 @@ svn_client_patch(const char *patch_abspa
svn_dirent_local_style(wc_dir_abspath,
scratch_pool));
- baton.patch_abspath = patch_abspath;
- baton.abs_wc_path = wc_dir_abspath;
- baton.dry_run = dry_run;
- baton.ctx = ctx;
- baton.strip_count = strip_count;
- baton.reverse = reverse;
- baton.ignore_whitespace = ignore_whitespace;
- baton.remove_tempfiles = remove_tempfiles;
- baton.patch_func = patch_func;
- baton.patch_baton = patch_baton;
-
- return svn_error_trace(
- svn_wc__call_with_write_lock(apply_patches, &baton,
- ctx->wc_ctx, wc_dir_abspath, FALSE,
- scratch_pool, scratch_pool));
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ apply_patches(patch_abspath, wc_dir_abspath, dry_run, strip_count,
+ reverse, ignore_whitespace, remove_tempfiles,
+ patch_func, patch_baton, ctx, scratch_pool),
+ ctx->wc_ctx, wc_dir_abspath, FALSE /* lock_anchor */, scratch_pool);
+ return SVN_NO_ERROR;
}
Modified: subversion/branches/fs-py/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_client/prop_commands.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_client/prop_commands.c Mon Dec 19 18:49:34 2011
@@ -248,37 +248,6 @@ propset_on_url(const char *propname,
return editor->close_edit(edit_baton, pool);
}
-/* Baton for set_props_cb */
-struct set_props_baton
-{
- svn_client_ctx_t *ctx;
- const char *local_abspath;
- svn_depth_t depth;
- svn_node_kind_t kind;
- const char *propname;
- const svn_string_t *propval;
- svn_boolean_t skip_checks;
- const apr_array_header_t *changelist_filter;
-};
-
-/* Working copy lock callback for svn_client_propset4 */
-static svn_error_t *
-set_props_cb(void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- struct set_props_baton *bt = baton;
-
- SVN_ERR(svn_wc_prop_set4(bt->ctx->wc_ctx, bt->local_abspath, bt->propname,
- bt->propval, bt->depth, bt->skip_checks,
- bt->changelist_filter,
- bt->ctx->cancel_func, bt->ctx->cancel_baton,
- bt->ctx->notify_func2, bt->ctx->notify_baton2,
- scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
/* Check that PROPNAME is a valid name for a versioned property. Return an
* error if it is not valid, specifically if it is:
* - the name of a standard Subversion rev-prop; or
@@ -342,7 +311,6 @@ svn_client_propset_local(const char *pro
svn_node_kind_t kind;
const char *target_abspath;
svn_error_t *err;
- struct set_props_baton baton;
const char *target = APR_ARRAY_IDX(targets, i, const char *);
svn_pool_clear(iterpool);
@@ -374,18 +342,12 @@ svn_client_propset_local(const char *pro
else
SVN_ERR(err);
- baton.ctx = ctx;
- baton.local_abspath = target_abspath;
- baton.depth = depth;
- baton.kind = kind;
- baton.propname = propname;
- baton.propval = propval;
- baton.skip_checks = skip_checks;
- baton.changelist_filter = changelists;
-
- SVN_ERR(svn_wc__call_with_write_lock(set_props_cb, &baton,
- ctx->wc_ctx, target_abspath,
- FALSE, iterpool, iterpool));
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ svn_wc_prop_set4(ctx->wc_ctx, target_abspath, propname,
+ propval, depth, skip_checks, changelists,
+ ctx->cancel_func, ctx->cancel_baton,
+ ctx->notify_func2, ctx->notify_baton2, iterpool),
+ ctx->wc_ctx, target_abspath, FALSE /* lock_anchor */, iterpool);
}
svn_pool_destroy(iterpool);
Modified: subversion/branches/fs-py/subversion/libsvn_client/ra.c
URL: http://svn.apache.org/viewvc/subversion/branches/fs-py/subversion/libsvn_client/ra.c?rev=1220893&r1=1220892&r2=1220893&view=diff
==============================================================================
--- subversion/branches/fs-py/subversion/libsvn_client/ra.c (original)
+++ subversion/branches/fs-py/subversion/libsvn_client/ra.c Mon Dec 19 18:49:34 2011
@@ -40,6 +40,7 @@
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
+#include "private/svn_client_private.h"
/* This is the baton that we pass svn_ra_open3(), and is associated with
@@ -385,56 +386,8 @@ svn_client_open_ra_session(svn_ra_sessio
}
-svn_error_t *
-svn_client_uuid_from_url(const char **uuid,
- const char *url,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
-{
- svn_ra_session_t *ra_session;
- apr_pool_t *subpool = svn_pool_create(pool);
-
- /* use subpool to create a temporary RA session */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url,
- NULL, /* no base dir */
- NULL, FALSE, TRUE,
- ctx, subpool));
-
- SVN_ERR(svn_ra_get_uuid2(ra_session, uuid, pool));
-
- /* destroy the RA session */
- svn_pool_destroy(subpool);
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_client_uuid_from_path2(const char **uuid,
- const char *local_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- return svn_error_trace(
- svn_wc__node_get_repos_info(NULL, uuid, ctx->wc_ctx, local_abspath,
- result_pool, scratch_pool));
-}
-
-
-/* Convert a path or URL for display: if it is a local path, convert it to
- * the local path style; if it is a URL, return it unchanged. */
-static const char *
-path_or_url_local_style(const char *path_or_url,
- apr_pool_t *pool)
-{
- if (svn_path_is_url(path_or_url))
- return path_or_url;
- return svn_dirent_local_style(path_or_url, pool);
-}
-
/* Given PATH_OR_URL, which contains either a working copy path or an
absolute URL, a peg revision PEG_REVISION, and a desired revision
REVISION, find the path at which that object exists in REVISION,
@@ -443,11 +396,17 @@ path_or_url_local_style(const char *path
PEG_REVISION and REVISION, and return @c
SVN_ERR_CLIENT_UNRELATED_RESOURCES if it is not the same node.
- If PEG_REVISION's kind is svn_opt_revision_unspecified, interpret it
- as "head" for a URL or "working" for a working-copy path.
+ If PEG_REVISION->kind is 'unspecified', the peg revision is 'head'
+ for a URL or 'working' for a WC path. If REVISION->kind is
+ 'unspecified', the operative revision is the peg revision.
Store the actual revision number of the object in *REV_P, and the
- final resulting URL in *URL_P.
+ final resulting URL in *URL_P. REV_P and/or URL_P may be NULL if not
+ wanted.
+
+ RA_SESSION should be an open RA session pointing at the URL of
+ PATH_OR_URL, or NULL, in which case this function will open its own
+ temporary session.
Use authentication baton cached in CTX to authenticate against the
repository.
@@ -465,28 +424,25 @@ resolve_rev_and_url(svn_revnum_t *rev_p,
{
svn_opt_revision_t peg_rev = *peg_revision;
svn_opt_revision_t start_rev = *revision;
- svn_opt_revision_t *good_rev;
const char *url;
svn_revnum_t rev;
+ /* Default revisions: peg -> working or head; operative -> peg. */
SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev,
svn_path_is_url(path_or_url),
- TRUE,
+ TRUE /* notice_local_mods */,
pool));
/* Run the history function to get the object's (possibly
different) url in REVISION. */
- SVN_ERR(svn_client__repos_locations(&url, &good_rev, NULL, NULL,
+ SVN_ERR(svn_client__repos_locations(&url, &rev, NULL, NULL,
ra_session, path_or_url, &peg_rev,
&start_rev, NULL, ctx, pool));
- /* Resolve good_rev into a real revnum. */
- if (good_rev->kind == svn_opt_revision_unspecified)
- good_rev->kind = svn_opt_revision_head;
- SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, url,
- ra_session, good_rev, pool));
- *rev_p = rev;
- *url_p = url;
+ if (rev_p)
+ *rev_p = rev;
+ if (url_p)
+ *url_p = url;
return SVN_NO_ERROR;
}
@@ -505,6 +461,7 @@ svn_client__ra_session_from_path(svn_ra_
svn_ra_session_t *ra_session;
const char *initial_url;
const char *corrected_url;
+ const char *resolved_url;
SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool,
pool));
@@ -523,14 +480,16 @@ svn_client__ra_session_from_path(svn_ra_
if (corrected_url && svn_path_is_url(path_or_url))
path_or_url = corrected_url;
- SVN_ERR(resolve_rev_and_url(rev_p, url_p, ra_session,
+ SVN_ERR(resolve_rev_and_url(rev_p, &resolved_url, ra_session,
path_or_url, peg_revision, revision,
ctx, pool));
/* Make the session point to the real URL. */
- SVN_ERR(svn_ra_reparent(ra_session, *url_p, pool));
+ SVN_ERR(svn_ra_reparent(ra_session, resolved_url, pool));
*ra_session_p = ra_session;
+ if (url_p)
+ *url_p = resolved_url;
return SVN_NO_ERROR;
}
@@ -614,12 +573,106 @@ svn_client__repos_location_segments(apr_
return SVN_NO_ERROR;
}
+/* Set *START_URL and *END_URL to the URLs that the object URL@PEG_REVNUM
+ * had in revisions START_REVNUM and END_REVNUM. Return an error if the
+ * node cannot be traced back to one of the requested revisions.
+ *
+ * START_URL and/or END_URL may be NULL if not wanted. START_REVNUM and
+ * END_REVNUM must be valid revision numbers except that END_REVNUM may
+ * be SVN_INVALID_REVNUM if END_URL is NULL. RA_SESSION is required.
+ */
+static svn_error_t *
+repos_locations(const char **start_url,
+ const char **end_url,
+ svn_ra_session_t *ra_session,
+ const char *url,
+ svn_revnum_t peg_revnum,
+ svn_revnum_t start_revnum,
+ svn_revnum_t end_revnum,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *repos_url, *start_path, *end_path;
+ apr_array_header_t *revs;
+ apr_hash_t *rev_locs;
+
+ SVN_ERR_ASSERT(peg_revnum != SVN_INVALID_REVNUM);
+ SVN_ERR_ASSERT(start_revnum != SVN_INVALID_REVNUM);
+ SVN_ERR_ASSERT(end_revnum != SVN_INVALID_REVNUM || end_url == NULL);
+
+ /* Avoid a network request in the common easy case. */
+ if (start_revnum == peg_revnum
+ && (end_revnum == peg_revnum || end_revnum == SVN_INVALID_REVNUM))
+ {
+ if (start_url)
+ *start_url = url;
+ if (end_url)
+ *end_url = url;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool));
+
+ revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t));
+ APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum;
+ if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM)
+ APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum;
+
+ SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum,
+ revs, scratch_pool));
+
+ /* We'd better have all the paths we were looking for! */
+ if (start_url)
+ {
+ start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t));
+ if (! start_path)
+ return svn_error_createf
+ (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("Unable to find repository location for '%s' in revision %ld"),
+ url, start_revnum);
+ *start_url = svn_path_url_add_component2(repos_url, start_path + 1,
+ result_pool);
+ }
+
+ if (end_url)
+ {
+ end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t));
+ if (! end_path)
+ return svn_error_createf
+ (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("The location for '%s' for revision %ld does not exist in the "
+ "repository or refers to an unrelated object"),
+ url, end_revnum);
+
+ *end_url = svn_path_url_add_component2(repos_url, end_path + 1,
+ result_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__repos_location(const char **op_url,
+ svn_ra_session_t *ra_session,
+ const char *peg_url,
+ svn_revnum_t peg_revnum,
+ svn_revnum_t op_revnum,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(repos_locations(op_url, NULL,
+ ra_session, peg_url, peg_revnum,
+ op_revnum, SVN_INVALID_REVNUM,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
svn_error_t *
svn_client__repos_locations(const char **start_url,
- svn_opt_revision_t **start_revision,
+ svn_revnum_t *start_revision,
const char **end_url,
- svn_opt_revision_t **end_revision,
+ svn_revnum_t *end_revision,
svn_ra_session_t *ra_session,
const char *path,
const svn_opt_revision_t *revision,
@@ -628,16 +681,11 @@ svn_client__repos_locations(const char *
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- const char *repos_url;
const char *url;
- const char *start_path = NULL;
- const char *end_path = NULL;
const char *local_abspath_or_url;
svn_revnum_t peg_revnum = SVN_INVALID_REVNUM;
svn_revnum_t start_revnum, end_revnum;
svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
- apr_array_header_t *revs;
- apr_hash_t *rev_locs;
apr_pool_t *subpool = svn_pool_create(pool);
/* Ensure that we are given some real revision data to work with.
@@ -739,102 +787,48 @@ svn_client__repos_locations(const char *
ra_session, end, pool));
/* Set the output revision variables. */
- *start_revision = apr_pcalloc(pool, sizeof(**start_revision));
- (*start_revision)->kind = svn_opt_revision_number;
- (*start_revision)->value.number = start_revnum;
- if (end->kind != svn_opt_revision_unspecified)
+ if (start_revision)
{
- *end_revision = apr_pcalloc(pool, sizeof(**end_revision));
- (*end_revision)->kind = svn_opt_revision_number;
- (*end_revision)->value.number = end_revnum;
+ *start_revision = start_revnum;
}
-
- if (start_revnum == peg_revnum && end_revnum == peg_revnum)
+ if (end_revision && end->kind != svn_opt_revision_unspecified)
{
- /* Avoid a network request in the common easy case. */
- *start_url = url;
- if (end->kind != svn_opt_revision_unspecified)
- *end_url = url;
- svn_pool_destroy(subpool);
- return SVN_NO_ERROR;
+ *end_revision = end_revnum;
}
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, subpool));
-
- revs = apr_array_make(subpool, 2, sizeof(svn_revnum_t));
- APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum;
- if (end_revnum != start_revnum)
- APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum;
-
- SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum,
- revs, subpool));
-
- /* We'd better have all the paths we were looking for! */
- start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t));
- if (! start_path)
- return svn_error_createf
- (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
- _("Unable to find repository location for '%s' in revision %ld"),
- path_or_url_local_style(path, pool), start_revnum);
-
- end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t));
- if (! end_path)
- return svn_error_createf
- (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
- _("The location for '%s' for revision %ld does not exist in the "
- "repository or refers to an unrelated object"),
- path_or_url_local_style(path, pool), end_revnum);
-
- /* Set our return variables */
- *start_url = svn_path_url_add_component2(repos_url, start_path + 1, pool);
- if (end->kind != svn_opt_revision_unspecified)
- *end_url = svn_path_url_add_component2(repos_url, end_path + 1, pool);
-
+ SVN_ERR(repos_locations(start_url, end_url,
+ ra_session, url, peg_revnum,
+ start_revnum, end_revnum,
+ pool, subpool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
svn_error_t *
-svn_client__get_youngest_common_ancestor(const char **ancestor_path,
+svn_client__get_youngest_common_ancestor(const char **ancestor_relpath,
+ const char **ancestor_url,
svn_revnum_t *ancestor_revision,
- const char *path_or_url1,
+ const char *url1,
svn_revnum_t rev1,
- const char *path_or_url2,
+ const char *url2,
svn_revnum_t rev2,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_pool_t *sesspool = svn_pool_create(pool);
- svn_ra_session_t *session1, *session2;
+ svn_ra_session_t *session;
+ const char *repos_root_url;
apr_hash_t *history1, *history2;
apr_hash_index_t *hi;
svn_revnum_t yc_revision = SVN_INVALID_REVNUM;
- const char *yc_path = NULL;
- svn_opt_revision_t revision1, revision2;
+ const char *yc_relpath = NULL;
svn_boolean_t has_rev_zero_history1;
svn_boolean_t has_rev_zero_history2;
- revision1.kind = revision2.kind = svn_opt_revision_number;
- revision1.value.number = rev1;
- revision2.value.number = rev2;
-
- /* Open RA sessions for the two locations.
- * ### TODO: As they are assumed to be in the same repository, we
- * should share a single session, tracking the two URLs separately. */
- {
- svn_revnum_t peg;
- const char *url;
-
- SVN_ERR(svn_client__ra_session_from_path(&session1, &peg, &url,
- path_or_url1, NULL,
- &revision1, &revision1,
- ctx, pool));
- SVN_ERR(svn_client__ra_session_from_path(&session2, &peg, &url,
- path_or_url2, NULL,
- &revision2, &revision2,
- ctx, pool));
- }
+ /* Open an RA session for the two locations. */
+ SVN_ERR(svn_client_open_ra_session(&session, url1, ctx, sesspool));
+ SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool));
/* We're going to cheat and use history-as-mergeinfo because it
saves us a bunch of annoying custom data comparisons and such. */
@@ -843,14 +837,15 @@ svn_client__get_youngest_common_ancestor
rev1,
SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM,
- session1, ctx, pool));
+ session, ctx, pool));
+ SVN_ERR(svn_ra_reparent(session, url2, pool));
SVN_ERR(svn_client__get_history_as_mergeinfo(&history2,
&has_rev_zero_history2,
rev2,
SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM,
- session2, ctx, pool));
-
+ session, ctx, pool));
+ /* Close the source and target sessions. */
svn_pool_destroy(sesspool);
/* Loop through the first location's history, check for overlapping
@@ -878,7 +873,7 @@ svn_client__get_youngest_common_ancestor
|| (yc_range->end > yc_revision))
{
yc_revision = yc_range->end;
- yc_path = path + 1;
+ yc_relpath = path + 1;
}
}
}
@@ -886,13 +881,52 @@ svn_client__get_youngest_common_ancestor
/* It's possible that PATH_OR_URL1 and PATH_OR_URL2's only common
history is revision 0. */
- if (!yc_path && has_rev_zero_history1 && has_rev_zero_history2)
+ if (!yc_relpath && has_rev_zero_history1 && has_rev_zero_history2)
{
- yc_path = "/";
+ yc_relpath = "";
yc_revision = 0;
}
- *ancestor_path = yc_path;
- *ancestor_revision = yc_revision;
+ if (ancestor_relpath)
+ *ancestor_relpath = yc_relpath;
+ if (ancestor_url)
+ *ancestor_url
+ = yc_relpath ? svn_path_url_add_component2(repos_root_url, yc_relpath,
+ pool) : NULL;
+ if (ancestor_revision)
+ *ancestor_revision = yc_revision;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__youngest_common_ancestor(const char **ancestor_url,
+ svn_revnum_t *ancestor_rev,
+ const char *path_or_url1,
+ const svn_opt_revision_t *revision1,
+ const char *path_or_url2,
+ const svn_opt_revision_t *revision2,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *sesspool = svn_pool_create(scratch_pool);
+ svn_ra_session_t *session;
+ const char *url1, *url2;
+ svn_revnum_t rev1, rev2;
+
+ /* Resolve the two locations */
+ SVN_ERR(svn_client__ra_session_from_path(&session, &rev1, &url1,
+ path_or_url1, NULL,
+ revision1, revision1,
+ ctx, sesspool));
+ SVN_ERR(resolve_rev_and_url(&rev2, &url2, session,
+ path_or_url2, revision2, revision2,
+ ctx, scratch_pool));
+
+ SVN_ERR(svn_client__get_youngest_common_ancestor(
+ NULL, ancestor_url, ancestor_rev,
+ url1, rev1, url2, rev2, ctx, result_pool));
+
+ svn_pool_destroy(sesspool);
return SVN_NO_ERROR;
}