You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/08/10 22:56:05 UTC
svn commit: r984206 [8/35] - in /subversion/branches/ignore-mergeinfo: ./
build/ build/generator/ build/generator/templates/ build/hudson/
build/hudson/jobs/subversion-1.6.x-solaris/
build/hudson/jobs/subversion-1.6.x-ubuntu/ build/hudson/jobs/subversi...
Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c?rev=984206&r1=984205&r2=984206&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c Tue Aug 10 20:55:56 2010
@@ -145,7 +145,7 @@
* string. The elements of CHILDREN_WITH_MERGINFO should never be NULL.
*
* For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
- * the doc string for honor_mergeinfo().
+ * the doc string for HONOR_MERGEINFO().
*/
/*-----------------------------------------------------------------------*/
@@ -320,6 +320,23 @@ typedef struct merge_cmd_baton_t {
apr_pool_t *pool;
} merge_cmd_baton_t;
+
+/* If the merge source server is 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. */
+#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. */
+#define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \
+ && !(merge_b)->dry_run)
+
+
apr_hash_t *
svn_client__dry_run_deletions(void *merge_cmd_baton)
{
@@ -350,62 +367,17 @@ is_path_conflicted_by_merge(merge_cmd_ba
apr_hash_count(merge_b->conflicted_paths) > 0);
}
-/* Return the node kind of the working version of local path PATH,
- * according to the WC metadata in ENTRY. If ENTRY is null, assume the node
- * is unversioned and so set the kind to 'none'.
- *
- * However, if this is a dry run, set *NODE_KIND to 'none' if the node would
- * already have been deleted by the merge if this were not a dry run. Use
- * MERGE_B to determine the dry-run details. */
-static svn_node_kind_t
-node_kind_working(const char *path,
- const merge_cmd_baton_t *merge_b,
- const svn_wc_entry_t *entry)
-{
- if (!entry
- || (entry->schedule == svn_wc_schedule_delete)
- || (merge_b->dry_run && dry_run_deleted_p(merge_b, path))
- || (entry->deleted && entry->schedule != svn_wc_schedule_add))
- return svn_node_none;
- else
- return entry->kind;
-}
-
-/* Return the node kind that is found on disk at local path PATH.
- *
- * However, if this is a dry run, set *NODE_KIND to 'none' if the node would
- * already have been deleted by the merge if this were not a dry run. Use
- * MERGE_B to determine the dry-run details. */
-static svn_node_kind_t
-node_kind_on_disk(const char *path,
- const merge_cmd_baton_t *merge_b,
- apr_pool_t *pool)
-{
- svn_error_t *err;
- svn_node_kind_t node_kind;
-
- err = svn_io_check_path(path, &node_kind, pool);
- if (err)
- {
- svn_error_clear(err);
- return svn_node_unknown;
- }
- else if (dry_run_deleted_p(merge_b, path))
- return svn_node_none;
- else
- return node_kind;
-}
-
-/* Return a state indicating whether the WC metadata in ENTRY matches the
- * node kind on disk of the local path PATH. If ENTRY is null, assume the
- * node is unversioned. In the case of a dry-run merge, use the disk node
- * kind that would exist if it were not a dry run. Use MERGE_B to determine
- * the dry-run details.
+/* Return a state indicating whether the WC metadata matches the
+ * node kind on disk of the local path PATH.
+ * Use MERGE_B to determine the dry-run details; particularly, if a dry run
+ * noted that it deleted this path, assume matching node kinds (as if both
+ * kinds were svn_node_none).
*
* - Return svn_wc_notify_state_inapplicable if the node kind matches.
* - Return 'obstructed' if there is a node on disk where none or a
* different kind is expected, or if the disk node cannot be read.
- * - Return 'missing' if there is no node on disk but one is expected. */
+ * - Return 'missing' if there is no node on disk but one is expected.
+ * Also return 'missing' for absent nodes (not here due to authz).*/
static svn_wc_notify_state_t
obstructed_or_missing(const char *path,
const char *local_dir_abspath,
@@ -413,55 +385,66 @@ obstructed_or_missing(const char *path,
apr_pool_t *pool)
{
svn_error_t *err;
- const svn_wc_entry_t *entry = NULL;
- svn_node_kind_t kind_expected, kind_on_disk, wc_kind;
+ svn_node_kind_t kind_expected, kind_on_disk;
const char *local_abspath;
+ /* In a dry run, make as if nodes "deleted" by the dry run appear so. */
+ if (merge_b->dry_run && dry_run_deleted_p(merge_b, path))
+ return svn_wc_notify_state_inapplicable;
+
+ /* Since this function returns no svn_error_t, we make all errors look like
+ * no node found in the wc. */
err = svn_dirent_get_absolute(&local_abspath, path, pool);
if (!err)
- err = svn_wc__node_get_kind(&wc_kind, merge_b->ctx->wc_ctx, local_abspath,
- FALSE, pool);
+ err = svn_wc_read_kind(&kind_expected, merge_b->ctx->wc_ctx,
+ local_abspath, FALSE, pool);
if (err)
{
- wc_kind = svn_node_none;
svn_error_clear(err);
- entry = NULL;
+ kind_expected = svn_node_none;
}
- else
- {
- err = svn_wc__get_entry_versioned(&entry, merge_b->ctx->wc_ctx,
- local_abspath, svn_node_unknown,
- TRUE, FALSE, pool, pool);
+ /* Report absent nodes as 'missing'. First of all, they count as 'hidden',
+ * and since we pass show_hidden == FALSE above, they will show up as
+ * 'none' here. */
+ if (kind_expected == svn_node_none)
+ {
+ svn_boolean_t is_absent;
+ err = svn_wc__node_is_status_absent(&is_absent, merge_b->ctx->wc_ctx,
+ local_abspath, pool);
if (err)
- {
- svn_error_clear(err);
- entry = NULL;
- }
+ svn_error_clear(err);
+ else if (is_absent)
+ return svn_wc_notify_state_missing;
+ }
+
+ /* If a node is deleted, we will still get its kind from the working copy.
+ * But to compare with disk state, we want to consider deleted nodes as
+ * svn_node_none instead of their original kind.
+ * ### Not necessary with central DB:
+ * Because deleted directories are expected to remain on disk until commit
+ * to keep the metadata available, we only do this for files, not dirs. */
+ if (kind_expected == svn_node_file)
+ {
+ svn_boolean_t is_deleted;
+ err = svn_wc__node_is_status_deleted(&is_deleted,
+ merge_b->ctx->wc_ctx,
+ local_abspath,
+ pool);
+ if (err)
+ svn_error_clear(err);
+ else if (is_deleted)
+ kind_expected = svn_node_none;
}
- if ((wc_kind == svn_node_dir || wc_kind == svn_node_file)
- && !entry)
- return svn_wc_notify_state_missing;
-
- /* ### This check (and most of this function)
- can be removed after we move to one DB */
- /* svn_wc__get_entry_versioned ignores node kind errors, so check if we
- didn't get the parent stub, instead of the real thing */
- if (entry && entry->kind == svn_node_dir && *entry->name != '\0')
- return svn_wc_notify_state_missing; /* Only found parent entry */
-
- kind_expected = node_kind_working(path, merge_b, entry);
- kind_on_disk = node_kind_on_disk(path, merge_b, pool);
-
- /* If it's a sched-delete directory, change the expected kind to "dir"
- * because the directory should not yet have gone from disk. */
- if (entry && entry->kind == svn_node_dir
- && entry->schedule == svn_wc_schedule_delete
- && kind_on_disk == svn_node_dir)
- kind_expected = svn_node_dir;
+ err = svn_io_check_path(local_abspath, &kind_on_disk, pool);
+ if (err)
+ {
+ svn_error_clear(err);
+ kind_on_disk = svn_node_unknown;
+ }
if (kind_expected == kind_on_disk)
return svn_wc_notify_state_inapplicable;
@@ -639,35 +622,6 @@ tree_conflict_on_add(merge_cmd_baton_t *
return SVN_NO_ERROR;
}
-/* Set *HONOR_MERGEINFO and *RECORD_MERGEINFO (if non-NULL) based on the
- merge being performed as described in MERGE_B.
-
- If the merge source server is 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 set *HONOR_MERGEINFO to true, otherwise
- set it to false.
-
- If *HONOR_MERGEINFO is set to TRUE and the merge is not a dry run then set
- *RECORD_MERGEINFO to true, otherwise set it to false.
- **/
-static APR_INLINE void
-mergeinfo_behavior(svn_boolean_t *honor_mergeinfo_p,
- svn_boolean_t *record_mergeinfo_p,
- merge_cmd_baton_t *merge_b)
-{
- svn_boolean_t honor_mergeinfo = (merge_b->mergeinfo_capable
- && merge_b->sources_ancestral
- && merge_b->same_repos
- && (! merge_b->ignore_ancestry));
-
- if (honor_mergeinfo_p)
- *honor_mergeinfo_p = honor_mergeinfo;
-
- if (record_mergeinfo_p)
- *record_mergeinfo_p = (honor_mergeinfo && (! merge_b->dry_run));
-}
-
/* Helper for filter_self_referential_mergeinfo()
@@ -758,61 +712,103 @@ split_mergeinfo_on_revision(svn_mergeinf
}
+/* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
+ omitting any svn:mergeinfo changes. */
+static svn_error_t *
+omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
+ const apr_array_header_t *propchanges,
+ apr_pool_t *result_pool)
+{
+ int i;
+
+ *trimmed_propchanges = apr_array_make(result_pool,
+ propchanges->nelts,
+ sizeof(svn_prop_t));
+
+ for (i = 0; i < propchanges->nelts; ++i)
+ {
+ const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
+
+ /* If this property is not svn:mergeinfo, then copy it. */
+ if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
+ APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
/* 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. MERGE_B is cascaded from
- the argument of the same name in merge_props_changed().
+ to be added to the working copy LOCAL_ABSPATH.
- If mergeinfo is not being honored, MERGE_B->SAME_REPOS is true, and
- MERGE_B->REINTEGRATE_MERGE is FALSE do nothing. Otherwise, if
- MERGE_B->SAME_REPOS is false, then filter out all mergeinfo
- property additions (Issue #3383) from *PROPS. If MERGE_B->SAME_REPOS is
+ HONOR_MERGEINFO determines whether mergeinfo will be honored by this
+ function (when applicable).
+
+ 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 LOCAL_ABSPATH when
those additions refer to the same line of history as LOCAL_ABSPATH as
described below.
- If mergeinfo is being honored and MERGE_B->SAME_REPOS is true
+ 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
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 any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
in POOL) of incoming *PROPS minus the filtered mergeinfo. */
static svn_error_t*
filter_self_referential_mergeinfo(apr_array_header_t **props,
const char *local_abspath,
- merge_cmd_baton_t *merge_b,
+ 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)
{
- svn_boolean_t honor_mergeinfo;
apr_array_header_t *adjusted_props;
int i;
apr_pool_t *iterpool;
svn_boolean_t is_added;
svn_revnum_t base_revision;
+ /* Issue #3383: We don't want mergeinfo from a foreign repos.
+
+ 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_return(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. */
- mergeinfo_behavior(&honor_mergeinfo, NULL, merge_b);
if (! honor_mergeinfo
- && merge_b->same_repos
- && ! merge_b->reintegrate_merge)
+ && ! reintegrate_merge)
return SVN_NO_ERROR;
- SVN_ERR(svn_wc__node_is_status_added(&is_added, merge_b->ctx->wc_ctx,
- local_abspath, pool));
-
/* If this is a merge from the same repository and PATH itself has been
added there is no need to filter. */
- if (merge_b->same_repos && is_added)
+ SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, local_abspath, pool));
+ if (is_added)
return SVN_NO_ERROR;
- SVN_ERR(svn_wc__node_get_base_rev(&base_revision, merge_b->ctx->wc_ctx,
+ SVN_ERR(svn_wc__node_get_base_rev(&base_revision, ctx->wc_ctx,
local_abspath, pool));
adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
@@ -827,20 +823,6 @@ filter_self_referential_mergeinfo(apr_ar
const char *target_url;
const char *old_url = NULL;
- /* 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 ((strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
- && (! merge_b->same_repos))
- {
- /* Issue #3383: We don't want mergeinfo from a foreign repos. */
- continue;
- }
-
if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
|| (! prop->value) /* Removal of mergeinfo */
|| (! prop->value->len)) /* Empty mergeinfo */
@@ -853,9 +835,9 @@ filter_self_referential_mergeinfo(apr_ar
/* Temporarily reparent our RA session to the merge
target's URL. */
SVN_ERR(svn_client_url_from_path2(&target_url, local_abspath,
- merge_b->ctx, pool, pool));
+ ctx, pool, pool));
SVN_ERR(svn_client__ensure_ra_session_url(&old_url,
- merge_b->ra_session2,
+ ra_session,
target_url, pool));
/* Parse the incoming mergeinfo to allow easier manipulation. */
@@ -896,7 +878,7 @@ filter_self_referential_mergeinfo(apr_ar
apr_hash_index_t *hi;
const char *merge_source_root_url;
- SVN_ERR(svn_ra_get_repos_root2(merge_b->ra_session2,
+ SVN_ERR(svn_ra_get_repos_root2(ra_session,
&merge_source_root_url, pool));
for (hi = apr_hash_first(pool, younger_mergeinfo);
@@ -942,12 +924,12 @@ filter_self_referential_mergeinfo(apr_ar
&start_revision,
NULL,
NULL,
- merge_b->ra_session2,
+ ra_session,
target_url,
&peg_rev,
&rev1_opt,
&rev2_opt,
- merge_b->ctx,
+ ctx,
pool);
if (err)
{
@@ -1029,8 +1011,8 @@ filter_self_referential_mergeinfo(apr_ar
local_abspath, &peg_rev,
base_revision,
SVN_INVALID_REVNUM,
- merge_b->ra_session2,
- merge_b->ctx,
+ ra_session,
+ ctx,
pool));
/* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
@@ -1039,10 +1021,10 @@ filter_self_referential_mergeinfo(apr_ar
mergeinfo, TRUE, pool, iterpool));
}
- /* If we reparented MERGE_B->RA_SESSION2 above, put it back
+ /* If we reparented RA_SESSION above, put it back
to the original URL. */
if (old_url)
- SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_url, pool));
+ SVN_ERR(svn_ra_reparent(ra_session, old_url, pool));
/* Combine whatever older and younger filtered mergeinfo exists
into filtered_mergeinfo. */
@@ -1148,11 +1130,17 @@ merge_props_changed(const char *local_di
/* If this is a forward merge then don't add new mergeinfo to
PATH that is already part of PATH's own history. */
if (merge_b->merge_source.rev1 < merge_b->merge_source.rev2)
- SVN_ERR(filter_self_referential_mergeinfo(&props, local_abspath,
- merge_b, subpool));
+ 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,
+ subpool));
err = svn_wc_merge_props3(state, ctx->wc_ctx, local_abspath, NULL, NULL,
- original_props, props, FALSE, merge_b->dry_run,
+ original_props, props, merge_b->dry_run,
ctx->conflict_func, ctx->conflict_baton,
ctx->cancel_func, ctx->cancel_baton,
subpool);
@@ -1355,11 +1343,29 @@ merge_file_changed(const char *local_dir
{
svn_node_kind_t kind;
svn_node_kind_t wc_kind;
+ svn_depth_t parent_depth;
- SVN_ERR(svn_wc__node_get_kind(&wc_kind, merge_b->ctx->wc_ctx, mine_abspath,
- FALSE, subpool));
+ SVN_ERR(svn_wc_read_kind(&wc_kind, merge_b->ctx->wc_ctx, mine_abspath,
+ FALSE, subpool));
SVN_ERR(svn_io_check_path(mine, &kind, subpool));
+ /* If the file isn't there due to depth restrictions, do not flag
+ * 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, subpool),
+ subpool));
+ if (wc_kind == svn_node_none && parent_depth < svn_depth_files)
+ {
+ if (content_state)
+ *content_state = svn_wc_notify_state_missing;
+ if (prop_state)
+ *prop_state = svn_wc_notify_state_missing;
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+ }
+
/* ### a future thought: if the file is under version control,
but the working file is missing, maybe we can 'restore' the
working file from the text-base, and then allow the merge to run? */
@@ -1554,7 +1560,7 @@ merge_file_added(const char *local_dir_a
const char *parent_abspath;
svn_node_kind_t kind;
int i;
- apr_hash_t *new_props;
+ apr_hash_t *file_props;
const char *mine_abspath;
/* Easy out: We are only applying mergeinfo differences. */
@@ -1582,7 +1588,7 @@ merge_file_added(const char *local_dir_a
*tree_conflicted = FALSE;
/* Apply the prop changes to a new hash table. */
- new_props = apr_hash_copy(subpool, original_props);
+ file_props = apr_hash_copy(subpool, original_props);
for (i = 0; i < prop_changes->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
@@ -1606,7 +1612,7 @@ merge_file_added(const char *local_dir_a
&& strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
continue;
- apr_hash_set(new_props, prop->name, APR_HASH_KEY_STRING, prop->value);
+ apr_hash_set(file_props, prop->name, APR_HASH_KEY_STRING, prop->value);
}
/* Easy out: if we have no adm_access for the parent directory,
@@ -1620,7 +1626,7 @@ merge_file_added(const char *local_dir_a
{
if (content_state)
*content_state = svn_wc_notify_state_changed;
- if (prop_state && apr_hash_count(new_props))
+ if (prop_state && apr_hash_count(file_props))
*prop_state = svn_wc_notify_state_changed;
}
else
@@ -1657,14 +1663,15 @@ merge_file_added(const char *local_dir_a
{
if (! merge_b->dry_run)
{
- const char *copyfrom_url = NULL;
- svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
- svn_stream_t *new_base_contents;
+ const char *copyfrom_url;
+ svn_revnum_t copyfrom_rev;
+ svn_stream_t *new_contents, *new_base_contents;
+ apr_hash_t *new_base_props, *new_props;
const svn_wc_conflict_description2_t *existing_conflict;
- /* If this is a merge from the same repository as our working copy,
- we handle adds as add-with-history. Otherwise, we'll use a pure
- add. */
+ /* If this is a merge from the same repository as our
+ working copy, we handle adds as add-with-history.
+ Otherwise, we'll use a pure add. */
if (merge_b->same_repos)
{
const char *child = svn_dirent_is_child(merge_b->target_abspath,
@@ -1679,10 +1686,22 @@ merge_file_added(const char *local_dir_a
SVN_ERR(check_scheme_match(merge_b->ctx->wc_ctx,
parent_abspath,
copyfrom_url, subpool));
+ new_base_props = file_props;
+ new_props = NULL; /* inherit from new_base_props */
+ SVN_ERR(svn_stream_open_readonly(&new_base_contents, yours,
+ subpool, subpool));
+ new_contents = NULL; /* inherit from new_base_contents */
+ }
+ else
+ {
+ copyfrom_url = NULL;
+ copyfrom_rev = SVN_INVALID_REVNUM;
+ new_base_props = apr_hash_make(subpool);
+ new_props = file_props;
+ new_base_contents = svn_stream_empty(subpool);
+ SVN_ERR(svn_stream_open_readonly(&new_contents, yours,
+ subpool, subpool));
}
-
- SVN_ERR(svn_stream_open_readonly(&new_base_contents, yours,
- subpool, subpool));
SVN_ERR(svn_wc__get_tree_conflict(&existing_conflict,
merge_b->ctx->wc_ctx,
@@ -1712,7 +1731,8 @@ merge_file_added(const char *local_dir_a
/* ### would be nice to have cancel/notify funcs to pass */
SVN_ERR(svn_wc_add_repos_file4(
merge_b->ctx->wc_ctx, mine_abspath,
- new_base_contents, NULL, new_props, NULL,
+ new_base_contents, new_contents,
+ new_base_props, new_props,
copyfrom_url, copyfrom_rev,
NULL, NULL, NULL, NULL, subpool));
@@ -1721,7 +1741,7 @@ merge_file_added(const char *local_dir_a
}
if (content_state)
*content_state = svn_wc_notify_state_changed;
- if (prop_state && apr_hash_count(new_props))
+ if (prop_state && apr_hash_count(file_props))
*prop_state = svn_wc_notify_state_changed;
}
break;
@@ -1740,8 +1760,8 @@ merge_file_added(const char *local_dir_a
{
/* directory already exists, is it under version control? */
svn_node_kind_t wc_kind;
- SVN_ERR(svn_wc__node_get_kind(&wc_kind, merge_b->ctx->wc_ctx,
- mine_abspath, FALSE, subpool));
+ SVN_ERR(svn_wc_read_kind(&wc_kind, merge_b->ctx->wc_ctx,
+ mine_abspath, FALSE, subpool));
if ((wc_kind != svn_node_none) && dry_run_deleted_p(merge_b, mine))
*content_state = svn_wc_notify_state_changed;
@@ -1812,8 +1832,9 @@ properties_same_p(svn_boolean_t *same,
return SVN_NO_ERROR;
}
-/* Compare the file OLDER (together with its normal properties in
- * ORIGINAL_PROPS which may also contain WC props and entry props) and MINE.
+/* Compare the file OLDER_ABSPATH (together with its normal properties in
+ * ORIGINAL_PROPS which may also contain WC props and entry props) with the
+ * versioned file MINE_ABSPATH (together with its versioned properties).
* Set *SAME to true if they are the same or false if they differ, ignoring
* the "svn:mergeinfo" property, and ignoring differences in keyword
* expansion and end-of-line style. */
@@ -1839,8 +1860,7 @@ files_same_p(svn_boolean_t *same,
/* Compare the file content, translating 'mine' to 'normal' form. */
SVN_ERR(svn_wc__versioned_file_modcheck(&modified, wc_ctx, mine_abspath,
- older_abspath, TRUE,
- scratch_pool));
+ older_abspath, scratch_pool));
*same = !modified;
}
@@ -2222,7 +2242,6 @@ merge_dir_deleted(const char *local_dir_
merge_cmd_baton_t *merge_b = baton;
apr_pool_t *subpool = svn_pool_create(merge_b->pool);
svn_node_kind_t kind;
- const char *parent_path;
svn_error_t *err;
const char *local_abspath;
svn_boolean_t is_versioned;
@@ -2310,7 +2329,6 @@ merge_dir_deleted(const char *local_dir_
notes/tree-conflicts/detection.txt.
*/
- parent_path = svn_dirent_dirname(path, subpool);
/* Passing NULL for the notify_func and notify_baton because
repos_diff.c:delete_entry() will do it for us. */
err = svn_client__wc_delete(path, merge_b->force,
@@ -2380,13 +2398,26 @@ merge_dir_deleted(const char *local_dir_
static svn_error_t *
merge_dir_opened(const char *local_dir_abspath,
svn_boolean_t *tree_conflicted,
+ svn_boolean_t *skip_children,
const char *path,
svn_revnum_t rev,
void *baton,
apr_pool_t *scratch_pool)
{
+ merge_cmd_baton_t *merge_b = baton;
+ apr_pool_t *subpool = svn_pool_create(merge_b->pool);
+ const char *local_abspath;
+ svn_depth_t parent_depth;
+ svn_node_kind_t kind;
+ svn_node_kind_t kind_on_disk;
+ svn_wc_notify_state_t obstr_state;
+ svn_boolean_t is_deleted;
+ svn_error_t *err;
+
if (tree_conflicted)
*tree_conflicted = FALSE;
+ if (skip_children)
+ *skip_children = FALSE;
if (local_dir_abspath == NULL)
{
@@ -2397,58 +2428,121 @@ merge_dir_opened(const char *local_dir_a
* additional tree conflicts for the child nodes inside. */
/* ### TODO: Verify that this holds true for explicit targets that
* # point deep into a nonexisting subtree. */
+ if (skip_children)
+ *skip_children = TRUE;
+ svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
- /* Detect a tree-conflict, if any. */
- {
- merge_cmd_baton_t *merge_b = baton;
- apr_pool_t *subpool = svn_pool_create(merge_b->pool);
- svn_node_kind_t kind;
- const char *local_abspath;
- svn_boolean_t is_versioned;
- svn_boolean_t is_deleted;
- svn_error_t *err;
-
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, subpool));
-
- /* Find out if this path is deleted and in asking this question also derive
- the path's version-control state, we'll need to know both below. */
- err = svn_wc__node_is_status_deleted(&is_deleted, merge_b->ctx->wc_ctx,
- local_abspath, subpool);
- if (err)
- {
- if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- {
- svn_error_clear(err);
- err = NULL;
- is_versioned = is_deleted = FALSE;
- }
- else
+ /* Check for an obstructed or missing node on disk. */
+ obstr_state = obstructed_or_missing(path, local_dir_abspath, merge_b,
+ subpool);
+ if (obstr_state != svn_wc_notify_state_inapplicable)
+ {
+ if (skip_children)
+ *skip_children = TRUE;
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+ }
+
+ err = svn_dirent_get_absolute(&local_abspath, path, subpool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ svn_error_clear(err);
+ else
+ {
+ svn_pool_destroy(subpool);
return svn_error_return(err);
- }
- else
- {
- is_versioned = TRUE;
- }
+ }
+ }
+
+ SVN_ERR(svn_wc_read_kind(&kind, merge_b->ctx->wc_ctx,
+ local_abspath, TRUE, subpool));
+ SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, subpool));
+
+ /* If the parent is too shallow to contain this directory,
+ * and the directory is not present on disk, skip it.
+ * Non-inheritable mergeinfo will be recorded, allowing
+ * future merges into non-shallow working copies to merge
+ * changes we missed this time around. */
+ err = svn_wc__node_get_depth(&parent_depth, merge_b->ctx->wc_ctx,
+ local_dir_abspath, subpool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ parent_depth = svn_depth_unknown;
+ }
+ else
+ {
+ svn_pool_destroy(subpool);
+ return svn_error_return(err);
+ }
+ }
+ if (kind == svn_node_none &&
+ parent_depth != svn_depth_unknown &&
+ parent_depth < svn_depth_immediates)
+ {
+ if (skip_children)
+ *skip_children = TRUE;
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+ }
+
+ /* Find out if this path is deleted. */
+ err = svn_wc__node_is_status_deleted(&is_deleted, merge_b->ctx->wc_ctx,
+ local_abspath, subpool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ is_deleted = FALSE;
+ }
+ else
+ {
+ svn_pool_destroy(subpool);
+ return svn_error_return(err);
+ }
+ }
- SVN_ERR(svn_io_check_path(path, &kind, subpool));
+ /* Check for tree conflicts, if any. */
- /* If we're trying to open a directory that's not a directory,
- * raise a tree conflict. */
- if (!is_versioned || is_deleted
- || kind != svn_node_dir)
- {
- SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
- svn_wc_conflict_action_edit,
- svn_wc_conflict_reason_deleted));
- if (tree_conflicted)
- *tree_conflicted = TRUE;
- }
+ /* If we're trying to open a file, the reason for the conflict is
+ * 'replaced'. Because the merge is trying to open the directory,
+ * rather than adding it, the directory must have existed in the
+ * history of the target branch and has been replaced with a file. */
+ if (kind == svn_node_file)
+ {
+ SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
+ svn_wc_conflict_action_edit,
+ svn_wc_conflict_reason_replaced));
+ if (tree_conflicted)
+ *tree_conflicted = TRUE;
+ }
- svn_pool_destroy(subpool);
- }
+ /* If we're trying to open a directory that's locally deleted,
+ * or not present because it was deleted in the history of the
+ * target branch, the reason for the conflict is 'deleted'.
+ *
+ * If the DB says something should be here, but there is
+ * nothing on disk, we're probably in a mixed-revision
+ * working copy and the parent has an outdated idea about
+ * the state of its child. Flag a tree conflict in this case
+ * forcing the user to sanity-check the merge result. */
+ else if (is_deleted || kind == svn_node_none ||
+ (kind_on_disk != kind && kind_on_disk == svn_node_none))
+ {
+ SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
+ svn_wc_conflict_action_edit,
+ svn_wc_conflict_reason_deleted));
+ if (tree_conflicted)
+ *tree_conflicted = TRUE;
+ }
+ svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
@@ -4018,6 +4112,9 @@ populate_remaining_ranges(apr_array_head
apr_pool_t *iterpool;
int i;
svn_revnum_t gap_start, gap_end;
+ svn_boolean_t child_inherits_implicit;
+ svn_client__merge_path_t *parent;
+ int parent_index;
iterpool = svn_pool_create(pool);
@@ -4031,6 +4128,46 @@ populate_remaining_ranges(apr_array_head
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
+
+ parent = NULL;
+ svn_pool_clear(iterpool);
+
+ /* Issue #3646 'record-only merges create self-referential
+ mergeinfo'. Get the merge target's implicit mergeinfo (natural
+ history). We'll use it later to avoid setting self-referential
+ mergeinfo -- see filter_natural_history_from_mergeinfo(). */
+ if (i == 0) /* First item is always the merge target. */
+ {
+ SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */
+ &(child->implicit_mergeinfo),
+ NULL, /* child->indirect_mergeinfo */
+ svn_mergeinfo_inherited, ra_session,
+ child->abspath,
+ MAX(revision1, revision2),
+ MIN(revision1, revision2),
+ merge_b->ctx, pool, iterpool));
+ }
+ else
+ {
+ /* Issue #3443 - Subtrees of the merge target can inherit
+ their parent's implicit mergeinfo in most cases. */
+ 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 *);
+ /* If CHILD is a subtree then its parent must be in
+ CHILDREN_WITH_MERGEINFO, see the global comment
+ 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
+ SVN_ERR_ASSERT(parent);
+
+ child_inherits_implicit = (parent && !child->switched);
+ SVN_ERR(ensure_implicit_mergeinfo(parent, child,
+ child_inherits_implicit,
+ revision1, revision2,
+ ra_session, merge_b->ctx,
+ pool, iterpool));
+ }
+
child->remaining_ranges = svn_rangelist__initialize(revision1,
revision2,
TRUE, pool);
@@ -4069,8 +4206,8 @@ populate_remaining_ranges(apr_array_head
const char *child_url1, *child_url2;
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;
- svn_boolean_t child_inherits_implicit;
+
+ parent = NULL;
/* If the path is absent don't do subtree merge either. */
SVN_ERR_ASSERT(child);
@@ -4107,8 +4244,8 @@ populate_remaining_ranges(apr_array_head
/* 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_index = find_nearest_ancestor(children_with_mergeinfo,
+ FALSE, child->abspath);
parent = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
svn_client__merge_path_t *);
/* If CHILD is a subtree then its parent must be in
@@ -4152,7 +4289,6 @@ populate_remaining_ranges(apr_array_head
int j;
svn_revnum_t start, end;
svn_boolean_t proper_subset = FALSE;
- svn_boolean_t equals = FALSE;
svn_boolean_t overlaps_or_adjoins = FALSE;
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES
@@ -4174,7 +4310,6 @@ populate_remaining_ranges(apr_array_head
}
else if ((gap_start == start) && (end == gap_end))
{
- equals = TRUE;
break;
}
else if (gap_start <= end && start <= gap_end) /* intersect */
@@ -4238,8 +4373,8 @@ calculate_merge_inheritance(apr_array_he
{
svn_node_kind_t path_kind;
- SVN_ERR(svn_wc__node_get_kind(&path_kind, wc_ctx, local_abspath,
- FALSE, scratch_pool));
+ SVN_ERR(svn_wc_read_kind(&path_kind, wc_ctx, local_abspath, FALSE,
+ scratch_pool));
if (path_kind == svn_node_file)
{
/* Files *never* have non-inheritable mergeinfo. */
@@ -4430,7 +4565,7 @@ record_skips(const char *mergeinfo_path,
hi = apr_hash_next(hi))
{
const char *skipped_abspath = svn__apr_hash_index_key(hi);
- svn_wc_status2_t *status;
+ svn_wc_status3_t *status;
/* Before we override, make sure this is a versioned path, it
might be an unversioned obstruction. */
@@ -4581,7 +4716,7 @@ remove_children_with_deleted_mergeinfo(m
into TARGET_ABSPATH and drive it.
If mergeinfo is not being honored based on MERGE_B, see the doc string for
- mergeinfo_behavior() for how this is determined, then ignore
+ 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.
@@ -4660,7 +4795,7 @@ drive_merge_report_editor(const char *ta
const char *old_sess2_url;
svn_boolean_t is_rollback = revision1 > revision2;
- mergeinfo_behavior(&honor_mergeinfo, NULL, merge_b);
+ honor_mergeinfo = HONOR_MERGEINFO(merge_b);
/* Start with a safe default starting revision for the editor and the
merge target. */
@@ -4760,7 +4895,6 @@ drive_merge_report_editor(const char *ta
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
int parent_index;
- svn_boolean_t nearest_parent_is_target;
SVN_ERR_ASSERT(child);
if (child->absent)
@@ -4772,9 +4906,6 @@ drive_merge_report_editor(const char *ta
parent = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
svn_client__merge_path_t *);
- /* Note if the child's parent is the merge target. */
- nearest_parent_is_target = (parent_index == 0);
-
/* If a subtree needs the same range applied as its nearest parent
with mergeinfo or neither the subtree nor this parent need
REVISION1:REVISION2 merged, then we don't need to describe the
@@ -5156,6 +5287,7 @@ get_mergeinfo_walk_cb(const char *local_
svn_boolean_t deleted;
svn_boolean_t absent;
svn_boolean_t obstructed;
+ svn_boolean_t immediate_child_dir;
/* TODO(#2843) How to deal with a excluded item on merge? */
@@ -5194,11 +5326,16 @@ get_mergeinfo_walk_cb(const char *local_
scratch_pool));
}
- SVN_ERR(svn_wc__node_get_kind(&kind, wb->ctx->wc_ctx,
- local_abspath, TRUE, scratch_pool));
+ SVN_ERR(svn_wc_read_kind(&kind, wb->ctx->wc_ctx, local_abspath, TRUE,
+ scratch_pool));
SVN_ERR(svn_wc__node_get_depth(&depth, wb->ctx->wc_ctx, local_abspath,
scratch_pool));
+ immediate_child_dir = ((wb->depth == svn_depth_immediates)
+ &&(kind == svn_node_dir)
+ && (strcmp(abs_parent_path,
+ wb->merge_target_abspath) == 0));
+
/* Store PATHs with explict mergeinfo, which are switched, are missing
children due to a sparse checkout, are scheduled for deletion are absent
from the WC, are first level sub directories relative to merge target if
@@ -5211,9 +5348,7 @@ get_mergeinfo_walk_cb(const char *local_
|| depth == svn_depth_empty
|| depth == svn_depth_files
|| absent
- || ((wb->depth == svn_depth_immediates) &&
- (kind == svn_node_dir) &&
- (strcmp(abs_parent_path, wb->merge_target_abspath) == 0))
+ || immediate_child_dir
|| ((wb->depth == svn_depth_files) &&
(kind == svn_node_file) &&
(strcmp(abs_parent_path, wb->merge_target_abspath) == 0))
@@ -5246,7 +5381,9 @@ get_mergeinfo_walk_cb(const char *local_
if (!child->has_noninheritable
&& (depth == svn_depth_empty
|| depth == svn_depth_files))
- child->has_noninheritable = TRUE;
+ child->has_noninheritable = TRUE;
+
+ child->immediate_child_dir = immediate_child_dir;
APR_ARRAY_PUSH(wb->children_with_mergeinfo,
svn_client__merge_path_t *) = child;
@@ -5392,9 +5529,9 @@ insert_parent_and_sibs_of_sw_absent_del_
{
svn_node_kind_t child_kind;
- SVN_ERR(svn_wc__node_get_kind(&child_kind,
- merge_cmd_baton->ctx->wc_ctx,
- child_abspath, FALSE, iterpool));
+ SVN_ERR(svn_wc_read_kind(&child_kind,
+ merge_cmd_baton->ctx->wc_ctx,
+ child_abspath, FALSE, iterpool));
if (child_kind != svn_node_file)
continue;
}
@@ -5588,9 +5725,10 @@ get_mergeinfo_paths(apr_array_header_t *
if (depth == svn_depth_files)
{
svn_node_kind_t child_kind;
- SVN_ERR(svn_wc__node_get_kind(
- &child_kind, merge_cmd_baton->ctx->wc_ctx,
- child_abspath, FALSE, iterpool));
+ SVN_ERR(svn_wc_read_kind(&child_kind,
+ merge_cmd_baton->ctx->wc_ctx,
+ child_abspath, FALSE,
+ iterpool));
if (child_kind != svn_node_file)
continue;
}
@@ -5922,12 +6060,10 @@ normalize_merge_sources(apr_array_header
svn_revnum_t oldest_requested = SVN_INVALID_REVNUM;
svn_revnum_t youngest_requested = SVN_INVALID_REVNUM;
svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
- svn_opt_revision_t youngest_opt_rev;
apr_array_header_t *merge_range_ts, *segments;
const char *source_abspath_or_url;
apr_pool_t *subpool;
int i;
- youngest_opt_rev.kind = svn_opt_revision_head;
if(!svn_path_is_url(source))
SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source, pool));
@@ -6266,7 +6402,8 @@ do_file_merge(const char *url1,
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
- mergeinfo_behavior(&honor_mergeinfo, &record_mergeinfo, merge_b);
+ honor_mergeinfo = HONOR_MERGEINFO(merge_b);
+ record_mergeinfo = RECORD_MERGEINFO(merge_b);
/* Note that this is a single-file merge. */
notify_b->is_single_file_merge = TRUE;
@@ -6809,6 +6946,124 @@ do_mergeinfo_unaware_dir_merge(const cha
merge_b, pool);
}
+/* A svn_log_entry_receiver_t callback for
+ get_inoperative_immediate_children(). */
+static svn_error_t *
+log_find_operative_subtree_revs(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ apr_hash_t *immediate_children = baton;
+ apr_hash_index_t *hi, *hi2;
+
+ for (hi = apr_hash_first(pool, log_entry->changed_paths2);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ for (hi2 = apr_hash_first(pool, immediate_children);
+ hi2;
+ hi2 = apr_hash_next(hi2))
+ {
+ const char *immediate_path = svn__apr_hash_index_val(hi2);
+ if (svn_dirent_is_ancestor(immediate_path, path))
+ {
+ apr_hash_set(immediate_children, svn__apr_hash_index_key(hi2),
+ APR_HASH_KEY_STRING, NULL);
+ /* A path can't be the child of more than
+ one immediate child of the merge target. */
+ break;
+ }
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Issue #3642.
+
+ Find inoperative subtrees when record_mergeinfo_for_dir_merge() is
+ recording mergeinfo describing a merge at depth=immediates.
+
+ CHILDREN_WITH_MERGEINFO is the standard array of subtrees that are
+ interesting from a merge tracking perspective, see the global comment
+ 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
+
+ MERGE_SOURCE_REPOS_ABSPATH is the absolute repository path of the merge
+ source. OLDEST_REV and YOUNGEST_REV are the revisions merged from
+ MERGE_SOURCE_REPOS_ABSPATH to MERGE_TARGET_ABSPATH.
+
+ RA_SESSION points to MERGE_SOURCE_REPOS_ABSPATH.
+
+ Set *IMMEDIATE_CHILDREN to a hash (mapping const char * WC absolute
+ paths to const char * repos absolute paths) containing all the
+ subtrees which would be inoperative if MERGE_SOURCE_REPOS_PATH
+ -r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH
+ at --depth infinity. The keys of the hash point to copies of the ABSPATH
+ members of the svn_client__merge_path_t * in CHILDREN_WITH_MERGEINFO that
+ are inoperative. The hash values are the key's corresponding repository
+ absolute merge source path.
+
+ RESULT_POOL is used to allocate the contents of *IMMEDIATE_CHILDREN.
+ SCRATCH_POOL is used for temporary allocations. */
+static svn_error_t *
+get_inoperative_immediate_children(apr_hash_t **immediate_children,
+ apr_array_header_t *children_with_mergeinfo,
+ const char *merge_source_repos_abspath,
+ svn_revnum_t oldest_rev,
+ svn_revnum_t youngest_rev,
+ const char *merge_target_abspath,
+ svn_ra_session_t *ra_session,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ apr_pool_t *iterpool;
+
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
+ SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
+
+ *immediate_children = apr_hash_make(result_pool);
+ iterpool = svn_pool_create(scratch_pool);
+
+ /* Find all the children in CHILDREN_WITH_MERGEINFO with the
+ immediate_child_dir flag set and store them in *IMMEDIATE_CHILDREN. */
+ 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 *);
+
+ svn_pool_clear(iterpool);
+
+ if (child->immediate_child_dir)
+ apr_hash_set(*immediate_children,
+ apr_pstrdup(result_pool, child->abspath),
+ APR_HASH_KEY_STRING,
+ svn_uri_join(merge_source_repos_abspath,
+ svn_dirent_is_child(merge_target_abspath,
+ child->abspath,
+ iterpool),
+ result_pool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ /* Now remove any paths from *IMMEDIATE_CHILDREN that are inoperative when
+ merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to
+ MERGE_TARGET_ABSPATH at --depth infinity. */
+ if (apr_hash_count(*immediate_children))
+ {
+ apr_array_header_t *log_targets = apr_array_make(scratch_pool, 1,
+ sizeof(const char *));
+ APR_ARRAY_PUSH(log_targets, const char *) = "";
+ SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
+ oldest_rev, 0, TRUE, FALSE, FALSE,
+ NULL, log_find_operative_subtree_revs,
+ *immediate_children, scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
/* Helper for do_directory_merge().
Record mergeinfo describing a merge of MERGED_RANGE->START:
@@ -6817,7 +7072,7 @@ do_mergeinfo_unaware_dir_merge(const cha
NOTIFY_B->CHILDREN_WITH_MERGEINFO -- see the global comment
'THE CHILDREN_WITH_MERGEINFO ARRAY'. Obviously this should only
be called if recording mergeinfo -- see doc string for
- mergeinfo_behavior().
+ RECORD_MERGEINFO().
DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all
cascaded from do_directory_merge's arguments of the same names.
@@ -6834,6 +7089,7 @@ record_mergeinfo_for_dir_merge(const svn
int i;
svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
svn_boolean_t operative_merge = FALSE;
+ apr_hash_t *inoperative_immediate_children = NULL;
/* Update the WC mergeinfo here to account for our new
merges, minus any unresolved conflicts and skips. */
@@ -6871,6 +7127,19 @@ record_mergeinfo_for_dir_merge(const svn
remove_absent_children(merge_b->target_abspath,
notify_b->children_with_mergeinfo, notify_b);
+
+ /* Find all issue #3642 children (i.e immediate child directories of the
+ merge target, with no pre-existing explicit mergeinfo, during a --depth
+ immediates merge). Stash those that are inoperative at any depth in
+ INOPERATIVE_IMMEDIATE_CHILDREN. */
+ if (!merge_b->record_only && range.start <= range.end)
+ SVN_ERR(get_inoperative_immediate_children(
+ &inoperative_immediate_children,
+ notify_b->children_with_mergeinfo,
+ mergeinfo_path, range.start + 1, range.end,
+ merge_b->target_abspath, merge_b->ra_session1,
+ pool, iterpool));
+
/* Record mergeinfo on any subtree affected by the merge or for
every subtree if this is a --record-only merge. Always record
mergeinfo on the merge target CHILDREN_WITH_MERGEINFO[0]. */
@@ -6898,6 +7167,7 @@ record_mergeinfo_for_dir_merge(const svn
was affected by the merge. */
if (i > 0 /* Always record mergeinfo on the merge target. */
&& (!merge_b->record_only || merge_b->reintegrate_merge)
+ && (!child->immediate_child_dir || child->pre_merge_mergeinfo)
&& (!operative_merge
|| !subtree_touched_by_merge(child->abspath, notify_b,
iterpool)))
@@ -6929,6 +7199,14 @@ record_mergeinfo_for_dir_merge(const svn
if (child_is_deleted)
continue;
+ /* We don't need to record mergeinfo on those issue #3642 children
+ that are inoperative at any depth. */
+ if (inoperative_immediate_children
+ && apr_hash_get(inoperative_immediate_children,
+ child->abspath,
+ APR_HASH_KEY_STRING))
+ continue;
+
child_repos_path = svn_dirent_is_child(merge_b->target_abspath,
child->abspath, iterpool);
if (!child_repos_path)
@@ -7121,10 +7399,8 @@ record_mergeinfo_for_added_subtrees(
const char *rel_added_path;
const char *added_path_mergeinfo_path;
- SVN_ERR(svn_wc__node_get_kind(&added_path_kind,
- merge_b->ctx->wc_ctx,
- added_abspath, FALSE,
- iterpool));
+ SVN_ERR(svn_wc_read_kind(&added_path_kind, merge_b->ctx->wc_ctx,
+ added_abspath, FALSE, iterpool));
/* Calculate the mergeinfo resulting from this merge. */
merge_mergeinfo = apr_hash_make(iterpool);
@@ -7435,10 +7711,8 @@ remove_noop_subtree_ranges(const char *u
SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, NULL,
merge_b->ctx->wc_ctx,
- merge_b->target_abspath,
- FALSE,
- scratch_pool,
- scratch_pool));
+ merge_b->target_abspath, FALSE, FALSE,
+ scratch_pool, scratch_pool));
/* Set up the log baton. */
log_gap_baton.merge_b = merge_b;
@@ -7543,7 +7817,8 @@ do_directory_merge(const char *url1,
svn_boolean_t honor_mergeinfo, record_mergeinfo;
svn_boolean_t same_urls = (strcmp(url1, url2) == 0);
- mergeinfo_behavior(&honor_mergeinfo, &record_mergeinfo, merge_b);
+ honor_mergeinfo = HONOR_MERGEINFO(merge_b);
+ record_mergeinfo = RECORD_MERGEINFO(merge_b);
/* Initialize NOTIFY_B->CHILDREN_WITH_MERGEINFO. See the comment
'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
@@ -7866,7 +8141,7 @@ ensure_ra_session_url(svn_ra_session_t *
/* Drive a merge of MERGE_SOURCES into working copy path TARGET_ABSPATH
and possibly record mergeinfo describing the merge -- see
- mergeinfo_behavior().
+ RECORD_MERGEINFO().
If MODIFIED_SUBTREES is not NULL and SOURCES_ANCESTRAL or
REINTEGRATE_MERGE is true, then set *MODIFIED_SUBTREES to a hash
@@ -7964,8 +8239,8 @@ do_merge(apr_hash_t **modified_subtrees,
return SVN_NO_ERROR;
}
- SVN_ERR(svn_wc__node_get_kind(&target_kind, ctx->wc_ctx, target_abspath,
- FALSE, pool));
+ SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, target_abspath, FALSE,
+ pool));
/* Ensure a known depth. */
if (depth == svn_depth_unknown)
@@ -8133,8 +8408,8 @@ do_merge(apr_hash_t **modified_subtrees,
merge (unless this is record-only), followed by record-only merges
to represent the changed mergeinfo.
- The merge is between URL1@REV1 (in RA_SESSION1) and URL2@REV2 (in
- RA_SESSION2); YC_REV is their youngest common ancestor.
+ The merge is between URL1@REV1 (in URL1_RA_SESSION1) and URL2@REV2 (in
+ URL2_RA_SESSION2); YC_REV is their youngest common ancestor.
SOURCE_REPOS_ROOT and WC_REPOS_ROOT are the repository roots of the
source URL and the target working copy. Other arguments are as in
all of the public merge APIs.
@@ -8144,7 +8419,8 @@ do_merge(apr_hash_t **modified_subtrees,
*/
static svn_error_t *
merge_cousins_and_supplement_mergeinfo(const char *target_abspath,
- svn_ra_session_t *ra_session,
+ svn_ra_session_t *URL1_ra_session,
+ svn_ra_session_t *URL2_ra_session,
const char *URL1,
svn_revnum_t rev1,
const char *URL2,
@@ -8165,7 +8441,6 @@ merge_cousins_and_supplement_mergeinfo(c
svn_opt_revision_range_t *range;
apr_array_header_t *remove_sources, *add_sources, *ranges;
svn_opt_revision_t peg_revision;
- const char *old_url;
svn_boolean_t same_repos;
apr_hash_t *modified_subtrees = NULL;
@@ -8176,7 +8451,7 @@ merge_cousins_and_supplement_mergeinfo(c
const char *source_repos_uuid;
const char *wc_repos_uuid;
- SVN_ERR(svn_ra_get_uuid2(ra_session, &source_repos_uuid, pool));
+ SVN_ERR(svn_ra_get_uuid2(URL1_ra_session, &source_repos_uuid, pool));
SVN_ERR(svn_client_uuid_from_path2(&wc_repos_uuid, target_abspath,
ctx, pool, pool));
same_repos = (strcmp(wc_repos_uuid, source_repos_uuid) == 0);
@@ -8185,7 +8460,6 @@ merge_cousins_and_supplement_mergeinfo(c
same_repos = TRUE;
peg_revision.kind = svn_opt_revision_number;
- SVN_ERR(svn_ra_get_session_url(ra_session, &old_url, pool));
range = apr_pcalloc(pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
@@ -8195,10 +8469,9 @@ merge_cousins_and_supplement_mergeinfo(c
ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev1;
- SVN_ERR(svn_ra_reparent(ra_session, URL1, pool));
SVN_ERR(normalize_merge_sources(&remove_sources, URL1, URL1,
source_repos_root, &peg_revision,
- ranges, ra_session, ctx, pool));
+ ranges, URL1_ra_session, ctx, pool));
range = apr_pcalloc(pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
@@ -8208,12 +8481,9 @@ merge_cousins_and_supplement_mergeinfo(c
ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev2;
- SVN_ERR(svn_ra_reparent(ra_session, URL2, pool));
SVN_ERR(normalize_merge_sources(&add_sources, URL2, URL2,
source_repos_root, &peg_revision,
- ranges, ra_session, ctx, pool));
-
- SVN_ERR(svn_ra_reparent(ra_session, old_url, pool));
+ ranges, URL2_ra_session, ctx, pool));
/* If this isn't a record-only merge, we'll first do a stupid
point-to-point merge... */
@@ -8271,22 +8541,21 @@ merge_cousins_and_supplement_mergeinfo(c
/*** Public APIs ***/
-svn_error_t *
-svn_client_merge3(const char *source1,
- const svn_opt_revision_t *revision1,
- const char *source2,
- const svn_opt_revision_t *revision2,
- const char *target_wcpath,
- svn_depth_t depth,
- svn_boolean_t ignore_ancestry,
- svn_boolean_t force,
- svn_boolean_t record_only,
- svn_boolean_t dry_run,
- const apr_array_header_t *merge_options,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+static svn_error_t *
+merge_locked(const char *source1,
+ const svn_opt_revision_t *revision1,
+ const char *source2,
+ const svn_opt_revision_t *revision2,
+ const char *target_abspath,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_boolean_t force,
+ svn_boolean_t record_only,
+ svn_boolean_t dry_run,
+ const apr_array_header_t *merge_options,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- svn_wc_adm_access_t *adm_access;
const char *URL1, *URL2;
svn_revnum_t rev1, rev2;
svn_boolean_t related = FALSE, ancestral = FALSE;
@@ -8303,9 +8572,6 @@ svn_client_merge3(const char *source1,
apr_pool_t *sesspool;
svn_boolean_t same_repos;
const char *source_repos_uuid1, *source_repos_uuid2;
- const char *target_abspath;
-
- SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath, pool));
/* Sanity check our input -- we require specified revisions. */
if ((revision1->kind == svn_opt_revision_unspecified)
@@ -8325,31 +8591,28 @@ svn_client_merge3(const char *source1,
able to figure out some kind of revision specifications, but in
that case it won't matter, because those ways of specifying a
revision are meaningless for a url. */
- SVN_ERR(svn_client_url_from_path2(&URL1, source1, ctx, pool, pool));
+ SVN_ERR(svn_client_url_from_path2(&URL1, source1, ctx,
+ scratch_pool, scratch_pool));
if (! URL1)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"),
- svn_dirent_local_style(source1, pool));
+ svn_dirent_local_style(source1, scratch_pool));
- SVN_ERR(svn_client_url_from_path2(&URL2, source2, ctx, pool, pool));
+ SVN_ERR(svn_client_url_from_path2(&URL2, source2, ctx,
+ scratch_pool, scratch_pool));
if (! URL2)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"),
- svn_dirent_local_style(source2, pool));
-
- /* Open an admistrative session with the working copy. */
- SVN_ERR(svn_wc__adm_probe_in_context(&adm_access, ctx->wc_ctx,
- target_abspath,
- !dry_run, -1, ctx->cancel_func,
- ctx->cancel_baton, pool));
+ svn_dirent_local_style(source2, scratch_pool));
/* Determine the working copy target's repository root URL. */
working_rev.kind = svn_opt_revision_working;
SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath,
- &working_rev, ctx, pool, pool));
+ &working_rev, ctx,
+ scratch_pool, scratch_pool));
/* Open some RA sessions to our merge source sides. */
- sesspool = svn_pool_create(pool);
+ sesspool = svn_pool_create(scratch_pool);
SVN_ERR(svn_client__open_ra_session_internal(&ra_session1,
URL1, NULL, NULL,
FALSE, TRUE, ctx, sesspool));
@@ -8365,8 +8628,8 @@ svn_client_merge3(const char *source1,
NULL, ra_session2, revision2,
sesspool));
- SVN_ERR(svn_ra_get_uuid2(ra_session1, &source_repos_uuid1, pool));
- SVN_ERR(svn_ra_get_uuid2(ra_session2, &source_repos_uuid2, pool));
+ SVN_ERR(svn_ra_get_uuid2(ra_session1, &source_repos_uuid1, scratch_pool));
+ SVN_ERR(svn_ra_get_uuid2(ra_session2, &source_repos_uuid2, scratch_pool));
/* We can't do a diff between different repositories. */
if (strcmp(source_repos_uuid1, source_repos_uuid2) != 0)
@@ -8383,7 +8646,7 @@ svn_client_merge3(const char *source1,
const char *wc_repos_uuid;
SVN_ERR(svn_client_uuid_from_path2(&wc_repos_uuid, target_abspath,
- ctx, pool, pool));
+ ctx, scratch_pool, scratch_pool));
same_repos = (strcmp(wc_repos_uuid, source_repos_uuid1) == 0);
}
else
@@ -8394,7 +8657,7 @@ svn_client_merge3(const char *source1,
SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev,
URL1, rev1,
URL2, rev2,
- ctx, pool));
+ ctx, scratch_pool));
/* Check for a youngest common ancestor. If we have one, we'll be
doing merge tracking.
@@ -8424,41 +8687,46 @@ svn_client_merge3(const char *source1,
related = TRUE;
/* Make YC_PATH into a full URL. */
- yc_path = svn_path_url_add_component2(source_repos_root, yc_path, pool);
+ yc_path = svn_path_url_add_component2(source_repos_root, yc_path,
+ scratch_pool);
/* If the common ancestor matches the right side of our merge,
then we only need to reverse-merge the left side. */
if ((strcmp(yc_path, URL2) == 0) && (yc_rev == rev2))
{
ancestral = TRUE;
- range = apr_pcalloc(pool, sizeof(*range));
+ range = apr_pcalloc(scratch_pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
range->start.value.number = rev1;
range->end.kind = svn_opt_revision_number;
range->end.value.number = yc_rev;
- ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
+ ranges = apr_array_make(scratch_pool,
+ 2, sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev1;
SVN_ERR(normalize_merge_sources(&merge_sources, URL1, URL1,
source_repos_root, &peg_revision,
- ranges, ra_session1, ctx, pool));
+ ranges, ra_session1, ctx,
+ scratch_pool));
}
/* If the common ancestor matches the left side of our merge,
then we only need to merge the right side. */
else if ((strcmp(yc_path, URL1) == 0) && (yc_rev == rev1))
{
ancestral = TRUE;
- range = apr_pcalloc(pool, sizeof(*range));
+ range = apr_pcalloc(scratch_pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
range->start.value.number = yc_rev;
range->end.kind = svn_opt_revision_number;
range->end.value.number = rev2;
- ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
+ ranges = apr_array_make(scratch_pool,
+ 2, sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev2;
SVN_ERR(normalize_merge_sources(&merge_sources, URL2, URL2,
source_repos_root, &peg_revision,
- ranges, ra_session2, ctx, pool));
+ ranges, ra_session2, ctx,
+ scratch_pool));
}
/* And otherwise, we need to do both: reverse merge the left
side, and merge the right. */
@@ -8466,6 +8734,7 @@ svn_client_merge3(const char *source1,
{
err = merge_cousins_and_supplement_mergeinfo(target_abspath,
ra_session1,
+ ra_session2,
URL1, rev1,
URL2, rev2,
yc_rev,
@@ -8476,11 +8745,11 @@ svn_client_merge3(const char *source1,
record_only, dry_run,
merge_options,
&use_sleep, ctx,
- pool);
+ scratch_pool);
if (err)
{
if (use_sleep)
- svn_io_sleep_for_timestamps(target_wcpath, pool);
+ svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
return svn_error_return(err);
}
@@ -8490,14 +8759,14 @@ svn_client_merge3(const char *source1,
the merge_cousins_and_supplement_mergeinfo() routine). */
svn_pool_destroy(sesspool);
- return svn_wc_adm_close2(adm_access, pool);
+ return SVN_NO_ERROR;
}
}
else
{
/* Build a single-item merge_source_t array. */
- merge_sources = apr_array_make(pool, 1, sizeof(merge_source_t *));
- merge_source = apr_pcalloc(pool, sizeof(*merge_source));
+ merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
+ merge_source = apr_pcalloc(scratch_pool, sizeof(*merge_source));
merge_source->url1 = URL1;
merge_source->url2 = URL2;
merge_source->rev1 = rev1;
@@ -8512,21 +8781,115 @@ svn_client_merge3(const char *source1,
ancestral, related, same_repos,
ignore_ancestry, force, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
- &use_sleep, ctx, pool);
+ &use_sleep, ctx, scratch_pool);
if (use_sleep)
- svn_io_sleep_for_timestamps(target_wcpath, pool);
+ svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
if (err)
return svn_error_return(err);
- return svn_wc_adm_close2(adm_access, pool);
+ return SVN_NO_ERROR;
}
+struct merge_baton {
+ const char *source1;
+ const svn_opt_revision_t *revision1;
+ const char *source2;
+ const svn_opt_revision_t *revision2;
+ const char *target_abspath;
+ svn_depth_t depth;
+ svn_boolean_t ignore_ancestry;
+ svn_boolean_t force;
+ svn_boolean_t record_only;
+ svn_boolean_t dry_run;
+ const apr_array_header_t *merge_options;
+ svn_client_ctx_t *ctx;
+};
+
+/* Implements svn_wc__with_write_lock_func_t. */
+static svn_error_t *
+merge_cb(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ struct merge_baton *b = baton;
+
+ SVN_ERR(merge_locked(b->source1, b->revision1, b->source2, b->revision2,
+ b->target_abspath, b->depth, b->ignore_ancestry,
+ b->force, b->record_only, b->dry_run,
+ b->merge_options, b->ctx, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
-/* If TARGET_WCPATH does not reflect a single-revision,
- svn_depth_infinity, pristine, unswitched working copy -- in other
- words, a subtree found in a single revision -- raise
+/* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to
+ the absolute path to lock for, TARGET_WCPATH. */
+static svn_error_t *
+get_target_and_lock_abspath(const char **target_abspath,
+ const char **lock_abspath,
+ const char *target_wcpath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_node_kind_t kind;
+ SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
+ scratch_pool));
+ SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, *target_abspath, FALSE,
+ scratch_pool));
+ if (kind == svn_node_dir)
+ *lock_abspath = *target_abspath;
+ else
+ *lock_abspath = svn_dirent_dirname(*target_abspath, scratch_pool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_merge3(const char *source1,
+ const svn_opt_revision_t *revision1,
+ const char *source2,
+ const svn_opt_revision_t *revision2,
+ const char *target_wcpath,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_boolean_t force,
+ svn_boolean_t record_only,
+ svn_boolean_t dry_run,
+ const apr_array_header_t *merge_options,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ const char *target_abspath, *lock_abspath;
+ struct merge_baton baton;
+
+ SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
+ target_wcpath, ctx, pool));
+
+ baton.source1 = source1;
+ baton.revision1 = revision1;
+ baton.source2 = source2;
+ baton.revision2 = revision2;
+ baton.target_abspath = target_abspath;
+ baton.depth = depth;
+ baton.ignore_ancestry = ignore_ancestry;
+ baton.force = force;
+ baton.record_only = record_only;
+ baton.dry_run = dry_run;
+ baton.merge_options = merge_options;
+ baton.ctx = ctx;
+
+ if (!dry_run)
+ SVN_ERR(svn_wc__call_with_write_lock(merge_cb, &baton, ctx->wc_ctx,
+ lock_abspath, pool, pool));
+ else
+ SVN_ERR(merge_cb(&baton, pool, pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+/* If TARGET_WCPATH does not reflect a single-revision, pristine,
+ unswitched working copy -- in other words, a subtree found in a
+ single revision (although sparse checkouts are permitted) -- raise
SVN_ERR_CLIENT_NOT_READY_TO_MERGE. */
static svn_error_t *
ensure_wc_reflects_repository_subtree(const char *target_abspath,
@@ -8545,11 +8908,6 @@ ensure_wc_reflects_repository_subtree(co
_("Cannot reintegrate into a working copy "
"with a switched subtree"));
- if (wc_stat->sparse_checkout)
- return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
- _("Cannot reintegrate into a working copy "
- "not entirely at infinite depth"));
-
if (wc_stat->modified)
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Cannot reintegrate into a working copy "
@@ -8668,8 +9026,7 @@ typedef struct log_find_operative_baton_
apr_pool_t *result_pool;
} log_find_operative_baton_t;
-/* A svn_log_entry_receiver_t callback for
- ensure_all_missing_ranges_are_phantoms(). */
+/* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */
static svn_error_t *
log_find_operative_revs(void *baton,
svn_log_entry_t *log_entry,
@@ -8774,7 +9131,7 @@ log_find_operative_revs(void *baton,
MERGEINFO_CATALOG may be empty if the source has no explicit or inherited
mergeinfo.
- Using RA_SESSION, which is pointed at the repository root, check that all
+ Using RA_SESSION, which is pointed at TARGET_REPOS_REL_PATH, check that all
of the unmerged revisions in UNMERGED_CATALOG's mergeinfos are "phantoms",
that is, one of the following conditions holds:
@@ -8854,7 +9211,7 @@ find_unsynced_ranges(const char *source_
target_repos_rel_path);
log_baton.result_pool = result_pool;
- APR_ARRAY_PUSH(log_targets, const char *) = target_repos_rel_path;
+ APR_ARRAY_PUSH(log_targets, const char *) = "";
SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
oldest_rev, 0, TRUE, FALSE, FALSE,
@@ -8893,7 +9250,8 @@ find_unsynced_ranges(const char *source_
TARGET_REPOS_REL_PATH is at. SOURCE_REV is the peg revision of the
reintegrate source.
- RA_SESSION is a session opened to the repository root.
+ SOURCE_RA_SESSION is a session opened to the SOURCE_REPOS_REL_PATH
+ and TARGET_RA_SESSION is open to TARGET_REPOS_REL_PATH.
For each path/segment in TARGET_SEGMENTS_HASH check that the history that
segment represents is contained in either the explicit mergeinfo for the
@@ -8925,7 +9283,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
const char *target_repos_rel_path,
svn_revnum_t target_rev,
svn_revnum_t source_rev,
- svn_ra_session_t *ra_session,
+ svn_ra_session_t *source_ra_session,
+ svn_ra_session_t *target_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -8949,6 +9308,7 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
const char *path = svn__apr_hash_index_key(hi);
apr_array_header_t *segments = svn__apr_hash_index_val(hi);
const char *source_path;
+ const char *source_path_rel_to_session;
svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
svn_pool_clear(iterpool);
@@ -8958,6 +9318,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
source_path++;
source_path = svn_uri_join(source_repos_rel_path, source_path,
iterpool);
+ source_path_rel_to_session =
+ svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
/* Convert this target path's natural history into mergeinfo. */
SVN_ERR(svn_client__mergeinfo_from_segments(&target_history_as_mergeinfo,
@@ -9018,8 +9380,9 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
svn_node_kind_t kind;
svn_mergeinfo_catalog_t subtree_catalog;
apr_array_header_t *source_repos_rel_path_as_array;
- SVN_ERR(svn_ra_check_path(ra_session, source_path, source_rev,
- &kind, iterpool));
+ SVN_ERR(svn_ra_check_path(source_ra_session,
+ source_path_rel_to_session,
+ source_rev, &kind, iterpool));
if (kind == svn_node_none)
continue;
/* Else source_path does exist though it has no explicit mergeinfo.
@@ -9028,8 +9391,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
source_repos_rel_path_as_array =
apr_array_make(iterpool, 1, sizeof(const char *));
APR_ARRAY_PUSH(source_repos_rel_path_as_array, const char *)
- = source_path;
- SVN_ERR(svn_ra_get_mergeinfo(ra_session, &subtree_catalog,
+ = source_path_rel_to_session;
+ SVN_ERR(svn_ra_get_mergeinfo(source_ra_session, &subtree_catalog,
source_repos_rel_path_as_array,
source_rev, svn_mergeinfo_inherited,
FALSE, iterpool));
@@ -9046,8 +9409,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
Then merge that natural history into source path's explicit
or inherited mergeinfo. */
SVN_ERR(svn_client__repos_location_segments(&segments,
- ra_session,
- source_path,
+ source_ra_session,
+ source_path_rel_to_session,
source_rev, source_rev,
SVN_INVALID_REVNUM,
ctx, iterpool));
@@ -9094,6 +9457,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
hi = apr_hash_next(hi))
{
const char *source_path = svn__apr_hash_index_key(hi);
+ const char *source_path_rel_to_session =
+ svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi);
svn_mergeinfo_t filtered_mergeinfo;
const char *target_path;
@@ -9105,11 +9470,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
target_path = source_path + strlen(source_repos_rel_path);
if (target_path[0] == '/') /* Remove leading '/' for svn_uri_join. */
target_path++;
- target_path = svn_uri_join(target_repos_rel_path, target_path,
- iterpool);
-
err = svn_client__repos_location_segments(&segments,
- ra_session,
+ target_ra_session,
target_path,
target_rev, target_rev,
SVN_INVALID_REVNUM,
@@ -9161,13 +9523,14 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
/* Get the source path's natural history and convert it to
mergeinfo. Then merge that natural history into source
path's explicit or inherited mergeinfo. */
- SVN_ERR(svn_client__repos_location_segments(&segments,
- ra_session,
- source_path,
- target_rev,
- target_rev,
- SVN_INVALID_REVNUM,
- ctx, iterpool));
+ SVN_ERR(svn_client__repos_location_segments(
+ &segments,
+ source_ra_session,
+ source_path_rel_to_session,
+ target_rev,
+ target_rev,
+ SVN_INVALID_REVNUM,
+ ctx, iterpool));
SVN_ERR(svn_client__mergeinfo_from_segments(
&source_history_as_mergeinfo, segments, iterpool));
SVN_ERR(svn_mergeinfo_merge(source_mergeinfo,
@@ -9196,14 +9559,12 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
/* Limit new_catalog to the youngest revisions previously merged from
the target to the source. */
if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
- {
- SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
- new_catalog,
- *youngest_merged_rev,
- 0, /* No oldest bound. */
- TRUE,
- subpool, subpool));
- }
+ SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
+ new_catalog,
+ *youngest_merged_rev,
+ 0, /* No oldest bound. */
+ TRUE,
+ subpool, subpool));
/* Make a shiny new copy before blowing away all the temporary pools. */
*unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog, pool);
@@ -9236,7 +9597,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
from the target to the source if such exists, see doc string for
find_unmerged_mergeinfo().
- RA_SESSION is a session opened to the repository root. */
+ SOURCE_RA_SESSION is a session opened to the SOURCE_REPOS_REL_PATH
+ and TARGET_RA_SESSION is open to TARGET_REPOS_REL_PATH. */
static svn_error_t *
calculate_left_hand_side(const char **url_left,
svn_revnum_t *rev_left,
@@ -9248,7 +9610,8 @@ calculate_left_hand_side(const char **ur
const char *source_repos_rel_path,
const char *source_repos_root,
svn_revnum_t source_rev,
- svn_ra_session_t *ra_session,
+ svn_ra_session_t *source_ra_session,
+ svn_ra_session_t *target_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -9275,8 +9638,8 @@ calculate_left_hand_side(const char **ur
const char *path = svn__apr_hash_index_key(hi);
SVN_ERR(svn_client__repos_location_segments(&segments,
- ra_session,
- path,
+ target_ra_session,
+ "",
target_rev, target_rev,
SVN_INVALID_REVNUM,
ctx, subpool));
@@ -9290,7 +9653,7 @@ calculate_left_hand_side(const char **ur
get an initial value for *REV_LEFT. */
source_url = svn_path_url_add_component2(source_repos_root,
source_repos_rel_path,
- subpool),
+ subpool);
target_url = svn_path_url_add_component2(source_repos_root,
target_repos_rel_path,
subpool);
@@ -9307,12 +9670,17 @@ calculate_left_hand_side(const char **ur
/* Get the mergeinfo from the source, including its descendants
with differing explicit mergeinfo. */
- APR_ARRAY_PUSH(source_repos_rel_path_as_array, const char *)
- = source_repos_rel_path;
- SVN_ERR(svn_ra_get_mergeinfo(ra_session, &mergeinfo_catalog,
+ APR_ARRAY_PUSH(source_repos_rel_path_as_array, const char *) = "";
+ SVN_ERR(svn_ra_get_mergeinfo(source_ra_session, &mergeinfo_catalog,
source_repos_rel_path_as_array, source_rev,
svn_mergeinfo_inherited, TRUE, subpool));
+ if (mergeinfo_catalog)
+ SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&mergeinfo_catalog,
+ mergeinfo_catalog,
+ source_repos_rel_path,
+ subpool, subpool));
+
if (!mergeinfo_catalog)
mergeinfo_catalog = apr_hash_make(subpool);
@@ -9332,7 +9700,8 @@ calculate_left_hand_side(const char **ur
target_repos_rel_path,
target_rev,
source_rev,
- ra_session,
+ source_ra_session,
+ target_ra_session,
ctx,
subpool));
@@ -9353,19 +9722,27 @@ calculate_left_hand_side(const char **ur
/* We've previously merged some or all of the target, up to
youngest_merged_rev, from the target to the source. Set *URL_LEFT
and *REV_LEFT to cover the youngest part of this range. */
- svn_opt_revision_t peg_revision;
+ svn_opt_revision_t peg_revision, youngest_rev, unspecified_rev;
[... 507 lines stripped ...]