You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/06/27 17:13:42 UTC
svn commit: r1354571 [8/37] - in /subversion/branches/master-passphrase: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/server-side/ notes/
notes/api-errata/1.8/ notes/directory-in...
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/merge.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/merge.c Wed Jun 27 15:12:37 2012
@@ -59,6 +59,7 @@
#include "private/svn_fspath.h"
#include "private/svn_ra_private.h"
#include "private/svn_client_private.h"
+#include "private/svn_subr_private.h"
#include "svn_private_config.h"
@@ -67,7 +68,8 @@
/* MERGEINFO MERGE SOURCE NORMALIZATION
*
* Nearly any helper function herein that accepts two URL/revision
- * pairs expects one of two things to be true:
+ * pairs (or equivalent struct merge_source_t) expects one of two things
+ * to be true:
*
* 1. that mergeinfo is not being recorded at all for this
* operation, or
@@ -81,24 +83,78 @@
*
* We use svn_ra_get_location_segments() to split a given range of
* revisions across an object's history into several which obey these
- * rules. For example, a merge between r19500 and r27567 of
- * Subversion's own /tags/1.4.5 directory gets split into sequential
- * merges of the following location pairs:
+ * rules. For example, an extract from the log of Subversion's own
+ * /subversion/tags/1.4.5 directory shows the following copies between
+ * r859500 and r866500 (omitting the '/subversion' prefix for clarity):
+ *
+ * r859598:
+ * A /branches/1.4.x (from /trunk:859597)
+ *
+ * r865417:
+ * A /tags/1.4.4 (from /branches/1.4.x:865262)
+ * # Notice that this copy leaves a gap between 865262 and 865417.
+ *
+ * r866420:
+ * A /branches/1.4.5 (from /tags/1.4.4:866419)
+ *
+ * r866425:
+ * D /branches/1.4.5
+ * A /tags/1.4.5 (from /branches/1.4.5:866424)
+ *
+ * In graphical form:
+ *
+ * 859500 859597 865262 866419 866424 866500
+ * . . . . . .
+ * trunk ------------------------------------------------
+ * \ . . .
+ * branches/1.4.x A-------------------------------------
+ * . \______ . .
+ * . \ . .
+ * tags/1.4.4 . A-----------------------
+ * . . \ .
+ * branches/1.4.5 . . A------D
+ * . . . \.
+ * tags/1.4.5 . . . A---------
+ * . . . .
+ * 859598 865417 866420 866425
+ *
+ * A merge of the difference between r859500 and r866500 of this directory
+ * gets split into sequential merges of the following location pairs.
+ *
+ * 859500 859597 865262 865416 866419 866424 866500
+ * . . . . . . .
+ * trunk (======] . . . . .
+ * . . . . .
+ * trunk ( . . . . .
+ * branches/1.4.x ======] . . . .
+ * . . . .
+ * branches/1.4.x ( . . . .
+ * tags/1.4.4 =============] . .
+ * implicit_src_gap (======] . . .
+ * . . .
+ * tags/1.4.4 ( . .
+ * branches/1.4.5 ======] .
+ * . .
+ * branches/1.4.5 ( .
+ * tags/1.4.5 ======]
+ *
+ * which are represented in merge_source_t as:
+ *
+ * [/trunk:859500, /trunk:859597]
+ * (recorded in svn:mergeinfo as /trunk:859501-859597)
+ *
+ * [/trunk:859597, /branches/1.4.x:865262]
+ * (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
+ *
+ * [/branches/1.4.x:865262, /tags/1.4.4@866419]
+ * (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
+ * (and there is a gap, the revision range [865262, 865416])
*
- * [/trunk:19549, /trunk:19523]
- * (recorded in svn:mergeinfo as /trunk:19500-19523)
+ * [/tags/1.4.4@866419, /branches/1.4.5@866424]
+ * (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
*
- * [/trunk:19523, /branches/1.4.x:25188]
- * (recorded in svn:mergeinfo as /branches/1.4.x:19524-25188)
- *
- * [/branches/1.4.x:25188, /tags/1.4.4@26345]
- * (recorded in svn:mergeinfo as /tags/1.4.4:25189-26345)
- *
- * [/tags/1.4.4@26345, /branches/1.4.5@26350]
- * (recorded in svn:mergeinfo as /branches/1.4.5:26346-26350)
- *
- * [/branches/1.4.5@26350, /tags/1.4.5@27567]
- * (recorded in svn:mergeinfo as /tags/1.4.5:26351-27567)
+ * [/branches/1.4.5@866424, /tags/1.4.5@866500]
+ * (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
*
* Our helper functions would then operate on one of these location
* pairs at a time.
@@ -167,6 +223,8 @@ typedef struct merge_source_t
/* "right" side URL and revision (inclusive iff youngest) */
const svn_client__pathrev_t *loc2;
+ /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
+ svn_boolean_t ancestral;
} merge_source_t;
/* Description of the merge target root node (a WC working node) */
@@ -190,9 +248,6 @@ typedef struct merge_cmd_baton_t {
svn_boolean_t dry_run;
svn_boolean_t record_only; /* Whether to merge only mergeinfo
differences. */
- svn_boolean_t sources_ancestral; /* Whether the left-side merge source is
- an ancestor of the right-side, or
- vice-versa (history-wise). */
svn_boolean_t same_repos; /* Whether the merge source repository
is the same repository as the
target. Defaults to FALSE if DRY_RUN
@@ -202,9 +257,6 @@ typedef struct merge_cmd_baton_t {
svn_boolean_t ignore_ancestry; /* Are we ignoring ancestry (and by
extension, mergeinfo)? FALSE if
SOURCES_ANCESTRAL is FALSE. */
- svn_boolean_t target_missing_child; /* Whether working copy target of the
- merge is missing any immediate
- children. */
svn_boolean_t reintegrate_merge; /* Whether this is a --reintegrate
merge or not. */
const char *added_path; /* Set to the dir path whenever the
@@ -295,7 +347,7 @@ typedef struct merge_cmd_baton_t {
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)->merge_source.ancestral \
&& (merge_b)->same_repos \
&& (! (merge_b)->ignore_ancestry))
@@ -312,10 +364,11 @@ typedef struct merge_cmd_baton_t {
/*** Utilities ***/
/* Return a new merge_source_t structure, allocated in RESULT_POOL,
- * initialized with deep copies of LOC1 and LOC2. */
+ * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
static merge_source_t *
merge_source_create(const svn_client__pathrev_t *loc1,
const svn_client__pathrev_t *loc2,
+ svn_boolean_t ancestral,
apr_pool_t *result_pool)
{
merge_source_t *s
@@ -323,6 +376,7 @@ merge_source_create(const svn_client__pa
s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
+ s->ancestral = ancestral;
return s;
}
@@ -335,23 +389,24 @@ merge_source_dup(const merge_source_t *s
s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
+ s->ancestral = source->ancestral;
return s;
}
/* 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 *
-check_repos_match(merge_cmd_baton_t *merge_b,
+check_repos_match(const merge_target_t *target,
const char *local_abspath,
const char *url,
apr_pool_t *scratch_pool)
{
- if (!svn_uri__is_ancestor(merge_b->target->loc.repos_root_url, url))
+ 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, svn_dirent_local_style(local_abspath, scratch_pool),
- merge_b->target->loc.repos_root_url);
+ target->loc.repos_root_url);
return SVN_NO_ERROR;
}
@@ -521,59 +576,63 @@ perform_obstruction_check(svn_wc_notify_
/* Create *LEFT and *RIGHT conflict versions for conflict victim
* at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained
- * from MERGE_B.
- * Allocate returned conflict versions in MERGE_B->POOL. */
+ * from MERGE_SOURCE and TARGET.
+ * Allocate returned conflict versions in POOL. */
static svn_error_t *
make_conflict_versions(const svn_wc_conflict_version_t **left,
const svn_wc_conflict_version_t **right,
const char *victim_abspath,
svn_node_kind_t node_kind,
- merge_cmd_baton_t *merge_b)
+ const merge_source_t *merge_source,
+ const merge_target_t *target,
+ apr_pool_t *pool)
{
- const char *child = svn_dirent_skip_ancestor(merge_b->target->abspath,
+ const char *child = svn_dirent_skip_ancestor(target->abspath,
victim_abspath);
const char *left_relpath, *right_relpath;
SVN_ERR_ASSERT(child != NULL);
- left_relpath = svn_client__pathrev_relpath(merge_b->merge_source.loc1,
- merge_b->pool);
- right_relpath = svn_client__pathrev_relpath(merge_b->merge_source.loc2,
- merge_b->pool);
+ left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
+ pool);
+ right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
+ pool);
*left = svn_wc_conflict_version_create(
- merge_b->merge_source.loc1->repos_root_url,
- svn_relpath_join(left_relpath, child, merge_b->pool),
- merge_b->merge_source.loc1->rev, node_kind, merge_b->pool);
+ merge_source->loc1->repos_root_url,
+ svn_relpath_join(left_relpath, child, pool),
+ merge_source->loc1->rev, node_kind, pool);
*right = svn_wc_conflict_version_create(
- merge_b->merge_source.loc2->repos_root_url,
- svn_relpath_join(right_relpath, child, merge_b->pool),
- merge_b->merge_source.loc2->rev, node_kind, merge_b->pool);
+ merge_source->loc2->repos_root_url,
+ svn_relpath_join(right_relpath, child, pool),
+ merge_source->loc2->rev, node_kind, pool);
return SVN_NO_ERROR;
}
-/* Set *CONFLICT to a new tree-conflict description allocated in MERGE_B->pool,
- * populated with information from MERGE_B and the other parameters.
+/* Set *CONFLICT to a new tree-conflict description allocated in POOL,
+ * populated with information from the other parameters.
* See tree_conflict() for the other parameters.
*/
static svn_error_t*
make_tree_conflict(svn_wc_conflict_description2_t **conflict,
- merge_cmd_baton_t *merge_b,
const char *victim_abspath,
svn_node_kind_t node_kind,
svn_wc_conflict_action_t action,
- svn_wc_conflict_reason_t reason)
+ svn_wc_conflict_reason_t reason,
+ const merge_source_t *merge_source,
+ const merge_target_t *target,
+ apr_pool_t *pool)
{
const svn_wc_conflict_version_t *left;
const svn_wc_conflict_version_t *right;
SVN_ERR(make_conflict_versions(&left, &right, victim_abspath, node_kind,
- merge_b));
+ merge_source, target, pool));
*conflict = svn_wc_conflict_description_create_tree2(
victim_abspath, node_kind, svn_wc_operation_merge,
- left, right, merge_b->pool);
+ left, right, pool);
(*conflict)->action = action;
(*conflict)->reason = reason;
@@ -614,8 +673,10 @@ tree_conflict(merge_cmd_baton_t *merge_b
svn_wc_conflict_description2_t *conflict;
/* There is no existing tree conflict so it is safe to add one. */
- SVN_ERR(make_tree_conflict(&conflict, merge_b, victim_abspath,
- node_kind, action, reason));
+ SVN_ERR(make_tree_conflict(&conflict, victim_abspath,
+ node_kind, action, reason,
+ &merge_b->merge_source, merge_b->target,
+ merge_b->pool));
SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
merge_b->pool));
@@ -648,8 +709,10 @@ tree_conflict_on_add(merge_cmd_baton_t *
/* Construct the new conflict first compare the new conflict with
a possibly existing one. */
- SVN_ERR(make_tree_conflict(&conflict, merge_b, victim_abspath,
- node_kind, action, reason));
+ SVN_ERR(make_tree_conflict(&conflict, victim_abspath,
+ node_kind, action, reason,
+ &merge_b->merge_source, merge_b->target,
+ merge_b->pool));
SVN_ERR(svn_wc__get_tree_conflict(&existing_conflict, merge_b->ctx->wc_ctx,
victim_abspath, merge_b->pool,
@@ -778,8 +841,7 @@ split_mergeinfo_on_revision(svn_mergeinf
ranges from *MERGEINFO */
if (!(*younger_mergeinfo))
*younger_mergeinfo = apr_hash_make(pool);
- apr_hash_set(*younger_mergeinfo,
- (const char *)merge_source_path,
+ apr_hash_set(*younger_mergeinfo, merge_source_path,
APR_HASH_KEY_STRING, younger_rangelist);
SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
*mergeinfo, TRUE, pool, iterpool));
@@ -825,25 +887,19 @@ omit_mergeinfo_changes(apr_array_header_
*PROPS is an array of svn_prop_t structures representing regular properties
to be added to the working copy TARGET_ABSPATH.
- HONOR_MERGEINFO determines whether mergeinfo will be honored by this
- function (when applicable).
+ The merge source and target are assumed to be in the same repository.
- If mergeinfo is not being honored, SAME_REPOS is true, and
- 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 TARGET_ABSPATH when
+ 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)
+ 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 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.
- If SAME_REPOS is true, RA_SESSION is an open RA session to the repository
+ 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.
@@ -854,9 +910,6 @@ omit_mergeinfo_changes(apr_array_header_
static svn_error_t *
filter_self_referential_mergeinfo(apr_array_header_t **props,
const char *target_abspath,
- svn_boolean_t honor_mergeinfo,
- svn_boolean_t same_repos,
- svn_boolean_t reintegrate_merge,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
@@ -864,42 +917,22 @@ filter_self_referential_mergeinfo(apr_ar
apr_array_header_t *adjusted_props;
int i;
apr_pool_t *iterpool;
- svn_boolean_t is_added;
+ svn_boolean_t is_copy;
+ const char *repos_relpath;
svn_client__pathrev_t target_base;
- /* Issue #3383: We don't want mergeinfo from a foreign repos.
+ /* If PATH itself has been added there is no need to filter. */
+ SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath,
+ &target_base.repos_root_url,
+ &target_base.repos_uuid, NULL,
+ ctx->wc_ctx, target_abspath, FALSE,
+ pool, pool));
- If this is a merge from a foreign repository we must strip all
- incoming mergeinfo (including mergeinfo deletions). Otherwise if
- this property isn't mergeinfo or is NULL valued (i.e. prop removal)
- or empty mergeinfo it does not require any special handling. There
- is nothing to filter out of empty mergeinfo and the concept of
- filtering doesn't apply if we are trying to remove mergeinfo
- entirely. */
- if (! same_repos)
- return svn_error_trace(omit_mergeinfo_changes(props, *props, pool));
-
- /* If we aren't honoring mergeinfo and this is a merge from the
- same repository, then get outta here. If this is a reintegrate
- merge or a merge from a foreign repository we still need to
- filter regardless of whether we are honoring mergeinfo or not. */
- if (! honor_mergeinfo
- && ! reintegrate_merge)
- return SVN_NO_ERROR;
-
- /* 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, target_abspath, pool));
- if (is_added)
- return SVN_NO_ERROR;
+ if (is_copy || !repos_relpath)
+ return SVN_NO_ERROR; /* A copy or a local addition */
- SVN_ERR(svn_wc__node_get_url(&target_base.url, ctx->wc_ctx, target_abspath,
- pool, pool));
- SVN_ERR(svn_wc__node_get_base_rev(&target_base.rev, ctx->wc_ctx,
- target_abspath, pool));
- SVN_ERR(svn_wc__node_get_repos_info(&target_base.repos_root_url,
- &target_base.repos_uuid,
- ctx->wc_ctx, target_abspath, pool, pool));
+ target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
+ repos_relpath, pool);
adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
iterpool = svn_pool_create(pool);
@@ -1129,31 +1162,33 @@ filter_self_referential_mergeinfo(apr_ar
return SVN_NO_ERROR;
}
-/* Used for both file and directory property merges. */
+/* Prepare a set of property changes PROPCHANGES to be used for a merge
+ operation on LOCAL_ABSPATH. Store the result in *PROP_UPDATES.
+
+ Store information on where mergeinfo is updated in MERGE_B.
+
+ Used for both file and directory property merges. */
static svn_error_t *
-merge_props_changed(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *local_abspath,
- const apr_array_header_t *propchanges,
- apr_hash_t *original_props,
- void *baton,
- apr_pool_t *scratch_pool)
+prepare_merge_props_changed(const apr_array_header_t **prop_updates,
+ const char *local_abspath,
+ const apr_array_header_t *propchanges,
+ merge_cmd_baton_t *merge_b,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_array_header_t *props;
- merge_cmd_baton_t *merge_b = baton;
- svn_client_ctx_t *ctx = merge_b->ctx;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
- scratch_pool));
+ result_pool));
/* If we are only applying mergeinfo changes then we need to do
additional filtering of PROPS so it contains only mergeinfo changes. */
if (merge_b->record_only && props->nelts)
{
apr_array_header_t *mergeinfo_props =
- apr_array_make(scratch_pool, 1, sizeof(svn_prop_t));
+ apr_array_make(result_pool, 1, sizeof(svn_prop_t));
int i;
for (i = 0; i < props->nelts; i++)
@@ -1173,104 +1208,92 @@ merge_props_changed(svn_wc_notify_state_
definition, 'svn merge' shouldn't touch any data within .svn/ */
if (props->nelts)
{
- svn_error_t *err;
-
/* If this is a forward merge then don't add new mergeinfo to
PATH that is already part of PATH's own history, see
http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the
merge sources are not ancestral then there is no concept of a
'forward' or 'reverse' merge and we filter unconditionally. */
if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
- || !merge_b->sources_ancestral)
- SVN_ERR(filter_self_referential_mergeinfo(&props,
- local_abspath,
- HONOR_MERGEINFO(merge_b),
- merge_b->same_repos,
- merge_b->reintegrate_merge,
- merge_b->ra_session2,
- merge_b->ctx,
- scratch_pool));
-
- err = svn_wc_merge_props3(state, ctx->wc_ctx, local_abspath, NULL, NULL,
- original_props, props, merge_b->dry_run,
- ctx->conflict_func2, ctx->conflict_baton2,
- ctx->cancel_func, ctx->cancel_baton,
- scratch_pool);
-
- /* If this is not a dry run then make a record in BATON if we find a
- PATH where mergeinfo is added where none existed previously or PATH
- is having its existing mergeinfo deleted. */
- if (!merge_b->dry_run)
+ || !merge_b->merge_source.ancestral)
{
- int i;
+ /* Issue #3383: We don't want mergeinfo from a foreign repos.
- for (i = 0; i < props->nelts; ++i)
- {
- svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
+ If this is a merge from a foreign repository we must strip all
+ incoming mergeinfo (including mergeinfo deletions). Otherwise if
+ this property isn't mergeinfo or is NULL valued (i.e. prop removal)
+ or empty mergeinfo it does not require any special handling. There
+ is nothing to filter out of empty mergeinfo and the concept of
+ filtering doesn't apply if we are trying to remove mergeinfo
+ entirely. */
+ if (! merge_b->same_repos)
+ SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
+ else if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
+ SVN_ERR(filter_self_referential_mergeinfo(&props,
+ local_abspath,
+ merge_b->ra_session2,
+ merge_b->ctx,
+ result_pool));
+ }
+ }
+ *prop_updates = props;
+
+ /* If this is not a dry run then make a record in BATON if we find a
+ PATH where mergeinfo is added where none existed previously or PATH
+ is having its existing mergeinfo deleted. */
+ if (!merge_b->dry_run && props->nelts)
+ {
+ int i;
- if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
- {
- /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
- svn_boolean_t has_pristine_mergeinfo = FALSE;
- apr_hash_t *pristine_props;
-
- SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
- ctx->wc_ctx,
- local_abspath,
- scratch_pool,
- scratch_pool));
+ for (i = 0; i < props->nelts; ++i)
+ {
+ svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
+
+ if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
+ {
+ /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
+ svn_boolean_t has_pristine_mergeinfo = FALSE;
+ apr_hash_t *pristine_props;
+
+ SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
+ merge_b->ctx->wc_ctx,
+ local_abspath,
+ scratch_pool,
+ scratch_pool));
- if (pristine_props
- && apr_hash_get(pristine_props, SVN_PROP_MERGEINFO,
- APR_HASH_KEY_STRING))
- has_pristine_mergeinfo = TRUE;
+ if (pristine_props
+ && apr_hash_get(pristine_props, SVN_PROP_MERGEINFO,
+ APR_HASH_KEY_STRING))
+ has_pristine_mergeinfo = TRUE;
- if (!has_pristine_mergeinfo && prop->value)
- {
- /* If BATON->PATHS_WITH_NEW_MERGEINFO needs to be
- allocated do so in BATON->POOL so it has a
- sufficient lifetime. */
- if (!merge_b->paths_with_new_mergeinfo)
- merge_b->paths_with_new_mergeinfo =
- apr_hash_make(merge_b->pool);
-
- apr_hash_set(merge_b->paths_with_new_mergeinfo,
- apr_pstrdup(merge_b->pool, local_abspath),
- APR_HASH_KEY_STRING, local_abspath);
- }
- else if (has_pristine_mergeinfo && !prop->value)
- {
- /* If BATON->PATHS_WITH_DELETED_MERGEINFO needs to be
- allocated do so in BATON->POOL so it has a
- sufficient lifetime. */
- if (!merge_b->paths_with_deleted_mergeinfo)
- merge_b->paths_with_deleted_mergeinfo =
- apr_hash_make(merge_b->pool);
-
- apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
- apr_pstrdup(merge_b->pool, local_abspath),
- APR_HASH_KEY_STRING, local_abspath);
- }
+ if (!has_pristine_mergeinfo && prop->value)
+ {
+ /* If BATON->PATHS_WITH_NEW_MERGEINFO needs to be
+ allocated do so in BATON->POOL so it has a
+ sufficient lifetime. */
+ if (!merge_b->paths_with_new_mergeinfo)
+ merge_b->paths_with_new_mergeinfo =
+ apr_hash_make(merge_b->pool);
+
+ apr_hash_set(merge_b->paths_with_new_mergeinfo,
+ apr_pstrdup(merge_b->pool, local_abspath),
+ APR_HASH_KEY_STRING, local_abspath);
+ }
+ else if (has_pristine_mergeinfo && !prop->value)
+ {
+ /* If BATON->PATHS_WITH_DELETED_MERGEINFO needs to be
+ allocated do so in BATON->POOL so it has a
+ sufficient lifetime. */
+ if (!merge_b->paths_with_deleted_mergeinfo)
+ merge_b->paths_with_deleted_mergeinfo =
+ apr_hash_make(merge_b->pool);
+
+ apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
+ apr_pstrdup(merge_b->pool, local_abspath),
+ APR_HASH_KEY_STRING, local_abspath);
}
}
}
-
- if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
- || err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS))
- {
- /* If the entry doesn't exist in the wc, this is a tree-conflict. */
- if (state)
- *state = svn_wc_notify_state_missing;
- if (tree_conflicted)
- *tree_conflicted = TRUE;
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
- else if (err)
- return svn_error_trace(err);
}
- else if (state)
- *state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
@@ -1287,12 +1310,15 @@ merge_dir_props_changed(svn_wc_notify_st
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = diff_baton;
+ const apr_array_header_t *props;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
local_relpath, scratch_pool);
svn_wc_notify_state_t obstr_state;
+ svn_boolean_t is_deleted;
+ svn_node_kind_t kind;
- SVN_ERR(perform_obstruction_check(&obstr_state, NULL, NULL,
- NULL,
+ SVN_ERR(perform_obstruction_check(&obstr_state, NULL, &is_deleted,
+ &kind,
merge_b, local_abspath, svn_node_dir,
scratch_pool));
@@ -1303,6 +1329,26 @@ merge_dir_props_changed(svn_wc_notify_st
return SVN_NO_ERROR;
}
+ if (kind != svn_node_dir || is_deleted)
+ {
+ svn_wc_conflict_reason_t reason;
+
+ if (is_deleted)
+ reason = svn_wc_conflict_reason_deleted;
+ else
+ reason = svn_wc_conflict_reason_missing;
+
+ SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_file,
+ svn_wc_conflict_action_edit, reason));
+
+ if (tree_conflicted)
+ *tree_conflicted = TRUE;
+ if (state)
+ *state = svn_wc_notify_state_missing;
+
+ return SVN_NO_ERROR;
+ }
+
if (dir_was_added
&& merge_b->dry_run
&& dry_run_added_p(merge_b, local_abspath))
@@ -1310,13 +1356,26 @@ merge_dir_props_changed(svn_wc_notify_st
return SVN_NO_ERROR; /* We can't do a real prop merge for added dirs */
}
- return svn_error_trace(merge_props_changed(state,
- tree_conflicted,
- local_abspath,
- propchanges,
- original_props,
- diff_baton,
- scratch_pool));
+ SVN_ERR(prepare_merge_props_changed(&props, local_abspath, propchanges,
+ merge_b, scratch_pool, scratch_pool));
+
+ /* We only want to merge "regular" version properties: by
+ definition, 'svn merge' shouldn't touch any pristine data */
+ if (props->nelts)
+ {
+ svn_client_ctx_t *ctx = merge_b->ctx;
+
+ SVN_ERR(svn_wc_merge_props3(state, ctx->wc_ctx, local_abspath,
+ NULL, NULL, original_props, props,
+ merge_b->dry_run,
+ NULL, NULL, /* postpone conflicts */
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool));
+ }
+ else if (state)
+ *state = svn_wc_notify_state_unchanged;
+
+ return SVN_NO_ERROR;
}
/* Contains any state collected while resolving conflicts. */
@@ -1470,12 +1529,15 @@ 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,
- mine_relpath, scratch_pool);
+ svn_client_ctx_t *ctx = merge_b->ctx;
+ const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
+ mine_relpath, scratch_pool);
svn_node_kind_t wc_kind;
svn_boolean_t is_deleted;
+ const svn_wc_conflict_version_t *left;
+ const svn_wc_conflict_version_t *right;
- SVN_ERR_ASSERT(mine_abspath && svn_dirent_is_absolute(mine_abspath));
+ SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(!older_abspath || svn_dirent_is_absolute(older_abspath));
SVN_ERR_ASSERT(!yours_abspath || svn_dirent_is_absolute(yours_abspath));
@@ -1488,7 +1550,7 @@ merge_file_changed(svn_wc_notify_state_t
SVN_ERR(perform_obstruction_check(&obstr_state, NULL,
&is_deleted, &wc_kind,
- merge_b, mine_abspath, svn_node_unknown,
+ merge_b, local_abspath, svn_node_unknown,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
@@ -1506,7 +1568,7 @@ merge_file_changed(svn_wc_notify_state_t
/* Other easy outs: if the merge target isn't under version
control, or is just missing from disk, fogettaboutit. There's no
- way svn_wc_merge4() can do the merge. */
+ way svn_wc_merge5() can do the merge. */
if (wc_kind != svn_node_file || is_deleted)
{
const char *moved_to_abspath;
@@ -1522,8 +1584,8 @@ merge_file_changed(svn_wc_notify_state_t
* a conflict. Non-inheritable mergeinfo will be recorded, allowing
* future merges into non-shallow working copies to merge changes
* we missed this time around. */
- SVN_ERR(svn_wc__node_get_depth(&parent_depth, merge_b->ctx->wc_ctx,
- svn_dirent_dirname(mine_abspath,
+ SVN_ERR(svn_wc__node_get_depth(&parent_depth, ctx->wc_ctx,
+ svn_dirent_dirname(local_abspath,
scratch_pool),
scratch_pool));
if (parent_depth < svn_depth_files
@@ -1541,12 +1603,15 @@ merge_file_changed(svn_wc_notify_state_t
* #2282. See also notes/tree-conflicts/detection.txt
*/
err = svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
- merge_b->ctx->wc_ctx, mine_abspath,
+ ctx->wc_ctx, local_abspath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- svn_error_clear(err);
+ {
+ svn_error_clear(err);
+ moved_to_abspath = NULL;
+ }
else
return svn_error_trace(err);
}
@@ -1555,7 +1620,7 @@ merge_file_changed(svn_wc_notify_state_t
{
/* File has been moved away locally -- apply incoming
* changes at the new location. */
- mine_abspath = moved_to_abspath;
+ local_abspath = moved_to_abspath;
}
else
{
@@ -1565,7 +1630,7 @@ merge_file_changed(svn_wc_notify_state_t
reason = svn_wc_conflict_reason_deleted;
else
reason = svn_wc_conflict_reason_missing;
- SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
+ SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_file,
svn_wc_conflict_action_edit, reason));
if (tree_conflicted)
*tree_conflicted = TRUE;
@@ -1591,31 +1656,36 @@ merge_file_changed(svn_wc_notify_state_t
*/
/* This callback is essentially no more than a wrapper around
- svn_wc_merge4(). Thank goodness that all the
+ svn_wc_merge5(). Thank goodness that all the
diff-editor-mechanisms are doing the hard work of getting the
fulltexts! */
- /* Do property merge before text merge so that keyword expansion takes
- into account the new property values. */
+ if (prop_state)
+ *prop_state = svn_wc_notify_state_unchanged;
+
if (prop_changes->nelts > 0)
{
- svn_boolean_t tree_conflicted2 = FALSE;
+ /* Filter entry-props and unneeded properties in case of a record only
+ merge */
+ SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
+ prop_changes, merge_b,
+ scratch_pool, scratch_pool));
+ }
- SVN_ERR(merge_props_changed(prop_state, &tree_conflicted2,
- mine_abspath, prop_changes, original_props,
- baton, scratch_pool));
+ SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
+ svn_node_file, &merge_b->merge_source, merge_b->target, merge_b->pool));
- /* If the prop change caused a tree-conflict, just bail. */
- if (tree_conflicted2)
- {
- if (tree_conflicted != NULL)
- *tree_conflicted = TRUE;
-
- return SVN_NO_ERROR;
- }
+ /* Do property merge now, if we are not going to perform a text merge */
+ if ((merge_b->record_only || !older_abspath) && prop_changes->nelts)
+ {
+ SVN_ERR(svn_wc_merge_props3(prop_state, ctx->wc_ctx, local_abspath,
+ left, right,
+ original_props, prop_changes,
+ merge_b->dry_run,
+ NULL, NULL, /* postpone conflicts */
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool));
}
- else if (prop_state)
- *prop_state = svn_wc_notify_state_unchanged;
/* Easy out: We are only applying mergeinfo differences. */
if (merge_b->record_only)
@@ -1628,7 +1698,7 @@ merge_file_changed(svn_wc_notify_state_t
if (older_abspath)
{
svn_boolean_t has_local_mods;
- enum svn_wc_merge_outcome_t merge_outcome;
+ enum svn_wc_merge_outcome_t content_outcome;
/* xgettext: the '.working', '.merge-left.r%ld' and
'.merge-right.r%ld' strings are used to tag onto a file
@@ -1641,40 +1711,41 @@ merge_file_changed(svn_wc_notify_state_t
_(".merge-right.r%ld"),
yours_rev);
conflict_resolver_baton_t conflict_baton = { 0 };
- const svn_wc_conflict_version_t *left;
- const svn_wc_conflict_version_t *right;
- SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, merge_b->ctx->wc_ctx,
- mine_abspath, FALSE, scratch_pool));
+ SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
+ local_abspath, FALSE, scratch_pool));
+
+ /* Postpone all conflicts. */
+ conflict_baton.wrapped_func = NULL;
+ conflict_baton.wrapped_baton = NULL;
- conflict_baton.wrapped_func = merge_b->ctx->conflict_func2;
- conflict_baton.wrapped_baton = merge_b->ctx->conflict_baton2;
conflict_baton.conflicted_paths = &merge_b->conflicted_paths;
conflict_baton.pool = merge_b->pool;
- SVN_ERR(make_conflict_versions(&left, &right, mine_abspath,
- svn_node_file, merge_b));
- SVN_ERR(svn_wc_merge4(&merge_outcome, merge_b->ctx->wc_ctx,
- older_abspath, yours_abspath, mine_abspath,
+ /* Do property merge and text merge in one step so that keyword expansion
+ takes into account the new property values. */
+ SVN_ERR(svn_wc_merge5(&content_outcome, prop_state, ctx->wc_ctx,
+ older_abspath, yours_abspath, local_abspath,
left_label, right_label, target_label,
left, right,
merge_b->dry_run, merge_b->diff3_cmd,
- merge_b->merge_options, prop_changes,
+ merge_b->merge_options,
+ original_props, prop_changes,
conflict_resolver, &conflict_baton,
- merge_b->ctx->cancel_func,
- merge_b->ctx->cancel_baton,
+ ctx->cancel_func,
+ ctx->cancel_baton,
scratch_pool));
if (content_state)
{
- if (merge_outcome == svn_wc_merge_conflict)
+ if (content_outcome == svn_wc_merge_conflict)
*content_state = svn_wc_notify_state_conflicted;
else if (has_local_mods
- && merge_outcome != svn_wc_merge_unchanged)
+ && content_outcome != svn_wc_merge_unchanged)
*content_state = svn_wc_notify_state_merged;
- else if (merge_outcome == svn_wc_merge_merged)
+ else if (content_outcome == svn_wc_merge_merged)
*content_state = svn_wc_notify_state_changed;
- else if (merge_outcome == svn_wc_merge_no_merge)
+ else if (content_outcome == svn_wc_merge_no_merge)
*content_state = svn_wc_notify_state_missing;
else /* merge_outcome == svn_wc_merge_unchanged */
*content_state = svn_wc_notify_state_unchanged;
@@ -1706,6 +1777,7 @@ merge_file_added(svn_wc_notify_state_t *
merge_cmd_baton_t *merge_b = baton;
const char *mine_abspath = svn_dirent_join(merge_b->target->abspath,
mine_relpath, scratch_pool);
+ svn_node_kind_t wc_kind;
svn_node_kind_t kind;
int i;
apr_hash_t *file_props;
@@ -1764,7 +1836,7 @@ merge_file_added(svn_wc_notify_state_t *
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, NULL, NULL,
- &kind,
+ &wc_kind,
merge_b, mine_abspath, svn_node_unknown,
scratch_pool));
@@ -1811,8 +1883,8 @@ merge_file_added(svn_wc_notify_state_t *
merge_b->merge_source.loc2->url,
child, scratch_pool);
copyfrom_rev = rev2;
- SVN_ERR(check_repos_match(merge_b, mine_abspath, copyfrom_url,
- scratch_pool));
+ SVN_ERR(check_repos_match(merge_b->target, mine_abspath,
+ copyfrom_url, scratch_pool));
new_base_props = file_props;
new_props = NULL; /* inherit from new_base_props */
SVN_ERR(svn_stream_open_readonly(&new_base_contents,
@@ -1897,10 +1969,6 @@ merge_file_added(svn_wc_notify_state_t *
if (content_state)
{
/* directory already exists, is it under version control? */
- svn_node_kind_t wc_kind;
- SVN_ERR(svn_wc_read_kind(&wc_kind, merge_b->ctx->wc_ctx,
- mine_abspath, FALSE, scratch_pool));
-
if ((wc_kind != svn_node_none)
&& dry_run_deleted_p(merge_b, mine_abspath))
*content_state = svn_wc_notify_state_changed;
@@ -2207,7 +2275,7 @@ merge_dir_added(svn_wc_notify_state_t *s
child, scratch_pool);
copyfrom_rev = rev;
- SVN_ERR(check_repos_match(merge_b, parent_abspath, copyfrom_url,
+ SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
scratch_pool));
}
@@ -2674,10 +2742,8 @@ typedef struct notification_receiver_bat
MERGE_B->REINTEGRATE_MERGE are both false. */
apr_hash_t *skipped_abspaths;
- /* A list of the absolute root paths of any added subtrees which might
- require their own explicit mergeinfo. Is NULL if
- MERGE_B->SOURCES_ANCESTRAL and MERGE_B->REINTEGRATE_MERGE are both
- false. */
+ /* A hash of (const char *) absolute WC paths mapped to the same which
+ represent the roots of subtrees added by the merge. May be NULL. */
apr_hash_t *added_abspaths;
/* A list of tree conflict victim absolute paths which may be NULL. Is NULL
@@ -2750,20 +2816,21 @@ find_nearest_ancestor(const apr_array_he
static void
notify_merge_begin(const char *target_abspath,
const svn_merge_range_t *range,
- merge_cmd_baton_t *merge_b,
+ svn_boolean_t same_repos,
+ svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- if (merge_b->ctx->notify_func2)
+ if (ctx->notify_func2)
{
svn_wc_notify_t *n
= svn_wc_create_notify(target_abspath,
- merge_b->same_repos
+ 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);
+ ctx->notify_func2(ctx->notify_baton2, n, pool);
}
}
@@ -2813,6 +2880,67 @@ notify_merge_completed(const char *targe
}
}
+/* Helper for notification_receiver: Cache the roots of subtrees added under
+ TARGET_ABSPATH.
+
+ If *ADDED_ABSPATHS is not null, then it is a hash of (const char *)
+ absolute WC paths mapped to the same. If it is null, then allocate a
+ new hash in RESULT_POOL.
+
+ If ADDED_ABSPATH is a subtree of TARGET_ABSPATH, is not already found in
+ *ADDED_ABSPATHS, nor is a subtree of any path already found within the
+ hash, then add a copy of ADDED_ABSPATH to *ADDED_ABSPATHS.
+
+ All additions to *ADDED_ABSPATHS are allocated in RESULT_POOL.
+ SCRATCH_POOL is used for temporary allocations. */
+static void
+update_the_list_of_added_subtrees(const char *target_abspath,
+ const char *added_abspath,
+ apr_hash_t **added_abspaths,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t root_of_added_subtree = TRUE;
+
+ /* Stash the root path of any added subtrees. */
+ if (*added_abspaths == NULL)
+ {
+ /* The first added path is always a root. */
+ *added_abspaths = apr_hash_make(result_pool);
+ }
+ else
+ {
+ apr_pool_t *subpool = svn_pool_create(scratch_pool);
+ const char *added_path_parent =
+ svn_dirent_dirname(added_abspath, subpool);
+
+ /* Is NOTIFY->PATH the root of an added subtree? */
+ while (strcmp(target_abspath, added_path_parent))
+ {
+ if (apr_hash_get(*added_abspaths,
+ added_path_parent,
+ APR_HASH_KEY_STRING))
+ {
+ root_of_added_subtree = FALSE;
+ break;
+ }
+
+ added_path_parent = svn_dirent_dirname(
+ added_path_parent, subpool);
+ }
+
+ svn_pool_destroy(subpool);
+ }
+
+ if (root_of_added_subtree)
+ {
+ const char *added_root_path = apr_pstrdup(result_pool,
+ added_abspath);
+ apr_hash_set(*added_abspaths, added_root_path,
+ APR_HASH_KEY_STRING, added_root_path);
+ }
+}
+
/* Is the notification the result of a real operative merge? */
#define IS_OPERATIVE_NOTIFICATION(notify) \
(notify->content_state == svn_wc_notify_state_conflicted \
@@ -2894,7 +3022,7 @@ notification_receiver(void *baton, const
}
/* Update the lists of merged, skipped, tree-conflicted and added paths. */
- if (notify_b->merge_b->sources_ancestral
+ if (notify_b->merge_b->merge_source.ancestral
|| notify_b->merge_b->reintegrate_merge)
{
if (notify->content_state == svn_wc_notify_state_merged
@@ -2941,37 +3069,27 @@ notification_receiver(void *baton, const
if (notify->action == svn_wc_notify_update_add)
{
- svn_boolean_t is_root_of_added_subtree = FALSE;
- const char *added_path = apr_pstrdup(notify_b->pool,
- notify_abspath);
- const char *added_path_parent = NULL;
+ update_the_list_of_added_subtrees(notify_b->merge_b->target->abspath,
+ notify_abspath,
+ &(notify_b->added_abspaths),
+ notify_b->pool, pool);
+ }
- /* Stash the root path of any added subtrees. */
- if (notify_b->added_abspaths == NULL)
- {
- notify_b->added_abspaths = apr_hash_make(notify_b->pool);
- is_root_of_added_subtree = TRUE;
- }
- 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;
- }
- if (is_root_of_added_subtree)
- apr_hash_set(notify_b->added_abspaths, added_path,
- APR_HASH_KEY_STRING, added_path);
+ if (notify->action == svn_wc_notify_update_delete
+ && notify_b->added_abspaths)
+ {
+ /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
+ are now deleting it, then remove it from the list of added
+ paths. */
+ apr_hash_set(notify_b->added_abspaths, notify_abspath,
+ APR_HASH_KEY_STRING, NULL);
}
}
/* 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)
+ if (notify_b->merge_b->merge_source.ancestral)
{
/* See if this is an operative directory merge. */
if (!(notify_b->is_single_file_merge) && is_operative_notification)
@@ -3006,7 +3124,8 @@ notification_receiver(void *baton, const
notify_merge_begin(child->abspath,
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *),
- notify_b->merge_b, pool);
+ notify_b->merge_b->same_repos,
+ notify_b->merge_b->ctx, pool);
}
}
}
@@ -3017,7 +3136,8 @@ notification_receiver(void *baton, const
&& is_operative_notification)
{
notify_merge_begin(notify_b->merge_b->target->abspath, NULL,
- notify_b->merge_b, pool);
+ notify_b->merge_b->same_repos,
+ notify_b->merge_b->ctx, pool);
}
if (notify_b->wrapped_func)
@@ -3178,7 +3298,10 @@ adjust_deleted_subtree_ranges(svn_client
forward merge over ra_neon then we get SVN_ERR_RA_DAV_REQUEST_FAILED.
http://subversion.tigris.org/issues/show_bug.cgi?id=3137 fixed some of
the cases where different RA layers returned different error codes to
- signal the "path not found"...but it looks like there is more to do. */
+ signal the "path not found"...but it looks like there is more to do.
+
+ ### Do we still need to special case for ra_neon (since it no longer
+ exists)? */
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
@@ -3343,9 +3466,9 @@ adjust_deleted_subtree_ranges(svn_client
/* Helper for do_directory_merge().
- 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.
+ SOURCE is cascaded from the argument of the same name in
+ do_directory_merge(). TARGET is the merge target. RA_SESSION is the
+ session for the younger of SOURCE->url1@rev1 and SOURCE->url2@rev2.
Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
later try to describe invalid paths in drive_merge_report_editor().
@@ -3357,9 +3480,10 @@ adjust_deleted_subtree_ranges(svn_client
*/
static svn_error_t *
fix_deleted_subtree_ranges(const merge_source_t *source,
+ const merge_target_t *target,
svn_ra_session_t *ra_session,
apr_array_header_t *children_with_mergeinfo,
- merge_cmd_baton_t *merge_b,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -3431,8 +3555,7 @@ fix_deleted_subtree_ranges(const merge_s
{
const char *child_primary_source_url;
const char *child_repos_src_path =
- svn_dirent_is_child(merge_b->target->abspath, child->abspath,
- iterpool);
+ svn_dirent_is_child(target->abspath, child->abspath, iterpool);
/* This loop is only processing subtrees, so CHILD->ABSPATH
better be a proper child of the merge target. */
@@ -3448,8 +3571,7 @@ fix_deleted_subtree_ranges(const merge_s
source->loc2->rev,
child_primary_source_url,
ra_session,
- merge_b->ctx, result_pool,
- iterpool));
+ ctx, result_pool, iterpool));
}
}
@@ -3529,7 +3651,7 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
scratch_pool, scratch_pool));
- if (! target->url)
+ if (! target)
{
/* We've been asked to operate on a locally added target, so its
* implicit mergeinfo is empty. */
@@ -3709,10 +3831,13 @@ ensure_implicit_mergeinfo(svn_client__me
REVISION1 and REVISION2 describe the merge range requested from
MERGEINFO_PATH.
- TARGET_MERGEINFO is the CHILD->ABSPATH's explicit or inherited mergeinfo.
- TARGET_MERGEINFO should be NULL if there is no explicit or inherited
- mergeinfo on CHILD->ABSPATH or an empty hash if CHILD->ABSPATH has empty
- mergeinfo.
+ TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
+ mergeinfo that intersects with the merge history described by
+ MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2. TARGET_RANGELIST
+ should be NULL if there is no explicit or inherited mergeinfo on
+ CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
+ explicit mergeinfo that exclusively describes non-intersecting history
+ with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
SCRATCH_POOL is used for all temporary allocations.
@@ -3725,7 +3850,7 @@ static svn_error_t *
filter_merged_revisions(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
const char *mergeinfo_path,
- svn_mergeinfo_t target_mergeinfo,
+ apr_array_header_t *target_rangelist,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_boolean_t child_inherits_implicit,
@@ -3734,7 +3859,7 @@ filter_merged_revisions(svn_client__merg
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- apr_array_header_t *requested_rangelist, *target_rangelist,
+ apr_array_header_t *requested_rangelist,
*target_implicit_rangelist, *explicit_rangelist;
/* Convert REVISION1 and REVISION2 to a rangelist.
@@ -3759,12 +3884,8 @@ filter_merged_revisions(svn_client__merg
our svn_rangelist_* APIs to work properly. */
SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
- if (target_mergeinfo)
- target_rangelist = apr_hash_get(target_mergeinfo,
- mergeinfo_path, APR_HASH_KEY_STRING);
- else
- target_rangelist = NULL;
-
+ /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
+ already recorded as merged to target. */
if (target_rangelist)
{
/* Return the intersection of the revs which are both already
@@ -3848,16 +3969,12 @@ filter_merged_revisions(svn_client__merg
}
else /* This is a forward merge */
{
- if (target_mergeinfo)
- target_rangelist = apr_hash_get(target_mergeinfo, mergeinfo_path,
- APR_HASH_KEY_STRING);
- else
- target_rangelist = NULL;
-
- /* See earlier comment preceding svn_rangelist_intersect() for
- why we don't consider inheritance here. */
+ /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
+ NOT already recorded as merged to target. */
if (target_rangelist)
{
+ /* See earlier comment preceding svn_rangelist_intersect() for
+ why we don't consider inheritance here. */
SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
target_rangelist,
requested_rangelist, FALSE,
@@ -3988,48 +4105,49 @@ calculate_remaining_ranges(svn_client__m
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- const char *mergeinfo_path;
- const char *primary_url = (source->loc1->rev < source->loc2->rev)
- ? source->loc2->url : source->loc1->url;
- svn_mergeinfo_t adjusted_target_mergeinfo = NULL;
+ const svn_client__pathrev_t *primary_src
+ = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
+ const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
+ scratch_pool);
+ /* Intersection of TARGET_MERGEINFO and the merge history
+ described by SOURCE. */
+ apr_array_header_t *target_rangelist;
svn_revnum_t child_base_revision;
- /* Determine which of the requested ranges to consider merging... */
- SVN_ERR(svn_ra__get_fspath_relative_to_root(ra_session, &mergeinfo_path,
- primary_url, result_pool));
+ /* Since this function should only be called when honoring mergeinfo and
+ * SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE
+ * NORMALIZATION', SOURCE must be 'ancestral'. */
+ SVN_ERR_ASSERT(source->ancestral);
- /* Consider: CHILD might have explicit mergeinfo '/MERGEINFO_PATH:M-N'
- 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 SOURCE and we
- *don't* want to consider those revisions merged already. */
- if (implicit_src_gap && child->pre_merge_mergeinfo)
- {
- apr_array_header_t *explicit_mergeinfo_gap_ranges =
- apr_hash_get(child->pre_merge_mergeinfo, mergeinfo_path,
- APR_HASH_KEY_STRING);
-
- if (explicit_mergeinfo_gap_ranges)
- {
- svn_mergeinfo_t gap_mergeinfo = apr_hash_make(scratch_pool);
+ /* Determine which of the requested ranges to consider merging... */
- apr_hash_set(gap_mergeinfo, mergeinfo_path, APR_HASH_KEY_STRING,
- implicit_src_gap);
- SVN_ERR(svn_mergeinfo_remove2(&adjusted_target_mergeinfo,
- gap_mergeinfo, target_mergeinfo,
- FALSE, result_pool, scratch_pool));
- }
- }
+ /* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers
+ to SOURCE (excluding any gap in SOURCE): first get all ranges from
+ TARGET_MERGEINFO that refer to the path of SOURCE, and then prune
+ any ranges that lie in the gap in SOURCE.
+
+ ### [JAF] In fact, that may still leave some ranges that lie entirely
+ outside the range of SOURCE; it seems we don't care about that. */
+ if (target_mergeinfo)
+ target_rangelist = apr_hash_get(target_mergeinfo, mergeinfo_path,
+ APR_HASH_KEY_STRING);
else
+ target_rangelist = NULL;
+ if (implicit_src_gap && target_rangelist)
{
- adjusted_target_mergeinfo = target_mergeinfo;
+ /* Remove any mergeinfo referring to the 'gap' in SOURCE, as that
+ mergeinfo doesn't really refer to SOURCE at all but instead
+ refers to locations that are non-existent or on a different
+ line of history. (Issue #3242.) */
+ SVN_ERR(svn_rangelist_remove(&target_rangelist,
+ implicit_src_gap, target_rangelist,
+ FALSE, result_pool));
}
/* Initialize CHILD->REMAINING_RANGES and filter out revisions already
merged (or, in the case of reverse merges, ranges not yet merged). */
SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
- adjusted_target_mergeinfo,
+ target_rangelist,
source->loc1->rev, source->loc2->rev,
child_inherits_implicit,
ra_session, ctx, result_pool,
@@ -4061,8 +4179,9 @@ calculate_remaining_ranges(svn_client__m
So in the name of user friendliness, return an error suggesting a helpful
course of action.
*/
- SVN_ERR(svn_wc__node_get_base_rev(&child_base_revision, ctx->wc_ctx,
- child->abspath, scratch_pool));
+ SVN_ERR(svn_wc__node_get_base(&child_base_revision, NULL, NULL, NULL,
+ ctx->wc_ctx, child->abspath,
+ scratch_pool, scratch_pool));
/* If CHILD has no base revision then it hasn't been committed yet, so it
can't have any "future" history. */
if (SVN_IS_VALID_REVNUM(child_base_revision)
@@ -4107,21 +4226,21 @@ calculate_remaining_ranges(svn_client__m
/* Helper for populate_remaining_ranges().
- SOURCE and MERGE_B are cascaded from the arguments of the same name in
+ SOURCE is 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.
+ Note: The following comments assume a forward merge, i.e.
+ SOURCE->loc1->rev < SOURCE->loc2->rev. If this is a reverse merge then
+ all the following comments still apply, but with SOURCE->loc1 switched
+ with SOURCE->loc2.
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.
+ allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive
+ (where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev),
+ if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1. 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
@@ -4129,6 +4248,9 @@ calculate_remaining_ranges(svn_client__m
might exist at r3-6, but it would not be on the same line of history as
trunk@9.
+ ### GAP_START is basically redundant, as (if there is a gap at all) it is
+ necessarily the older revision of SOURCE.
+
RA_SESSION is an open RA session to the repository in which SOURCE lives.
*/
static svn_error_t *
@@ -4136,16 +4258,19 @@ find_gaps_in_merge_source_history(svn_re
svn_revnum_t *gap_end,
const merge_source_t *source,
svn_ra_session_t *ra_session,
- merge_cmd_baton_t *merge_b,
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t implicit_src_mergeinfo;
svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev);
const svn_client__pathrev_t *primary_src
= (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
- const char *merge_src_fspath;
+ const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src,
+ scratch_pool);
apr_array_header_t *rangelist;
+ SVN_ERR_ASSERT(source->ancestral);
+
/* Start by assuming there is no gap. */
*gap_start = *gap_end = SVN_INVALID_REVNUM;
@@ -4154,10 +4279,8 @@ find_gaps_in_merge_source_history(svn_re
primary_src,
primary_src->rev, old_rev,
ra_session,
- merge_b->ctx, scratch_pool));
+ ctx, scratch_pool));
- SVN_ERR(svn_ra__get_fspath_relative_to_root(
- ra_session, &merge_src_fspath, primary_src->url, scratch_pool));
rangelist = apr_hash_get(implicit_src_mergeinfo,
merge_src_fspath,
APR_HASH_KEY_STRING);
@@ -4225,6 +4348,9 @@ find_gaps_in_merge_source_history(svn_re
}
}
+ SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev)
+ || (*gap_start == SVN_INVALID_REVNUM
+ && *gap_end == SVN_INVALID_REVNUM));
return SVN_NO_ERROR;
}
@@ -4343,7 +4469,7 @@ populate_remaining_ranges(apr_array_head
non-existent paths to the editor. */
SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
source,
- ra_session, merge_b,
+ ra_session, merge_b->ctx,
iterpool));
/* Stash any gap in the merge command baton, we'll need it later when
@@ -4354,12 +4480,11 @@ populate_remaining_ranges(apr_array_head
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
- const char *child_repos_path;
- svn_client__pathrev_t loc1 = *source->loc1;
- svn_client__pathrev_t loc2 = *source->loc2;
- merge_source_t child_source = { &loc1, &loc2 };
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
+ const char *child_repos_path
+ = svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath);
+ merge_source_t child_source;
svn_client__merge_path_t *parent = NULL;
svn_boolean_t child_inherits_implicit;
@@ -4370,15 +4495,20 @@ populate_remaining_ranges(apr_array_head
if (child->absent)
continue;
- svn_pool_clear(iterpool);
-
- child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
- child->abspath);
SVN_ERR_ASSERT(child_repos_path != NULL);
- loc1.url = svn_path_url_add_component2(
- source->loc1->url, child_repos_path, iterpool);
- loc2.url = svn_path_url_add_component2(
- source->loc2->url, child_repos_path, iterpool);
+ child_source.loc1 = svn_client__pathrev_join_relpath(
+ source->loc1, child_repos_path, iterpool);
+ child_source.loc2 = svn_client__pathrev_join_relpath(
+ source->loc2, child_repos_path, iterpool);
+ /* ### Is the child 'ancestral' over the same revision range? It's
+ * not necessarily true that a child is 'ancestral' if the parent is,
+ * nor that it's not if the parent is not. However, here we claim
+ * that it is. Before we had this 'ancestral' field that we need to
+ * set explicitly, the claim was implicit. Either way, the impact is
+ * that we might pass calculate_remaining_ranges() a source that is
+ * not in fact 'ancestral' (despite its 'ancestral' field being true),
+ * contrary to its doc-string. */
+ child_source.ancestral = source->ancestral;
/* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the
merge target then also get its implicit mergeinfo. Otherwise defer
@@ -4865,7 +4995,7 @@ remove_children_with_deleted_mergeinfo(m
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
+ If SOURCE->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
SOURCE in this case).
@@ -5213,8 +5343,8 @@ remove_first_range_from_remaining_ranges
and set *PROPS to a new hash of its properties.
RA_SESSION is a session open to the correct repository, which will be
- temporarily reparented to URL which is the URL of the file itself,
- and REV is the revision to get.
+ temporarily reparented to the URL of the file itself. LOCATION is the
+ repository location of the file.
The new temporary file will be created as a sibling of WC_TARGET.
WC_TARGET should be the local path to the working copy of the file, but
@@ -5230,8 +5360,7 @@ static svn_error_t *
single_file_merge_get_file(const char **filename,
apr_hash_t **props,
svn_ra_session_t *ra_session,
- const char *url,
- svn_revnum_t rev,
+ const svn_client__pathrev_t *location,
const char *wc_target,
apr_pool_t *pool)
{
@@ -5242,9 +5371,9 @@ single_file_merge_get_file(const char **
svn_dirent_dirname(wc_target, pool),
svn_io_file_del_none, pool, pool));
- SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, url,
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url,
pool));
- SVN_ERR(svn_ra_get_file(ra_session, "", rev,
+ SVN_ERR(svn_ra_get_file(ra_session, "", location->rev,
stream, NULL, props, pool));
SVN_ERR(svn_ra_reparent(ra_session, old_sess_url, pool));
@@ -5277,8 +5406,9 @@ single_file_merge_notify(notification_re
if (IS_OPERATIVE_NOTIFICATION(notify) && (! *header_sent))
{
notify_merge_begin(notify_baton->merge_b->target->abspath,
- (notify_baton->merge_b->sources_ancestral ? r : NULL),
- notify_baton->merge_b, pool);
+ (notify_baton->merge_b->merge_source.ancestral ? r : NULL),
+ notify_baton->merge_b->same_repos,
+ notify_baton->merge_b->ctx, pool);
*header_sent = TRUE;
}
notification_receiver(notify_baton, notify, pool);
@@ -5348,9 +5478,11 @@ insert_child_to_merge(apr_array_header_t
/* Helper for get_mergeinfo_paths().
- CHILDREN_WITH_MERGEINFO, MERGE_CMD_BATON, DEPTH, and POOL are
+ CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are
all cascaded from the arguments of the same name to get_mergeinfo_paths().
+ TARGET is the merge target.
+
*CHILD is the element in in CHILDREN_WITH_MERGEINFO that
get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
*CHILD.
@@ -5369,10 +5501,11 @@ insert_child_to_merge(apr_array_header_t
static svn_error_t *
insert_parent_and_sibs_of_sw_absent_del_subtree(
apr_array_header_t *children_with_mergeinfo,
- merge_cmd_baton_t *merge_cmd_baton,
+ const merge_target_t *target,
int *curr_index,
svn_client__merge_path_t *child,
svn_depth_t depth,
+ svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_client__merge_path_t *parent;
@@ -5383,7 +5516,7 @@ insert_parent_and_sibs_of_sw_absent_del_
if (!(child->absent
|| (child->switched
- && strcmp(merge_cmd_baton->target->abspath,
+ && strcmp(target->abspath,
child->abspath) != 0)))
return SVN_NO_ERROR;
@@ -5407,7 +5540,7 @@ insert_parent_and_sibs_of_sw_absent_del_
} /*(parent == NULL) */
/* Add all of PARENT's non-missing children that are not already present.*/
- SVN_ERR(svn_wc__node_get_children(&children, merge_cmd_baton->ctx->wc_ctx,
+ SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx,
parent_abspath, FALSE, pool, pool));
iterpool = svn_pool_create(pool);
for (i = 0; i < children->nelts; i++)
@@ -5429,7 +5562,7 @@ insert_parent_and_sibs_of_sw_absent_del_
svn_node_kind_t child_kind;
SVN_ERR(svn_wc_read_kind(&child_kind,
- merge_cmd_baton->ctx->wc_ctx,
+ ctx->wc_ctx,
child_abspath, FALSE, iterpool));
if (child_kind != svn_node_file)
continue;
@@ -5469,36 +5602,20 @@ struct pre_merge_status_baton_t
apr_pool_t *pool;
};
-/* A svn_client_status_func_t callback used by get_mergeinfo_paths to gather
- all switched, absent, and missing subtrees under a merge target. */
+/* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather
+ all switched, depth filtered and missing subtrees under a merge target.
+
+ Note that this doesn't see server and user excluded trees. */
static svn_error_t *
pre_merge_status_cb(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
struct pre_merge_status_baton_t *pmsb = baton;
const char *dup_abspath = NULL;
- /* ### Probably needed: Calculate file external status */
- svn_boolean_t is_file_external = FALSE;
-
- /* ### This block can go once we bumped to the EXTERNALS store */
- if (status->versioned
- && status->switched
- && status->kind == svn_node_file)
- {
- svn_node_kind_t external_kind;
-
- SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
- NULL,
- pmsb->wc_ctx, local_abspath,
- local_abspath, TRUE, pool, pool));
-
- is_file_external = (external_kind == svn_node_file);
- }
-
- if (status->switched && !is_file_external)
+ if (status->switched && !status->file_external)
{
if (!dup_abspath)
dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
@@ -5532,7 +5649,7 @@ pre_merge_status_cb(void *baton,
if (!dup_abspath)
dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
- for (hi = apr_hash_first(pool, pmsb->missing_subtrees);
+ for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
hi;
hi = apr_hash_next(hi))
{
@@ -5622,14 +5739,14 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
/* Helper for do_directory_merge() when performing merge-tracking aware
merges.
- Walk of the working copy tree rooted at MERGE_CMD_BATON->target->abspath to
+ Walk of the working copy tree rooted at TARGET->abspath to
depth DEPTH. Create an svn_client__merge_path_t * for any path which meets
one or more of the following criteria:
1) Path has working svn:mergeinfo.
2) Path is switched.
3) Path is a subtree of the merge target (i.e. is not equal to
- MERGE_CMD_BATON->target->abspath) and has no mergeinfo of its own but
+ TARGET->abspath) and has no mergeinfo of its own but
its immediate parent has mergeinfo with non-inheritable ranges. If
this isn't a dry-run and the merge is between differences in the same
repository, then this function will set working mergeinfo on the path
@@ -5641,10 +5758,10 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
sibling is switched, absent, scheduled for deletion, or missing due to
a sparse checkout.
6) Path is absent from disk due to an authz restriction.
- 7) Path is equal to MERGE_CMD_BATON->target->abspath.
+ 7) Path is equal to TARGET->abspath.
8) Path is an immediate *directory* child of
- MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
- 9) Path is an immediate *file* child of MERGE_CMD_BATON->target->abspath
+ TARGET->abspath and DEPTH is svn_depth_immediates.
+ 9) Path is an immediate *file* child of TARGET->abspath
and DEPTH is svn_depth_files.
10) Path is at a depth of 'empty' or 'files'.
11) Path is missing from disk (e.g. due to an OS-level deletion).
@@ -5657,7 +5774,7 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
sorted by svn_path_compare_paths(). Set the remaining_ranges field of each
element to NULL.
- Note: Since the walk is rooted at MERGE_CMD_BATON->target->abspath, the
+ Note: Since the walk is rooted at TARGET->abspath, the
latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
depth-first ordering it is guaranteed to be the first element in
*CHILDREN_WITH_MERGEINFO.
@@ -5667,15 +5784,18 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
*/
static svn_error_t *
get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
- merge_cmd_baton_t *merge_cmd_baton,
+ const merge_target_t *target,
svn_depth_t depth,
+ svn_boolean_t dry_run,
+ svn_boolean_t same_repos,
+ svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_t *subtrees_with_mergeinfo;
- apr_hash_t *server_excluded_subtrees;
+ apr_hash_t *excluded_subtrees;
apr_hash_t *switched_subtrees;
apr_hash_t *shallow_subtrees;
apr_hash_t *missing_subtrees;
@@ -5683,8 +5803,8 @@ get_mergeinfo_paths(apr_array_header_t *
/* Case 1: Subtrees with explicit mergeinfo. */
SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
- merge_cmd_baton->target->abspath,
- depth, merge_cmd_baton->ctx,
+ target->abspath,
+ depth, ctx,
result_pool, scratch_pool));
if (subtrees_with_mergeinfo)
{
@@ -5726,7 +5846,7 @@ get_mergeinfo_paths(apr_array_header_t *
/* Case 2: Switched subtrees
Case 10: Paths at depths of 'empty' or 'files'
Case 11: Paths missing from disk */
- pre_merge_status_baton.wc_ctx = merge_cmd_baton->ctx->wc_ctx;
+ pre_merge_status_baton.wc_ctx = ctx->wc_ctx;
switched_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.switched_subtrees = switched_subtrees;
shallow_subtrees = apr_hash_make(scratch_pool);
@@ -5734,13 +5854,15 @@ get_mergeinfo_paths(apr_array_header_t *
missing_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.missing_subtrees = missing_subtrees;
pre_merge_status_baton.pool = scratch_pool;
- SVN_ERR(svn_wc_walk_status(merge_cmd_baton->ctx->wc_ctx,
- merge_cmd_baton->target->abspath,
- depth, TRUE, TRUE, TRUE, NULL,
- pre_merge_status_cb,
- &pre_merge_status_baton,
- merge_cmd_baton->ctx->cancel_func,
- merge_cmd_baton->ctx->cancel_baton,
+ SVN_ERR(svn_wc_walk_status(ctx->wc_ctx,
+ target->abspath,
+ depth,
+ TRUE /* get_all */,
+ FALSE /* no_ignore */,
+ TRUE /* ignore_text_mods */,
+ NULL /* ingore_patterns */,
+ pre_merge_status_cb, &pre_merge_status_baton,
+ ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
/* Issue #2915: Raise an error describing the roots of any missing
@@ -5848,16 +5970,15 @@ get_mergeinfo_paths(apr_array_header_t *
}
}
- /* Case 6: Paths absent from disk due to server-side exclusion. */
- SVN_ERR(svn_wc__get_server_excluded_subtrees(&server_excluded_subtrees,
- merge_cmd_baton->ctx->wc_ctx,
- merge_cmd_baton->target->abspath,
- result_pool, scratch_pool));
- if (server_excluded_subtrees)
+ /* Case 6: Paths absent from disk due to server or user exclusion. */
+ SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees,
+ ctx->wc_ctx, target->abspath,
+ result_pool, scratch_pool));
+ if (excluded_subtrees)
{
apr_hash_index_t *hi;
- for (hi = apr_hash_first(scratch_pool, server_excluded_subtrees);
+ for (hi = apr_hash_first(scratch_pool, excluded_subtrees);
hi;
hi = apr_hash_next(hi))
{
@@ -5883,10 +6004,10 @@ get_mergeinfo_paths(apr_array_header_t *
/* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
present. */
if (!get_child_with_mergeinfo(children_with_mergeinfo,
- merge_cmd_baton->target->abspath))
+ target->abspath))
{
svn_client__merge_path_t *target_child =
- svn_client__merge_path_create(merge_cmd_baton->target->abspath,
+ svn_client__merge_path_create(target->abspath,
result_pool);
insert_child_to_merge(children_with_mergeinfo, target_child,
result_pool);
@@ -5903,8 +6024,8 @@ get_mergeinfo_paths(apr_array_header_t *
const apr_array_header_t *immediate_children;
SVN_ERR(svn_wc__node_get_children_of_working_node(
- &immediate_children, merge_cmd_baton->ctx->wc_ctx,
- merge_cmd_baton->target->abspath, FALSE, scratch_pool, scratch_pool));
+ &immediate_children, ctx->wc_ctx,
+ target->abspath, FALSE, scratch_pool, scratch_pool));
for (j = 0; j < immediate_children->nelts; j++)
{
@@ -5914,7 +6035,7 @@ get_mergeinfo_paths(apr_array_header_t *
svn_pool_clear(iterpool);
SVN_ERR(svn_wc_read_kind(&immediate_child_kind,
- merge_cmd_baton->ctx->wc_ctx,
+ ctx->wc_ctx,
immediate_child_abspath, FALSE,
iterpool));
if ((immediate_child_kind == svn_node_dir
@@ -5990,7 +6111,7 @@ get_mergeinfo_paths(apr_array_header_t *
int j;
SVN_ERR(svn_wc__node_get_children(&children,
- merge_cmd_baton->ctx->wc_ctx,
+ ctx->wc_ctx,
child->abspath, FALSE,
iterpool, iterpool));
for (j = 0; j < children->nelts; j++)
@@ -6014,7 +6135,7 @@ get_mergeinfo_paths(apr_array_header_t *
{
svn_node_kind_t child_kind;
SVN_ERR(svn_wc_read_kind(&child_kind,
- merge_cmd_baton->ctx->wc_ctx,
+ ctx->wc_ctx,
child_abspath, FALSE,
iterpool));
if (child_kind != svn_node_file)
@@ -6029,8 +6150,7 @@ get_mergeinfo_paths(apr_array_header_t *
insert_child_to_merge(children_with_mergeinfo,
child_of_noninheritable,
result_pool);
- if (!merge_cmd_baton->dry_run
- && merge_cmd_baton->same_repos)
+ if (!dry_run && same_repos)
{
svn_mergeinfo_t mergeinfo;
@@ -6038,20 +6158,20 @@ get_mergeinfo_paths(apr_array_header_t *
&mergeinfo, NULL,
svn_mergeinfo_nearest_ancestor,
child_of_noninheritable->abspath,
- merge_cmd_baton->target->abspath, NULL, FALSE,
- merge_cmd_baton->ctx, iterpool, iterpool));
+ target->abspath, NULL, FALSE,
+ ctx, iterpool, iterpool));
SVN_ERR(svn_client__record_wc_mergeinfo(
child_of_noninheritable->abspath, mergeinfo,
- FALSE, merge_cmd_baton->ctx, iterpool));
+ FALSE, ctx, iterpool));
}
}
}
}
/* Case 4 and 5 are handled by the following function. */
SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
- children_with_mergeinfo, merge_cmd_baton, &i, child,
- depth, result_pool));
[... 1816 lines stripped ...]