You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pr...@apache.org on 2013/06/05 11:22:51 UTC
svn commit: r1489765 [6/22] - in /subversion/branches/verify-keep-going: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
contrib/hook-scripts/ contrib/server-side/fsfsfixer/
contrib/server-side/fsfsfixer/fixer/ notes/ subversion/...
Modified: subversion/branches/verify-keep-going/subversion/libsvn_client/export.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_client/export.c?rev=1489765&r1=1489764&r2=1489765&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_client/export.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_client/export.c Wed Jun 5 09:22:43 2013
@@ -193,7 +193,7 @@ export_node(void *baton,
svn_stream_t *dst_stream;
const char *dst_tmp;
svn_error_t *err;
-
+
const char *to_abspath = svn_dirent_join(
eib->to_path,
svn_dirent_skip_ancestor(eib->origin_abspath,
@@ -392,10 +392,11 @@ export_node(void *baton,
suffix = "";
}
- SVN_ERR(svn_subst_build_keywords2
- (&kw, keywords->data,
- apr_psprintf(scratch_pool, "%ld%s", changed_rev, suffix),
- url, tm, author, scratch_pool));
+ SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data,
+ apr_psprintf(scratch_pool, "%ld%s",
+ changed_rev, suffix),
+ url, status->repos_root_url, tm,
+ author, scratch_pool));
}
/* For atomicity, we translate to a tmp file and then rename the tmp file
@@ -495,6 +496,7 @@ open_root_internal(const char *path,
struct edit_baton
{
+ const char *repos_root_url;
const char *root_path;
const char *root_url;
svn_boolean_t force;
@@ -541,6 +543,7 @@ struct file_baton
/* Any keyword vals to be substituted */
const char *revision;
const char *url;
+ const char *repos_root_url;
const char *author;
apr_time_t date;
@@ -662,6 +665,7 @@ add_file(const char *path,
fb->edit_baton = eb;
fb->path = full_path;
fb->url = full_url;
+ fb->repos_root_url = eb->repos_root_url;
fb->pool = pool;
*baton = fb;
@@ -827,8 +831,9 @@ close_file(void *file_baton,
}
if (fb->keywords_val)
- SVN_ERR(svn_subst_build_keywords2(&final_kw, fb->keywords_val->data,
- fb->revision, fb->url, fb->date,
+ SVN_ERR(svn_subst_build_keywords3(&final_kw, fb->keywords_val->data,
+ fb->revision, fb->url,
+ fb->repos_root_url, fb->date,
fb->author, pool));
SVN_ERR(svn_subst_copy_and_translate4(fb->tmppath, fb->path,
@@ -898,7 +903,7 @@ get_editor_ev1(const svn_delta_editor_t
apr_pool_t *scratch_pool)
{
svn_delta_editor_t *editor = svn_delta_default_editor(result_pool);
-
+
editor->set_target_revision = set_target_revision;
editor->open_root = open_root;
editor->add_directory = add_directory;
@@ -948,7 +953,7 @@ add_file_ev2(void *baton,
/* Any keyword vals to be substituted */
const char *revision = NULL;
const char *author = NULL;
- apr_time_t date = 0;
+ apr_time_t date = 0;
/* Look at any properties for additional information. */
if ( (val = svn_hash_gets(props, SVN_PROP_EOL_STYLE)) )
@@ -959,14 +964,14 @@ add_file_ev2(void *baton,
if ( (val = svn_hash_gets(props, SVN_PROP_EXECUTABLE)) )
executable_val = val;
-
+
/* Try to fill out the baton's keywords-structure too. */
if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_REV)) )
revision = val->data;
if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_DATE)) )
SVN_ERR(svn_time_from_cstring(&date, val->data, scratch_pool));
-
+
if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_LAST_AUTHOR)) )
author = val->data;
@@ -1012,9 +1017,10 @@ add_file_ev2(void *baton,
}
if (keywords_val)
- SVN_ERR(svn_subst_build_keywords2(&final_kw, keywords_val->data,
- revision, full_url, date,
- author, scratch_pool));
+ SVN_ERR(svn_subst_build_keywords3(&final_kw, keywords_val->data,
+ revision, full_url,
+ eb->repos_root_url,
+ date, author, scratch_pool));
/* Writing through a translated stream is more efficient than
reading through one, so we wrap TMP_STREAM and not CONTENTS. */
@@ -1076,7 +1082,7 @@ add_directory_ev2(void *baton,
if ( (val = svn_hash_gets(props, SVN_PROP_EXTERNALS)) )
SVN_ERR(add_externals(eb->externals, full_path, val));
-
+
if (eb->notify_func)
{
svn_wc_notify_t *notify = svn_wc_create_notify(full_path,
@@ -1247,6 +1253,7 @@ export_file(const char *from_path_or_url
fb->path = eb->root_path;
fb->url = eb->root_url;
fb->pool = scratch_pool;
+ fb->repos_root_url = eb->repos_root_url;
/* Copied from apply_textdelta(). */
SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath,
@@ -1342,15 +1349,12 @@ export_directory(const char *from_path_o
if (! ignore_externals && depth == svn_depth_infinity)
{
- const char *repos_root_url;
const char *to_abspath;
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
- scratch_pool));
SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, scratch_pool));
SVN_ERR(svn_client__export_externals(eb->externals,
from_path_or_url,
- to_abspath, repos_root_url,
+ to_abspath, eb->repos_root_url,
depth, native_eol,
ignore_keywords,
ctx, scratch_pool));
@@ -1404,6 +1408,7 @@ svn_client_export5(svn_revnum_t *result_
peg_revision,
revision, ctx, pool));
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &eb->repos_root_url, pool));
eb->root_path = to_path;
eb->root_url = loc->url;
eb->force = overwrite;
Modified: subversion/branches/verify-keep-going/subversion/libsvn_client/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_client/externals.c?rev=1489765&r1=1489764&r2=1489765&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_client/externals.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_client/externals.c Wed Jun 5 09:22:43 2013
@@ -24,7 +24,7 @@
/* ==================================================================== */
-
+
/*** Includes. ***/
#include <apr_uri.h>
@@ -42,7 +42,7 @@
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
-
+
/* Remove the directory at LOCAL_ABSPATH from revision control, and do the
* same to any revision controlled directories underneath LOCAL_ABSPATH
Modified: subversion/branches/verify-keep-going/subversion/libsvn_client/import.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_client/import.c?rev=1489765&r1=1489764&r2=1489765&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_client/import.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_client/import.c Wed Jun 5 09:22:43 2013
@@ -134,9 +134,9 @@ send_file_contents(const char *local_abs
}
if (keywords_val)
- SVN_ERR(svn_subst_build_keywords2(&keywords, keywords_val->data,
+ SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
APR_STRINGIFY(SVN_INVALID_REVNUM),
- "", 0, "", pool));
+ "", "", 0, "", pool));
else
keywords = NULL;
@@ -593,6 +593,9 @@ import_dir(const svn_delta_editor_t *edi
* DEPTH is the depth at which to import PATH; it behaves as for
* svn_client_import4().
*
+ * BASE_REV is the revision to use for the root of the commit. We
+ * checked the preconditions against this revision.
+ *
* NEW_ENTRIES is an ordered array of path components that must be
* created in the repository (where the ordering direction is
* parent-to-child). If PATH is a directory, NEW_ENTRIES may be empty
@@ -641,6 +644,7 @@ import(const char *local_abspath,
const svn_delta_editor_t *editor,
void *edit_baton,
svn_depth_t depth,
+ svn_revnum_t base_rev,
apr_hash_t *excludes,
apr_hash_t *autoprops,
apr_array_header_t *local_ignores,
@@ -662,14 +666,13 @@ import(const char *local_abspath,
import_ctx->autoprops = autoprops;
svn_magic__init(&import_ctx->magic_cookie, pool);
- /* Get a root dir baton. We pass an invalid revnum to open_root
- to mean "base this on the youngest revision". Should we have an
- SVN_YOUNGEST_REVNUM defined for these purposes? */
- SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
- pool, &root_baton));
+ /* Get a root dir baton. We pass the revnum we used for testing our
+ assumptions and obtaining inherited properties. */
+ SVN_ERR(editor->open_root(edit_baton, base_rev, pool, &root_baton));
/* Import a file or a directory tree. */
- SVN_ERR(svn_io_stat_dirent(&dirent, local_abspath, FALSE, pool, pool));
+ SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE,
+ pool, pool));
/* Make the intermediate directory components necessary for properly
rooting our import source tree. */
@@ -808,6 +811,9 @@ svn_client_import5(const char *path,
apr_hash_t *autoprops = NULL;
apr_array_header_t *global_ignores;
apr_array_header_t *local_ignores_arr;
+ svn_revnum_t base_rev;
+ apr_array_header_t *inherited_props = NULL;
+ apr_hash_t *url_props = NULL;
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
@@ -848,10 +854,11 @@ svn_client_import5(const char *path,
SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
ctx, scratch_pool, iterpool));
+ SVN_ERR(svn_ra_get_latest_revnum(ra_session, &base_rev, iterpool));
+
/* Figure out all the path components we need to create just to have
a place to stick our imported tree. */
- SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
- iterpool));
+ SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
/* We can import into directories, but if a file already exists, that's
an error. */
@@ -870,8 +877,7 @@ svn_client_import5(const char *path,
APR_ARRAY_PUSH(new_entries, const char *) = dir;
SVN_ERR(svn_ra_reparent(ra_session, url, iterpool));
- SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
- iterpool));
+ SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
}
/* Reverse the order of the components we added to our NEW_ENTRIES array. */
@@ -894,6 +900,17 @@ svn_client_import5(const char *path,
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
log_msg, ctx, scratch_pool));
+ /* Obtain properties before opening the commit editor, as at that point we are
+ not allowed to use the existing ra-session */
+ if (! no_ignore /*|| ! no_autoprops*/)
+ {
+ SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &url_props, "",
+ base_rev, SVN_DIRENT_KIND, scratch_pool));
+
+ SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", base_rev,
+ scratch_pool, iterpool));
+ }
+
/* Fetch RA commit editor. */
SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
svn_client__get_shim_callbacks(ctx->wc_ctx,
@@ -906,8 +923,13 @@ svn_client_import5(const char *path,
/* Get inherited svn:auto-props, svn:global-ignores, and
svn:ignores for the location we are importing to. */
if (!no_autoprops)
- SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
- scratch_pool, iterpool));
+ {
+ /* ### This should use inherited_props and url_props to avoid creating
+ another ra session to obtain the same values, but using a possibly
+ different HEAD revision */
+ SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
+ scratch_pool, iterpool));
+ }
if (no_ignore)
{
global_ignores = NULL;
@@ -915,38 +937,48 @@ svn_client_import5(const char *path,
}
else
{
- svn_opt_revision_t rev;
apr_array_header_t *config_ignores;
- apr_hash_t *local_ignores_hash;
+ svn_string_t *val;
+ int i;
+
+ global_ignores = apr_array_make(scratch_pool, 64, sizeof(const char *));
- SVN_ERR(svn_client__get_inherited_ignores(&global_ignores, url, ctx,
- scratch_pool, iterpool));
SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config,
scratch_pool));
global_ignores = apr_array_append(scratch_pool, global_ignores,
config_ignores);
- rev.kind = svn_opt_revision_head;
- SVN_ERR(svn_client_propget5(&local_ignores_hash, NULL, SVN_PROP_IGNORE, url,
- &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
- scratch_pool, scratch_pool));
+ val = svn_hash_gets(url_props, SVN_PROP_INHERITABLE_IGNORES);
+ if (val)
+ svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
+ FALSE, scratch_pool);
+
+ for (i = 0; i < inherited_props->nelts; i++)
+ {
+ svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
+ inherited_props, i, svn_prop_inherited_item_t *);
+
+ val = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES);
+
+ if (val)
+ svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
+ FALSE, scratch_pool);
+ }
local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *));
- if (apr_hash_count(local_ignores_hash))
+ val = svn_hash_gets(url_props, SVN_PROP_IGNORE);
+
+ if (val)
{
- svn_string_t *propval = svn_hash_gets(local_ignores_hash, url);
- if (propval)
- {
- svn_cstring_split_append(local_ignores_arr, propval->data,
- "\n\r\t\v ", FALSE, scratch_pool);
- }
+ svn_cstring_split_append(local_ignores_arr, val->data,
+ "\n\r\t\v ", FALSE, scratch_pool);
}
}
/* If an error occurred during the commit, abort the edit and return
the error. We don't even care if the abort itself fails. */
if ((err = import(local_abspath, new_entries, editor, edit_baton,
- depth, excludes, autoprops, local_ignores_arr,
+ depth, base_rev, excludes, autoprops, local_ignores_arr,
global_ignores, no_ignore, no_autoprops,
ignore_unknown_node_types, filter_callback,
filter_baton, ctx, iterpool)))
Modified: subversion/branches/verify-keep-going/subversion/libsvn_client/iprops.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_client/iprops.c?rev=1489765&r1=1489764&r2=1489765&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_client/iprops.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_client/iprops.c Wed Jun 5 09:22:43 2013
@@ -118,7 +118,7 @@ svn_client__iprop_relpaths_to_urls(apr_a
{
elt->path_or_url = svn_path_url_add_component2(repos_root_url,
elt->path_or_url,
- result_pool);
+ result_pool);
}
}
return SVN_NO_ERROR;
Modified: subversion/branches/verify-keep-going/subversion/libsvn_client/list.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_client/list.c?rev=1489765&r1=1489764&r2=1489765&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_client/list.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_client/list.c Wed Jun 5 09:22:43 2013
@@ -38,8 +38,8 @@
#include "svn_private_config.h"
/* Prototypes for referencing before declaration */
-static svn_error_t *
-list_externals(apr_hash_t *externals,
+static svn_error_t *
+list_externals(apr_hash_t *externals,
svn_depth_t depth,
apr_uint32_t dirent_fields,
svn_boolean_t fetch_locks,
@@ -78,9 +78,9 @@ list_internal(const char *path_or_url,
objects and FS_PATH is the absolute filesystem path of the RA session.
Use SCRATCH_POOL for temporary allocations.
- If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS
+ If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS
hash table whose keys are URLs of the directory which has externals
- definitions, and whose values are the externals description text.
+ definitions, and whose values are the externals description text.
Allocate the hash's keys and values in RESULT_POOL.
EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items
@@ -113,13 +113,13 @@ get_dir_contents(apr_uint32_t dirent_fie
if (depth == svn_depth_empty)
return SVN_NO_ERROR;
-
+
/* Get the directory's entries. If externals hash is non-NULL, get its
properties also. Ignore any not-authorized errors. */
- err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL,
+ err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL,
externals ? &prop_hash : NULL,
dir, rev, dirent_fields, scratch_pool);
-
+
if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) ||
(err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN)))
{
@@ -127,16 +127,16 @@ get_dir_contents(apr_uint32_t dirent_fie
return SVN_NO_ERROR;
}
SVN_ERR(err);
-
- /* Filter out svn:externals from all properties hash. */
- if (prop_hash)
+
+ /* Filter out svn:externals from all properties hash. */
+ if (prop_hash)
prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
if (prop_val)
{
const char *url;
SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
-
+
svn_hash_sets(externals,
svn_path_url_add_component2(url, dir, result_pool),
svn_string_dup(prop_val, result_pool));
@@ -146,7 +146,7 @@ get_dir_contents(apr_uint32_t dirent_fie
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
/* Sort the hash, so we can call the callback in a "deterministic" order. */
- array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically,
+ array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically,
scratch_pool);
for (i = 0; i < array->nelts; ++i)
{
@@ -172,12 +172,12 @@ get_dir_contents(apr_uint32_t dirent_fie
|| depth == svn_depth_infinity)
SVN_ERR(list_func(baton, path, the_ent, lock, fs_path,
external_parent_url, external_target, iterpool));
-
- /* If externals is non-NULL, populate the externals hash table
+
+ /* If externals is non-NULL, populate the externals hash table
recursively for all directory entries. */
if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
SVN_ERR(get_dir_contents(dirent_fields, path, rev,
- ra_session, locks, fs_path, depth, ctx,
+ ra_session, locks, fs_path, depth, ctx,
externals, external_parent_url,
external_target, list_func, baton,
result_pool, iterpool));
@@ -297,36 +297,36 @@ svn_client__ra_stat_compatible(svn_ra_se
}
/* List the file/directory entries for PATH_OR_URL at REVISION.
- The actual node revision selected is determined by the path as
- it exists in PEG_REVISION.
-
- If DEPTH is svn_depth_infinity, then list all file and directory entries
- recursively. Else if DEPTH is svn_depth_files, list all files under
+ The actual node revision selected is determined by the path as
+ it exists in PEG_REVISION.
+
+ If DEPTH is svn_depth_infinity, then list all file and directory entries
+ recursively. Else if DEPTH is svn_depth_files, list all files under
PATH_OR_URL (if any), but not subdirectories. Else if DEPTH is
svn_depth_immediates, list all files and include immediate
subdirectories (at svn_depth_empty). Else if DEPTH is
svn_depth_empty, just list PATH_OR_URL with none of its entries.
-
+
DIRENT_FIELDS controls which fields in the svn_dirent_t's are
filled in. To have them totally filled in use SVN_DIRENT_ALL,
otherwise simply bitwise OR together the combination of SVN_DIRENT_*
fields you care about.
-
+
If FETCH_LOCKS is TRUE, include locks when reporting directory entries.
-
- If INCLUDE_EXTERNALS is TRUE, also list all external items
+
+ If INCLUDE_EXTERNALS is TRUE, also list all external items
reached by recursion. DEPTH value passed to the original list target
- applies for the externals also. EXTERNAL_PARENT_URL is url of the
+ applies for the externals also. EXTERNAL_PARENT_URL is url of the
directory which has the externals definitions. EXTERNAL_TARGET is the
target subdirectory of externals definitions.
- Report directory entries by invoking LIST_FUNC/BATON.
+ Report directory entries by invoking LIST_FUNC/BATON.
Pass EXTERNAL_PARENT_URL and EXTERNAL_TARGET to LIST_FUNC when external
items are listed, otherwise both are set to NULL.
-
+
Use authentication baton cached in CTX to authenticate against the
repository.
-
+
Use POOL for all allocations.
*/
static svn_error_t *
@@ -397,7 +397,7 @@ list_internal(const char *path_or_url,
/* Report the dirent for the target. */
SVN_ERR(list_func(baton, "", dirent, locks
? (svn_hash_gets(locks, fs_path))
- : NULL, fs_path, external_parent_url,
+ : NULL, fs_path, external_parent_url,
external_target, pool));
if (dirent->kind == svn_node_dir
@@ -405,22 +405,22 @@ list_internal(const char *path_or_url,
|| depth == svn_depth_immediates
|| depth == svn_depth_infinity))
SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks,
- fs_path, depth, ctx, externals,
+ fs_path, depth, ctx, externals,
external_parent_url, external_target, list_func,
baton, pool, pool));
-
+
/* We handle externals after listing entries under path_or_url, so that
handling external items (and any errors therefrom) doesn't delay
the primary operation. */
if (include_externals && apr_hash_count(externals))
{
- /* The 'externals' hash populated by get_dir_contents() is processed
+ /* The 'externals' hash populated by get_dir_contents() is processed
here. */
- SVN_ERR(list_externals(externals, depth, dirent_fields,
+ SVN_ERR(list_externals(externals, depth, dirent_fields,
fetch_locks, list_func, baton,
ctx, pool));
- }
-
+ }
+
return SVN_NO_ERROR;
}
@@ -511,7 +511,7 @@ list_external_items(apr_array_header_t *
/* List external items defined on each external in EXTERNALS, a const char *
externals_parent_url(url of the directory which has the externals
definitions) of all externals mapping to the svn_string_t * externals_desc
- (externals description text). All other options are the same as those
+ (externals description text). All other options are the same as those
passed to svn_client_list(). */
static svn_error_t *
list_externals(apr_hash_t *externals,
Modified: subversion/branches/verify-keep-going/subversion/libsvn_client/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_client/log.c?rev=1489765&r1=1489764&r2=1489765&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_client/log.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_client/log.c Wed Jun 5 09:22:43 2013
@@ -42,6 +42,7 @@
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
+#include <assert.h>
/*** Getting misc. information ***/
@@ -260,57 +261,225 @@ limit_receiver(void *baton, svn_log_entr
return rb->receiver(rb->baton, log_entry, pool);
}
-
-/*** Public Interface. ***/
+/* Resolve the URLs or WC path in TARGETS as per the svn_client_log5 API.
+ The limitations on TARGETS specified by svn_client_log5 are enforced here.
+ So TARGETS can only contain a single WC path or a URL and zero or more
+ relative paths -- anything else will raise an error.
-svn_error_t *
-svn_client_log5(const apr_array_header_t *targets,
- const svn_opt_revision_t *peg_revision,
- const apr_array_header_t *revision_ranges,
- int limit,
- svn_boolean_t discover_changed_paths,
- svn_boolean_t strict_node_history,
- svn_boolean_t include_merged_revisions,
- const apr_array_header_t *revprops,
- svn_log_entry_receiver_t real_receiver,
- void *real_receiver_baton,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ PEG_REVISION, TARGETS, and CTX are as per svn_client_log5.
+
+ If TARGETS contains a single WC path then set *RA_TARGET to the absolute
+ path of that single path if PEG_REVISION is dependent on the working copy
+ (e.g. PREV). Otherwise set *RA_TARGET to the corresponding URL for the
+ single WC path. Set *RELATIVE_TARGETS to an array with a single
+ element "".
+
+ If TARGETS contains only a single URL, then set *RA_TARGET to a copy of
+ that URL and *RELATIVE_TARGETS to an array with a single element "".
+
+ If TARGETS contains a single URL and one or more relative paths, then
+ set *RA_TARGET to a copy of that URL and *RELATIVE_TARGETS to a copy of
+ each relative path after the URL.
+
+ If *PEG_REVISION is svn_opt_revision_unspecified, then *PEG_REVISION is
+ set to svn_opt_revision_head for URLs or svn_opt_revision_working for a
+ WC path.
+
+ *RA_TARGET and *RELATIVE_TARGETS are allocated in RESULT_POOL. */
+static svn_error_t *
+resolve_log_targets(apr_array_header_t **relative_targets,
+ const char **ra_target,
+ svn_opt_revision_t *peg_revision,
+ const apr_array_header_t *targets,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- svn_ra_session_t *ra_session;
- const char *url_or_path;
- svn_boolean_t has_log_revprops;
- apr_array_header_t *condensed_targets;
- svn_opt_revision_t session_opt_rev;
- const char *ra_target;
- pre_15_receiver_baton_t rb = {0};
- apr_pool_t *iterpool;
int i;
- svn_opt_revision_t peg_rev;
- svn_boolean_t url_targets = FALSE;
+ svn_boolean_t url_targets;
+
+ /* Per svn_client_log5, TARGETS contains either a URL followed by zero or
+ more relative paths, or one working copy path. */
+ const char *url_or_path = APR_ARRAY_IDX(targets, 0, const char *);
+
+ /* svn_client_log5 requires at least one target. */
+ if (targets->nelts == 0)
+ return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("No valid target found"));
+
+ /* Initialize the output array. At a minimum, we need room for one
+ (possibly empty) relpath. Otherwise, we have to hold a relpath
+ for every item in TARGETS except the first. */
+ *relative_targets = apr_array_make(result_pool,
+ MAX(1, targets->nelts - 1),
+ sizeof(const char *));
- if (revision_ranges->nelts == 0)
+ if (svn_path_is_url(url_or_path))
{
- return svn_error_create
- (SVN_ERR_CLIENT_BAD_REVISION, NULL,
- _("Missing required revision specification"));
+ /* An unspecified PEG_REVISION for a URL path defaults
+ to svn_opt_revision_head. */
+ if (peg_revision->kind == svn_opt_revision_unspecified)
+ peg_revision->kind = svn_opt_revision_head;
+
+ /* The logic here is this: If we get passed one argument, we assume
+ it is the full URL to a file/dir we want log info for. If we get
+ a URL plus some paths, then we assume that the URL is the base,
+ and that the paths passed are relative to it. */
+ if (targets->nelts > 1)
+ {
+ /* We have some paths, let's use them. Start after the URL. */
+ for (i = 1; i < targets->nelts; i++)
+ {
+ const char *target;
+
+ target = APR_ARRAY_IDX(targets, i, const char *);
+
+ if (svn_path_is_url(target) || svn_dirent_is_absolute(target))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a relative path"),
+ target);
+
+ APR_ARRAY_PUSH(*relative_targets, const char *) =
+ apr_pstrdup(result_pool, target);
+ }
+ }
+ else
+ {
+ /* If we have a single URL, then the session will be rooted at
+ it, so just send an empty string for the paths we are
+ interested in. */
+ APR_ARRAY_PUSH(*relative_targets, const char *) = "";
+ }
+
+ /* Remember that our targets are URLs. */
+ url_targets = TRUE;
}
+ else /* WC path target. */
+ {
+ const char *target;
+ const char *target_abspath;
- /* Make a copy of PEG_REVISION, we may need to change it to a
- default value. */
- peg_rev = *peg_revision;
+ url_targets = FALSE;
+ if (targets->nelts > 1)
+ return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("When specifying working copy paths, only "
+ "one target may be given"));
- /* Use the passed URL, if there is one. */
- url_or_path = APR_ARRAY_IDX(targets, 0, const char *);
- session_opt_rev.kind = svn_opt_revision_unspecified;
+ /* An unspecified PEG_REVISION for a working copy path defaults
+ to svn_opt_revision_working. */
+ if (peg_revision->kind == svn_opt_revision_unspecified)
+ peg_revision->kind = svn_opt_revision_working;
- for (i = 0; i < revision_ranges->nelts; i++)
+ /* Get URLs for each target */
+ target = APR_ARRAY_IDX(targets, 0, const char *);
+
+ SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, scratch_pool));
+ SVN_ERR(svn_wc__node_get_url(&url_or_path, ctx->wc_ctx, target_abspath,
+ scratch_pool, scratch_pool));
+ APR_ARRAY_PUSH(*relative_targets, const char *) = "";
+ }
+
+ /* If this is a revision type that requires access to the working copy,
+ * we use our initial target path to figure out where to root the RA
+ * session, otherwise we use our URL. */
+ if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
+ {
+ if (url_targets)
+ return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("PREV, BASE, or COMMITTED revision "
+ "keywords are invalid for URL"));
+
+ else
+ SVN_ERR(svn_dirent_get_absolute(
+ ra_target, APR_ARRAY_IDX(targets, 0, const char *), result_pool));
+ }
+ else
+ {
+ *ra_target = apr_pstrdup(result_pool, url_or_path);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Keep track of oldest and youngest opt revs found.
+
+ If REV is younger than *YOUNGEST_REV, or *YOUNGEST_REV is
+ svn_opt_revision_unspecified, then set *YOUNGEST_REV equal to REV.
+
+ If REV is older than *OLDEST_REV, or *OLDEST_REV is
+ svn_opt_revision_unspecified, then set *OLDEST_REV equal to REV. */
+static void
+find_youngest_and_oldest_revs(svn_revnum_t *youngest_rev,
+ svn_revnum_t *oldest_rev,
+ svn_revnum_t rev)
+{
+ /* Is REV younger than YOUNGEST_REV? */
+ if (! SVN_IS_VALID_REVNUM(*youngest_rev)
+ || rev > *youngest_rev)
+ *youngest_rev = rev;
+
+ if (! SVN_IS_VALID_REVNUM(*oldest_rev)
+ || rev < *oldest_rev)
+ *oldest_rev = rev;
+}
+
+typedef struct rev_range_t
+{
+ svn_revnum_t range_start;
+ svn_revnum_t range_end;
+} rev_range_t;
+
+/* Convert array of svn_opt_revision_t ranges to an array of svn_revnum_t
+ ranges.
+
+ Given a log target URL_OR_ABSPATH@PEG_REV and an array of
+ svn_opt_revision_range_t's OPT_REV_RANGES, resolve the opt revs in
+ OPT_REV_RANGES to svn_revnum_t's and return these in *REVISION_RANGES, an
+ array of rev_range_t *.
+
+ Set *YOUNGEST_REV and *OLDEST_REV to the youngest and oldest revisions
+ found in *REVISION_RANGES.
+
+ If the repository needs to be contacted to resolve svn_opt_revision_date or
+ svn_opt_revision_head revisions, then the session used to do this is
+ RA_SESSION; it must be an open session to any URL in the right repository.
+*/
+static svn_error_t*
+convert_opt_rev_array_to_rev_range_array(
+ apr_array_header_t **revision_ranges,
+ svn_revnum_t *youngest_rev,
+ svn_revnum_t *oldest_rev,
+ svn_ra_session_t *ra_session,
+ const char *url_or_abspath,
+ const apr_array_header_t *opt_rev_ranges,
+ const svn_opt_revision_t *peg_rev,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ svn_revnum_t head_rev = SVN_INVALID_REVNUM;
+
+ /* Initialize the input/output parameters. */
+ *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
+
+ /* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
+ and oldest revision range that spans all of OPT_REV_RANGES. */
+ *revision_ranges = apr_array_make(result_pool, opt_rev_ranges->nelts,
+ sizeof(rev_range_t *));
+
+ for (i = 0; i < opt_rev_ranges->nelts; i++)
{
svn_opt_revision_range_t *range;
+ rev_range_t *rev_range;
+ svn_boolean_t start_same_as_end = FALSE;
- range = APR_ARRAY_IDX(revision_ranges, i, svn_opt_revision_range_t *);
+ range = APR_ARRAY_IDX(opt_rev_ranges, i, svn_opt_revision_range_t *);
+ /* Right now RANGE can be any valid pair of svn_opt_revision_t's. We
+ will now convert all RANGEs in place to the corresponding
+ svn_opt_revision_number kind. */
if ((range->start.kind != svn_opt_revision_unspecified)
&& (range->end.kind == svn_opt_revision_unspecified))
{
@@ -329,15 +498,15 @@ svn_client_log5(const apr_array_header_t
/* Default to any specified peg revision. Otherwise, if the
* first target is a URL, then we default to HEAD:0. Lastly,
* the default is BASE:0 since WC@HEAD may not exist. */
- if (peg_rev.kind == svn_opt_revision_unspecified)
+ if (peg_rev->kind == svn_opt_revision_unspecified)
{
- if (svn_path_is_url(url_or_path))
+ if (svn_path_is_url(url_or_abspath))
range->start.kind = svn_opt_revision_head;
else
range->start.kind = svn_opt_revision_base;
}
else
- range->start = peg_rev;
+ range->start = *peg_rev;
if (range->end.kind == svn_opt_revision_unspecified)
{
@@ -354,163 +523,117 @@ svn_client_log5(const apr_array_header_t
_("Missing required revision specification"));
}
- /* Determine the revision to open the RA session to. */
- if (session_opt_rev.kind == svn_opt_revision_unspecified)
+ /* Does RANGE describe a single svn_opt_revision_t? */
+ if (range->start.kind == range->end.kind)
{
- if (range->start.kind == svn_opt_revision_number &&
- range->end.kind == svn_opt_revision_number)
+ if (range->start.kind == svn_opt_revision_number)
{
- session_opt_rev =
- (range->start.value.number > range->end.value.number ?
- range->start : range->end);
+ if (range->start.value.number == range->end.value.number)
+ start_same_as_end = TRUE;
}
- else if (range->start.kind == svn_opt_revision_head ||
- range->end.kind == svn_opt_revision_head)
+ else if (range->start.kind == svn_opt_revision_date)
{
- session_opt_rev.kind = svn_opt_revision_head;
+ if (range->start.value.date == range->end.value.date)
+ start_same_as_end = TRUE;
}
- else if (range->start.kind == svn_opt_revision_date &&
- range->end.kind == svn_opt_revision_date)
+ else
{
- session_opt_rev =
- (range->start.value.date > range->end.value.date ?
- range->start : range->end);
+ start_same_as_end = TRUE;
}
}
- }
-
- /* Use the passed URL, if there is one. */
- if (svn_path_is_url(url_or_path))
- {
- /* Initialize this array, since we'll be building it below */
- condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
-
- /* The logic here is this: If we get passed one argument, we assume
- it is the full URL to a file/dir we want log info for. If we get
- a URL plus some paths, then we assume that the URL is the base,
- and that the paths passed are relative to it. */
- if (targets->nelts > 1)
- {
- /* We have some paths, let's use them. Start after the URL. */
- for (i = 1; i < targets->nelts; i++)
- {
- const char *target;
-
- target = APR_ARRAY_IDX(targets, i, const char *);
- if (svn_path_is_url(target) || svn_dirent_is_absolute(target))
- return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("'%s' is not a relative path"),
- target);
-
- APR_ARRAY_PUSH(condensed_targets, const char *) = target;
- }
- }
+ rev_range = apr_palloc(result_pool, sizeof(*rev_range));
+ SVN_ERR(svn_client__get_revision_number(
+ &rev_range->range_start, &head_rev,
+ ctx->wc_ctx, url_or_abspath, ra_session,
+ &range->start, scratch_pool));
+ if (start_same_as_end)
+ rev_range->range_end = rev_range->range_start;
else
- {
- /* If we have a single URL, then the session will be rooted at
- it, so just send an empty string for the paths we are
- interested in. */
- APR_ARRAY_PUSH(condensed_targets, const char *) = "";
- }
-
- /* Remember that our targets are URLs. */
- url_targets = TRUE;
+ SVN_ERR(svn_client__get_revision_number(
+ &rev_range->range_end, &head_rev,
+ ctx->wc_ctx, url_or_abspath, ra_session,
+ &range->end, scratch_pool));
+
+ /* Possibly update the oldest and youngest revisions requested. */
+ find_youngest_and_oldest_revs(youngest_rev,
+ oldest_rev,
+ rev_range->range_start);
+ find_youngest_and_oldest_revs(youngest_rev,
+ oldest_rev,
+ rev_range->range_end);
+ APR_ARRAY_PUSH(*revision_ranges, rev_range_t *) = rev_range;
}
- else
- {
- apr_array_header_t *target_urls;
- apr_array_header_t *real_targets;
- /* See FIXME about multiple wc targets, below. */
- if (targets->nelts > 1)
- return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("When specifying working copy paths, only "
- "one target may be given"));
+ return SVN_NO_ERROR;
+}
- /* An unspecified PEG_REVISION for a working copy path defaults
- to svn_opt_revision_working. */
- if (peg_rev.kind == svn_opt_revision_unspecified)
- peg_rev.kind = svn_opt_revision_working;
+static int
+compare_rev_to_segment(const void *key_p,
+ const void *element_p)
+{
+ svn_revnum_t rev =
+ * (svn_revnum_t *)key_p;
+ const svn_location_segment_t *segment =
+ *((const svn_location_segment_t * const *) element_p);
+
+ if (rev < segment->range_start)
+ return -1;
+ else if (rev > segment->range_end)
+ return 1;
+ else
+ return 0;
+}
- /* Get URLs for each target */
- target_urls = apr_array_make(pool, 1, sizeof(const char *));
- real_targets = apr_array_make(pool, 1, sizeof(const char *));
- iterpool = svn_pool_create(pool);
- for (i = 0; i < targets->nelts; i++)
- {
- const char *url;
- const char *target = APR_ARRAY_IDX(targets, i, const char *);
- const char *target_abspath;
-
- svn_pool_clear(iterpool);
- SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool));
- SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, target_abspath,
- pool, iterpool));
-
- if (! url)
- return svn_error_createf
- (SVN_ERR_ENTRY_MISSING_URL, NULL,
- _("Entry '%s' has no URL"),
- svn_dirent_local_style(target, pool));
-
- APR_ARRAY_PUSH(target_urls, const char *) = url;
- APR_ARRAY_PUSH(real_targets, const char *) = target;
- }
-
- /* if we have no valid target_urls, just exit. */
- if (target_urls->nelts == 0)
- return SVN_NO_ERROR;
-
- /* Find the base URL and condensed targets relative to it. */
- SVN_ERR(svn_uri_condense_targets(&url_or_path, &condensed_targets,
- target_urls, TRUE, pool, iterpool));
-
- if (condensed_targets->nelts == 0)
- APR_ARRAY_PUSH(condensed_targets, const char *) = "";
-
- /* 'targets' now becomes 'real_targets', which has bogus,
- unversioned things removed from it. */
- targets = real_targets;
- svn_pool_destroy(iterpool);
- }
-
-
- {
- svn_client__pathrev_t *actual_loc;
-
- /* If this is a revision type that requires access to the working copy,
- * we use our initial target path to figure out where to root the RA
- * session, otherwise we use our URL. */
- if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_rev.kind))
- {
- if (url_targets)
- SVN_ERR(svn_uri_condense_targets(&ra_target, NULL, targets,
- TRUE, pool, pool));
- else
- SVN_ERR(svn_dirent_condense_targets(&ra_target, NULL, targets,
- TRUE, pool, pool));
- }
- else
- ra_target = url_or_path;
-
- SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &actual_loc,
- ra_target, NULL,
- &peg_rev, &session_opt_rev,
- ctx, pool));
+/* Run svn_ra_get_log2 for PATHS, one or more paths relative to RA_SESSION's
+ common parent, for each revision in REVISION_RANGES, an array of
+ rev_range_t.
+
+ RA_SESSION is an open session pointing to ACTUAL_LOC.
+
+ LOG_SEGMENTS is an array of svn_location_segment_t * items representing the
+ history of PATHS from the oldest to youngest revisions found in
+ REVISION_RANGES.
+
+ The TARGETS, LIMIT, DISCOVER_CHANGED_PATHS, STRICT_NODE_HISTORY,
+ INCLUDE_MERGED_REVISIONS, REVPROPS, REAL_RECEIVER, and REAL_RECEIVER_BATON
+ parameters are all as per the svn_client_log5 API. */
+static svn_error_t *
+run_ra_get_log(apr_array_header_t *revision_ranges,
+ apr_array_header_t *paths,
+ apr_array_header_t *log_segments,
+ svn_client__pathrev_t *actual_loc,
+ svn_ra_session_t *ra_session,
+ /* The following are as per svn_client_log5. */
+ const apr_array_header_t *targets,
+ int limit,
+ svn_boolean_t discover_changed_paths,
+ svn_boolean_t strict_node_history,
+ svn_boolean_t include_merged_revisions,
+ const apr_array_header_t *revprops,
+ svn_log_entry_receiver_t real_receiver,
+ void *real_receiver_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ pre_15_receiver_baton_t rb = {0};
+ apr_pool_t *iterpool;
+ svn_boolean_t has_log_revprops;
- SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
- SVN_RA_CAPABILITY_LOG_REVPROPS, pool));
+ SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
+ SVN_RA_CAPABILITY_LOG_REVPROPS,
+ scratch_pool));
- if (!has_log_revprops) {
+ if (!has_log_revprops)
+ {
/* See above pre-1.5 notes. */
rb.ctx = ctx;
/* Create ra session on first use */
- rb.ra_session_pool = pool;
+ rb.ra_session_pool = scratch_pool;
rb.ra_session_url = actual_loc->url;
}
- }
/* It's a bit complex to correctly handle the special revision words
* such as "BASE", "COMMITTED", and "PREV". For example, if the
@@ -559,35 +682,54 @@ svn_client_log5(const apr_array_header_t
* epg wonders if the repository could send a unified stream of log
* entries if the paths and revisions were passed down.
*/
- iterpool = svn_pool_create(pool);
+ iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < revision_ranges->nelts; i++)
{
- svn_revnum_t start_revnum, end_revnum, youngest_rev = SVN_INVALID_REVNUM;
+ const char *old_session_url;
const char *path = APR_ARRAY_IDX(targets, 0, const char *);
const char *local_abspath_or_url;
- svn_opt_revision_range_t *range;
+ rev_range_t *range;
limit_receiver_baton_t lb;
svn_log_entry_receiver_t passed_receiver;
void *passed_receiver_baton;
const apr_array_header_t *passed_receiver_revprops;
+ svn_location_segment_t **matching_segment;
+ svn_revnum_t younger_rev;
svn_pool_clear(iterpool);
if (!svn_path_is_url(path))
- SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, iterpool));
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path,
+ iterpool));
else
local_abspath_or_url = path;
- range = APR_ARRAY_IDX(revision_ranges, i, svn_opt_revision_range_t *);
+ range = APR_ARRAY_IDX(revision_ranges, i, rev_range_t *);
- SVN_ERR(svn_client__get_revision_number(&start_revnum, &youngest_rev,
- ctx->wc_ctx, local_abspath_or_url,
- ra_session, &range->start,
- iterpool));
- SVN_ERR(svn_client__get_revision_number(&end_revnum, &youngest_rev,
- ctx->wc_ctx, local_abspath_or_url,
- ra_session, &range->end,
- iterpool));
+ /* Issue #4355: Account for renames spanning requested
+ revision ranges. */
+ younger_rev = MAX(range->range_start, range->range_end);
+ matching_segment = bsearch(&younger_rev, log_segments->elts,
+ log_segments->nelts, log_segments->elt_size,
+ compare_rev_to_segment);
+ SVN_ERR_ASSERT(*matching_segment);
+
+ /* A segment with a NULL path means there is gap in the history.
+ We'll just proceed and let svn_ra_get_log2 fail with a useful
+ error...*/
+ if ((*matching_segment)->path != NULL)
+ {
+ /* ...but if there is history, then we must account for issue
+ #4355 and make sure our RA session is pointing at the correct
+ location. */
+ const char *segment_url = svn_path_url_add_component2(
+ actual_loc->repos_root_url, (*matching_segment)->path,
+ scratch_pool);
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
+ ra_session,
+ segment_url,
+ scratch_pool));
+ }
if (has_log_revprops)
{
@@ -617,9 +759,9 @@ svn_client_log5(const apr_array_header_t
}
SVN_ERR(svn_ra_get_log2(ra_session,
- condensed_targets,
- start_revnum,
- end_revnum,
+ paths,
+ range->range_start,
+ range->range_end,
limit,
discover_changed_paths,
strict_node_history,
@@ -642,3 +784,85 @@ svn_client_log5(const apr_array_header_t
return SVN_NO_ERROR;
}
+
+/*** Public Interface. ***/
+
+svn_error_t *
+svn_client_log5(const apr_array_header_t *targets,
+ const svn_opt_revision_t *peg_revision,
+ const apr_array_header_t *opt_rev_ranges,
+ int limit,
+ svn_boolean_t discover_changed_paths,
+ svn_boolean_t strict_node_history,
+ svn_boolean_t include_merged_revisions,
+ const apr_array_header_t *revprops,
+ svn_log_entry_receiver_t real_receiver,
+ void *real_receiver_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ svn_ra_session_t *ra_session;
+ const char *old_session_url;
+ const char *ra_target;
+ svn_opt_revision_t youngest_opt_rev;
+ svn_revnum_t youngest_rev;
+ svn_revnum_t oldest_rev;
+ svn_opt_revision_t peg_rev;
+ svn_client__pathrev_t *actual_loc;
+ apr_array_header_t *log_segments;
+ apr_array_header_t *revision_ranges;
+ apr_array_header_t *relative_targets;
+
+ if (opt_rev_ranges->nelts == 0)
+ {
+ return svn_error_create
+ (SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("Missing required revision specification"));
+ }
+
+ /* Make a copy of PEG_REVISION, we may need to change it to a
+ default value. */
+ peg_rev = *peg_revision;
+
+ SVN_ERR(resolve_log_targets(&relative_targets, &ra_target, &peg_rev,
+ targets, ctx, pool, pool));
+
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &actual_loc,
+ ra_target, NULL, &peg_rev, &peg_rev,
+ ctx, pool));
+
+ /* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
+ and oldest revision range that spans all of OPT_REV_RANGES. */
+ SVN_ERR(convert_opt_rev_array_to_rev_range_array(&revision_ranges,
+ &youngest_rev,
+ &oldest_rev,
+ ra_session,
+ ra_target,
+ opt_rev_ranges, &peg_rev,
+ ctx, pool, pool));
+
+ /* Make ACTUAL_LOC and RA_SESSION point to the youngest operative rev. */
+ youngest_opt_rev.kind = svn_opt_revision_number;
+ youngest_opt_rev.value.number = youngest_rev;
+ SVN_ERR(svn_client__resolve_rev_and_url(&actual_loc, ra_session,
+ ra_target, &peg_rev,
+ &youngest_opt_rev, ctx, pool));
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+ actual_loc->url, pool));
+
+ /* Get the svn_location_segment_t's representing the requested log ranges. */
+ SVN_ERR(svn_client__repos_location_segments(&log_segments, ra_session,
+ actual_loc->url,
+ actual_loc->rev, /* peg */
+ actual_loc->rev, /* start */
+ oldest_rev, /* end */
+ ctx, pool));
+
+ SVN_ERR(run_ra_get_log(revision_ranges, relative_targets, log_segments,
+ actual_loc, ra_session, targets, limit,
+ discover_changed_paths, strict_node_history,
+ include_merged_revisions, revprops, real_receiver,
+ real_receiver_baton, ctx, pool));
+
+ return SVN_NO_ERROR;
+}
Modified: subversion/branches/verify-keep-going/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_client/merge.c?rev=1489765&r1=1489764&r2=1489765&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_client/merge.c Wed Jun 5 09:22:43 2013
@@ -436,7 +436,7 @@ check_repos_match(const merge_target_t *
if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Url '%s' of '%s' is not in repository '%s'"),
+ _("URL '%s' of '%s' is not in repository '%s'"),
url, svn_dirent_local_style(local_abspath, scratch_pool),
target->loc.repos_root_url);
@@ -2403,7 +2403,7 @@ merge_file_deleted(const char *relpath,
NULL, TRUE,
scratch_pool));
}
-
+
return SVN_NO_ERROR;
}
@@ -2487,7 +2487,7 @@ merge_dir_opened(void **new_dir_baton,
if (obstr_state == svn_wc_notify_state_obstructed)
{
svn_boolean_t is_wcroot;
-
+
SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
merge_b->ctx->wc_ctx,
local_abspath, scratch_pool));
@@ -2652,7 +2652,7 @@ merge_dir_opened(void **new_dir_baton,
return SVN_NO_ERROR;
}
}
-
+
if (! (merge_b->dry_run
&& ((pdb && pdb->added) || db->add_is_replace)))
{
@@ -2676,7 +2676,7 @@ merge_dir_opened(void **new_dir_baton,
{
svn_node_kind_t disk_kind;
- SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
+ SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
scratch_pool));
if (disk_kind == svn_node_dir)
@@ -2753,7 +2753,7 @@ merge_dir_opened(void **new_dir_baton,
SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
svn_depth_infinity,
original_url,
- merge_b->merge_source.loc2->rev,
+ right_source->revision,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL /* no notify! */,
@@ -2953,7 +2953,7 @@ merge_dir_added(const char *relpath,
copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
child, scratch_pool);
- copyfrom_rev = merge_b->merge_source.loc2->rev;
+ copyfrom_rev = right_source->revision;
SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
scratch_pool));
@@ -6229,7 +6229,7 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on '%s', "
- "mergetracking not possible"),
+ "merge tracking not possible"),
svn_dirent_local_style(wc_path, scratch_pool));
}
return svn_error_trace(err);
@@ -7378,7 +7378,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on merge target '%s', "
- "mergetracking not possible"),
+ "merge tracking not possible"),
svn_dirent_local_style(target_abspath, scratch_pool));
}
return svn_error_trace(err);
@@ -9012,24 +9012,23 @@ remove_noop_subtree_ranges(const merge_s
svn_pool_clear(iterpool);
- /* Issue #4269: Keep track of the longest common ancestor of all the
- subtrees which require merges. This may be a child of
- TARGET->ABSPATH, which will allow us to narrow the log request
- below. */
+ /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
if (child->remaining_ranges && child->remaining_ranges->nelts)
{
+ /* Issue #4269: Keep track of the longest common ancestor of all the
+ subtrees which require merges. This may be a child of
+ TARGET->ABSPATH, which will allow us to narrow the log request
+ below. */
if (longest_common_subtree_ancestor)
longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
longest_common_subtree_ancestor, child->abspath, scratch_pool);
else
longest_common_subtree_ancestor = child->abspath;
- }
- /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
- if (child->remaining_ranges && child->remaining_ranges->nelts)
- SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
- child->remaining_ranges,
- scratch_pool, iterpool));
+ SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
+ child->remaining_ranges,
+ scratch_pool, iterpool));
+ }
}
svn_pool_destroy(iterpool);
@@ -11444,10 +11443,6 @@ find_reintegrate_merge(merge_source_t **
&yc_ancestor, source.loc2, source.loc1, target_ra_session,
ctx, scratch_pool, scratch_pool));
- /* The source side of a reintegrate merge is not 'ancestral', except in
- * the degenerate case where source == YCA. */
- source.ancestral = (loc1->rev == yc_ancestor->rev);
-
if (! yc_ancestor)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
@@ -11455,6 +11450,10 @@ find_reintegrate_merge(merge_source_t **
source.loc1->url, source.loc1->rev,
source.loc2->url, source.loc2->rev);
+ /* The source side of a reintegrate merge is not 'ancestral', except in
+ * the degenerate case where source == YCA. */
+ source.ancestral = (loc1->rev == yc_ancestor->rev);
+
if (source.loc1->rev > yc_ancestor->rev)
{
/* Have we actually merged anything to the source from the
@@ -11742,6 +11741,40 @@ merge_peg_locked(conflict_report_t **con
return SVN_NO_ERROR;
}
+/* Details of an automatic merge. */
+typedef struct automatic_merge_t
+{
+ svn_client__pathrev_t *yca, *base, *right, *target;
+ svn_boolean_t is_reintegrate_like;
+ svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
+} automatic_merge_t;
+
+static svn_error_t *
+client_find_automatic_merge(automatic_merge_t **merge_p,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_abspath,
+ svn_boolean_t allow_mixed_rev,
+ svn_boolean_t allow_local_mods,
+ svn_boolean_t allow_switched_subtrees,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+static svn_error_t *
+do_automatic_merge_locked(conflict_report_t **conflict_report,
+ const automatic_merge_t *merge,
+ const char *target_abspath,
+ svn_depth_t depth,
+ svn_boolean_t diff_ignore_ancestry,
+ svn_boolean_t force_delete,
+ svn_boolean_t record_only,
+ svn_boolean_t dry_run,
+ const apr_array_header_t *merge_options,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
svn_error_t *
svn_client_merge_peg5(const char *source_path_or_url,
const apr_array_header_t *ranges_to_merge,
@@ -11771,7 +11804,7 @@ svn_client_merge_peg5(const char *source
/* Do an automatic merge if no revision ranges are specified. */
if (ranges_to_merge == NULL)
{
- svn_client_automatic_merge_t *merge;
+ automatic_merge_t *merge;
if (ignore_mergeinfo)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
@@ -11779,24 +11812,31 @@ svn_client_merge_peg5(const char *source
"ignoring mergeinfo"));
/* Find the details of the merge needed. */
- SVN_ERR(svn_client_find_automatic_merge(&merge,
- source_path_or_url,
- source_peg_revision,
- target_wcpath,
- allow_mixed_rev,
- TRUE /*allow_local_mods*/,
- TRUE /*allow_switched_subtrees*/,
- ctx, pool, pool));
-
- SVN_ERR(svn_client_do_automatic_merge(merge, target_wcpath, depth,
- diff_ignore_ancestry,
- force_delete, record_only,
- dry_run, merge_options,
- ctx, pool));
-
- /* We already dealt with returning any conflict error, inside the
- * above calls. */
- conflict_report = NULL;
+ SVN_ERR(client_find_automatic_merge(
+ &merge,
+ source_path_or_url, source_peg_revision,
+ target_abspath,
+ allow_mixed_rev,
+ TRUE /*allow_local_mods*/,
+ TRUE /*allow_switched_subtrees*/,
+ ctx, pool, pool));
+
+ if (!dry_run)
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ do_automatic_merge_locked(&conflict_report,
+ merge,
+ target_abspath, depth,
+ diff_ignore_ancestry,
+ force_delete, record_only, dry_run,
+ merge_options, ctx, pool, pool),
+ ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
+ else
+ SVN_ERR(do_automatic_merge_locked(&conflict_report,
+ merge,
+ target_abspath, depth,
+ diff_ignore_ancestry,
+ force_delete, record_only, dry_run,
+ merge_options, ctx, pool, pool));
}
else if (!dry_run)
SVN_WC__CALL_WITH_WRITE_LOCK(
@@ -11973,20 +12013,32 @@ operative_rev_receiver(void *baton,
*operative_rev = log_entry->revision;
/* We've found the youngest merged or oldest eligible revision, so
- we're done. */
+ we're done...
+
+ ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
+ true? Because if it is, then LOG_ENTRY->REVISION is only
+ partially merged/elgibile! And our only caller,
+ find_last_merged_location (via short_circuit_mergeinfo_log) is
+ interested in *fully* merged revisions. That's all true, but if
+ find_last_merged_location() finds the youngest merged revision it
+ will also check for the oldest eligible revision. So in the case
+ the youngest merged rev is non-inheritable, the *same* non-inheritable
+ rev will be found as the oldest eligible rev -- and
+ find_last_merged_location() handles that situation. */
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
-/* Wrapper around svn_client_mergeinfo_log2. All arguments are as per
- that API. The discover_changed_paths, depth, and revprops args to
- svn_client_mergeinfo_log2 are always TRUE, svn_depth_infinity_t,
+/* Wrapper around svn_client__mergeinfo_log. All arguments are as per
+ that private API. The discover_changed_paths, depth, and revprops args to
+ svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
and NULL respectively.
If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
*REVISION to a valid revnum, then clear the error. Otherwise return
any error. */
static svn_error_t*
-short_circuit_mergeinfo_log(svn_boolean_t finding_merged,
+short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
+ svn_boolean_t finding_merged,
const char *target_path_or_url,
const svn_opt_revision_t *target_peg_revision,
const char *source_path_or_url,
@@ -11996,18 +12048,21 @@ short_circuit_mergeinfo_log(svn_boolean_
svn_log_entry_receiver_t receiver,
svn_revnum_t *revision,
svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_error_t *err = svn_client_mergeinfo_log2(finding_merged,
+ svn_error_t *err = svn_client__mergeinfo_log(finding_merged,
target_path_or_url,
target_peg_revision,
+ target_mergeinfo_cat,
source_path_or_url,
source_peg_revision,
source_start_revision,
source_end_revision,
receiver, revision,
TRUE, svn_depth_infinity,
- NULL, ctx, scratch_pool);
+ NULL, ctx, result_pool,
+ scratch_pool);
if (err)
{
@@ -12077,6 +12132,7 @@ find_last_merged_location(svn_client__pa
svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
target_opt_rev;
svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
+ svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
source_peg_rev.kind = svn_opt_revision_number;
source_peg_rev.value.number = source_branch->tip->rev;
@@ -12089,14 +12145,15 @@ find_last_merged_location(svn_client__pa
/* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
if such a revision exists. */
- SVN_ERR(short_circuit_mergeinfo_log(TRUE, /* Find merged */
+ SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
+ TRUE, /* Find merged */
target->url, &target_opt_rev,
source_branch->tip->url,
&source_peg_rev,
&source_end_rev, &source_start_rev,
operative_rev_receiver,
&youngest_merged_rev,
- ctx, scratch_pool));
+ ctx, result_pool, scratch_pool));
if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
{
@@ -12107,20 +12164,31 @@ find_last_merged_location(svn_client__pa
else
{
/* One or more revisions have already been completely merged from
- SOURCE_BRANCH to TARGET, now find the oldest revision which is
- still eligible to be merged, if such exists. */
+ SOURCE_BRANCH to TARGET, now find the oldest revision, older
+ than the youngest merged revision, which is still eligible to
+ be merged, if such exists. */
branch_history_t *contiguous_source;
svn_revnum_t base_rev;
svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
- SVN_ERR(short_circuit_mergeinfo_log(FALSE, /* Find eligible */
+ /* If the only revisions eligible are younger than the youngest merged
+ revision we can simply assume that the youngest eligible revision
+ is the youngest merged revision. Obviously this may not be true!
+ The revisions between the youngest merged revision and the tip of
+ the branch may have several inoperative revisions -- they may *all*
+ be inoperative revisions! But for the purpose of this function
+ (i.e. finding the youngest revision after the YCA where all revs have
+ been merged) that doesn't matter. */
+ source_end_rev.value.number = youngest_merged_rev;
+ SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
+ FALSE, /* Find eligible */
target->url, &target_opt_rev,
source_branch->tip->url,
&source_peg_rev,
&source_start_rev, &source_end_rev,
operative_rev_receiver,
&oldest_eligible_rev,
- ctx, scratch_pool));
+ ctx, scratch_pool, scratch_pool));
/* If there are revisions eligible for merging, use the oldest one
to calculate the base. Otherwise there are no operative revisions
@@ -12212,7 +12280,7 @@ find_base_on_target(svn_client__pathrev_
return SVN_NO_ERROR;
}
-/* The body of svn_client_find_automatic_merge(), which see.
+/* The body of client_find_automatic_merge(), which see.
*/
static svn_error_t *
find_automatic_merge(svn_client__pathrev_t **base_p,
@@ -12266,28 +12334,26 @@ find_automatic_merge(svn_client__pathrev
return SVN_NO_ERROR;
}
-/* Details of an automatic merge. */
-struct svn_client_automatic_merge_t
-{
- svn_client__pathrev_t *yca, *base, *right, *target;
- svn_boolean_t is_reintegrate_like;
- svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
-};
-
-svn_error_t *
-svn_client_find_automatic_merge_no_wc(
- svn_client_automatic_merge_t **merge_p,
- const char *source_path_or_url,
- const svn_opt_revision_t *source_revision,
- const char *target_path_or_url,
- const svn_opt_revision_t *target_revision,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+/** Find out what kind of automatic merge would be needed, when the target
+ * is only known as a repository location rather than a WC.
+ *
+ * Like find_automatic_merge() except that the target is
+ * specified by @a target_path_or_url at @a target_revision, which must
+ * refer to a repository location, instead of by a WC path argument.
+ */
+static svn_error_t *
+find_automatic_merge_no_wc(automatic_merge_t **merge_p,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_path_or_url,
+ const svn_opt_revision_t *target_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
svn_client__pathrev_t *target_loc;
- svn_client_automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
+ automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
/* Source */
SVN_ERR(svn_client__ra_session_from_path2(
@@ -12315,32 +12381,49 @@ svn_client_find_automatic_merge_no_wc(
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_client_find_automatic_merge(svn_client_automatic_merge_t **merge_p,
- const char *source_path_or_url,
- const svn_opt_revision_t *source_revision,
- const char *target_wcpath,
- svn_boolean_t allow_mixed_rev,
- svn_boolean_t allow_local_mods,
- svn_boolean_t allow_switched_subtrees,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+/* Find the information needed to merge all unmerged changes from a source
+ * branch into a target branch.
+ *
+ * Set @a *merge_p to the information needed to merge all unmerged changes
+ * (up to @a source_revision) from the source branch @a source_path_or_url
+ * at @a source_revision into the target WC at @a target_abspath.
+ *
+ * The flags @a allow_mixed_rev, @a allow_local_mods and
+ * @a allow_switched_subtrees enable merging into a WC that is in any or all
+ * of the states described by their names, but only if this function decides
+ * that the merge will be in the same direction as the last automatic merge.
+ * If, on the other hand, the last automatic merge was in the opposite
+ * direction, then such states of the WC are not allowed regardless
+ * of these flags. This function merely records these flags in the
+ * @a *merge_p structure; do_automatic_merge_locked() checks the WC
+ * state for compliance.
+ *
+ * Allocate the @a *merge_p structure in @a result_pool.
+ */
+static svn_error_t *
+client_find_automatic_merge(automatic_merge_t **merge_p,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_abspath,
+ svn_boolean_t allow_mixed_rev,
+ svn_boolean_t allow_local_mods,
+ svn_boolean_t allow_switched_subtrees,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- const char *target_abspath;
source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
- svn_client_automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
-
- SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath, scratch_pool));
+ automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
- /* "Open" the target WC. We're not going to check the target WC for
- * mixed-rev, local mods or switched subtrees yet. After we find out
- * what kind of merge is required, then if a reintegrate-like merge is
- * required we'll do the stricter checks, in do_automatic_merge_locked(). */
+ /* "Open" the target WC. Check the target WC for mixed-rev, local mods and
+ * switched subtrees yet to faster exit and notify user before contacting
+ * with server. After we find out what kind of merge is required, then if a
+ * reintegrate-like merge is required we'll do the stricter checks, in
+ * do_automatic_merge_locked(). */
SVN_ERR(open_target_wc(&s_t->target, target_abspath,
- TRUE /*allow_mixed_rev*/,
- TRUE /*allow_local_mods*/,
- TRUE /*allow_switched_subtrees*/,
+ allow_mixed_rev,
+ allow_local_mods,
+ allow_switched_subtrees,
ctx, result_pool, scratch_pool));
/* Open RA sessions to the source and target trees. */
@@ -12356,7 +12439,7 @@ svn_client_find_automatic_merge(svn_clie
/* Check source is in same repos as target. */
SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
- &s_t->target->loc, target_wcpath,
+ &s_t->target->loc, target_abspath,
TRUE /* strict_urls */, scratch_pool));
SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
@@ -12374,7 +12457,8 @@ svn_client_find_automatic_merge(svn_clie
return SVN_NO_ERROR;
}
-/* The body of svn_client_do_automatic_merge(), which see.
+/* Perform an automatic merge, given the information in MERGE which
+ * must have come from calling client_find_automatic_merge().
*
* Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
* depending on whether the base is on the source branch or the target
@@ -12403,7 +12487,7 @@ svn_client_find_automatic_merge(svn_clie
*/
static svn_error_t *
do_automatic_merge_locked(conflict_report_t **conflict_report,
- const svn_client_automatic_merge_t *merge,
+ const automatic_merge_t *merge,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t diff_ignore_ancestry,
@@ -12531,65 +12615,42 @@ do_automatic_merge_locked(conflict_repor
}
svn_error_t *
-svn_client_do_automatic_merge(const svn_client_automatic_merge_t *merge,
- const char *target_wcpath,
- svn_depth_t depth,
- svn_boolean_t diff_ignore_ancestry,
- svn_boolean_t force_delete,
- svn_boolean_t record_only,
- svn_boolean_t dry_run,
- const apr_array_header_t *merge_options,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
+ const char **yca_url, svn_revnum_t *yca_rev,
+ const char **base_url, svn_revnum_t *base_rev,
+ const char **right_url, svn_revnum_t *right_rev,
+ const char **target_url, svn_revnum_t *target_rev,
+ const char **repos_root_url,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_path_or_url,
+ const svn_opt_revision_t *target_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- const char *target_abspath, *lock_abspath;
- conflict_report_t *conflict_report;
-
- SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
- target_wcpath, ctx, pool));
+ svn_boolean_t target_is_wc;
+ automatic_merge_t *merge;
- if (!dry_run)
- SVN_WC__CALL_WITH_WRITE_LOCK(
- do_automatic_merge_locked(&conflict_report,
- merge,
- target_abspath, depth,
- diff_ignore_ancestry,
- force_delete, record_only, dry_run,
- merge_options, ctx, pool, pool),
- ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
+ target_is_wc = (! svn_path_is_url(target_path_or_url))
+ && (target_revision->kind == svn_opt_revision_unspecified
+ || target_revision->kind == svn_opt_revision_working);
+ if (target_is_wc)
+ SVN_ERR(client_find_automatic_merge(
+ &merge,
+ source_path_or_url, source_revision,
+ target_path_or_url,
+ TRUE, TRUE, TRUE, /* allow_* */
+ ctx, scratch_pool, scratch_pool));
else
- SVN_ERR(do_automatic_merge_locked(&conflict_report,
- merge,
- target_abspath, depth,
- diff_ignore_ancestry,
- force_delete, record_only, dry_run,
- merge_options, ctx, pool, pool));
-
- SVN_ERR(make_merge_conflict_error(conflict_report, pool));
- return SVN_NO_ERROR;
-}
+ SVN_ERR(find_automatic_merge_no_wc(
+ &merge,
+ source_path_or_url, source_revision,
+ target_path_or_url, target_revision,
+ ctx, scratch_pool, scratch_pool));
-svn_boolean_t
-svn_client_automatic_merge_is_reintegrate_like(
- const svn_client_automatic_merge_t *merge)
-{
- return merge->is_reintegrate_like;
-}
-
-svn_error_t *
-svn_client_automatic_merge_get_locations(
- const char **yca_url,
- svn_revnum_t *yca_rev,
- const char **base_url,
- svn_revnum_t *base_rev,
- const char **right_url,
- svn_revnum_t *right_rev,
- const char **target_url,
- svn_revnum_t *target_rev,
- const char **repos_root_url,
- const svn_client_automatic_merge_t *merge,
- apr_pool_t *result_pool)
-{
+ if (needs_reintegration)
+ *needs_reintegration = merge->is_reintegrate_like;
if (yca_url)
*yca_url = apr_pstrdup(result_pool, merge->yca->url);
if (yca_rev)