Copied: subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c (from r1922042, subversion/branches/apply-processor/subversion/libsvn_client/merge.c) URL: http://svn.apache.org/viewvc/subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c?p2=subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c&p1=subversion/branches/apply-processor/subversion/libsvn_client/merge.c&r1=1922042&r2=1922048&rev=1922048&view=diff ============================================================================== --- subversion/branches/apply-processor/subversion/libsvn_client/merge.c (original) +++ subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c Sun Nov 24 14:10:54 2024 @@ -1,5 +1,6 @@ /* - * merge.c: merging + * merge_processor.c: svn_diff_tree_processor implementation for merge + * apply operation. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -65,173 +66,11 @@ #include "svn_private_config.h" -/*-----------------------------------------------------------------------*/ - -/* MERGEINFO MERGE SOURCE NORMALIZATION - * - * Nearly any helper function herein that accepts two URL/revision - * 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 - * - * 2. that the pairs represent two locations along a single line - * of version history such that there are no copies in the - * history of the object between the locations when treating - * the oldest of the two locations as non-inclusive. In other - * words, if there is a copy at all between them, there is only - * one copy and its source was the oldest of the two locations. - * - * 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, 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]) - * - * [/tags/1.4.4@866419, /branches/1.4.5@866424] - * (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424) - * - * [/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. - */ - -/* WHICH SVN_CLIENT_MERGE* API DO I WANT? - * - * libsvn_client has three public merge APIs; they are all wrappers - * around the do_merge engine. Which one to use depends on the number - * of URLs passed as arguments and whether or not specific merge - * ranges (-c/-r) are specified. - * - * 1 URL 2 URLs - * +----+--------------------------------+---------------------+ - * | -c | mergeinfo-driven | | - * | or | cherrypicking | | - * | -r | (svn_client_merge_peg) | | - * |----+--------------------------------+ | - * | | mergeinfo-driven | unsupported | - * | | 'cherry harvest', i.e. merge | | - * | | all revisions from URL that | | - * | no | have not already been merged | | - * | -c | (svn_client_merge_peg) | | - * | or +--------------------------------+---------------------+ - * | -r | mergeinfo-driven | mergeinfo-writing | - * | | whole-branch | diff-and-apply | - * | | heuristic merge | (svn_client_merge) | - * | | (svn_client_merge_reintegrate) | | - * +----+--------------------------------+---------------------+ - * - * - */ - -/* THE CHILDREN_WITH_MERGEINFO ARRAY - * - * Many of the helper functions in this file pass around an - * apr_array_header_t *CHILDREN_WITH_MERGEINFO. This is a depth first - * sorted array filled with svn_client__merge_path_t * describing the - * merge target and any of its subtrees which have explicit mergeinfo - * or otherwise need special attention during a merge. - * - * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains - * contains only one element (added by do_mergeinfo_unaware_dir_merge) - * describing a contiguous range to be merged to the WC merge target. - * - * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created - * by get_mergeinfo_paths() and outside of that function and its helpers - * should always meet the criteria dictated in get_mergeinfo_paths()'s doc - * string. The elements of CHILDREN_WITH_MERGEINFO should never be NULL. - * - * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see - * the doc string for HONOR_MERGEINFO(). - */ - - -/*-----------------------------------------------------------------------*/ /*** Repos-Diff Editor Callbacks ***/ -struct merge_cmd_baton_t; - -struct notify_begin_state_t +typedef struct merge_apply_processor_baton_t { - /* Cache of which abspath was last notified. */ - const char *last_abspath; - - /* Reference to the main merge baton */ - struct merge_cmd_baton_t *merge_b; - - /* the wrapped notification callback */ - svn_wc_notify_func2_t notify_func2; - void *notify_baton2; -}; - -typedef struct merge_cmd_baton_t { svn_boolean_t force_delete; /* Delete a file/dir even if modified */ svn_boolean_t dry_run; svn_boolean_t record_only; /* Whether to merge only mergeinfo @@ -240,77 +79,15 @@ typedef struct merge_cmd_baton_t { is the same repository as the target. Defaults to FALSE if DRY_RUN is TRUE.*/ - svn_boolean_t mergeinfo_capable; /* Whether the merge source server - is capable of Merge Tracking. */ - svn_boolean_t ignore_mergeinfo; /* Don't honor mergeinfo; see - doc string of do_merge(). FALSE if - MERGE_SOURCE->ancestral is FALSE. */ - svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see - doc string of do_merge(). FALSE if - MERGE_SOURCE->ancestral is FALSE. */ - svn_boolean_t reintegrate_merge; /* Whether this is a --reintegrate - merge or not. */ /* Description of merge target node */ const svn_client__merge_target_t *target; - /* The left and right URLs and revs. The value of this field changes to - reflect the merge_source_t *currently* being merged by do_merge(). */ + /* The left and right URLs and revs. */ svn_client__merge_source_t merge_source; - /* Rangelist containing single range which describes the gap, if any, - in the natural history of the merge source currently being processed. - See https://issues.apache.org/jira/browse/SVN-3432. - Updated during each call to do_directory_merge(). May be NULL if there - is no gap. */ - svn_rangelist_t *implicit_src_gap; - - /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global - comment) or a similar list for single-file-merges */ - apr_array_header_t *children_with_mergeinfo; - svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */ - /* The list of any paths which remained in conflict after a - resolution attempt was made. We track this in-memory, rather - than just using WC entry state, since the latter doesn't help us - when in dry_run mode. - ### And because we only want to resolve conflicts that were - generated by this merge, not pre-existing ones? */ - apr_hash_t *conflicted_paths; - - /* A list of absolute paths which had no explicit mergeinfo prior to the - merge but got explicit mergeinfo added by the merge. This is populated - by merge_change_props() and is allocated in POOL so it is subject to the - lifetime limitations of POOL. Is NULL if no paths are found which - meet the criteria or DRY_RUN is true. */ - apr_hash_t *paths_with_new_mergeinfo; - - /* A list of absolute paths whose mergeinfo doesn't need updating after - the merge. This can be caused by the removal of mergeinfo by the merge - or by deleting the node itself. This is populated by merge_change_props() - and the delete callbacks and is allocated in POOL so it is subject to the - lifetime limitations of POOL. Is NULL if no paths are found which - meet the criteria or DRY_RUN is true. */ - apr_hash_t *paths_with_deleted_mergeinfo; - - /* The list of absolute skipped paths, which should be examined and - cleared after each invocation of the callback. The paths - are absolute. Is NULL if MERGE_B->MERGE_SOURCE->ancestral and - MERGE_B->REINTEGRATE_MERGE are both false. */ - apr_hash_t *skipped_abspaths; - - /* The list of absolute merged paths. Unused if MERGE_B->MERGE_SOURCE->ancestral - and MERGE_B->REINTEGRATE_MERGE are both false. */ - apr_hash_t *merged_abspaths; - - /* A hash of (const char *) absolute WC paths mapped to the same which - represent the roots of subtrees added by the merge. */ - apr_hash_t *added_abspaths; - - /* A list of tree conflict victim absolute paths which may be NULL. */ - apr_hash_t *tree_conflicted_abspaths; - /* The diff3_cmd in ctx->config, if any, else null. We could just extract this as needed, but since more than one caller uses it, we just set it up when this baton is created. */ @@ -321,140 +98,12 @@ typedef struct merge_cmd_baton_t { generated conflict files. */ const apr_array_header_t *ext_patterns; - /* RA sessions used throughout a merge operation. Opened/re-parented - as needed. - - NOTE: During the actual merge editor drive, RA_SESSION1 is used - for the primary editing and RA_SESSION2 for fetching additional - information -- as necessary -- from the repository. So during - this phase of the merge, you *must not* reparent RA_SESSION1; use - (temporarily reparenting if you must) RA_SESSION2 instead. */ - svn_ra_session_t *ra_session1; - svn_ra_session_t *ra_session2; - - /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required - afterwards to ensure timestamp integrity, or unchanged if not. */ - svn_boolean_t *use_sleep; - - /* Pool which has a lifetime limited to one iteration over a given - merge source, i.e. it is cleared on every call to do_directory_merge() - or do_file_merge() in do_merge(). */ - apr_pool_t *pool; - /* Our notification callback, that adds a 'begin' notification */ svn_wc_notify_func2_t notify_func; void *notify_baton; - struct notify_begin_state_t notify_begin; - -} merge_cmd_baton_t; - - -/* Return TRUE iff we should be taking account of mergeinfo in deciding what - changes to merge, for the merge described by MERGE_B. Specifically, that - is if the merge source server is capable of merge tracking, the left-side - merge source is an ancestor of the right-side (or vice-versa), the merge - source is in the same repository as the merge target, and we are not - ignoring mergeinfo. */ -static svn_boolean_t -HONOR_MERGEINFO(const merge_cmd_baton_t *merge_b) -{ - return (merge_b->mergeinfo_capable - && merge_b->merge_source.ancestral - && merge_b->same_repos - && (!merge_b->ignore_mergeinfo)); -} +} merge_apply_processor_baton_t; -/* Return TRUE iff we should be recording mergeinfo for the merge described - by MERGE_B. Specifically, that is if we are honoring mergeinfo and the - merge is not a dry run. */ -static svn_boolean_t -RECORD_MERGEINFO(const merge_cmd_baton_t *merge_b) -{ - return (HONOR_MERGEINFO(merge_b) - && !merge_b->dry_run); -} - - -/*-----------------------------------------------------------------------*/ - -/*** Utilities ***/ - -/* Return TRUE iff the session URL of RA_SESSION is equal to URL. Useful in - * asserting preconditions. */ -static svn_boolean_t -session_url_is(svn_ra_session_t *ra_session, - const char *url, - apr_pool_t *scratch_pool) -{ - const char *session_url; - svn_error_t *err - = svn_ra_get_session_url(ra_session, &session_url, scratch_pool); - - SVN_ERR_ASSERT_NO_RETURN(! err); - return strcmp(url, session_url) == 0; -} - -/* Return a new merge_source_t structure, allocated in RESULT_POOL, - * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */ -svn_client__merge_source_t * -svn_client__merge_source_create(const svn_client__pathrev_t *loc1, - const svn_client__pathrev_t *loc2, - svn_boolean_t ancestral, - apr_pool_t *result_pool) -{ - svn_client__merge_source_t *s - = apr_palloc(result_pool, sizeof(*s)); - - s->loc1 = svn_client__pathrev_dup(loc1, result_pool); - s->loc2 = svn_client__pathrev_dup(loc2, result_pool); - s->ancestral = ancestral; - return s; -} - -/* Return a deep copy of SOURCE, allocated in RESULT_POOL. */ -svn_client__merge_source_t * -svn_client__merge_source_dup(const svn_client__merge_source_t *source, - apr_pool_t *result_pool) -{ - svn_client__merge_source_t *s = apr_palloc(result_pool, sizeof(*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; -} - -/* Decide whether ambiguous foreign merge should be a warning or an error */ -#define WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING \ - (SVN_VER_MAJOR == 1 && SVN_VER_MINOR < 16) - -#if WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING -/* Notify a warning, given in WARNING, if WARNING is non-null. - * - * We plan to replace this with a hard error in Subversion 1.16. - * This clears the error object WARNING before returning. - */ -static void -notify_pre_1_16_warning(svn_error_t *warning, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - if (!warning) - return; - - if (ctx->notify_func2) - { - svn_wc_notify_t *n - = svn_wc_create_notify("" /*path*/, svn_wc_notify_warning, pool); - - n->err = svn_error_quick_wrap(warning, - _("In Subversion 1.16 this warning will become a fatal error")); - ctx->notify_func2(ctx->notify_baton2, n, pool); - } - svn_error_clear(warning); -} -#endif /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository of LOCAL_ABSPATH. Use SCRATCH_POOL for temporary allocations. */ @@ -474,75 +123,9 @@ check_repos_match(const svn_client__merg return SVN_NO_ERROR; } -/* Decide whether LOCATION1 and LOCATION2 point to the same repository - * (with the same root URL) or to two different repositories. - * - same repository root URL -> set *SAME_REPOS true - * - different repositories -> set *SAME_REPOS false - * - different URLs but same UUID -> return an error - * - * The last case is unsupported for practical and historical reasons - * (see issue #4874) even though different URLs pointing to the same or - * equivalent repositories could be supported in principle. - */ -static svn_error_t * -is_same_repos(svn_boolean_t *same_repos, - const svn_client__pathrev_t *location1, - const char *path1, - const svn_client__pathrev_t *location2, - const char *path2, - const char *message) -{ - if (strcmp(location1->repos_root_url, location2->repos_root_url) == 0) - *same_repos = TRUE; - else if (strcmp(location1->repos_uuid, location2->repos_uuid) != 0) - *same_repos = FALSE; - else - { - svn_error_t *err - = svn_error_create(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, message); - - return svn_error_quick_wrapf(err, - _("The locations '%s' and '%s' point to repositories with the " - "same repository UUID using different repository root URLs " - "('%s' and '%s')"), - path1, path2, - location1->repos_root_url, location2->repos_root_url); - } - return SVN_NO_ERROR; -} - -/* Check that LOCATION1 and LOCATION2 point to the same repository, with - * the same root URL. If not, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES - * error mentioning PATH_OR_URL1 and PATH_OR_URL2. - */ -static svn_error_t * -check_same_repos(const svn_client__pathrev_t *location1, - const char *path_or_url1, - const svn_client__pathrev_t *location2, - const char *path_or_url2, - const char *message) -{ - svn_boolean_t same_repos; - - SVN_ERR(is_same_repos(&same_repos, - location1, path_or_url1, location2, path_or_url2, - message)); - if (! same_repos) - { - svn_error_t *err - = svn_error_create(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, message); - - return svn_error_quick_wrapf(err, - _("The locations '%s' and '%s' point to different repositories " - "(root URLs '%s' and '%s', and differing UUIDs)"), - path_or_url1, path_or_url2, - location1->repos_root_url, location2->repos_root_url); - } - return SVN_NO_ERROR; -} - /* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool containing PATH_HASH. */ +/* TODO: deduplicate with merge.c */ static APR_INLINE void store_path(apr_hash_t *path_hash, const char *local_abspath) { @@ -552,28 +135,6 @@ store_path(apr_hash_t *path_hash, const svn_hash_sets(path_hash, dup_path, dup_path); } -/* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool - containing *PATH_HASH_P. If *PATH_HASH_P is NULL, then first set - *PATH_HASH_P to a new hash allocated from POOL. */ -static APR_INLINE void -alloc_and_store_path(apr_hash_t **path_hash_p, - const char *local_abspath, - apr_pool_t *pool) -{ - if (! *path_hash_p) - *path_hash_p = apr_hash_make(pool); - store_path(*path_hash_p, local_abspath); -} - -/* Return whether any WC path was put in conflict by the merge - operation corresponding to MERGE_B. */ -static APR_INLINE svn_boolean_t -is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b) -{ - return (merge_b->conflicted_paths && - apr_hash_count(merge_b->conflicted_paths) > 0); -} - /* Return a state indicating whether the WC metadata matches the * node kind on disk of the local path LOCAL_ABSPATH. * Use MERGE_B to determine the dry-run details; particularly, if a dry run @@ -589,13 +150,14 @@ is_path_conflicted_by_merge(merge_cmd_ba * * Optionally return a bit more info for interested users. **/ +/* TODO: deduplicate with merge.c */ static svn_error_t * perform_obstruction_check(svn_wc_notify_state_t *obstruction_state, svn_boolean_t *deleted, svn_boolean_t *excluded, svn_node_kind_t *kind, svn_depth_t *parent_depth, - const merge_cmd_baton_t *merge_b, + const merge_apply_processor_baton_t *merge_b, const char *local_abspath, apr_pool_t *scratch_pool) { @@ -1086,7 +648,7 @@ static svn_error_t * 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, + merge_apply_processor_baton_t *merge_b, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -1137,12 +699,14 @@ prepare_merge_props_changed(const apr_ar if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev || !merge_b->merge_source.ancestral) { +#if TODO_FILTER_MERGEINFO 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)); +#endif } } *prop_updates = props; @@ -1176,13 +740,17 @@ prepare_merge_props_changed(const apr_ar if (!has_pristine_mergeinfo && prop->value) { +#if TODO_STORE_PATH alloc_and_store_path(&merge_b->paths_with_new_mergeinfo, local_abspath, merge_b->pool); +#endif } else if (has_pristine_mergeinfo && !prop->value) { +#if TODO_STORE_PATH alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo, local_abspath, merge_b->pool); +#endif } } } @@ -1315,7 +883,7 @@ struct merge_file_baton_t /* Record the skip for future processing and (later) produce the skip notification */ static svn_error_t * -record_skip(merge_cmd_baton_t *merge_b, +record_skip(merge_apply_processor_baton_t *merge_b, const char *local_abspath, svn_node_kind_t kind, svn_wc_notify_action_t action, @@ -1326,11 +894,13 @@ record_skip(merge_cmd_baton_t *merge_b, if (merge_b->record_only) return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */ +#if TODO_STORE_PATH if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge) && !(pdb && pdb->shadowed)) { store_path(merge_b->skipped_abspaths, local_abspath); } +#endif if (merge_b->notify_func) { @@ -1367,7 +937,7 @@ find_nearest_ancestor_with_intersecting_ * of the same names in svn_wc_tree_conflict_description_t. */ static svn_error_t * -record_tree_conflict(merge_cmd_baton_t *merge_b, +record_tree_conflict(merge_apply_processor_baton_t *merge_b, const char *local_abspath, struct merge_dir_baton_t *parent_baton, svn_node_kind_t local_node_kind, @@ -1384,6 +954,7 @@ record_tree_conflict(merge_cmd_baton_t * if (merge_b->record_only) return SVN_NO_ERROR; +#if TODO_STORE_PATH if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) { @@ -1392,6 +963,7 @@ record_tree_conflict(merge_cmd_baton_t * alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, merge_b->pool); +#endif if (!merge_b->dry_run) { @@ -1426,6 +998,7 @@ record_tree_conflict(merge_cmd_baton_t * reason = svn_wc_conflict_reason_moved_here; } +#if TODO_FILTER_MERGEINFO if (HONOR_MERGEINFO(merge_b) && merge_b->merge_source.ancestral) { struct svn_client__merge_source_t *source; @@ -1460,6 +1033,7 @@ record_tree_conflict(merge_cmd_baton_t * result_pool, scratch_pool)); } else +#endif SVN_ERR(make_conflict_versions(&left, &right, local_abspath, merge_left_node_kind, merge_right_node_kind, @@ -1519,16 +1093,18 @@ record_tree_conflict(merge_cmd_baton_t * update_add notification */ static svn_error_t * -record_update_add(merge_cmd_baton_t *merge_b, +record_update_add(merge_apply_processor_baton_t *merge_b, const char *local_abspath, svn_node_kind_t kind, svn_boolean_t notify_replaced, apr_pool_t *scratch_pool) { +#if TODO_STORE_PATH if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) { store_path(merge_b->merged_abspaths, local_abspath); } +#endif if (merge_b->notify_func) { @@ -1551,17 +1127,19 @@ record_update_add(merge_cmd_baton_t *mer /* Record the update for future processing and produce the update_update notification */ static svn_error_t * -record_update_update(merge_cmd_baton_t *merge_b, +record_update_update(merge_apply_processor_baton_t *merge_b, const char *local_abspath, svn_node_kind_t kind, svn_wc_notify_state_t content_state, svn_wc_notify_state_t prop_state, apr_pool_t *scratch_pool) { +#if TODO_STORE_PATH if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) { store_path(merge_b->merged_abspaths, local_abspath); } +#endif if (merge_b->notify_func) { @@ -1583,12 +1161,13 @@ record_update_update(merge_cmd_baton_t * /* Record the delete for future processing and for (later) producing the update_delete notification */ static svn_error_t * -record_update_delete(merge_cmd_baton_t *merge_b, +record_update_delete(merge_apply_processor_baton_t *merge_b, struct merge_dir_baton_t *parent_db, const char *local_abspath, svn_node_kind_t kind, apr_pool_t *scratch_pool) { +#if TODO_STORE_PATH /* Update the lists of merged, skipped, tree-conflicted and added paths. */ if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) @@ -1629,6 +1208,7 @@ record_update_delete(merge_cmd_baton_t * } } } +#endif return SVN_NO_ERROR; } @@ -1636,7 +1216,7 @@ record_update_delete(merge_cmd_baton_t * /* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd might make them a 'R'eplace. */ static svn_error_t * -handle_pending_notifications(merge_cmd_baton_t *merge_b, +handle_pending_notifications(merge_apply_processor_baton_t *merge_b, struct merge_dir_baton_t *db, apr_pool_t *scratch_pool) { @@ -1672,7 +1252,7 @@ handle_pending_notifications(merge_cmd_b ancestors of operational merges */ static svn_error_t * -mark_dir_edited(merge_cmd_baton_t *merge_b, +mark_dir_edited(merge_apply_processor_baton_t *merge_b, struct merge_dir_baton_t *db, const char *local_abspath, apr_pool_t *scratch_pool) @@ -1726,11 +1306,13 @@ mark_dir_edited(merge_cmd_baton_t *merge scratch_pool); } +#if TODO_STORE_PATH if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) { store_path(merge_b->skipped_abspaths, local_abspath); } +#endif } else if (db->tree_conflict_reason != CONFLICT_REASON_NONE) { @@ -1755,7 +1337,7 @@ mark_dir_edited(merge_cmd_baton_t *merge ancestors of operational merges */ static svn_error_t * -mark_file_edited(merge_cmd_baton_t *merge_b, +mark_file_edited(merge_apply_processor_baton_t *merge_b, struct merge_file_baton_t *fb, const char *local_abspath, apr_pool_t *scratch_pool) @@ -1805,11 +1387,13 @@ mark_file_edited(merge_cmd_baton_t *merg scratch_pool); } +#if TODO_STORE_PATH if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) { store_path(merge_b->skipped_abspaths, local_abspath); } +#endif } else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE) { @@ -1848,7 +1432,7 @@ merge_file_opened(void **new_file_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_dir_baton_t *pdb = dir_baton; struct merge_file_baton_t *fb; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, @@ -2097,7 +1681,7 @@ merge_file_changed(const char *relpath, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_file_baton_t *fb = file_baton; svn_client_ctx_t *ctx = merge_b->ctx; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, @@ -2154,11 +1738,13 @@ merge_file_changed(const char *relpath, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, scratch_pool)); +#if TODO_STORE_PATH if (property_state == svn_wc_notify_state_conflicted) { alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, merge_b->pool); } +#endif } /* Easy out: We are only applying mergeinfo differences. */ @@ -2218,12 +1804,14 @@ merge_file_changed(const char *relpath, ctx->cancel_baton, scratch_pool)); +#if TODO_STORE_PATH if (content_outcome == svn_wc_merge_conflict || property_state == svn_wc_notify_state_conflicted) { alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, merge_b->pool); } +#endif if (content_outcome == svn_wc_merge_conflict) text_state = svn_wc_notify_state_conflicted; @@ -2273,7 +1861,7 @@ merge_file_added(const char *relpath, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_file_baton_t *fb = file_baton; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, relpath, scratch_pool); @@ -2304,12 +1892,14 @@ merge_file_added(const char *relpath, return SVN_NO_ERROR; } +#if TODO_STORE_PATH if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge) && ( !fb->parent_baton || !fb->parent_baton->added)) { /* Store the roots of added subtrees */ store_path(merge_b->added_abspaths, local_abspath); } +#endif if (!merge_b->dry_run) { @@ -2337,11 +1927,13 @@ merge_file_added(const char *relpath, pristine_props = right_props; /* Includes last_* information */ new_props = NULL; /* No local changes */ +#if TODO_STORE_PATH if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO)) { alloc_and_store_path(&merge_b->paths_with_new_mergeinfo, local_abspath, merge_b->pool); } +#endif } else { @@ -2379,8 +1971,10 @@ merge_file_added(const char *relpath, merge_b->ctx->cancel_baton, scratch_pool)); +#if TODO_USE_SLEEP /* Caller must call svn_sleep_for_timestamps() */ *merge_b->use_sleep = TRUE; +#endif } SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file, @@ -2514,7 +2108,7 @@ merge_file_deleted(const char *relpath, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_file_baton_t *fb = file_baton; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, relpath, scratch_pool); @@ -2555,9 +2149,11 @@ merge_file_deleted(const char *relpath, { if (same) { +#if TODO_STORE_PATH /* Note that we checked this file */ store_path(fb->parent_baton->delete_state->compared_abspaths, local_abspath); +#endif } else { @@ -2577,9 +2173,11 @@ merge_file_deleted(const char *relpath, NULL, NULL /* no notify */, scratch_pool)); +#if TODO_STORE_PATH /* Record that we might have deleted mergeinfo */ alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo, local_abspath, merge_b->pool); +#endif /* And notify the deletion */ SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath, @@ -2631,7 +2229,7 @@ merge_dir_opened(void **new_dir_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_dir_baton_t *db; struct merge_dir_baton_t *pdb = parent_dir_baton; @@ -3029,7 +2627,7 @@ merge_dir_changed(const char *relpath, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_dir_baton_t *db = dir_baton; const apr_array_header_t *props; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, @@ -3077,11 +2675,13 @@ merge_dir_changed(const char *relpath, ctx->cancel_func, ctx->cancel_baton, scratch_pool)); +#if TODO_STORE_PATH if (prop_state == svn_wc_notify_state_conflicted) { alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, merge_b->pool); } +#endif if (prop_state == svn_wc_notify_state_conflicted || prop_state == svn_wc_notify_state_merged @@ -3116,7 +2716,7 @@ merge_dir_added(const char *relpath, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_dir_baton_t *db = dir_baton; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, relpath, scratch_pool); @@ -3144,12 +2744,14 @@ merge_dir_added(const char *relpath, && ! merge_b->record_only /* Skip details from merge_open_dir() */ ); +#if TODO_STORE_PATH if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge) && ( !db->parent_baton || !db->parent_baton->added)) { /* Store the roots of added subtrees */ store_path(merge_b->added_abspaths, local_abspath); } +#endif if (merge_b->same_repos) { @@ -3194,11 +2796,13 @@ merge_dir_added(const char *relpath, scratch_pool)); } +#if TODO_STORE_PATH if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO)) { alloc_and_store_path(&merge_b->paths_with_new_mergeinfo, local_abspath, merge_b->pool); } +#endif } else { @@ -3230,11 +2834,13 @@ merge_dir_added(const char *relpath, merge_b->ctx->cancel_func, merge_b->ctx->cancel_baton, scratch_pool)); +#if TODO_STORE_PATH if (prop_state == svn_wc_notify_state_conflicted) { alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, merge_b->pool); } +#endif } return SVN_NO_ERROR; @@ -3283,7 +2889,7 @@ merge_dir_deleted(const char *relpath, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_dir_baton_t *db = dir_baton; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, relpath, scratch_pool); @@ -3428,8 +3034,10 @@ merge_dir_deleted(const char *relpath, if (working_props && svn_hash_gets(working_props, SVN_PROP_MERGEINFO)) { +#if TODO_STORE_PATH alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo, local_abspath, merge_b->pool); +#endif } SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath, @@ -3455,7 +3063,7 @@ merge_dir_closed(const char *relpath, const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_dir_baton_t *db = dir_baton; SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool)); @@ -3482,7 +3090,7 @@ merge_node_absent(const char *relpath, const svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - merge_cmd_baton_t *merge_b = processor->baton; + merge_apply_processor_baton_t *merge_b = processor->baton; struct merge_dir_baton_t *db = dir_baton; const char *local_abspath = svn_dirent_join(merge_b->target->abspath, @@ -3495,15 +3103,41 @@ merge_node_absent(const char *relpath, return SVN_NO_ERROR; } -/* Return a diff processor that will apply the merge to the WC. - */ -static svn_diff_tree_processor_t * -merge_apply_processor(merge_cmd_baton_t *merge_cmd_baton, - apr_pool_t *result_pool) +svn_diff_tree_processor_t * +svn_client__apply_processor_create(const svn_client__merge_target_t *target, + const svn_client__merge_source_t *source, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + svn_boolean_t same_repos, + svn_boolean_t force_delete, + svn_boolean_t record_only, + svn_boolean_t dry_run, + const char *diff3_cmd, + const apr_array_header_t *merge_options, + const apr_array_header_t *ext_patterns, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool, + apr_pool_t *result_pool) { svn_diff_tree_processor_t *merge_processor; - merge_processor = svn_diff__tree_processor_create(merge_cmd_baton, + merge_apply_processor_baton_t *baton = + apr_pcalloc(result_pool, sizeof(*baton)); + + baton->target = target; + baton->merge_source = *source; + baton->diff3_cmd = diff3_cmd; + baton->merge_options = merge_options; + baton->ext_patterns = ext_patterns; + baton->same_repos = same_repos; + baton->force_delete = force_delete; + baton->record_only = record_only; + baton->dry_run = dry_run; + baton->notify_func = notify_func; + baton->notify_baton = notify_baton; + baton->ctx = ctx; + + merge_processor = svn_diff__tree_processor_create(baton, result_pool); merge_processor->dir_opened = merge_dir_opened; @@ -3525,6 +3159,7 @@ merge_apply_processor(merge_cmd_baton_t /* Initialize minimal dir baton to allow calculating 'R'eplace from 'D'elete + 'A'dd. */ +/* TODO: doesn't yet works */ static void * open_dir_for_replace_single_file(apr_pool_t *result_pool) { @@ -3537,9508 +3172,3 @@ open_dir_for_replace_single_file(apr_poo return dir_baton; } - -/*-----------------------------------------------------------------------*/ - -/*** Merge Notification ***/ - - -/* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If - PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO - where child->abspath == PATH is considered PATH's ancestor. If FALSE, - then child->abspath must be a proper ancestor of PATH. - - CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first - order of path. */ -static svn_client__merge_path_t * -find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo, - svn_boolean_t path_is_own_ancestor, - const char *local_abspath) -{ - int i; - - SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL); - - for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--) - { - svn_client__merge_path_t *child = - APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); - - if (svn_dirent_is_ancestor(child->abspath, local_abspath) - && (path_is_own_ancestor - || strcmp(child->abspath, local_abspath) != 0)) - return child; - } - return NULL; -} - -/* Find the highest level path in a merge target (possibly the merge target - itself) to use in a merge notification header. - - Return the svn_client__merge_path_t * representing the most distant - ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said - ancestor's first remaining ranges element (per the REMAINING_RANGES - member of the ancestor) intersect with the first remaining ranges element - for every intermediate ancestor svn_client__merge_path_t * of - LOCAL_ABSPATH. If no such ancestor is found return NULL. - - If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO - represent a forward merge, then set *START to the oldest revision found - in any of the intersecting ancestors and *END to the youngest revision - found. If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO - represent a reverse merge, then set *START to the youngest revision - found and *END to the oldest revision found. If no ancestors are found - then set *START and *END to SVN_INVALID_REVNUM. - - If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO - where child->abspath == PATH is considered PATH's ancestor. If FALSE, - then child->abspath must be a proper ancestor of PATH. - - See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more - information. */ -static svn_client__merge_path_t * -find_nearest_ancestor_with_intersecting_ranges( - svn_revnum_t *start, - svn_revnum_t *end, - const apr_array_header_t *children_with_mergeinfo, - svn_boolean_t path_is_own_ancestor, - const char *local_abspath) -{ - int i; - svn_client__merge_path_t *nearest_ancestor = NULL; - - *start = SVN_INVALID_REVNUM; - *end = SVN_INVALID_REVNUM; - - SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL); - - for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--) - { - svn_client__merge_path_t *child = - APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); - - if (svn_dirent_is_ancestor(child->abspath, local_abspath) - && (path_is_own_ancestor - || strcmp(child->abspath, local_abspath) != 0)) - { - if (nearest_ancestor == NULL) - { - /* Found an ancestor. */ - nearest_ancestor = child; - - if (child->remaining_ranges) - { - svn_merge_range_t *r1 = APR_ARRAY_IDX( - child->remaining_ranges, 0, svn_merge_range_t *); - *start = r1->start; - *end = r1->end; - } - else - { - /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH - is inside an absent subtree in the merge target. */ - *start = SVN_INVALID_REVNUM; - *end = SVN_INVALID_REVNUM; - break; - } - } - else - { - /* We'e found another ancestor for LOCAL_ABSPATH. Do its - first remaining range intersect with the previously - found ancestor? */ - svn_merge_range_t *r1 = - APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0, - svn_merge_range_t *); - svn_merge_range_t *r2 = - APR_ARRAY_IDX(child->remaining_ranges, 0, - svn_merge_range_t *); - - if (r1 && r2) - { - svn_merge_range_t range1; - svn_merge_range_t range2; - svn_boolean_t reverse_merge = r1->start > r2->end; - - /* Flip endpoints if this is a reverse merge. */ - if (reverse_merge) - { - range1.start = r1->end; - range1.end = r1->start; - range2.start = r2->end; - range2.end = r2->start; - } - else - { - range1.start = r1->start; - range1.end = r1->end; - range2.start = r2->start; - range2.end = r2->end; - } - - if (range1.start < range2.end && range2.start < range1.end) - { - *start = reverse_merge ? - MAX(r1->start, r2->start) : MIN(r1->start, r2->start); - *end = reverse_merge ? - MIN(r1->end, r2->end) : MAX(r1->end, r2->end); - nearest_ancestor = child; - } - } - } - } - } - return nearest_ancestor; -} - -/* Notify that we're starting to record mergeinfo for the merge of the - * revision range RANGE into TARGET_ABSPATH. RANGE should be null if the - * merge sources are not from the same URL. - * - * This calls the client's notification receiver (as found in the client - * context), with a WC abspath. - */ -static void -notify_mergeinfo_recording(const char *target_abspath, - const svn_merge_range_t *range, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - if (ctx->notify_func2) - { - svn_wc_notify_t *n = svn_wc_create_notify( - target_abspath, svn_wc_notify_merge_record_info_begin, pool); - - n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL; - ctx->notify_func2(ctx->notify_baton2, n, pool); - } -} - -/* Notify that we're completing the merge into TARGET_ABSPATH. - * - * This calls the client's notification receiver (as found in the client - * context), with a WC abspath. - */ -static void -notify_merge_completed(const char *target_abspath, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - if (ctx->notify_func2) - { - svn_wc_notify_t *n - = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed, - pool); - ctx->notify_func2(ctx->notify_baton2, n, pool); - } -} - - -/* Remove merge source gaps from range used for merge notifications. - See https://issues.apache.org/jira/browse/SVN-4138 - - If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a - single range (see the implicit_src_gap member of merge_cmd_baton_t). - RANGE describes a (possibly reverse) merge. - - If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with - the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range - from *RANGE. */ -static void -remove_source_gap(svn_merge_range_t *range, - apr_array_header_t *implicit_src_gap) -{ - if (implicit_src_gap) - { - svn_merge_range_t *gap_range = - APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *); - if (range->start < range->end) - { - if (gap_range->start == range->start) - range->start = gap_range->end; - } - else /* Reverse merge */ - { - if (gap_range->start == range->end) - range->end = gap_range->end; - } - } -} - -/* Notify that we're starting a merge - * - * This calls the client's notification receiver (as found in the client - * context), with a WC abspath. - */ -static void -notify_merge_begin(struct notify_begin_state_t *notify_begin_state, - const char *local_abspath, - svn_boolean_t delete_action, - apr_pool_t *scratch_pool) -{ - merge_cmd_baton_t *merge_b = notify_begin_state->merge_b; - svn_wc_notify_t *notify; - svn_merge_range_t n_range = - {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE}; - const char *notify_abspath; - - if (! notify_begin_state->notify_func2) - return; - - /* If our merge sources are ancestors of one another... */ - if (merge_b->merge_source.ancestral) - { - const svn_client__merge_path_t *child; - /* Find LOCAL_ABSPATH's nearest ancestor in - CHILDREN_WITH_MERGEINFO. Normally we consider a child in - CHILDREN_WITH_MERGEINFO representing PATH to be an - ancestor of PATH, but if this is a deletion of PATH then the - notification must be for a proper ancestor of PATH. This ensures - we don't get notifications like: - - --- Merging rX into 'PARENT/CHILD' - D PARENT/CHILD - - But rather: - - --- Merging rX into 'PARENT' - D PARENT/CHILD - */ - - child = find_nearest_ancestor_with_intersecting_ranges( - &(n_range.start), &(n_range.end), - merge_b->children_with_mergeinfo, - ! delete_action, local_abspath); - - if (!child && delete_action) - { - /* Triggered by file replace in single-file-merge */ - child = find_nearest_ancestor(merge_b->children_with_mergeinfo, - TRUE, local_abspath); - } - - assert(child != NULL); /* Should always find the merge anchor */ - - if (! child) - return; - - if (notify_begin_state->last_abspath != NULL - && strcmp(child->abspath, notify_begin_state->last_abspath) == 0) - { - /* Don't notify the same merge again */ - return; - } - - notify_begin_state->last_abspath = child->abspath; - - if (child->absent || child->remaining_ranges->nelts == 0 - || !SVN_IS_VALID_REVNUM(n_range.start)) - { - /* No valid information for an header */ - return; - } - - notify_abspath = child->abspath; - } - else - { - if (notify_begin_state->last_abspath) - return; /* already notified */ - - notify_abspath = merge_b->target->abspath; - /* Store something in last_abspath. Any value would do */ - notify_begin_state->last_abspath = merge_b->target->abspath; - } - - notify = svn_wc_create_notify(notify_abspath, - merge_b->same_repos - ? svn_wc_notify_merge_begin - : svn_wc_notify_foreign_merge_begin, - scratch_pool); - - if (SVN_IS_VALID_REVNUM(n_range.start)) - { - /* If the merge source has a gap, then don't mention - those gap revisions in the notification. */ - remove_source_gap(&n_range, merge_b->implicit_src_gap); - notify->merge_range = &n_range; - } - else - { - notify->merge_range = NULL; - } - - notify_begin_state->notify_func2(notify_begin_state->notify_baton2, notify, - scratch_pool); -} - -/* Our notification callback, that adds a 'begin' notification */ -static void -notify_merging(void *baton, - const svn_wc_notify_t *notify, - apr_pool_t *pool) -{ - struct notify_begin_state_t *b = baton; - - notify_merge_begin(b, notify->path, - notify->action == svn_wc_notify_update_delete, - pool); - - b->notify_func2(b->notify_baton2, notify, pool); -} - -/* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple - * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE. - * If REV1 is equal to REV2, the result is an empty rangelist, otherwise - * REV1 must be less than REV2. - * - * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non- - * inheritable input ranges as if they were inheritable. If it is TRUE, the - * effect is to discard any non-inheritable input ranges. Therefore the - * ranges in *OUT_RANGELIST will always be inheritable. */ -static svn_error_t * -rangelist_intersect_range(svn_rangelist_t **out_rangelist, - const svn_rangelist_t *in_rangelist, - svn_revnum_t rev1, - svn_revnum_t rev2, - svn_boolean_t consider_inheritance, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - SVN_ERR_ASSERT(rev1 <= rev2); - - if (rev1 < rev2) - { - svn_rangelist_t *simple_rangelist = - svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool); - - SVN_ERR(svn_rangelist_intersect(out_rangelist, - simple_rangelist, in_rangelist, - consider_inheritance, result_pool)); - } - else - { - *out_rangelist = apr_array_make(result_pool, 0, - sizeof(svn_merge_range_t *)); - } - return SVN_NO_ERROR; -} - -/* Helper for fix_deleted_subtree_ranges(). Like fix_deleted_subtree_ranges() - this function should only be called when honoring mergeinfo. - - CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from - fix_deleted_subtree_ranges() -- see that function for more information on - each. - - If PARENT is not the merge target then PARENT must have already have been - processed by this function as a child. Specifically, this means that - PARENT->REMAINING_RANGES must already be populated -- it can be an empty - rangelist but cannot be NULL. - - PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1 - and REVISION2. - - Since this function is only invoked for subtrees of the merge target, the - guarantees afforded by normalize_merge_sources() don't apply - see the - 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file. - Therefore it is possible that PRIMARY_URL@REVISION1 and - PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of - history. The purpose of this helper is to identify these cases of broken - history and adjust CHILD->REMAINING_RANGES in such a way we don't later try - to describe nonexistent path/revisions to the merge report editor -- see - drive_merge_report_editor(). - - If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken - line of history then do nothing and leave CHILD->REMAINING_RANGES as-is. - - If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then - there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES - equal to PARENT->REMAINING_RANGES. This will cause the subtree to - effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...' - in drive_merge_report_editor()'s doc string. - - If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the - subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which - PRIMARY_URL doesn't exist and set that subset equal to - PARENT->REMAINING_RANGES' intersection with that non-existent range. Why? - Because this causes CHILD->REMAINING_RANGES to be identical to - PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at - which PRIMARY_URL doesn't exist. As mentioned above this means that - drive_merge_report_editor() won't attempt to describe these non-existent - subtree path/ranges to the reporter (which would break the merge). - - If the preceding paragraph wasn't terribly clear then what follows spells - out this function's behavior a bit more explicitly: - - For forward merges (REVISION1 < REVISION2) - - If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then - find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted. Leave - the subset of CHILD->REMAINING_RANGES that intersects with - REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES - that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES' - intersection with (N - 1):REVISION2. - - If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does, - then find the revision 'M' in which PRIMARY_URL@REVISION2 came into - existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with - (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES - that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES' - intersection with REVISION1:(M - 1). - - For reverse merges (REVISION1 > REVISION2) - - If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then - find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence. - Leave the subset of CHILD->REMAINING_RANGES that intersects with - REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES - that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES' - intersection with (N - 1):REVISION1. - - If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does, - then find the revision 'M' in which PRIMARY_URL@REVISION2 came into - existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with - REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES - that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES' - intersection with REVISION1:(M - 1). - - SCRATCH_POOL is used for all temporary allocations. Changes to CHILD are - allocated in RESULT_POOL. */ -static svn_error_t * -adjust_deleted_subtree_ranges(svn_client__merge_path_t *child, - svn_client__merge_path_t *parent, - svn_revnum_t revision1, - svn_revnum_t revision2, - const char *primary_url, - svn_ra_session_t *ra_session, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_boolean_t is_rollback = revision2 < revision1; - svn_revnum_t younger_rev = is_rollback ? revision1 : revision2; - svn_revnum_t peg_rev = younger_rev; - svn_revnum_t older_rev = is_rollback ? revision2 : revision1; - apr_array_header_t *segments; - svn_error_t *err; - - SVN_ERR_ASSERT(parent->remaining_ranges); - - err = svn_client__repos_location_segments(&segments, ra_session, - primary_url, peg_rev, - younger_rev, older_rev, ctx, - scratch_pool); - - if (err) - { - const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */ - svn_node_kind_t kind; - - if (err->apr_err != SVN_ERR_FS_NOT_FOUND) - return svn_error_trace(err); - - svn_error_clear(err); - - /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev - exists, if neither exist then the editor can simply ignore this - subtree. */ - - SVN_ERR(svn_ra_get_path_relative_to_session( - ra_session, &rel_source_path, primary_url, scratch_pool)); - - SVN_ERR(svn_ra_check_path(ra_session, rel_source_path, - older_rev, &kind, scratch_pool)); - if (kind == svn_node_none) - { - /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist, - so there is nothing to merge. Set CHILD->REMAINING_RANGES - identical to PARENT's. */ - child->remaining_ranges = - svn_rangelist_dup(parent->remaining_ranges, scratch_pool); - } - else - { - svn_rangelist_t *deleted_rangelist; - svn_revnum_t rev_primary_url_deleted; - - /* PRIMARY_URL@older_rev exists, so it was deleted at some - revision prior to peg_rev, find that revision. */ - SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path, - older_rev, younger_rev, - &rev_primary_url_deleted, - scratch_pool)); - - /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't, - so svn_ra_get_deleted_rev() should always find the revision - PRIMARY_URL@older_rev was deleted. */ - SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted)); - - /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and - PARENT->REMAINING_RANGES so both will work with the - svn_rangelist_* APIs below. */ - if (is_rollback) - { - /* svn_rangelist_reverse operates in place so it's safe - to use our scratch_pool. */ - SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, - scratch_pool)); - SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, - scratch_pool)); - } - - /* Find the intersection of CHILD->REMAINING_RANGES with the - range over which PRIMARY_URL@older_rev exists (ending at - the youngest revision at which it still exists). */ - SVN_ERR(rangelist_intersect_range(&child->remaining_ranges, - child->remaining_ranges, - older_rev, - rev_primary_url_deleted - 1, - FALSE, - scratch_pool, scratch_pool)); - - /* Merge into CHILD->REMAINING_RANGES the intersection of - PARENT->REMAINING_RANGES with the range beginning when - PRIMARY_URL@older_rev was deleted until younger_rev. */ - SVN_ERR(rangelist_intersect_range(&deleted_rangelist, - parent->remaining_ranges, - rev_primary_url_deleted - 1, - peg_rev, - FALSE, - scratch_pool, scratch_pool)); - SVN_ERR(svn_rangelist_merge2(child->remaining_ranges, - deleted_rangelist, scratch_pool, - scratch_pool)); - - /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES - to reverse order if necessary. */ - if (is_rollback) - { - SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, - scratch_pool)); - SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, - scratch_pool)); - } - } - } - else /* PRIMARY_URL@peg_rev exists. */ - { - svn_rangelist_t *non_existent_rangelist; - svn_location_segment_t *segment = - APR_ARRAY_IDX(segments, (segments->nelts - 1), - svn_location_segment_t *); - - /* We know PRIMARY_URL@peg_rev exists as the call to - svn_client__repos_location_segments() succeeded. If there is only - one segment that starts at oldest_rev then we know that - PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken - line of history, so there is nothing more to adjust in - CHILD->REMAINING_RANGES. */ - if (segment->range_start == older_rev) - { - return SVN_NO_ERROR; - } - - /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and - PARENT->REMAINING_RANGES so both will work with the - svn_rangelist_* APIs below. */ - if (is_rollback) - { - SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, - scratch_pool)); - SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, - scratch_pool)); - } - - /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL - exists. Since segment doesn't span older_rev:peg_rev we know - PRIMARY_URL@peg_rev didn't come into existence until - segment->range_start + 1. */ - SVN_ERR(rangelist_intersect_range(&child->remaining_ranges, - child->remaining_ranges, - segment->range_start, peg_rev, - FALSE, scratch_pool, scratch_pool)); - - /* Merge into CHILD->REMAINING_RANGES the intersection of - PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev - came into existence. */ - SVN_ERR(rangelist_intersect_range(&non_existent_rangelist, - parent->remaining_ranges, - older_rev, segment->range_start, - FALSE, scratch_pool, scratch_pool)); - SVN_ERR(svn_rangelist_merge2(child->remaining_ranges, - non_existent_rangelist, scratch_pool, - scratch_pool)); - - /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES - to reverse order if necessary. */ - if (is_rollback) - { - SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, - scratch_pool)); - SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, - scratch_pool)); - } - } - - /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */ - child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges, - result_pool); - return SVN_NO_ERROR; -} - -/* Helper for do_directory_merge(). - - 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->loc1 and SOURCE->loc2. - - Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't - later try to describe invalid paths in drive_merge_report_editor(). - This function is just a thin wrapper around - adjust_deleted_subtree_ranges(), which see for further details. - - SCRATCH_POOL is used for all temporary allocations. Changes to - CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL. -*/ -static svn_error_t * -fix_deleted_subtree_ranges(const svn_client__merge_source_t *source, - const svn_client__merge_target_t *target, - svn_ra_session_t *ra_session, - apr_array_header_t *children_with_mergeinfo, - 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); - svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev; - - assert(session_url_is(ra_session, - (is_rollback ? source->loc1 : source->loc2)->url, - scratch_pool)); - - /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so - start at index 1 to examine only subtrees. */ - for (i = 1; 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_client__merge_path_t *parent; - svn_rangelist_t *deleted_rangelist, *added_rangelist; - - SVN_ERR_ASSERT(child); - if (child->absent) - continue; - - svn_pool_clear(iterpool); - - /* Find CHILD's parent. */ - parent = find_nearest_ancestor(children_with_mergeinfo, - FALSE, child->abspath); - - /* Since CHILD is a subtree then its parent must be in - CHILDREN_WITH_MERGEINFO, see the global comment - 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */ - SVN_ERR_ASSERT(parent); - - /* If this is a reverse merge reorder CHILD->REMAINING_RANGES - so it will work with the svn_rangelist_diff API. */ - if (is_rollback) - { - SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool)); - SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool)); - } - - SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist, - child->remaining_ranges, - parent->remaining_ranges, - TRUE, iterpool)); - - if (is_rollback) - { - SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool)); - SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool)); - } - - /* If CHILD is the merge target we then know that SOURCE is provided - by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE - NORMALIZATION'. Due to this normalization we know that SOURCE - describes an unbroken line of history such that the entire range - described by SOURCE can potentially be merged to CHILD. - - But if CHILD is a subtree we don't have the same guarantees about - SOURCE as we do for the merge target. SOURCE->loc1 and/or - SOURCE->loc2 might not exist. - - If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES - such that we don't later try to describe invalid subtrees in - drive_merge_report_editor(), as that will break the merge. - If CHILD has the same remaining ranges as PARENT however, then - there is no need to make these adjustments, since - drive_merge_report_editor() won't attempt to describe CHILD in this - case, see the 'Note' in drive_merge_report_editor's docstring. */ - if (deleted_rangelist->nelts || added_rangelist->nelts) - { - const char *child_primary_source_url; - const char *child_repos_src_path = - 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. */ - SVN_ERR_ASSERT(child_repos_src_path); - - child_primary_source_url = - svn_path_url_add_component2((source->loc1->rev < source->loc2->rev) - ? source->loc2->url : source->loc1->url, - child_repos_src_path, iterpool); - - SVN_ERR(adjust_deleted_subtree_ranges(child, parent, - source->loc1->rev, - source->loc2->rev, - child_primary_source_url, - ra_session, - ctx, result_pool, iterpool)); - } - } - - svn_pool_destroy(iterpool); - return SVN_NO_ERROR; -} - -/*-----------------------------------------------------------------------*/ - -/*** Determining What Remains To Be Merged ***/ - -/* Get explicit and/or implicit mergeinfo for the working copy path - TARGET_ABSPATH. - - If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO - to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by - INHERIT. - - If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO - to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history). - - If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and - *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be - removed from *RECORDED_MERGEINFO. - - If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO - is inherited rather than explicit. If RECORDED_MERGEINFO is NULL then - INHERITED is ignored. - - - If IMPLICIT_MERGEINFO is not NULL then START and END are limits on - the natural history sought, must both be valid revision numbers, and - START must be greater than END. If TARGET_ABSPATH's base revision - is older than START, then the base revision is used as the younger - bound in place of START. - - RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH - lives. It may be temporarily reparented as needed by this function. - - Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL. - Use SCRATCH_POOL for any temporary allocations. */ -static svn_error_t * -get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo, - svn_mergeinfo_t *implicit_mergeinfo, - svn_boolean_t *inherited, - svn_mergeinfo_inheritance_t inherit, - svn_ra_session_t *ra_session, - const char *target_abspath, - svn_revnum_t start, - svn_revnum_t end, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - /* First, we get the real mergeinfo. */ - if (recorded_mergeinfo) - { - SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo, - inherited, - NULL /* from_repos */, - FALSE, - inherit, ra_session, - target_abspath, - ctx, result_pool)); - } - - if (implicit_mergeinfo) - { - svn_client__pathrev_t *target; - - /* Assert that we have sane input. */ - SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end) - && (start > end)); - - /* Retrieve the origin (original_*) of the node, or just the - url if the node was not copied. */ - SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx, - scratch_pool, scratch_pool)); - - if (! target) - { - /* We've been asked to operate on a locally added target, so its - * implicit mergeinfo is empty. */ - *implicit_mergeinfo = apr_hash_make(result_pool); - } - else if (target->rev <= end) - { - /* We're asking about a range outside our natural history - altogether. That means our implicit mergeinfo is empty. */ - *implicit_mergeinfo = apr_hash_make(result_pool); - } - else - { - /* Fetch so-called "implicit mergeinfo" (that is, natural - history). */ - - /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future. - TARGET_ABSPATH might not even exist, and even if it does the - working copy is *at* TARGET_REV so its implicit history ends - at TARGET_REV! */ - if (target->rev < start) - start = target->rev; - - /* Fetch the implicit mergeinfo. */ - SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo, - NULL, - target, start, end, - ra_session, ctx, - result_pool)); - } - } /*if (implicit_mergeinfo) */ - - return SVN_NO_ERROR; -} - -/* Helper for ensure_implicit_mergeinfo(). - - PARENT, CHILD, REVISION1, REVISION2 and CTX - are all cascaded from the arguments of the same names in - ensure_implicit_mergeinfo(). PARENT and CHILD must both exist, i.e. - this function should never be called where CHILD is the merge target. - - If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server. - - Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from - PARENT->IMPLICIT_MERGEINFO. CHILD->IMPLICIT_MERGEINFO is allocated - in RESULT_POOL. - - RA_SESSION is an RA session open to the repository that contains CHILD. - It may be temporarily reparented by this function. - */ -static svn_error_t * -inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent, - svn_client__merge_path_t *child, - svn_revnum_t revision1, - svn_revnum_t revision2, - svn_ra_session_t *ra_session, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const char *path_diff; - - /* This only works on subtrees! */ - SVN_ERR_ASSERT(parent); - SVN_ERR_ASSERT(child); - - /* While PARENT must exist, it is possible we've deferred - getting its implicit mergeinfo. If so get it now. */ - if (!parent->implicit_mergeinfo) - SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo), - NULL, svn_mergeinfo_inherited, - ra_session, child->abspath, - MAX(revision1, revision2), - MIN(revision1, revision2), - ctx, result_pool, scratch_pool)); - - /* Let CHILD inherit PARENT's implicit mergeinfo. */ - - path_diff = svn_dirent_is_child(parent->abspath, child->abspath, - scratch_pool); - /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */ - SVN_ERR_ASSERT(path_diff); - - SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo( - &child->implicit_mergeinfo, parent->implicit_mergeinfo, - path_diff, result_pool, scratch_pool)); - child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo, - result_pool); - return SVN_NO_ERROR; -} - -/* Helper of filter_merged_revisions(). - - If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get - it now, allocating it in RESULT_POOL. If CHILD_INHERITS_PARENT is true - then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from - PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use - SCRATCH_POOL for all temporary allocations. - - RA_SESSION is an RA session open to the repository that contains CHILD. - It may be temporarily reparented by this function. - - PARENT, CHILD, REVISION1, REVISION2 and - CTX are all cascaded from the arguments of the same name in - filter_merged_revisions() and the same conditions for that function - hold here. */ -static svn_error_t * -ensure_implicit_mergeinfo(svn_client__merge_path_t *parent, - svn_client__merge_path_t *child, - svn_boolean_t child_inherits_parent, - svn_revnum_t revision1, - svn_revnum_t revision2, - svn_ra_session_t *ra_session, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then - contact the server to get it. */ - - if (child->implicit_mergeinfo) - return SVN_NO_ERROR; - - if (child_inherits_parent) - SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent, - child, - revision1, - revision2, - ra_session, - ctx, - result_pool, - scratch_pool)); - else - SVN_ERR(get_full_mergeinfo(NULL, - &(child->implicit_mergeinfo), - NULL, svn_mergeinfo_inherited, - ra_session, child->abspath, - MAX(revision1, revision2), - MIN(revision1, revision2), - ctx, result_pool, scratch_pool)); - - return SVN_NO_ERROR; -} - -/* Helper for calculate_remaining_ranges(). - - Initialize CHILD->REMAINING_RANGES to a rangelist representing the - requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to - CHILD->ABSPATH. - - For forward merges remove any ranges from CHILD->REMAINING_RANGES that - have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or - CHILD->IMPLICIT_MERGEINFO. For reverse merges remove any ranges from - CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH - per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO. If we have deferred - obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for - these calculations, then get it from the server, allocating it in - RESULT_POOL. - - CHILD represents a working copy path which is the merge target or one of - the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise - ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. - - If the function needs to consider CHILD->IMPLICIT_MERGEINFO and - CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the - mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact - the repository for CHILD->IMPLICIT_MERGEINFO. - - NOTE: If PARENT is present then this function must have previously been - called for PARENT, i.e. if populate_remaining_ranges() is calling this - function for a set of svn_client__merge_path_t* the calls must be made - in depth-first order. - - MERGEINFO_PATH is the merge source relative to the repository root. - - REVISION1 and REVISION2 describe the merge range requested from - MERGEINFO_PATH. - - 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. - - NOTE: This should only be called when honoring mergeinfo. - - NOTE: Like calculate_remaining_ranges() if PARENT is present then this - function must have previously been called for PARENT. -*/ -static svn_error_t * -filter_merged_revisions(svn_client__merge_path_t *parent, - svn_client__merge_path_t *child, - const char *mergeinfo_path, - svn_rangelist_t *target_rangelist, - svn_revnum_t revision1, - svn_revnum_t revision2, - svn_boolean_t child_inherits_implicit, - svn_ra_session_t *ra_session, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_rangelist_t *requested_rangelist, - *target_implicit_rangelist, *explicit_rangelist; - - /* Convert REVISION1 and REVISION2 to a rangelist. - - Note: Talking about a requested merge range's inheritability - doesn't make much sense, but as we are using svn_merge_range_t - to describe it we need to pick *something*. Since all the - rangelist manipulations in this function either don't consider - inheritance by default or we are requesting that they don't (i.e. - svn_rangelist_remove and svn_rangelist_intersect) then we could - set the inheritability as FALSE, it won't matter either way. */ - requested_rangelist = svn_rangelist__initialize(revision1, revision2, - TRUE, scratch_pool); - - /* Now filter out revisions that have already been merged to CHILD. */ - - if (revision1 > revision2) /* This is a reverse merge. */ - { - svn_rangelist_t *added_rangelist, *deleted_rangelist; - - /* The revert range and will need to be reversed for - our svn_rangelist_* APIs to work properly. */ - SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool)); - - /* 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 - represented by CHILD's explicit or inherited mergeinfo. - - We don't consider inheritance when determining intersecting - ranges. If we *did* consider inheritance, then our calculation - would be wrong. For example, if the CHILD->REMAINING_RANGES is - 5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the
[... 8421 lines stripped ...]
