You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ar...@apache.org on 2012/07/30 08:39:38 UTC
svn commit: r1367002 [5/21] - in /subversion/branches/svn-bisect: ./ build/
build/ac-macros/ build/generator/ build/generator/templates/
contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/ notes/
notes/api-errata/1.7/ notes/http-and-webdav/ ...
Modified: subversion/branches/svn-bisect/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_client/merge.c?rev=1367002&r1=1367001&r2=1367002&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_client/merge.c Mon Jul 30 06:39:28 2012
@@ -48,6 +48,7 @@
#include "svn_props.h"
#include "svn_time.h"
#include "svn_sorts.h"
+#include "svn_subst.h"
#include "svn_ra.h"
#include "client.h"
#include "mergeinfo.h"
@@ -145,7 +146,7 @@
* During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
* by get_mergeinfo_paths() and outside of that function and its helpers
* should always meet the criteria dictated in get_mergeinfo_paths()'s doc
- * string. The elements of CHILDREN_WITH_MERGINFO should never be NULL.
+ * string. The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
*
* For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
* the doc string for HONOR_MERGEINFO().
@@ -156,6 +157,14 @@
/*** Repos-Diff Editor Callbacks ***/
+/* Repository root and UUID for a repository. */
+typedef struct url_uuid_t
+{
+ const char *url;
+ const char *uuid;
+} url_uuid_t;
+
+/* */
typedef struct merge_source_t
{
/* "left" side URL and revision (inclusive iff youngest) */
@@ -168,6 +177,26 @@ typedef struct merge_source_t
} merge_source_t;
+/* Description of the merge target root node (a WC working node) */
+typedef struct merge_target_t
+{
+ /* Absolute path to the WC node */
+ const char *abspath;
+
+ /* Node kind of the WC node (at the start of the merge) */
+ svn_node_kind_t kind;
+
+ /* URL of the node, or NULL if node is locally added */
+ const char *url;
+
+ /* Revision of the node, or NULL if node is locally added */
+ svn_revnum_t rev;
+
+ /* Repository root URL and UUID, even if node is locally added */
+ url_uuid_t repos_root;
+
+} merge_target_t;
+
typedef struct merge_cmd_baton_t {
svn_boolean_t force;
svn_boolean_t dry_run;
@@ -193,10 +222,7 @@ typedef struct merge_cmd_baton_t {
const char *added_path; /* Set to the dir path whenever the
dir is added as a child of a
versioned dir (dry-run only) */
- const char *target_abspath; /* Absolute working copy target of
- the merge. */
- const char *repos_root_url; /* The repository root of the repository
- containing TARGET_ABSPATH */
+ const merge_target_t *target; /* Description of merge target node */
/* The left and right URLs and revs. The value of this field changes to
reflect the merge_source_t *currently* being merged by do_merge(). */
@@ -274,18 +300,21 @@ typedef struct merge_cmd_baton_t {
} merge_cmd_baton_t;
-/* If the merge source server is is capable of merge tracking, the left-side
+/* Return TRUE iff we should be taking account of mergeinfo in deciding what
+ changes to merge, for the merge described by MERGE_B. Specifically, that
+ is if the merge source server is capable of merge tracking, the left-side
merge source is an ancestor of the right-side (or vice-versa), the merge
- source repository is the same repository as the MERGE_B->TARGET_ABSPATH, and
- ancestry is being considered then return TRUE. */
+ source is in the same repository as the merge target, and ancestry is
+ being considered. */
#define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable \
&& (merge_b)->sources_ancestral \
&& (merge_b)->same_repos \
&& (! (merge_b)->ignore_ancestry))
-/* If HONOR_MERGEINFO is TRUE and the merge is not a dry run
- then return TRUE. */
+/* Return TRUE iff we should be recording mergeinfo for the merge described
+ by MERGE_B. Specifically, that is if we are honoring mergeinfo and the
+ merge is not a dry run. */
#define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \
&& !(merge_b)->dry_run)
@@ -294,13 +323,6 @@ typedef struct merge_cmd_baton_t {
/*** Utilities ***/
-/* Repository root and UUID for a repository. */
-typedef struct url_uuid_t
-{
- const char *url;
- const char *uuid;
-} url_uuid_t;
-
/* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
of LOCAL_ABSPATH. Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
@@ -309,12 +331,12 @@ check_repos_match(merge_cmd_baton_t *mer
const char *url,
apr_pool_t *scratch_pool)
{
- if (!svn_uri__is_ancestor(merge_b->repos_root_url, url))
+ if (!svn_uri__is_ancestor(merge_b->target->repos_root.url, url))
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Url '%s' of '%s' is not in repository '%s'"),
url, svn_dirent_local_style(local_abspath, scratch_pool),
- merge_b->repos_root_url);
+ merge_b->target->repos_root.url);
return SVN_NO_ERROR;
}
@@ -462,7 +484,7 @@ perform_obstruction_check(svn_wc_notify_
if (kind == NULL)
kind = &wc_kind;
- check_root = ! strcmp(local_abspath, merge_b->target_abspath);
+ check_root = ! strcmp(local_abspath, merge_b->target->abspath);
SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
kind,
@@ -502,7 +524,7 @@ make_conflict_versions(const svn_wc_conf
/* Construct the source URLs of the victim. */
{
- const char *child = svn_dirent_skip_ancestor(merge_b->target_abspath,
+ const char *child = svn_dirent_skip_ancestor(merge_b->target->abspath,
victim_abspath);
SVN_ERR_ASSERT(child != NULL);
left_url = svn_path_url_add_component2(merge_b->merge_source.url1,
@@ -592,6 +614,7 @@ tree_conflict(merge_cmd_baton_t *merge_b
if (merge_b->conflicted_paths == NULL)
merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
+ victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
apr_hash_set(merge_b->conflicted_paths, victim_abspath,
APR_HASH_KEY_STRING, victim_abspath);
@@ -633,6 +656,7 @@ tree_conflict_on_add(merge_cmd_baton_t *
if (merge_b->conflicted_paths == NULL)
merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
+ victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
apr_hash_set(merge_b->conflicted_paths, victim_abspath,
APR_HASH_KEY_STRING, victim_abspath);
@@ -663,6 +687,7 @@ tree_conflict_on_add(merge_cmd_baton_t *
if (merge_b->conflicted_paths == NULL)
merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
+ victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
apr_hash_set(merge_b->conflicted_paths, victim_abspath,
APR_HASH_KEY_STRING, victim_abspath);
@@ -791,7 +816,7 @@ omit_mergeinfo_changes(apr_array_header_
/* Helper for merge_props_changed().
*PROPS is an array of svn_prop_t structures representing regular properties
- to be added to the working copy LOCAL_ABSPATH.
+ to be added to the working copy TARGET_ABSPATH.
HONOR_MERGEINFO determines whether mergeinfo will be honored by this
function (when applicable).
@@ -800,25 +825,28 @@ omit_mergeinfo_changes(apr_array_header_
REINTEGRATE_MERGE is FALSE do nothing. Otherwise, if
SAME_REPOS is false, then filter out all mergeinfo
property additions (Issue #3383) from *PROPS. If SAME_REPOS is
- true then filter out mergeinfo property additions to LOCAL_ABSPATH when
- those additions refer to the same line of history as LOCAL_ABSPATH as
+ true then filter out mergeinfo property additions to TARGET_ABSPATH when
+ those additions refer to the same line of history as TARGET_ABSPATH as
described below.
If mergeinfo is being honored and SAME_REPOS is true
then examine the added mergeinfo, looking at each range (or single rev)
of each source path. If a source_path/range refers to the same line of
- history as LOCAL_ABSPATH (pegged at its base revision), then filter out
+ history as TARGET_ABSPATH (pegged at its base revision), then filter out
that range. If the entire rangelist for a given path is filtered then
filter out the path as well.
- Use RA_SESSION for any communication to the repository, and CTX for any
- further client operations.
+ If SAME_REPOS is true, RA_SESSION is an open RA session to the repository
+ in which both the source and target live, else RA_SESSION is not used. It
+ may be temporarily reparented as needed by this function.
+
+ Use CTX for any further client operations.
If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
in POOL) of incoming *PROPS minus the filtered mergeinfo. */
-static svn_error_t*
+static svn_error_t *
filter_self_referential_mergeinfo(apr_array_header_t **props,
- const char *local_abspath,
+ const char *target_abspath,
svn_boolean_t honor_mergeinfo,
svn_boolean_t same_repos,
svn_boolean_t reintegrate_merge,
@@ -830,7 +858,8 @@ filter_self_referential_mergeinfo(apr_ar
int i;
apr_pool_t *iterpool;
svn_boolean_t is_added;
- svn_revnum_t base_revision;
+ const char *target_base_url;
+ svn_revnum_t target_base_rev;
/* Issue #3383: We don't want mergeinfo from a foreign repos.
@@ -854,12 +883,14 @@ filter_self_referential_mergeinfo(apr_ar
/* If this is a merge from the same repository and PATH itself has been
added there is no need to filter. */
- SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, local_abspath, pool));
+ SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath, pool));
if (is_added)
return SVN_NO_ERROR;
- SVN_ERR(svn_wc__node_get_base_rev(&base_revision, ctx->wc_ctx,
- local_abspath, pool));
+ SVN_ERR(svn_client_url_from_path2(&target_base_url, target_abspath,
+ ctx, pool, pool));
+ SVN_ERR(svn_wc__node_get_base_rev(&target_base_rev, ctx->wc_ctx,
+ target_abspath, pool));
adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
iterpool = svn_pool_create(pool);
@@ -870,8 +901,6 @@ filter_self_referential_mergeinfo(apr_ar
svn_mergeinfo_t mergeinfo, younger_mergeinfo;
svn_mergeinfo_t filtered_mergeinfo = NULL;
svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
- const char *target_url;
- const char *old_url = NULL;
svn_error_t *err;
if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
@@ -885,13 +914,6 @@ filter_self_referential_mergeinfo(apr_ar
svn_pool_clear(iterpool);
/* Non-empty mergeinfo; filter self-referential mergeinfo out. */
- /* Temporarily reparent our RA session to the merge
- target's URL. */
- SVN_ERR(svn_client_url_from_path2(&target_url, local_abspath,
- ctx, iterpool, iterpool));
- SVN_ERR(svn_client__ensure_ra_session_url(&old_url,
- ra_session,
- target_url, iterpool));
/* Parse the incoming mergeinfo to allow easier manipulation. */
err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
@@ -904,12 +926,6 @@ filter_self_referential_mergeinfo(apr_ar
{
svn_error_clear(err);
APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
-
- /* If we reparented RA_SESSION above, put it back
- to the original URL. */
- if (old_url)
- SVN_ERR(svn_ra_reparent(ra_session, old_url, iterpool));
-
continue;
}
else
@@ -944,7 +960,7 @@ filter_self_referential_mergeinfo(apr_ar
the cost of a roundtrip communication with the repository. */
SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
&mergeinfo,
- base_revision,
+ target_base_rev,
iterpool));
/* Filter self-referential mergeinfo from younger_mergeinfo. */
@@ -986,7 +1002,7 @@ filter_self_referential_mergeinfo(apr_ar
RANGE->START on the same line of history.
(start+1 because RANGE->start is not inclusive.) */
err2 = svn_client__repos_location(&start_url, ra_session,
- target_url, base_revision,
+ target_base_url, target_base_rev,
range->start + 1,
ctx, iterpool, iterpool);
if (err2)
@@ -1062,11 +1078,9 @@ filter_self_referential_mergeinfo(apr_ar
svn_mergeinfo_t implicit_mergeinfo;
SVN_ERR(svn_client__get_history_as_mergeinfo(
- &implicit_mergeinfo, NULL,
- base_revision, base_revision, SVN_INVALID_REVNUM,
- ra_session,
- ctx,
- iterpool));
+ &implicit_mergeinfo, NULL, target_base_url,
+ target_base_rev, target_base_rev, SVN_INVALID_REVNUM,
+ ra_session, ctx, iterpool));
/* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
@@ -1074,11 +1088,6 @@ filter_self_referential_mergeinfo(apr_ar
mergeinfo, TRUE, iterpool, iterpool));
}
- /* If we reparented RA_SESSION above, put it back
- to the original URL. */
- if (old_url)
- SVN_ERR(svn_ra_reparent(ra_session, old_url, iterpool));
-
/* Combine whatever older and younger filtered mergeinfo exists
into filtered_mergeinfo. */
if (filtered_mergeinfo && filtered_younger_mergeinfo)
@@ -1251,6 +1260,8 @@ merge_props_changed(svn_wc_notify_state_
else if (err)
return svn_error_trace(err);
}
+ else if (state)
+ *state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
@@ -1267,7 +1278,7 @@ merge_dir_props_changed(svn_wc_notify_st
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = diff_baton;
- const char *local_abspath = svn_dirent_join(merge_b->target_abspath,
+ const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
local_relpath, scratch_pool);
svn_wc_notify_state_t obstr_state;
@@ -1310,8 +1321,8 @@ typedef struct conflict_resolver_baton_t
resolution attempt was made. */
apr_hash_t **conflicted_paths;
- /* Pool used in notification_receiver() to avoid the iteration
- sub-pool which is passed in, then subsequently destroyed. */
+ /* Pool with a sufficient lifetime to be used for output members such as
+ * *CONFLICTED_PATHS. */
apr_pool_t *pool;
} conflict_resolver_baton_t;
@@ -1450,7 +1461,7 @@ merge_file_changed(svn_wc_notify_state_t
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
- const char *mine_abspath = svn_dirent_join(merge_b->target_abspath,
+ const char *mine_abspath = svn_dirent_join(merge_b->target->abspath,
mine_relpath, scratch_pool);
svn_node_kind_t wc_kind;
svn_boolean_t is_deleted;
@@ -1459,6 +1470,9 @@ merge_file_changed(svn_wc_notify_state_t
SVN_ERR_ASSERT(!older_abspath || svn_dirent_is_absolute(older_abspath));
SVN_ERR_ASSERT(!yours_abspath || svn_dirent_is_absolute(yours_abspath));
+ if (tree_conflicted)
+ *tree_conflicted = FALSE;
+
/* Check for an obstructed or missing node on disk. */
{
svn_wc_notify_state_t obstr_state;
@@ -1681,7 +1695,7 @@ merge_file_added(svn_wc_notify_state_t *
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
- const char *mine_abspath = svn_dirent_join(merge_b->target_abspath,
+ const char *mine_abspath = svn_dirent_join(merge_b->target->abspath,
mine_relpath, scratch_pool);
svn_node_kind_t kind;
int i;
@@ -1689,6 +1703,9 @@ merge_file_added(svn_wc_notify_state_t *
SVN_ERR_ASSERT(svn_dirent_is_absolute(mine_abspath));
+ if (tree_conflicted)
+ *tree_conflicted = FALSE;
+
/* Easy out: We are only applying mergeinfo differences. */
if (merge_b->record_only)
{
@@ -1778,7 +1795,7 @@ merge_file_added(svn_wc_notify_state_t *
if (merge_b->same_repos)
{
const char *child =
- svn_dirent_skip_ancestor(merge_b->target_abspath,
+ svn_dirent_skip_ancestor(merge_b->target->abspath,
mine_abspath);
SVN_ERR_ASSERT(child != NULL);
copyfrom_url = svn_path_url_add_component2(
@@ -1979,10 +1996,14 @@ files_same_p(svn_boolean_t *same,
svn_opt_revision_t working_rev = { svn_opt_revision_working, { 0 } };
/* Compare the file content, translating 'mine' to 'normal' form. */
- SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx,
- mine_abspath, &working_rev,
- FALSE, TRUE, NULL, NULL,
- scratch_pool, scratch_pool));
+ if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL)
+ SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
+ scratch_pool, scratch_pool));
+ else
+ SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx,
+ mine_abspath, &working_rev,
+ FALSE, TRUE, NULL, NULL,
+ scratch_pool, scratch_pool));
SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
scratch_pool, scratch_pool));
@@ -2009,10 +2030,13 @@ merge_file_deleted(svn_wc_notify_state_t
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
- const char *mine_abspath = svn_dirent_join(merge_b->target_abspath,
+ const char *mine_abspath = svn_dirent_join(merge_b->target->abspath,
mine_relpath, scratch_pool);
svn_node_kind_t kind;
+ if (tree_conflicted)
+ *tree_conflicted = FALSE;
+
if (merge_b->dry_run)
{
const char *wcpath = apr_pstrdup(merge_b->pool, mine_abspath);
@@ -2143,7 +2167,7 @@ merge_dir_added(svn_wc_notify_state_t *s
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
- const char *local_abspath = svn_dirent_join(merge_b->target_abspath,
+ const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
local_relpath, scratch_pool);
svn_node_kind_t kind;
const char *copyfrom_url = NULL, *child;
@@ -2162,7 +2186,7 @@ merge_dir_added(svn_wc_notify_state_t *s
parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
- child = svn_dirent_is_child(merge_b->target_abspath, local_abspath, NULL);
+ child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
SVN_ERR_ASSERT(child != NULL);
/* If this is a merge from the same repository as our working copy,
@@ -2344,7 +2368,7 @@ merge_dir_deleted(svn_wc_notify_state_t
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
- const char *local_abspath = svn_dirent_join(merge_b->target_abspath,
+ const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
local_relpath, scratch_pool);
svn_node_kind_t kind;
svn_boolean_t is_versioned;
@@ -2493,7 +2517,7 @@ merge_dir_opened(svn_boolean_t *tree_con
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
- const char *local_abspath = svn_dirent_join(merge_b->target_abspath,
+ const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
local_relpath, scratch_pool);
svn_node_kind_t wc_kind;
svn_wc_notify_state_t obstr_state;
@@ -2628,9 +2652,6 @@ typedef struct notification_receiver_bat
svn_wc_notify_func2_t wrapped_func;
void *wrapped_baton;
- /* The number of notifications received. */
- apr_uint32_t nbr_notifications;
-
/* The number of operative notifications received. */
apr_uint32_t nbr_operative_notifications;
@@ -2659,20 +2680,22 @@ typedef struct notification_receiver_bat
svn_boolean_t is_single_file_merge;
/* Depth first ordered list of paths that needs special care while merging.
+ ### And ...? This is not just a list of paths. See the global comment
+ 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
This defaults to NULL. For 'same_url' merge alone we set it to
proper array. This is used by notification_receiver to put a
merge notification begin lines. */
apr_array_header_t *children_with_mergeinfo;
- /* The index in CHILDREN_WITH_MERGEINFO where we found the nearest ancestor
- for merged path. Default value is '-1'.*/
- int cur_ancestor_index;
+ /* The path in CHILDREN_WITH_MERGEINFO where we found the nearest ancestor
+ for merged path. Default value is null. */
+ const char *cur_ancestor_abspath;
/* We use this to make a decision on merge begin line notifications. */
merge_cmd_baton_t *merge_b;
- /* Pool used in notification_receiver() to avoid the iteration
- sub-pool which is passed in, then subsequently destroyed. */
+ /* Pool with a sufficient lifetime to be used for output members such as
+ * MERGED_ABSPATHS. */
apr_pool_t *pool;
} notification_receiver_baton_t;
@@ -2684,22 +2707,17 @@ typedef struct notification_receiver_bat
then child->abspath must be a proper ancestor of PATH.
CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
- order of path. Nearest ancestor's index from
- CHILDREN_WITH_MERGEINFO is returned. */
-static int
+ order of path. */
+static svn_client__merge_path_t *
find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
svn_boolean_t path_is_own_ancestor,
const char *path)
{
int i;
- int ancestor_index = 0;
+ svn_client__merge_path_t *ancestor = NULL;
+
+ SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
- /* This if condition is not needed as this function should be used
- from the context of same_url merge where CHILDREN_WITH_MERGEINFO
- will not be NULL and of size atleast 1. We have this if condition
- just to protect the wrong caller. */
- if (!children_with_mergeinfo)
- return 0;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
@@ -2707,12 +2725,86 @@ find_nearest_ancestor(const apr_array_he
if (svn_dirent_is_ancestor(child->abspath, path)
&& (path_is_own_ancestor
|| svn_path_compare_paths(child->abspath, path) != 0))
- ancestor_index = i;
+ ancestor = child;
+ }
+ return ancestor;
+}
+
+
+/* Notify that we're starting to record the merge of the
+ * revision range RANGE into TARGET_ABSPATH. RANGE should be null if the
+ * merge sources are not from the same URL.
+ *
+ * This calls the client's notification receiver (as found in the client
+ * context), with a WC abspath.
+ */
+static void
+notify_merge_begin(const char *target_abspath,
+ const svn_merge_range_t *range,
+ merge_cmd_baton_t *merge_b,
+ apr_pool_t *pool)
+{
+ if (merge_b->ctx->notify_func2)
+ {
+ svn_wc_notify_t *n
+ = svn_wc_create_notify(target_abspath,
+ merge_b->same_repos
+ ? svn_wc_notify_merge_begin
+ : svn_wc_notify_foreign_merge_begin,
+ pool);
+
+ n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, n, pool);
+ }
+}
+
+/* Notify that we're starting to record mergeinfo for the merge of the
+ * revision range RANGE into TARGET_ABSPATH. RANGE should be null if the
+ * merge sources are not from the same URL.
+ *
+ * This calls the client's notification receiver (as found in the client
+ * context), with a WC abspath.
+ */
+static void
+notify_mergeinfo_recording(const char *target_abspath,
+ const svn_merge_range_t *range,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *n = svn_wc_create_notify(
+ target_abspath, svn_wc_notify_merge_record_info_begin, pool);
+
+ n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
+ ctx->notify_func2(ctx->notify_baton2, n, pool);
}
- return ancestor_index;
}
+/* Notify that we're completing the merge into TARGET_ABSPATH.
+ *
+ * This calls the client's notification receiver (as found in the client
+ * context), with a WC abspath.
+ */
+static void
+notify_merge_completed(const char *target_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *n
+ = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
+ pool);
+ n->kind = svn_node_none;
+ n->content_state = n->prop_state = svn_wc_notify_state_inapplicable;
+ n->lock_state = svn_wc_notify_lock_state_inapplicable;
+ n->revision = SVN_INVALID_REVNUM;
+ ctx->notify_func2(ctx->notify_baton2, n, pool);
+ }
+}
+/* Is the notification the result of a real operative merge? */
#define IS_OPERATIVE_NOTIFICATION(notify) \
(notify->content_state == svn_wc_notify_state_conflicted \
|| notify->content_state == svn_wc_notify_state_merged \
@@ -2723,13 +2815,26 @@ find_nearest_ancestor(const apr_array_he
|| notify->action == svn_wc_notify_update_add \
|| notify->action == svn_wc_notify_tree_conflict)
-/* Our svn_wc_notify_func2_t wrapper.*/
+/* Handle a diff notification by calling the client's notification callback
+ * and also by recording which paths changed (in BATON->*_abspaths).
+ *
+ * In some cases, notify that a merge is beginning, if we haven't already
+ * done so. (### TODO: Harmonize this so it handles all cases.)
+ *
+ * The paths in NOTIFY are relpaths, relative to the root of the diff (the
+ * merge source). We convert these to abspaths in the merge target WC before
+ * passing the notification structure on to the client.
+ *
+ * This function is not used for 'starting a merge', 'starting to record
+ * mergeinfo' and 'completing a merge' notifications.
+ *
+ * Implements svn_wc_notify_func2_t.*/
static void
notification_receiver(void *baton, const svn_wc_notify_t *notify,
apr_pool_t *pool)
{
notification_receiver_baton_t *notify_b = baton;
- svn_boolean_t is_operative_notification = FALSE;
+ svn_boolean_t is_operative_notification = IS_OPERATIVE_NOTIFICATION(notify);
const char *notify_abspath;
/* Skip notifications if this is a --record-only merge that is adding
@@ -2737,15 +2842,12 @@ notification_receiver(void *baton, const
We will already have skipped the actual addition or deletion, but will
still get a notification callback for it. */
if (notify_b->merge_b->record_only
- && (notify->action != svn_wc_notify_update_update
- && notify->action != svn_wc_notify_merge_record_info_begin))
+ && notify->action != svn_wc_notify_update_update)
return;
- /* Is the notification the result of a real operative merge? */
- if (IS_OPERATIVE_NOTIFICATION(notify))
+ if (is_operative_notification)
{
notify_b->nbr_operative_notifications++;
- is_operative_notification = TRUE;
}
/* If the node was moved-away, use its new path in the notification. */
@@ -2753,7 +2855,7 @@ notification_receiver(void *baton, const
* not yet implemented.
* ### We should stash the info about which moves have been followed and
* retrieve that info here, instead of querying the WC again here. */
- notify_abspath = svn_dirent_join(notify_b->merge_b->target_abspath,
+ notify_abspath = svn_dirent_join(notify_b->merge_b->target->abspath,
notify->path, pool);
if (notify->action == svn_wc_notify_update_update
&& notify->kind == svn_node_file)
@@ -2782,6 +2884,7 @@ notification_receiver(void *baton, const
notify_abspath = moved_to_abspath;
}
+ /* Update the lists of merged, skipped, tree-conflicted and added paths. */
if (notify_b->merge_b->sources_ancestral
|| notify_b->merge_b->reintegrate_merge)
{
@@ -2843,6 +2946,9 @@ notification_receiver(void *baton, const
else
{
added_path_parent = svn_dirent_dirname(added_path, pool);
+ /* ### Bug. Testing whether its immediate parent is in the
+ * hash isn't enough: this is letting every other level of
+ * the added subtree hierarchy into the hash. */
if (!apr_hash_get(notify_b->added_abspaths, added_path_parent,
APR_HASH_KEY_STRING))
is_root_of_added_subtree = TRUE;
@@ -2853,11 +2959,11 @@ notification_receiver(void *baton, const
}
}
+ /* Notify that a merge is beginning, if we haven't already done so.
+ * (A single-file merge is notified separately: see single_file_merge_notify().) */
/* If our merge sources are ancestors of one another... */
if (notify_b->merge_b->sources_ancestral)
{
- notify_b->nbr_notifications++;
-
/* See if this is an operative directory merge. */
if (!(notify_b->is_single_file_merge) && is_operative_notification)
{
@@ -2876,36 +2982,22 @@ notification_receiver(void *baton, const
--- Merging rX into 'PARENT'
D PARENT/CHILD
*/
- int new_nearest_ancestor_index =
- find_nearest_ancestor(
- notify_b->children_with_mergeinfo,
- notify->action != svn_wc_notify_update_delete,
- notify_abspath);
-
- if (new_nearest_ancestor_index != notify_b->cur_ancestor_index)
- {
- svn_client__merge_path_t *child =
- APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
- new_nearest_ancestor_index,
- svn_client__merge_path_t *);
- notify_b->cur_ancestor_index = new_nearest_ancestor_index;
- if (!child->absent && child->remaining_ranges->nelts > 0
- && !(new_nearest_ancestor_index == 0
- && child->remaining_ranges == 0))
+ const svn_client__merge_path_t *child
+ = find_nearest_ancestor(
+ notify_b->children_with_mergeinfo,
+ notify->action != svn_wc_notify_update_delete,
+ notify_abspath);
+
+ if (notify_b->cur_ancestor_abspath == NULL
+ || strcmp(child->abspath, notify_b->cur_ancestor_abspath) != 0)
+ {
+ notify_b->cur_ancestor_abspath = child->abspath;
+ if (!child->absent && child->remaining_ranges->nelts > 0)
{
- svn_wc_notify_t *notify_merge_begin;
- notify_merge_begin =
- svn_wc_create_notify(child->abspath,
- notify_b->merge_b->same_repos
- ? svn_wc_notify_merge_begin
- : svn_wc_notify_foreign_merge_begin,
- pool);
- notify_merge_begin->merge_range =
- APR_ARRAY_IDX(child->remaining_ranges, 0,
- svn_merge_range_t *);
- if (notify_b->wrapped_func)
- (*notify_b->wrapped_func)(notify_b->wrapped_baton,
- notify_merge_begin, pool);
+ notify_merge_begin(child->abspath,
+ APR_ARRAY_IDX(child->remaining_ranges, 0,
+ svn_merge_range_t *),
+ notify_b->merge_b, pool);
}
}
}
@@ -2915,16 +3007,8 @@ notification_receiver(void *baton, const
&& notify_b->nbr_operative_notifications == 1
&& is_operative_notification)
{
- svn_wc_notify_t *notify_merge_begin;
- notify_merge_begin =
- svn_wc_create_notify(notify_b->merge_b->target_abspath,
- notify_b->merge_b->same_repos
- ? svn_wc_notify_merge_begin
- : svn_wc_notify_foreign_merge_begin,
- pool);
- if (notify_b->wrapped_func)
- (*notify_b->wrapped_func)(notify_b->wrapped_baton, notify_merge_begin,
- pool);
+ notify_merge_begin(notify_b->merge_b->target->abspath, NULL,
+ notify_b->merge_b, pool);
}
if (notify_b->wrapped_func)
@@ -2986,8 +3070,7 @@ rangelist_intersect_range(apr_array_head
rangelist but cannot be NULL.
PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
- and REVISION2. MERGEINFO_PATH is the absolute repository path of
- PRIMARY_URL (i.e. the path value of mergeinfo from PRIMARY_URL).
+ and REVISION2.
Since this function is only invoked for subtrees of the merge target, the
guarantees afforded by normalize_merge_sources() don't apply - see the
@@ -3018,7 +3101,7 @@ rangelist_intersect_range(apr_array_head
drive_merge_report_editor() won't attempt to describe these non-existent
subtree path/ranges to the reporter (which would break the merge).
- If the preceeding paragraph wasn't terribly clear then what follows spells
+ If the preceding paragraph wasn't terribly clear then what follows spells
out this function's behavior a bit more explicitly:
For forward merges (REVISION1 < REVISION2)
@@ -3058,7 +3141,6 @@ rangelist_intersect_range(apr_array_head
static svn_error_t *
adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
svn_client__merge_path_t *parent,
- const char *mergeinfo_path,
svn_revnum_t revision1,
svn_revnum_t revision2,
const char *primary_url,
@@ -3072,17 +3154,12 @@ adjust_deleted_subtree_ranges(svn_client
svn_revnum_t peg_rev = younger_rev;
svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
apr_array_header_t *segments;
- const char *rel_source_path;
svn_error_t *err;
SVN_ERR_ASSERT(parent->remaining_ranges);
- /* We want to know about PRIMARY_URL@peg_rev, but we need PRIMARY_URL's
- path relative to RA_SESSION's URL. */
- SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &rel_source_path,
- primary_url, scratch_pool));
err = svn_client__repos_location_segments(&segments, ra_session,
- rel_source_path, peg_rev,
+ primary_url, peg_rev,
younger_rev, older_rev, ctx,
scratch_pool);
@@ -3101,10 +3178,15 @@ adjust_deleted_subtree_ranges(svn_client
/* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
exists, if neither exist then the editor can simply ignore this
subtree. */
+ const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
svn_node_kind_t kind;
svn_error_clear(err);
err = NULL;
+
+ SVN_ERR(svn_ra_get_path_relative_to_session(
+ ra_session, &rel_source_path, primary_url, scratch_pool));
+
SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
older_rev, &kind, scratch_pool));
if (kind == svn_node_none)
@@ -3155,7 +3237,7 @@ adjust_deleted_subtree_ranges(svn_client
FALSE,
scratch_pool, scratch_pool));
- /* Merge into CHILD->REMANING_RANGES the intersection of
+ /* Merge into CHILD->REMAINING_RANGES the intersection of
PARENT->REMAINING_RANGES with the range beginning when
PRIMARY_URL@older_rev was deleted until younger_rev. */
SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
@@ -3222,7 +3304,7 @@ adjust_deleted_subtree_ranges(svn_client
segment->range_start, peg_rev,
FALSE, scratch_pool, scratch_pool));
- /* Merge into CHILD->REMANING_RANGES the intersection of
+ /* Merge into CHILD->REMAINING_RANGES the intersection of
PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
came into existence. */
SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
@@ -3252,44 +3334,38 @@ adjust_deleted_subtree_ranges(svn_client
/* Helper for do_directory_merge().
- URL1, REVISION1, URL2, REVISION2, NOTIFY_B, and MERGE_B are
- cascaded from the arguments of the same name in do_directory_merge().
- RA_SESSION is the session for the younger of URL1@REVISION1 and
- URL2@REVISION2.
+ SOURCE and MERGE_B are cascaded from the arguments of the same name in
+ do_directory_merge(). RA_SESSION is the session for the younger of
+ SOURCE->url1@rev1 and SOURCE->url2@rev2.
- Adjust the subtrees in NOTIFY_B->CHILDREN_WITH_MERGEINFO so that we don't
+ Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
later try to describe invalid paths in drive_merge_report_editor().
This function is just a thin wrapper around
adjust_deleted_subtree_ranges(), which see for further details.
SCRATCH_POOL is used for all temporary allocations. Changes to
- NOTIFY_B->CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
+ CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
*/
static svn_error_t *
-fix_deleted_subtree_ranges(const char *url1,
- svn_revnum_t revision1,
- const char *url2,
- svn_revnum_t revision2,
+fix_deleted_subtree_ranges(const merge_source_t *source,
svn_ra_session_t *ra_session,
- notification_receiver_baton_t *notify_b,
+ apr_array_header_t *children_with_mergeinfo,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- svn_boolean_t is_rollback = revision2 < revision1;
+ svn_boolean_t is_rollback = source->rev2 < source->rev1;
- /* NOTIFY_B->CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
+ /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
start at index 1 to examine only subtrees. */
- for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
+ for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
- APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
- svn_client__merge_path_t *);
+ APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
svn_client__merge_path_t *parent;
apr_array_header_t *deleted_rangelist, *added_rangelist;
- int parent_index;
SVN_ERR_ASSERT(child);
if (child->absent)
@@ -3298,10 +3374,8 @@ fix_deleted_subtree_ranges(const char *u
svn_pool_clear(iterpool);
/* Find CHILD's parent. */
- parent_index = find_nearest_ancestor(notify_b->children_with_mergeinfo,
- FALSE, child->abspath);
- parent = APR_ARRAY_IDX(notify_b->children_with_mergeinfo, parent_index,
- svn_client__merge_path_t *);
+ parent = find_nearest_ancestor(children_with_mergeinfo,
+ FALSE, child->abspath);
/* Since CHILD is a subtree then its parent must be in
CHILDREN_WITH_MERGEINFO, see the global comment
@@ -3327,16 +3401,15 @@ fix_deleted_subtree_ranges(const char *u
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
}
- /* If CHILD is the merge target we then know that URL1, URL2,
- REVISION1, and REVISION2 are provided by normalize_merge_sources()
- -- see 'MERGEINFO MERGE SOURCE NORMALIZATION'. Due to this
- normalization we know that URL1@REVISION1 and URL2@REVISION2
- describe an unbroken line of history such that the entire range
- described by REVISION1:REVISION2 can potentially be merged to CHILD.
+ /* If CHILD is the merge target we then know that SOURCE is provided
+ by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
+ NORMALIZATION'. Due to this normalization we know that SOURCE
+ describes an unbroken line of history such that the entire range
+ described by SOURCE can potentially be merged to CHILD.
But if CHILD is a subtree we don't have the same guarantees about
- URL1, URL2, REVISION1, and REVISION2 as we do for the merge target.
- URL1@REVSION1 and/or URL2@REVSION2 might not exist.
+ SOURCE as we do for the merge target. SOURCE->url1@rev1 and/or
+ SOURCE->url2@rev2 might not exist.
If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
such that we don't later try to describe invalid subtrees in
@@ -3347,10 +3420,9 @@ fix_deleted_subtree_ranges(const char *u
case, see the 'Note' in drive_merge_report_editor's docstring. */
if (deleted_rangelist->nelts || added_rangelist->nelts)
{
- const char *child_mergeinfo_path;
const char *child_primary_source_url;
const char *child_repos_src_path =
- svn_dirent_is_child(merge_b->target_abspath, child->abspath,
+ svn_dirent_is_child(merge_b->target->abspath, child->abspath,
iterpool);
/* This loop is only processing subtrees, so CHILD->ABSPATH
@@ -3358,16 +3430,12 @@ fix_deleted_subtree_ranges(const char *u
SVN_ERR_ASSERT(child_repos_src_path);
child_primary_source_url =
- svn_path_url_add_component2((revision1 < revision2) ? url2 : url1,
+ svn_path_url_add_component2((source->rev1 < source->rev2)
+ ? source->url2 : source->url1,
child_repos_src_path, iterpool);
- SVN_ERR(svn_ra__get_fspath_relative_to_root(ra_session,
- &child_mergeinfo_path,
- child_primary_source_url,
- iterpool));
SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
- child_mergeinfo_path,
- revision1, revision2,
+ source->rev1, source->rev2,
child_primary_source_url,
ra_session,
merge_b->ctx, result_pool,
@@ -3402,14 +3470,14 @@ fix_deleted_subtree_ranges(const char *u
INHERITED is ignored.
- If IMPLICIT_MERGEINFO is not NULL then START and END are limits on the
+ If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
the natural history sought, must both be valid revision numbers, and
START must be greater than END. If TARGET_ABSPATH's base revision
is older than START, then the base revision is used as the younger
bound in place of START.
- RA_SESSION is an open session that may be temporarily reparented as
- needed by this function.
+ RA_SESSION is an open RA session to the repository in which SOURCE lives.
+ It may be temporarily reparented as needed by this function.
Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
Use SCRATCH_POOL for any temporary allocations. */
@@ -3429,11 +3497,9 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
/* First, we get the real mergeinfo. */
if (recorded_mergeinfo)
{
- svn_boolean_t inherited_from_repos;
-
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
inherited,
- &inherited_from_repos,
+ NULL /* from_repos */,
FALSE,
inherit, ra_session,
target_abspath,
@@ -3444,18 +3510,16 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
{
const char *repos_root;
const char *repos_relpath;
- const char *session_url = NULL;
svn_revnum_t target_rev;
/* Assert that we have sane input. */
- SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start)
- && SVN_IS_VALID_REVNUM(end)
- && (start > end));
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
+ && (start > end));
/* Retrieve the origin (original_*) of the node, or just the
url if the node was not copied. */
SVN_ERR(svn_wc__node_get_origin(FALSE, &target_rev, &repos_relpath,
- &repos_root, NULL,
+ &repos_root, NULL, NULL,
ctx->wc_ctx, target_abspath, FALSE,
scratch_pool, scratch_pool));
@@ -3481,12 +3545,8 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
url = svn_path_url_add_component2(repos_root, repos_relpath,
scratch_pool);
- /* Temporarily point our RA_SESSION at our target URL so we can
- fetch so-called "implicit mergeinfo" (that is, natural
+ /* Fetch so-called "implicit mergeinfo" (that is, natural
history). */
- SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
- ra_session, url,
- scratch_pool));
/* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
TARGET_ABSPATH might not even exist, and even if it does the
@@ -3498,13 +3558,10 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
/* Fetch the implicit mergeinfo. */
SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
NULL,
- target_rev,
+ url, target_rev,
start, end,
ra_session, ctx,
result_pool));
-
- /* Return RA_SESSION back to where it was when we were called. */
- SVN_ERR(svn_ra_reparent(ra_session, session_url, scratch_pool));
}
} /*if (implicit_mergeinfo) */
@@ -3570,11 +3627,11 @@ inherit_implicit_mergeinfo_from_parent(s
If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
it now, allocating it in RESULT_POOL. If CHILD_INHERITS_PARENT is true
then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
- PARNET->IMPLICIT_MERGEINFO, otherwise contact the repository. Use
+ PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use
SCRATCH_POOL for all temporary allocations.
PARENT, CHILD, REVISION1, REVISION2, RA_SESSION, and
- CTX are all cascased from the arguments of the same name in
+ CTX are all cascaded from the arguments of the same name in
filter_merged_revisions() and the same conditions for that function
hold here. */
static svn_error_t *
@@ -3794,7 +3851,7 @@ filter_merged_revisions(svn_client__merg
else
target_rangelist = NULL;
- /* See earlier comment preceeding svn_rangelist_intersect() for
+ /* See earlier comment preceding svn_rangelist_intersect() for
why we don't consider inheritance here. */
if (target_rangelist)
{
@@ -3876,13 +3933,12 @@ filter_merged_revisions(svn_client__merg
/* Helper for do_file_merge and do_directory_merge (by way of
populate_remaining_ranges() for the latter).
- Determine what portions of URL1@REVISION1 -> URL2@REVISION2 have already
+ Determine what portions of SOURCE have already
been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with
the ranges that still need merging.
- URL1, REVISION1, URL2, REVISION2, and CTX are all cascaded
- from the caller's arguments of the same names. Note that this means URL1,
- REVISION1, URL2, and REVISION2 adhere to the requirements noted in
+ SOURCE and CTX are all cascaded from the caller's arguments of the same
+ names. Note that this means SOURCE adheres to the requirements noted in
`MERGEINFO MERGE SOURCE NORMALIZATION'.
CHILD represents a working copy path which is the merge target or one of
@@ -3890,8 +3946,8 @@ filter_merged_revisions(svn_client__merg
ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. TARGET_MERGEINFO is
the working mergeinfo on CHILD.
- RA_SESSION is the session for the younger of URL1@REVISION1 and
- URL2@REVISION2.
+ RA_SESSION is the session for the younger of SOURCE->url1@rev1 and
+ SOURCE->url2@rev2.
If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
@@ -3899,7 +3955,7 @@ filter_merged_revisions(svn_client__merg
the repository for CHILD->IMPLICIT_MERGEINFO.
If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history
- of URL1@REVISION1:URL2@REVISION2, see merge_cmd_baton_t.implicit_src_gap.
+ of SOURCE, see merge_cmd_baton_t.implicit_src_gap.
SCRATCH_POOL is used for all temporary allocations. Changes to CHILD and
PARENT are made in RESULT_POOL.
@@ -3912,18 +3968,15 @@ filter_merged_revisions(svn_client__merg
in depth-first order.
NOTE: When performing reverse merges, return
- SVN_ERR_CLIENT_NOT_READY_TO_MERGE if URL1@REVISION1, URL2@REVISION2, and
+ SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and
CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's
- base revision is older than the REVISION1-REVISION2 range, see comment re
+ base revision is older than the SOURCE->rev1:rev2 range, see comment re
issue #2973 below.
*/
static svn_error_t *
calculate_remaining_ranges(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
- const char *url1,
- svn_revnum_t revision1,
- const char *url2,
- svn_revnum_t revision2,
+ const merge_source_t *source,
svn_mergeinfo_t target_mergeinfo,
const apr_array_header_t *implicit_src_gap,
svn_boolean_t child_inherits_implicit,
@@ -3933,7 +3986,8 @@ calculate_remaining_ranges(svn_client__m
apr_pool_t *scratch_pool)
{
const char *mergeinfo_path;
- const char *primary_url = (revision1 < revision2) ? url2 : url1;
+ const char *primary_url = (source->rev1 < source->rev2)
+ ? source->url2 : source->url1;
svn_mergeinfo_t adjusted_target_mergeinfo = NULL;
svn_revnum_t child_base_revision;
@@ -3942,10 +3996,10 @@ calculate_remaining_ranges(svn_client__m
primary_url, result_pool));
/* Consider: CHILD might have explicit mergeinfo '/MERGEINFO_PATH:M-N'
- where M-N fall into the gap in URL1@REVISION1:URL2@REVISION2's natural
+ where M-N fall into the gap in SOURCE's natural
history allowed by 'MERGEINFO MERGE SOURCE NORMALIZATION'. If this is
the case, then '/MERGEINFO_PATH:N' actually refers to a completely
- different line of history than URL1@REVISION1:URL2@REVISION2 and we
+ different line of history than SOURCE and we
*don't* want to consider those revisions merged already. */
if (implicit_src_gap && child->pre_merge_mergeinfo)
{
@@ -3973,7 +4027,7 @@ calculate_remaining_ranges(svn_client__m
merged (or, in the case of reverse merges, ranges not yet merged). */
SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
adjusted_target_mergeinfo,
- revision1, revision2,
+ source->rev1, source->rev2,
child_inherits_implicit,
ra_session, ctx, result_pool,
scratch_pool));
@@ -4010,8 +4064,8 @@ calculate_remaining_ranges(svn_client__m
can't have any "future" history. */
if (SVN_IS_VALID_REVNUM(child_base_revision)
&& ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
- && (revision2 < revision1) /* Reverse merge */
- && (child_base_revision <= revision2)) /* From CHILD's future */
+ && (source->rev2 < source->rev1) /* Reverse merge */
+ && (child_base_revision <= source->rev2)) /* From CHILD's future */
{
/* Hmmm, an inoperative reverse merge from the "future". If it is
from our own future return a helpful error. */
@@ -4019,7 +4073,7 @@ calculate_remaining_ranges(svn_client__m
const char *start_url;
err = svn_client__repos_location(&start_url,
- ra_session, url1, revision1,
+ ra_session, source->url1, source->rev1,
child_base_revision,
ctx, scratch_pool, scratch_pool);
if (err)
@@ -4049,58 +4103,59 @@ calculate_remaining_ranges(svn_client__m
/* Helper for populate_remaining_ranges().
- URL1, REVISION1, URL2, REVISION2, RA_SESSION, MERGE_SRC_CANON_PATH,
- and MERGE_B are all cascaded from the arguments of the same name in
- populate_remaining_ranges(). MERGE_SRC_CANON_PATH is the absolute
- repository path of URL2.
-
- Note: The following comments assume a forward merge, i.e.
- REVISION1 < REVISION2. If this is a reverse merge then all the following
- comments still apply, but with URL1 switched with URL2 and REVISION1
- switched with REVISION2.
-
- Like populate_remaining_ranges(), URL1@REVISION1:URL2@REVISION2 must adhere
- to the restrictions documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'.
- These restrictions allow for a *single* gap, URL@GAP_REV1:URL2@GAP_REV2,
- (where REVISION1 < GAP_REV1 <= GAP_REV2 < REVISION2) in
- URL1@REVISION1:URL2@REVISION2 if URL2@REVISION2 was copied from
- URL1@REVISION1. If such a gap exists, set *GAP_START and *GAP_END to the
- starting and ending revisions of the gap. Otherwise set both to
- SVN_INVALID_REVNUM.
+ SOURCE and MERGE_B are cascaded from the arguments of the same name in
+ populate_remaining_ranges().
+
+ Note: The following comments assume a forward merge, i.e. SOURCE->rev1
+ < SOURCE->rev2. If this is a reverse merge then all the following
+ comments still apply, but with SOURCE->url1 switched with SOURCE->url2
+ and SOURCE->rev1 switched with SOURCE->rev2.
+
+ Like populate_remaining_ranges(), SOURCE must adhere to the restrictions
+ documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'. These restrictions
+ allow for a *single* gap, URL@GAP_REV1:URL2@GAP_REV2, (where SOURCE->rev1
+ < GAP_REV1 <= GAP_REV2 < SOURCE->rev2) in SOURCE if SOURCE->url2@rev2 was
+ copied from SOURCE->url1@rev1. If such a gap exists, set *GAP_START and
+ *GAP_END to the starting and ending revisions of the gap. Otherwise set
+ both to SVN_INVALID_REVNUM.
For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this
would indicate that trunk@7 was copied from trunk@2. This function would
return GAP_START:GAP_END of 2:6 in this case. Note that a path 'trunk'
might exist at r3-6, but it would not be on the same line of history as
- trunk@9. */
+ trunk@9.
+
+ RA_SESSION is an open RA session to the repository in which SOURCE lives.
+*/
static svn_error_t *
find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
svn_revnum_t *gap_end,
- const char *merge_src_canon_path,
- const char *url1,
- svn_revnum_t revision1,
- const char *url2,
- svn_revnum_t revision2,
+ const merge_source_t *source,
svn_ra_session_t *ra_session,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t implicit_src_mergeinfo;
- svn_revnum_t young_rev = MAX(revision1, revision2);
- svn_revnum_t old_rev = MIN(revision1, revision2);
+ svn_revnum_t young_rev = MAX(source->rev1, source->rev2);
+ svn_revnum_t old_rev = MIN(source->rev1, source->rev2);
+ const char *primary_url = (source->rev1 < source->rev2)
+ ? source->url2 : source->url1;
+ const char *merge_src_fspath;
apr_array_header_t *rangelist;
/* Start by assuming there is no gap. */
*gap_start = *gap_end = SVN_INVALID_REVNUM;
- /* Get URL1@REVISION1:URL2@REVISION2 as mergeinfo. */
+ /* Get SOURCE as mergeinfo. */
SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
- young_rev, young_rev,
- old_rev, ra_session,
+ primary_url, young_rev,
+ young_rev, old_rev, ra_session,
merge_b->ctx, scratch_pool));
+ SVN_ERR(svn_ra__get_fspath_relative_to_root(
+ ra_session, &merge_src_fspath, primary_url, scratch_pool));
rangelist = apr_hash_get(implicit_src_mergeinfo,
- merge_src_canon_path,
+ merge_src_fspath,
APR_HASH_KEY_STRING);
if (!rangelist) /* ### Can we ever not find a rangelist? */
@@ -4133,7 +4188,7 @@ find_gaps_in_merge_source_history(svn_re
/* As mentioned above, multiple gaps *shouldn't* be possible. */
SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
- *gap_start = MIN(revision1, revision2);
+ *gap_start = MIN(source->rev1, source->rev2);
*gap_end = (APR_ARRAY_IDX(rangelist,
rangelist->nelts - 1,
svn_merge_range_t *))->start;
@@ -4141,8 +4196,8 @@ find_gaps_in_merge_source_history(svn_re
else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
{
apr_array_header_t *requested_rangelist =
- svn_rangelist__initialize(MIN(revision1, revision2),
- MAX(revision1, revision2),
+ svn_rangelist__initialize(MIN(source->rev1, source->rev2),
+ MAX(source->rev1, source->rev2),
TRUE, scratch_pool);
apr_array_header_t *implicit_rangelist =
apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
@@ -4171,35 +4226,34 @@ find_gaps_in_merge_source_history(svn_re
/* Helper for do_directory_merge().
- For each child in CHILDREN_WITH_MERGEINFO, populate that
- child's remaining_ranges list. CHILDREN_WITH_MERGEINFO is expected
+ For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO,
+ populate that child's 'remaining_ranges' list with (### ... what?),
+ and populate that child's 'implicit_mergeinfo' with its implicit
+ mergeinfo (natural history). CHILDREN_WITH_MERGEINFO is expected
to be sorted in depth first order and each child must be processed in
that order. The inheritability of all calculated ranges is TRUE.
- If HONOR_MERGEINFO is set, this function will actually try to be
+ If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO()
+ for how this is determined), this function will actually try to be
intelligent about populating remaining_ranges list. Otherwise, it
will claim that each child has a single remaining range, from
- revision1, to revision2.
+ SOURCE->rev1, to SOURCE->rev2.
+ ### We also take the short-cut if doing record-only. Why?
SCRATCH_POOL is used for all temporary allocations. Changes to
CHILDREN_WITH_MERGEINFO are made in RESULT_POOL.
- Note that if REVISION1 > REVISION2, then each child's remaining_ranges
+ Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges
member does not adhere to the API rules for rangelists described in
svn_mergeinfo.h -- See svn_client__merge_path_t.
See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
- around the values of URL1, REVISION1, URL2, and REVISION2.
+ around SOURCE.
*/
static svn_error_t *
populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
- const char *url1,
- svn_revnum_t revision1,
- const char *url2,
- svn_revnum_t revision2,
- svn_boolean_t honor_mergeinfo,
+ const merge_source_t *source,
svn_ra_session_t *ra_session,
- const char *parent_merge_src_canon_path,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -4209,9 +4263,9 @@ populate_remaining_ranges(apr_array_head
svn_revnum_t gap_start, gap_end;
/* If we aren't honoring mergeinfo or this is a --record-only merge,
- we'll make quick work of this by simply adding dummy REVISION1:REVISION2
+ we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2
ranges for all children. */
- if (! honor_mergeinfo || merge_b->record_only)
+ if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only)
{
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
@@ -4232,8 +4286,8 @@ populate_remaining_ranges(apr_array_head
NULL, /* child->inherited_mergeinfo */
svn_mergeinfo_inherited, ra_session,
child->abspath,
- MAX(revision1, revision2),
- MIN(revision1, revision2),
+ MAX(source->rev1, source->rev2),
+ MIN(source->rev1, source->rev2),
merge_b->ctx, result_pool,
iterpool));
}
@@ -4241,11 +4295,9 @@ populate_remaining_ranges(apr_array_head
{
/* Issue #3443 - Subtrees of the merge target can inherit
their parent's implicit mergeinfo in most cases. */
- int parent_index = find_nearest_ancestor(children_with_mergeinfo,
- FALSE, child->abspath);
svn_client__merge_path_t *parent
- = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
- svn_client__merge_path_t *);
+ = find_nearest_ancestor(children_with_mergeinfo,
+ FALSE, child->abspath);
svn_boolean_t child_inherits_implicit;
/* If CHILD is a subtree then its parent must be in
@@ -4256,13 +4308,13 @@ populate_remaining_ranges(apr_array_head
child_inherits_implicit = (parent && !child->switched);
SVN_ERR(ensure_implicit_mergeinfo(parent, child,
child_inherits_implicit,
- revision1, revision2,
+ source->rev1, source->rev2,
ra_session, merge_b->ctx,
result_pool, iterpool));
}
- child->remaining_ranges = svn_rangelist__initialize(revision1,
- revision2,
+ child->remaining_ranges = svn_rangelist__initialize(source->rev1,
+ source->rev2,
TRUE,
result_pool);
}
@@ -4271,21 +4323,19 @@ populate_remaining_ranges(apr_array_head
}
/* If, in the merge source's history, there was a copy from a older
- revision, then URL2 won't exist at some range M:N, where
- REVISION1 < M < N < REVISION2. The rules of 'MERGEINFO MERGE SOURCE
- NORMALIZATION' allow this, but we must ignore these gaps when
- calculating what ranges remain to be merged from
- URL1@REVISION1:URL2@REVISION2. If we don't and try to merge any part
- of URL2@M:URL2@N we would break the editor since no part of that
- actually exists. See http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
+ revision, then SOURCE->url2 won't exist at some range M:N, where
+ source->rev1 < M < N < source->rev2. The rules of 'MERGEINFO MERGE
+ SOURCE NORMALIZATION' allow this, but we must ignore these gaps when
+ calculating what ranges remain to be merged from SOURCE. If we don't
+ and try to merge any part of SOURCE->url2@M:N we would break the
+ editor since no part of that actually exists. See
+ http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
Find the gaps in the merge target's history, if any. Eventually
we will adjust CHILD->REMAINING_RANGES such that we don't describe
non-existent paths to the editor. */
SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
- parent_merge_src_canon_path,
- url1, revision1,
- url2, revision2,
+ source,
ra_session, merge_b,
iterpool));
@@ -4298,7 +4348,7 @@ populate_remaining_ranges(apr_array_head
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
const char *child_repos_path;
- const char *child_url1, *child_url2;
+ merge_source_t child_source = *source;
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
svn_client__merge_path_t *parent = NULL;
@@ -4313,13 +4363,13 @@ populate_remaining_ranges(apr_array_head
svn_pool_clear(iterpool);
- child_repos_path = svn_dirent_skip_ancestor(merge_b->target_abspath,
+ child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
child->abspath);
SVN_ERR_ASSERT(child_repos_path != NULL);
- child_url1 = svn_path_url_add_component2(url1, child_repos_path,
- iterpool);
- child_url2 = svn_path_url_add_component2(url2, child_repos_path,
- iterpool);
+ child_source.url1 = svn_path_url_add_component2(source->url1, child_repos_path,
+ iterpool);
+ child_source.url2 = svn_path_url_add_component2(source->url2, child_repos_path,
+ iterpool);
/* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the
merge target then also get its implicit mergeinfo. Otherwise defer
@@ -4332,17 +4382,15 @@ populate_remaining_ranges(apr_array_head
&(child->inherited_mergeinfo),
svn_mergeinfo_inherited, ra_session,
child->abspath,
- MAX(revision1, revision2),
- MIN(revision1, revision2),
+ MAX(source->rev1, source->rev2),
+ MIN(source->rev1, source->rev2),
merge_b->ctx, result_pool, iterpool));
/* If CHILD isn't the merge target find its parent. */
if (i > 0)
{
- int parent_index = find_nearest_ancestor(children_with_mergeinfo,
- FALSE, child->abspath);
- parent = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
- svn_client__merge_path_t *);
+ parent = find_nearest_ancestor(children_with_mergeinfo,
+ FALSE, child->abspath);
/* If CHILD is a subtree then its parent must be in
CHILDREN_WITH_MERGEINFO, see the global comment
'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
@@ -4357,8 +4405,7 @@ populate_remaining_ranges(apr_array_head
child_inherits_implicit = (parent && !child->switched);
SVN_ERR(calculate_remaining_ranges(parent, child,
- child_url1, revision1,
- child_url2, revision2,
+ &child_source,
child->pre_merge_mergeinfo,
merge_b->implicit_src_gap,
child_inherits_implicit,
@@ -4366,7 +4413,7 @@ populate_remaining_ranges(apr_array_head
merge_b->ctx, result_pool,
iterpool));
- /* Deal with any gap in URL1@REVISION1:URL2@REVISION2's natural history.
+ /* Deal with any gap in SOURCE's natural history.
If the gap is a proper subset of CHILD->REMAINING_RANGES then we can
safely ignore it since we won't describe this path/rev pair.
@@ -4387,7 +4434,7 @@ populate_remaining_ranges(apr_array_head
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES
so it will work with the svn_rangelist_* APIs below. */
- if (revision1 > revision2)
+ if (source->rev1 > source->rev2)
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
for (j = 0; j < child->remaining_ranges->nelts; j++)
@@ -4415,7 +4462,7 @@ populate_remaining_ranges(apr_array_head
if (!proper_subset)
{
- /* We need to make adjustements. Remove from, or add the gap
+ /* We need to make adjustments. Remove from, or add the gap
to, CHILD->REMAINING_RANGES as appropriate. */
if (overlaps_or_adjoins)
@@ -4429,7 +4476,7 @@ populate_remaining_ranges(apr_array_head
result_pool));
}
- if (revision1 > revision2) /* Reverse merge */
+ if (source->rev1 > source->rev2) /* Reverse merge */
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
}
}
@@ -4443,76 +4490,6 @@ populate_remaining_ranges(apr_array_head
/*** Other Helper Functions ***/
-/* Helper for record_mergeinfo_for_dir_merge().
-
- Adjust, in place, the inheritability of the ranges in RANGELIST to
- describe a merge of RANGELIST into WC_WCPATH at depth DEPTH. Set
- *RANGELIST_INHERITANCE to the inheritability set.
-
- WC_PATH_IS_MERGE_TARGET is true if WC_PATH is the target of the merge,
- otherwise WC_PATH is a subtree.
-
- WC_PATH_HAS_MISSING_CHILD is true if WC_PATH is missing an immediate child
- because the child is switched or absent from the WC, or due to a sparse
- checkout -- see get_mergeinfo_paths().
-
- Perform any temporary allocations in SCRATCH_POOL. */
-static svn_error_t *
-calculate_merge_inheritance(apr_array_header_t *rangelist,
- svn_boolean_t *rangelist_inheritance,
- const char *local_abspath,
- svn_boolean_t wc_path_is_merge_target,
- svn_boolean_t wc_path_has_missing_child,
- svn_depth_t depth,
- svn_wc_context_t *wc_ctx,
- apr_pool_t * scratch_pool)
-{
- svn_node_kind_t path_kind;
-
- SVN_ERR(svn_wc_read_kind(&path_kind, wc_ctx, local_abspath, FALSE,
- scratch_pool));
-
- /* Starting assumption. */
- *rangelist_inheritance = TRUE;
-
- if (path_kind == svn_node_file)
- {
- /* Files *never* have non-inheritable mergeinfo. */
- svn_rangelist__set_inheritance(rangelist, TRUE);
- }
- else if (path_kind == svn_node_dir)
- {
- if (wc_path_is_merge_target)
- {
- if (wc_path_has_missing_child
- || depth == svn_depth_files
- || depth == svn_depth_empty)
- {
- svn_rangelist__set_inheritance(rangelist, FALSE);
- *rangelist_inheritance = FALSE;
- }
- else /* depth == svn_depth_files || depth == svn_depth_empty */
- {
- svn_rangelist__set_inheritance(rangelist, TRUE);
- }
- }
- else /* WC_PATH is a directory subtree of the target. */
- {
- if (wc_path_has_missing_child
- || depth == svn_depth_immediates)
- {
- svn_rangelist__set_inheritance(rangelist, FALSE);
- *rangelist_inheritance = FALSE;
- }
- else /* depth == infinity */
- {
- svn_rangelist__set_inheritance(rangelist, TRUE);
- }
- }
- }
- return SVN_NO_ERROR;
-}
-
/* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH
based on MERGES (a mapping of absolute WC paths to rangelists representing
a merge from the source SOURCE_FSPATH).
@@ -4663,11 +4640,11 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
Record override mergeinfo on any paths skipped during a merge.
- Set empty mergeinfo on each path in NOTIFY_B->SKIPPED_PATHS so the path
+ Set empty mergeinfo on each path in SKIPPED_ABSPATHS so the path
does not incorrectly inherit mergeinfo that will later be describing
the merge.
- MERGEINFO_PATH, NOTIFY_B, and MERGE_B are all cascased from
+ MERGEINFO_PATH and MERGE_B are cascaded from
arguments of the same name in the caller.
IS_ROLLBACK is true if the caller is recording a reverse merge and false
@@ -4677,14 +4654,14 @@ static svn_error_t *
record_skips(const char *mergeinfo_path,
const apr_array_header_t *rangelist,
svn_boolean_t is_rollback,
- notification_receiver_baton_t *notify_b,
+ apr_hash_t *skipped_abspaths,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
apr_hash_t *merges;
- apr_size_t nbr_skips = (notify_b->skipped_abspaths != NULL ?
- apr_hash_count(notify_b->skipped_abspaths) : 0);
+ apr_size_t nbr_skips = (skipped_abspaths != NULL ?
+ apr_hash_count(skipped_abspaths) : 0);
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
if (nbr_skips == 0)
@@ -4693,7 +4670,7 @@ record_skips(const char *mergeinfo_path,
merges = apr_hash_make(scratch_pool);
/* Override the mergeinfo for child paths which weren't actually merged. */
- for (hi = apr_hash_first(scratch_pool, notify_b->skipped_abspaths); hi;
+ for (hi = apr_hash_first(scratch_pool, skipped_abspaths); hi;
hi = apr_hash_next(hi))
{
const char *skipped_abspath = svn__apr_hash_index_key(hi);
@@ -4727,13 +4704,12 @@ record_skips(const char *mergeinfo_path,
apr_array_make(scratch_pool, 0,
sizeof(svn_merge_range_t *)));
- if (nbr_skips < notify_b->nbr_notifications)
- /* ### Use RANGELIST as the mergeinfo for all children of
+ /* if (nbr_skips < notify_b->nbr_notifications)
+ ### Use RANGELIST as the mergeinfo for all children of
### this path which were not also explicitly
### skipped? */
- ;
}
- SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target_abspath,
+ SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath,
mergeinfo_path, merges,
is_rollback, merge_b->ctx, iterpool));
svn_pool_destroy(iterpool);
@@ -4766,8 +4742,7 @@ make_merge_conflict_error(const char *ta
or are descendants of TARGET_WCPATH by setting those children to NULL. */
static void
remove_absent_children(const char *target_wcpath,
- apr_array_header_t *children_with_mergeinfo,
- notification_receiver_baton_t *notify_b)
+ apr_array_header_t *children_with_mergeinfo)
{
/* Before we try to override mergeinfo for skipped paths, make sure
the path isn't absent due to authz restrictions, because there's
@@ -4776,8 +4751,7 @@ remove_absent_children(const char *targe
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
- APR_ARRAY_IDX(children_with_mergeinfo,
- i, svn_client__merge_path_t *);
+ APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if ((child->absent || child->scheduled_for_deletion)
&& svn_dirent_is_ancestor(target_wcpath, child->abspath))
{
@@ -4786,51 +4760,48 @@ remove_absent_children(const char *targe
}
}
-/* Helper for do_directory_merge() to handle the case were a merge editor
+/* Helper for do_directory_merge() to handle the case where a merge editor
drive removes explicit mergeinfo from a subtree of the merge target.
- MERGE_B, NOTIFY_B are cascaded from the arguments of the same name in
+ MERGE_B is cascaded from the argument of the same name in
do_directory_merge(). If MERGE_B->DRY_RUN is true do nothing, if it is
false then for each path (if any) in MERGE_B->PATHS_WITH_DELETED_MERGEINFO
- remove that path from NOTIFY_B->CHILDREN_WITH_MERGEINFO by setting that
+ remove that path from CHILDREN_WITH_MERGEINFO by setting that
child to NULL. The one exception is for the merge target itself,
MERGE_B->TARGET_ABSPATH, this must always be present in
- NOTIFY_B->CHILDREN_WITH_MERGEINFO so this is never removed by this
+ CHILDREN_WITH_MERGEINFO so this is never removed by this
function. */
static void
remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
- notification_receiver_baton_t *notify_b)
+ apr_array_header_t *children_with_mergeinfo)
{
int i;
if (merge_b->dry_run || !merge_b->paths_with_deleted_mergeinfo)
return;
- /* NOTIFY_B->CHILDREN_WITH_MERGEINFO[0] is the always the merge target
+ /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
so start at the first child. */
- for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
+ for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
- APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
- i, svn_client__merge_path_t *);
+ APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if (apr_hash_get(merge_b->paths_with_deleted_mergeinfo,
- child->abspath,
- APR_HASH_KEY_STRING))
+ child->abspath, APR_HASH_KEY_STRING))
{
- svn_sort__array_delete(notify_b->children_with_mergeinfo, i--, 1);
+ svn_sort__array_delete(children_with_mergeinfo, i--, 1);
}
}
}
/* Helper for do_directory_merge().
- Set up the diff editor report to merge URL1@REVISION1 to URL2@REVISION2
+ Set up the diff editor report to merge the SOURCE diff
into TARGET_ABSPATH and drive it.
- If mergeinfo is not being honored based on MERGE_B, see the doc string for
- HONOR_MERGEINFO() for how this is determined, then ignore
- CHILDREN_WITH_MERGEINFO and merge the diff between URL1@REVISION1 and
- URL2@REVISION2 to TARGET_ABSPATH.
+ If mergeinfo is not being honored (based on MERGE_B -- see the doc
+ string for HONOR_MERGEINFO() for how this is determined), then ignore
+ CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH.
If mergeinfo is being honored then perform a history-aware merge,
describing TARGET_ABSPATH and its subtrees to the reporter in such as way
@@ -4842,56 +4813,57 @@ remove_children_with_deleted_mergeinfo(m
svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY'
comment at the top of this file for more info. Note that it is possible
TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part,
- of REVISION1:REVISION2 to be merged. Though there is little point to
+ of SOURCE to be merged. Though there is little point to
calling this function if TARGET_ABSPATH and all its subtrees have already
- had URL1@REVISION1 to URL2@REVISION2 merged, this will work but is a no-op.
+ had SOURCE merged, this will work but is a no-op.
- REVISION1 and REVISION2 must be bound by the set of remaining_ranges
+ SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges
fields in CHILDREN_WITH_MERGEINFO's elements, specifically:
- For forward merges (REVISION1 < REVISION2):
+ For forward merges (SOURCE->rev1 < SOURCE->rev2):
1) The first svn_merge_range_t * element of each child's remaining_ranges
array must meet one of the following conditions:
- a) The range's start field is greater than or equal to REVISION2.
+ a) The range's start field is greater than or equal to SOURCE->rev2.
- b) The range's end field is REVISION2.
+ b) The range's end field is SOURCE->rev2.
2) Among all the ranges that meet condition 'b' the oldest start
- revision must equal REVISION1.
+ revision must equal SOURCE->rev1.
- For reverse merges (REVISION1 > REVISION2):
+ For reverse merges (SOURCE->rev1 > SOURCE->rev2):
1) The first svn_merge_range_t * element of each child's remaining_ranges
array must meet one of the following conditions:
- a) The range's start field is less than or equal to REVISION2.
+ a) The range's start field is less than or equal to SOURCE->rev2.
- b) The range's end field is REVISION2.
+ b) The range's end field is SOURCE->rev2.
2) Among all the ranges that meet condition 'b' the youngest start
- revision must equal REVISION1.
+ revision must equal SOURCE->rev1.
Note: If the first svn_merge_range_t * element of some subtree child's
remaining_ranges array is the same as the first range of that child's
nearest path-wise ancestor, then the subtree child *will not* be described
to the reporter.
- DEPTH, NOTIFY_B, and MERGE_B are cascasded from do_directory_merge(), see
+ DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see
that function for more info.
- If MERGE_B->sources_ancestral is set, then URL1@REVISION1 must be a
- historical ancestor of URL2@REVISION2, or vice-versa (see
+ MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any
+ URL in the repository of SOURCE; they may be temporarily reparented within
+ this function.
+
+ If MERGE_B->sources_ancestral is set, then SOURCE->url1@rev1 must be a
+ historical ancestor of SOURCE->url2@rev2, or vice-versa (see
`MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
- the values of URL1, REVISION1, URL2, and REVISION2 in this case).
+ SOURCE in this case).
*/
static svn_error_t *
drive_merge_report_editor(const char *target_abspath,
- const char *url1,
- svn_revnum_t revision1,
- const char *url2,
- svn_revnum_t revision2,
+ const merge_source_t *source,
const apr_array_header_t *children_with_mergeinfo,
svn_depth_t depth,
notification_receiver_baton_t *notify_b,
@@ -4903,20 +4875,18 @@ drive_merge_report_editor(const char *ta
void *diff_edit_baton;
void *report_baton;
svn_revnum_t target_start;
- svn_boolean_t honor_mergeinfo;
- const char *old_sess2_url;
- svn_boolean_t is_rollback = revision1 > revision2;
-
- honor_mergeinfo = HONOR_MERGEINFO(merge_b);
+ svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
+ const char *old_sess1_url, *old_sess2_url;
+ svn_boolean_t is_rollback = source->rev1 > source->rev2;
/* Start with a safe default starting revision for the editor and the
merge target. */
- target_start = revision1;
+ target_start = source->rev1;
/* If we are honoring mergeinfo the starting revision for the merge target
- might not be REVISION1, in fact the merge target might not need *any*
- part of REVISION1:REVISION2 merged -- Instead some subtree of the target
- needs REVISION1:REVISION2 -- So get the right starting revision for the
+ might not be SOURCE->rev1, in fact the merge target might not need *any*
+ part of SOURCE merged -- Instead some subtree of the target
+ needs SOURCE -- So get the right starting revision for the
target. */
if (honor_mergeinfo)
{
@@ -4937,22 +4907,22 @@ drive_merge_report_editor(const char *ta
if (child->remaining_ranges->nelts == 0)
{
/* The merge target doesn't need anything merged. */
- target_start = revision2;
+ target_start = source->rev2;
}
else
{
/* The merge target has remaining revisions to merge. These
ranges may fully or partially overlap the range described
- by REVISION1:REVISION2 or may not intersect that range at
+ by SOURCE->rev1:rev2 or may not intersect that range at
all. */
svn_merge_range_t *range =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
- if ((!is_rollback && range->start > revision2)
- || (is_rollback && range->start < revision2))
+ if ((!is_rollback && range->start > source->rev2)
+ || (is_rollback && range->start < source->rev2))
{
/* Merge target's first remaining range doesn't intersect. */
- target_start = revision2;
+ target_start = source->rev2;
}
else
{
@@ -4963,17 +4933,20 @@ drive_merge_report_editor(const char *ta
}
}
[... 4342 lines stripped ...]