You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2013/05/10 16:58:56 UTC
svn commit: r1481041 [10/38] - in /subversion/branches/master-passphrase: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
contrib/client-side/svncopy/ contrib/hook-scripts/
contrib/server-side/fsfsfixer/ contrib/server-side/fsfsf...
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/merge.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/merge.c Fri May 10 14:58:47 2013
@@ -436,7 +436,7 @@ check_repos_match(const merge_target_t *
if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Url '%s' of '%s' is not in repository '%s'"),
+ _("URL '%s' of '%s' is not in repository '%s'"),
url, svn_dirent_local_style(local_abspath, scratch_pool),
target->loc.repos_root_url);
@@ -1595,7 +1595,7 @@ mark_dir_edited(merge_cmd_baton_t *merge
local_abspath,
(db->tree_conflict_reason == CONFLICT_REASON_SKIP)
? svn_wc_notify_skip
- : svn_wc_notify_state_obstructed,
+ : svn_wc_notify_update_skip_obstruction,
scratch_pool);
notify->kind = svn_node_dir;
notify->content_state = notify->prop_state = db->skip_reason;
@@ -2002,9 +2002,14 @@ merge_file_changed(const char *relpath,
left, right,
left_props, prop_changes,
merge_b->dry_run,
- ctx->conflict_func2, ctx->conflict_baton2,
+ NULL, NULL,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
+ if (property_state == svn_wc_notify_state_conflicted)
+ {
+ alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
+ merge_b->pool);
+ }
}
/* Easy out: We are only applying mergeinfo differences. */
@@ -2040,7 +2045,7 @@ merge_file_changed(const char *relpath,
merge_b->dry_run, merge_b->diff3_cmd,
merge_b->merge_options,
left_props, prop_changes,
- ctx->conflict_func2, ctx->conflict_baton2,
+ NULL, NULL,
ctx->cancel_func,
ctx->cancel_baton,
scratch_pool));
@@ -2398,7 +2403,7 @@ merge_file_deleted(const char *relpath,
NULL, TRUE,
scratch_pool));
}
-
+
return SVN_NO_ERROR;
}
@@ -2482,7 +2487,7 @@ merge_dir_opened(void **new_dir_baton,
if (obstr_state == svn_wc_notify_state_obstructed)
{
svn_boolean_t is_wcroot;
-
+
SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
merge_b->ctx->wc_ctx,
local_abspath, scratch_pool));
@@ -2647,7 +2652,7 @@ merge_dir_opened(void **new_dir_baton,
return SVN_NO_ERROR;
}
}
-
+
if (! (merge_b->dry_run
&& ((pdb && pdb->added) || db->add_is_replace)))
{
@@ -2671,7 +2676,7 @@ merge_dir_opened(void **new_dir_baton,
{
svn_node_kind_t disk_kind;
- SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
+ SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
scratch_pool));
if (disk_kind == svn_node_dir)
@@ -2842,7 +2847,7 @@ merge_dir_changed(const char *relpath,
left, right,
left_props, props,
merge_b->dry_run,
- ctx->conflict_func2, ctx->conflict_baton2,
+ NULL, NULL,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
@@ -2994,11 +2999,15 @@ merge_dir_added(const char *relpath,
svn_prop_hash_to_array(new_props,
scratch_pool),
merge_b->dry_run,
- merge_b->ctx->conflict_func2,
- merge_b->ctx->conflict_baton2,
+ NULL, NULL,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
scratch_pool));
+ if (prop_state == svn_wc_notify_state_conflicted)
+ {
+ alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
+ merge_b->pool);
+ }
}
return SVN_NO_ERROR;
@@ -3460,6 +3469,37 @@ notify_merge_completed(const char *targe
|| notify->action == svn_wc_notify_tree_conflict)
+/* Remove merge source gaps from range used for merge notifications.
+ See http://subversion.tigris.org/issues/show_bug.cgi?id=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
@@ -3551,9 +3591,16 @@ notify_merge_begin(merge_cmd_baton_t *me
scratch_pool);
if (SVN_IS_VALID_REVNUM(n_range.start))
- notify->merge_range = &n_range;
+ {
+ /* 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->merge_range = NULL;
+ }
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
@@ -4690,6 +4737,10 @@ find_gaps_in_merge_source_history(svn_re
/* Start by assuming there is no gap. */
*gap_start = *gap_end = SVN_INVALID_REVNUM;
+ /* Easy out: There can't be a gap between adjacent revisions. */
+ if (abs(source->loc1->rev - source->loc2->rev) == 1)
+ return SVN_NO_ERROR;
+
/* Get SOURCE as mergeinfo. */
SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
primary_src,
@@ -5277,6 +5328,38 @@ record_skips_in_mergeinfo(const char *me
}
/* Data for reporting when a merge aborted because of raising conflicts.
+ */
+typedef struct single_range_conflict_report_t
+{
+ /* What sub-range of the requested source raised conflicts?
+ * The 'inheritable' flag is ignored. */
+ merge_source_t *conflicted_range;
+ /* What sub-range of the requested source remains to be merged?
+ * NULL if no more. The 'inheritable' flag is ignored. */
+ merge_source_t *remaining_source;
+
+} single_range_conflict_report_t;
+
+/* Create a single_range_conflict_report_t, containing deep copies of
+ * CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */
+static single_range_conflict_report_t *
+single_range_conflict_report_create(const merge_source_t *conflicted_range,
+ const merge_source_t *remaining_source,
+ apr_pool_t *result_pool)
+{
+ single_range_conflict_report_t *report
+ = apr_palloc(result_pool, sizeof(*report));
+
+ assert(conflicted_range != NULL);
+
+ report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
+ report->remaining_source
+ = remaining_source ? merge_source_dup(remaining_source, result_pool)
+ : NULL;
+ return report;
+}
+
+/* Data for reporting when a merge aborted because of raising conflicts.
*
* ### TODO: More info, including the ranges (or other parameters) the user
* needs to complete the merge.
@@ -5285,7 +5368,8 @@ typedef struct conflict_report_t
{
const char *target_abspath;
/* The revision range during which conflicts were raised */
- svn_merge_range_t *r;
+ const merge_source_t *conflicted_range;
+ /* Was the conflicted range the last range in the whole requested merge? */
svn_boolean_t was_last_range;
} conflict_report_t;
@@ -5293,14 +5377,14 @@ typedef struct conflict_report_t
* allocated in RESULT_POOL. */
static conflict_report_t *
conflict_report_create(const char *target_abspath,
- const svn_merge_range_t *conflicted_range,
+ const merge_source_t *conflicted_range,
svn_boolean_t was_last_range,
apr_pool_t *result_pool)
{
conflict_report_t *report = apr_palloc(result_pool, sizeof(*report));
report->target_abspath = apr_pstrdup(result_pool, target_abspath);
- report->r = svn_merge_range_dup(conflicted_range, result_pool);
+ report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
report->was_last_range = was_last_range;
return report;
}
@@ -5313,7 +5397,8 @@ conflict_report_dup(const conflict_repor
conflict_report_t *new = apr_pmemdup(result_pool, report, sizeof(*new));
new->target_abspath = apr_pstrdup(result_pool, report->target_abspath);
- new->r = svn_merge_range_dup(report->r, result_pool);
+ new->conflicted_range = merge_source_dup(report->conflicted_range,
+ result_pool);
return new;
}
@@ -5326,13 +5411,17 @@ make_merge_conflict_error(conflict_repor
assert(!report || svn_dirent_is_absolute(report->target_abspath));
if (report && ! report->was_last_range)
- return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
+ {
+ svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("One or more conflicts were produced while merging r%ld:%ld into\n"
"'%s' --\n"
"resolve all conflicts and rerun the merge to apply the remaining\n"
"unmerged revisions"),
- report->r->start, report->r->end,
+ report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev,
svn_dirent_local_style(report->target_abspath, scratch_pool));
+ assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */
+ return err;
+ }
return SVN_NO_ERROR;
}
@@ -6140,7 +6229,7 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on '%s', "
- "mergetracking not possible"),
+ "merge tracking not possible"),
svn_dirent_local_style(wc_path, scratch_pool));
}
return svn_error_trace(err);
@@ -7195,6 +7284,7 @@ subrange_source(const merge_source_t *so
/* For this function we require that the input source is 'ancestral'. */
SVN_ERR_ASSERT_NO_RETURN(source->ancestral);
+ SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev);
loc1.rev = start_rev;
loc2.rev = end_rev;
@@ -7235,7 +7325,7 @@ subrange_source(const merge_source_t *so
*/
static svn_error_t *
do_file_merge(svn_mergeinfo_catalog_t result_catalog,
- svn_merge_range_t **conflicted_range,
+ single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_abspath,
const svn_diff_tree_processor_t *processor,
@@ -7259,17 +7349,18 @@ do_file_merge(svn_mergeinfo_catalog_t re
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
- *conflicted_range = NULL;
+ *conflict_report = NULL;
/* Note that this is a single-file merge. */
range.start = source->loc1->rev;
range.end = source->loc2->rev;
range.inheritable = TRUE;
+
+ merge_target = svn_client__merge_path_create(target_abspath, scratch_pool);
+
if (honor_mergeinfo)
{
svn_error_t *err;
- merge_target = svn_client__merge_path_create(target_abspath,
- scratch_pool);
/* Fetch mergeinfo. */
err = get_full_mergeinfo(&target_mergeinfo,
@@ -7287,7 +7378,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on merge target '%s', "
- "mergetracking not possible"),
+ "merge tracking not possible"),
svn_dirent_local_style(target_abspath, scratch_pool));
}
return svn_error_trace(err);
@@ -7309,6 +7400,26 @@ do_file_merge(svn_mergeinfo_catalog_t re
ctx, scratch_pool,
iterpool));
remaining_ranges = merge_target->remaining_ranges;
+
+ /* We are honoring mergeinfo and this is not a simple record only
+ merge which blindly records mergeinfo describing the merge of
+ SOURCE->LOC1->URL@SOURCE->LOC1->REV through
+ SOURCE->LOC2->URL@SOURCE->LOC2->REV. This means that the oldest
+ and youngest revisions merged (as determined above by
+ calculate_remaining_ranges) might differ from those described
+ in SOURCE. To keep the '--- Merging *' notifications consistent
+ with the '--- Recording mergeinfo *' notifications, we adjust
+ RANGE to account for such changes. */
+ if (remaining_ranges->nelts)
+ {
+ svn_merge_range_t *adj_start_range =
+ APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *);
+ svn_merge_range_t *adj_end_range =
+ APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1,
+ svn_merge_range_t *);
+ range.start = adj_start_range->start;
+ range.end = adj_end_range->end;
+ }
}
}
@@ -7502,7 +7613,16 @@ do_file_merge(svn_mergeinfo_catalog_t re
if (is_path_conflicted_by_merge(merge_b))
{
- *conflicted_range = svn_merge_range_dup(r, result_pool);
+ merge_source_t *remaining_range = NULL;
+
+ if (real_source->loc2->rev != source->loc2->rev)
+ remaining_range = subrange_source(source,
+ real_source->loc2->rev,
+ source->loc2->rev,
+ scratch_pool);
+ *conflict_report = single_range_conflict_report_create(
+ real_source, remaining_range, result_pool);
+
/* Only record partial mergeinfo if only a partial merge was
performed before a conflict was encountered. */
range.end = r->end;
@@ -7759,14 +7879,14 @@ subtree_touched_by_merge(const char *loc
SOURCE, DEPTH, NOTIFY_B, and MERGE_B
are all cascaded from do_directory_merge's arguments of the same names.
- CONFLICTED_RANGE is as documented for do_directory_merge().
+ CONFLICT_REPORT is as documented for do_directory_merge().
NOTE: This is a very thin wrapper around drive_merge_report_editor() and
exists only to populate CHILDREN_WITH_MERGEINFO with the single element
expected during mergeinfo unaware merges.
*/
static svn_error_t *
-do_mergeinfo_unaware_dir_merge(svn_merge_range_t **conflicted_range,
+do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_dir_wcpath,
apr_array_header_t *children_with_mergeinfo,
@@ -7782,7 +7902,7 @@ do_mergeinfo_unaware_dir_merge(svn_merge
svn_client__merge_path_t *item
= svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
- *conflicted_range = NULL;
+ *conflict_report = NULL;
item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
source->loc2->rev,
TRUE, scratch_pool);
@@ -7794,11 +7914,8 @@ do_mergeinfo_unaware_dir_merge(svn_merge
merge_b, scratch_pool));
if (is_path_conflicted_by_merge(merge_b))
{
- svn_merge_range_t *r = apr_palloc(result_pool, sizeof(*r));
- r->start = source->loc1->rev;
- r->end = source->loc2->rev;
- r->inheritable = TRUE;
- *conflicted_range = r;
+ *conflict_report = single_range_conflict_report_create(
+ source, NULL, result_pool);
}
return SVN_NO_ERROR;
}
@@ -8309,8 +8426,13 @@ record_mergeinfo_for_dir_merge(svn_merge
continue;
if (!squelch_mergeinfo_notifications)
- notify_mergeinfo_recording(child->abspath, merged_range,
- merge_b->ctx, iterpool);
+ {
+ /* If the merge source has a gap, then don't mention
+ those gap revisions in the notification. */
+ remove_source_gap(&range, merge_b->implicit_src_gap);
+ notify_mergeinfo_recording(child->abspath, &range,
+ merge_b->ctx, iterpool);
+ }
/* If we are here we know we will be recording some mergeinfo, but
before we do, set override mergeinfo on skipped paths so they
@@ -8890,24 +9012,23 @@ remove_noop_subtree_ranges(const merge_s
svn_pool_clear(iterpool);
- /* Issue #4269: Keep track of the longest common ancestor of all the
- subtrees which require merges. This may be a child of
- TARGET->ABSPATH, which will allow us to narrow the log request
- below. */
+ /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
if (child->remaining_ranges && child->remaining_ranges->nelts)
{
+ /* Issue #4269: Keep track of the longest common ancestor of all the
+ subtrees which require merges. This may be a child of
+ TARGET->ABSPATH, which will allow us to narrow the log request
+ below. */
if (longest_common_subtree_ancestor)
longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
longest_common_subtree_ancestor, child->abspath, scratch_pool);
else
longest_common_subtree_ancestor = child->abspath;
- }
- /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
- if (child->remaining_ranges && child->remaining_ranges->nelts)
- SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
- child->remaining_ranges,
- scratch_pool, iterpool));
+ SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
+ child->remaining_ranges,
+ scratch_pool, iterpool));
+ }
}
svn_pool_destroy(iterpool);
@@ -9040,7 +9161,7 @@ remove_noop_subtree_ranges(const merge_s
Handle DEPTH as documented for svn_client_merge5().
- CONFLICTED_RANGE is as documented for do_directory_merge().
+ CONFLICT_REPORT is as documented for do_directory_merge().
Perform any temporary allocations in SCRATCH_POOL.
@@ -9051,7 +9172,7 @@ remove_noop_subtree_ranges(const merge_s
*/
static svn_error_t *
do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
- svn_merge_range_t **conflicted_range,
+ single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_abspath,
apr_array_header_t *children_with_mergeinfo,
@@ -9083,7 +9204,7 @@ do_mergeinfo_aware_dir_merge(svn_mergein
same repository as the target -- merge tracking might be
happenin'! ***/
- *conflicted_range = NULL;
+ *conflict_report = NULL;
/* Point our RA_SESSION to the URL of our youngest merge source side. */
ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
@@ -9272,11 +9393,17 @@ do_mergeinfo_aware_dir_merge(svn_mergein
we have merged. */
if (is_path_conflicted_by_merge(merge_b))
{
- svn_merge_range_t *r = apr_palloc(result_pool, sizeof(*r));
- r->start = start_rev;
- r->end = end_rev;
- r->inheritable = TRUE;
- *conflicted_range = r;
+ merge_source_t *remaining_range = NULL;
+
+ if (real_source->loc2->rev != source->loc2->rev)
+ remaining_range = subrange_source(source,
+ real_source->loc2->rev,
+ source->loc2->rev,
+ scratch_pool);
+ *conflict_report = single_range_conflict_report_create(
+ real_source, remaining_range,
+ result_pool);
+
range.end = end_rev;
break;
}
@@ -9364,7 +9491,7 @@ do_mergeinfo_aware_dir_merge(svn_mergein
*/
static svn_error_t *
do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
- svn_merge_range_t **conflicted_range,
+ single_range_conflict_report_t **conflict_report,
const merge_source_t *source,
const char *target_abspath,
const svn_diff_tree_processor_t *processor,
@@ -9387,14 +9514,14 @@ do_directory_merge(svn_mergeinfo_catalog
/* If we are not honoring mergeinfo we can skip right to the
business of merging changes! */
if (HONOR_MERGEINFO(merge_b))
- SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflicted_range,
+ SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report,
source, target_abspath,
children_with_mergeinfo,
processor, depth,
squelch_mergeinfo_notifications,
merge_b, result_pool, scratch_pool));
else
- SVN_ERR(do_mergeinfo_unaware_dir_merge(conflicted_range,
+ SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report,
source, target_abspath,
children_with_mergeinfo,
processor, depth,
@@ -9589,8 +9716,9 @@ do_merge(apr_hash_t **modified_subtrees,
depth = svn_depth_infinity;
/* Set up the diff3 command, so various callers don't have to. */
- cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
- APR_HASH_KEY_STRING) : NULL;
+ cfg = ctx->config
+ ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
+ : NULL;
svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
@@ -9660,7 +9788,7 @@ do_merge(apr_hash_t **modified_subtrees,
svn_node_kind_t src1_kind;
merge_source_t *source =
APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
- svn_merge_range_t *conflicted_range;
+ single_range_conflict_report_t *conflicted_range_report;
svn_pool_clear(iterpool);
@@ -9702,24 +9830,54 @@ do_merge(apr_hash_t **modified_subtrees,
SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
&src1_kind, iterpool));
- /* Call our merge helpers based on SRC1's kind. */
- if (src1_kind != svn_node_dir)
- {
- SVN_ERR(do_file_merge(result_catalog, &conflicted_range,
- source, target->abspath,
- processor,
- sources_related,
- squelch_mergeinfo_notifications,
- &merge_cmd_baton, iterpool, iterpool));
- }
- else /* Directory */
- {
- SVN_ERR(do_directory_merge(result_catalog, &conflicted_range,
- source, target->abspath,
- processor,
- depth, squelch_mergeinfo_notifications,
- &merge_cmd_baton, iterpool, iterpool));
+ /* Run the merge; if there are conflicts, allow the callback to
+ * resolve them, and if it resolves all of them, then run the
+ * merge again with the remaining revision range, until it is all
+ * done. */
+ do
+ {
+ /* Merge as far as possible without resolving any conflicts */
+ if (src1_kind != svn_node_dir)
+ {
+ SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report,
+ source, target->abspath,
+ processor,
+ sources_related,
+ squelch_mergeinfo_notifications,
+ &merge_cmd_baton, iterpool, iterpool));
+ }
+ else /* Directory */
+ {
+ SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report,
+ source, target->abspath,
+ processor,
+ depth, squelch_mergeinfo_notifications,
+ &merge_cmd_baton, iterpool, iterpool));
+ }
+
+ /* Give the conflict resolver callback the opportunity to
+ * resolve any conflicts that were raised. If it resolves all
+ * of them, go around again to merge the next sub-range (if any). */
+ if (conflicted_range_report && ctx->conflict_func2 && ! dry_run)
+ {
+ svn_boolean_t conflicts_remain;
+
+ SVN_ERR(svn_client__resolve_conflicts(
+ &conflicts_remain, merge_cmd_baton.conflicted_paths,
+ ctx, iterpool));
+ if (conflicts_remain)
+ break;
+
+ merge_cmd_baton.conflicted_paths = NULL;
+ /* Caution: this source is in iterpool */
+ source = conflicted_range_report->remaining_source;
+ conflicted_range_report = NULL;
+ }
+ else
+ break;
}
+ while (source);
+
/* The final mergeinfo on TARGET_WCPATH may itself elide. */
if (! dry_run)
SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL,
@@ -9729,12 +9887,12 @@ do_merge(apr_hash_t **modified_subtrees,
* range of a multi-pass merge, we raise an error that aborts
* the merge. The user will be asked to resolve conflicts
* before merging subsequent revision ranges. */
- if (conflicted_range)
+ if (conflicted_range_report)
{
*conflict_report = conflict_report_create(
- target->abspath, conflicted_range,
+ target->abspath, conflicted_range_report->conflicted_range,
(i == merge_sources->nelts - 1
- && conflicted_range->end == source->loc2->rev),
+ && ! conflicted_range_report->remaining_source),
result_pool);
break;
}
@@ -11285,10 +11443,6 @@ find_reintegrate_merge(merge_source_t **
&yc_ancestor, source.loc2, source.loc1, target_ra_session,
ctx, scratch_pool, scratch_pool));
- /* The source side of a reintegrate merge is not 'ancestral', except in
- * the degenerate case where source == YCA. */
- source.ancestral = (loc1->rev == yc_ancestor->rev);
-
if (! yc_ancestor)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
@@ -11296,6 +11450,10 @@ find_reintegrate_merge(merge_source_t **
source.loc1->url, source.loc1->rev,
source.loc2->url, source.loc2->rev);
+ /* The source side of a reintegrate merge is not 'ancestral', except in
+ * the degenerate case where source == YCA. */
+ source.ancestral = (loc1->rev == yc_ancestor->rev);
+
if (source.loc1->rev > yc_ancestor->rev)
{
/* Have we actually merged anything to the source from the
@@ -11426,7 +11584,7 @@ merge_reintegrate_locked(conflict_report
svn_client__pathrev_t *source_loc;
merge_source_t *source;
svn_client__pathrev_t *yc_ancestor;
- svn_boolean_t use_sleep;
+ svn_boolean_t use_sleep = FALSE;
svn_error_t *err;
SVN_ERR(open_reintegrate_source_and_target(
@@ -11583,6 +11741,40 @@ merge_peg_locked(conflict_report_t **con
return SVN_NO_ERROR;
}
+/* Details of an automatic merge. */
+typedef struct automatic_merge_t
+{
+ svn_client__pathrev_t *yca, *base, *right, *target;
+ svn_boolean_t is_reintegrate_like;
+ svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
+} automatic_merge_t;
+
+static svn_error_t *
+client_find_automatic_merge(automatic_merge_t **merge_p,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_abspath,
+ svn_boolean_t allow_mixed_rev,
+ svn_boolean_t allow_local_mods,
+ svn_boolean_t allow_switched_subtrees,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+static svn_error_t *
+do_automatic_merge_locked(conflict_report_t **conflict_report,
+ const automatic_merge_t *merge,
+ const char *target_abspath,
+ svn_depth_t depth,
+ svn_boolean_t diff_ignore_ancestry,
+ svn_boolean_t force_delete,
+ svn_boolean_t record_only,
+ svn_boolean_t dry_run,
+ const apr_array_header_t *merge_options,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
svn_error_t *
svn_client_merge_peg5(const char *source_path_or_url,
const apr_array_header_t *ranges_to_merge,
@@ -11603,13 +11795,50 @@ svn_client_merge_peg5(const char *source
conflict_report_t *conflict_report;
/* No ranges to merge? No problem. */
- if (ranges_to_merge->nelts == 0)
+ if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0)
return SVN_NO_ERROR;
SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
target_wcpath, ctx, pool));
- if (!dry_run)
+ /* Do an automatic merge if no revision ranges are specified. */
+ if (ranges_to_merge == NULL)
+ {
+ automatic_merge_t *merge;
+
+ if (ignore_mergeinfo)
+ return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Cannot merge automatically while "
+ "ignoring mergeinfo"));
+
+ /* Find the details of the merge needed. */
+ SVN_ERR(client_find_automatic_merge(
+ &merge,
+ source_path_or_url, source_peg_revision,
+ target_abspath,
+ allow_mixed_rev,
+ TRUE /*allow_local_mods*/,
+ TRUE /*allow_switched_subtrees*/,
+ ctx, pool, pool));
+
+ if (!dry_run)
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ do_automatic_merge_locked(&conflict_report,
+ merge,
+ target_abspath, depth,
+ diff_ignore_ancestry,
+ force_delete, record_only, dry_run,
+ merge_options, ctx, pool, pool),
+ ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
+ else
+ SVN_ERR(do_automatic_merge_locked(&conflict_report,
+ merge,
+ target_abspath, depth,
+ diff_ignore_ancestry,
+ force_delete, record_only, dry_run,
+ merge_options, ctx, pool, pool));
+ }
+ else if (!dry_run)
SVN_WC__CALL_WITH_WRITE_LOCK(
merge_peg_locked(&conflict_report,
source_path_or_url, source_peg_revision,
@@ -11696,14 +11925,6 @@ typedef struct source_and_target_t
svn_ra_session_t *target_ra_session;
branch_history_t target_branch;
- /* The complete mergeinfo on SOURCE.
- That is, the explicit or inherited mergeinfo. */
- svn_mergeinfo_t source_mergeinfo;
-
- /* The complete mergeinfo on (the current, working version of) TARGET.
- That is, the explicit or inherited mergeinfo. */
- svn_mergeinfo_t target_mergeinfo;
-
/* Repos location of the youngest common ancestor of SOURCE and TARGET. */
svn_client__pathrev_t *yca;
} source_and_target_t;
@@ -11779,17 +12000,100 @@ branch_history_get_endpoints(svn_client_
return SVN_NO_ERROR;
}
+/* Implements the svn_log_entry_receiver_t interface.
+
+ Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */
+static svn_error_t *
+operative_rev_receiver(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ svn_revnum_t *operative_rev = baton;
+
+ *operative_rev = log_entry->revision;
+
+ /* We've found the youngest merged or oldest eligible revision, so
+ we're done...
+
+ ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
+ true? Because if it is, then LOG_ENTRY->REVISION is only
+ partially merged/elgibile! And our only caller,
+ find_last_merged_location (via short_circuit_mergeinfo_log) is
+ interested in *fully* merged revisions. That's all true, but if
+ find_last_merged_location() finds the youngest merged revision it
+ will also check for the oldest eligible revision. So in the case
+ the youngest merged rev is non-inheritable, the *same* non-inheritable
+ rev will be found as the oldest eligible rev -- and
+ find_last_merged_location() handles that situation. */
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+}
+
+/* Wrapper around svn_client__mergeinfo_log. All arguments are as per
+ that private API. The discover_changed_paths, depth, and revprops args to
+ svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
+ and NULL respectively.
+
+ If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
+ *REVISION to a valid revnum, then clear the error. Otherwise return
+ any error. */
+static svn_error_t*
+short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
+ svn_boolean_t finding_merged,
+ const char *target_path_or_url,
+ const svn_opt_revision_t *target_peg_revision,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_peg_revision,
+ const svn_opt_revision_t *source_start_revision,
+ const svn_opt_revision_t *source_end_revision,
+ svn_log_entry_receiver_t receiver,
+ svn_revnum_t *revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err = svn_client__mergeinfo_log(finding_merged,
+ target_path_or_url,
+ target_peg_revision,
+ target_mergeinfo_cat,
+ source_path_or_url,
+ source_peg_revision,
+ source_start_revision,
+ source_end_revision,
+ receiver, revision,
+ TRUE, svn_depth_infinity,
+ NULL, ctx, result_pool,
+ scratch_pool);
+
+ if (err)
+ {
+ /* We expect RECEIVER to short-circuit the (potentially expensive) log
+ by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver.
+ So we can ignore that error, but only as long as we actually found a
+ valid revision. */
+ if (SVN_IS_VALID_REVNUM(*revision)
+ && err->apr_err == SVN_ERR_CEASE_INVOCATION)
+ {
+ svn_error_clear(err);
+ err = NULL;
+ }
+ else
+ {
+ return svn_error_trace(err);
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
/* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
* on SOURCE_BRANCH after YCA up to and including *BASE_P have already
- * been merged into the target branch -- or, specifically, are recorded in
- * TARGET_MERGEINFO.
+ * been fully merged into TARGET.
*
* *BASE_P TIP
* o-------o-----------o--- SOURCE_BRANCH
* / \
* -----o prev. \
* YCA \ merges \
- * o-----------o-----------
+ * o-----------o----------- TARGET branch
*
* In terms of mergeinfo:
*
@@ -11799,7 +12103,10 @@ branch_history_get_endpoints(svn_client_
*
* Eligible -.eee.eeeeeeeeeeeeeeeeeeee .=not a source branch location
*
- * Tgt-mi -.mmm.mm-mm-------m------- m=merged, -=not merged
+ * Tgt-mi -.mmm.mm-mm-------m------- m=merged to root of TARGET or
+ * subtree of TARGET with no
+ * operative changes outside of that
+ * subtree, -=not merged
*
* Eligible -.---.--e--eeeeeee-eeeeeee
*
@@ -11810,90 +12117,97 @@ branch_history_get_endpoints(svn_client_
* YCA \ merges \
* o-----------o-------------
*
- * If no locations on SOURCE_BRANCH are recorded in TARGET_MERGEINFO, set
- * *BASE_P to the YCA.
+ * If no revisions from SOURCE_BRANCH have been completely merged to TARGET,
+ * then set *BASE_P to the YCA.
*/
static svn_error_t *
find_last_merged_location(svn_client__pathrev_t **base_p,
svn_client__pathrev_t *yca,
const branch_history_t *source_branch,
- svn_mergeinfo_t target_mergeinfo,
+ svn_client__pathrev_t *target,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- /*
- To find the youngest location on BRANCH_A that is fully merged to BRANCH_B:
-
- Find the longest set of locations in BRANCH_A, starting after YCA,
- such that each location is (any of):
- (a) in BRANCH_B's mergeinfo; or
- (b) a merge onto BRANCH_A of logical changes that are all from the
- target branch or already in BRANCH_B's mergeinfo; or
- (c) inoperative on BRANCH_A.
-
- Report the youngest such location, or the YCA if there are none.
-
- Part (b) can perhaps, initially, be simplified to something like:
- a merge onto BRANCH_A of (including? entirely?) revisions from
- BRANCH_B's history.
-
- Part (c) is only necessary if we want to allow sparse mergeinfo --
- that is, if we don't want to do some partially- or completely-
- inoperative merges to fill in mergeinfo gaps.
- */
- branch_history_t *eligible_locations;
-
- /* Start with a list of all source locations after YCA up to the tip. */
- SVN_ERR(branch_history_intersect_range(
- &eligible_locations,
- source_branch, yca->rev + 1, source_branch->tip->rev,
- scratch_pool, scratch_pool));
-
- /* Remove any locations that match (a), (b) or (c). */
- /* For (a), remove any locations that are in TARGET's mergeinfo. */
- SVN_ERR(svn_mergeinfo_remove2(&eligible_locations->history,
- target_mergeinfo, eligible_locations->history,
- TRUE, scratch_pool, scratch_pool));
- /* For (b) ... */
-
- /* For (c) ... */
-
- /* This leaves a list of source locations that are eligible to merge.
- The location that we want is the source location just before oldest
- eligible location remaining in this list; or the youngest source
- location if there are none left in this list. */
- if (apr_hash_count(eligible_locations->history) > 0)
- {
- /* Find the oldest eligible rev.
- * Eligible -.---.--e--eeeeeee-eeeeeee
- * ^
- * BASE is just before here.
- */
+ svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
+ target_opt_rev;
+ svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
+ svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
+
+ source_peg_rev.kind = svn_opt_revision_number;
+ source_peg_rev.value.number = source_branch->tip->rev;
+ source_start_rev.kind = svn_opt_revision_number;
+ source_start_rev.value.number = yca->rev;
+ source_end_rev.kind = svn_opt_revision_number;
+ source_end_rev.value.number = source_branch->tip->rev;
+ target_opt_rev.kind = svn_opt_revision_number;
+ target_opt_rev.value.number = target->rev;
+
+ /* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
+ if such a revision exists. */
+ SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
+ TRUE, /* Find merged */
+ target->url, &target_opt_rev,
+ source_branch->tip->url,
+ &source_peg_rev,
+ &source_end_rev, &source_start_rev,
+ operative_rev_receiver,
+ &youngest_merged_rev,
+ ctx, result_pool, scratch_pool));
- svn_client__pathrev_t *oldest_eligible;
+ if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
+ {
+ /* No revisions have been completely merged from SOURCE_BRANCH to
+ TARGET so the base for the next merge is the YCA. */
+ *base_p = yca;
+ }
+ else
+ {
+ /* One or more revisions have already been completely merged from
+ SOURCE_BRANCH to TARGET, now find the oldest revision, older
+ than the youngest merged revision, which is still eligible to
+ be merged, if such exists. */
branch_history_t *contiguous_source;
+ svn_revnum_t base_rev;
+ svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
- SVN_ERR(branch_history_get_endpoints(
- &oldest_eligible, NULL,
- eligible_locations, scratch_pool, scratch_pool));
+ /* If the only revisions eligible are younger than the youngest merged
+ revision we can simply assume that the youngest eligible revision
+ is the youngest merged revision. Obviously this may not be true!
+ The revisions between the youngest merged revision and the tip of
+ the branch may have several inoperative revisions -- they may *all*
+ be inoperative revisions! But for the purpose of this function
+ (i.e. finding the youngest revision after the YCA where all revs have
+ been merged) that doesn't matter. */
+ source_end_rev.value.number = youngest_merged_rev;
+ SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
+ FALSE, /* Find eligible */
+ target->url, &target_opt_rev,
+ source_branch->tip->url,
+ &source_peg_rev,
+ &source_start_rev, &source_end_rev,
+ operative_rev_receiver,
+ &oldest_eligible_rev,
+ ctx, scratch_pool, scratch_pool));
+
+ /* If there are revisions eligible for merging, use the oldest one
+ to calculate the base. Otherwise there are no operative revisions
+ to merge and we can simple set the base to the youngest revision
+ already merged. */
+ if (SVN_IS_VALID_REVNUM(oldest_eligible_rev))
+ base_rev = oldest_eligible_rev - 1;
+ else
+ base_rev = youngest_merged_rev;
/* Find the branch location just before the oldest eligible rev.
- * (We can't just subtract 1 from the rev because the branch might
- * have a gap there.) */
- SVN_ERR(branch_history_intersect_range(
- &contiguous_source,
- source_branch, yca->rev, oldest_eligible->rev - 1,
- scratch_pool, scratch_pool));
- SVN_ERR(branch_history_get_endpoints(
- NULL, base_p,
- contiguous_source, result_pool, scratch_pool));
- }
- else
- {
- /* The whole source branch is merged already, so the base for
- * the next merge is its tip. */
- *base_p = source_branch->tip;
+ (We can't just use the base revs calculated above because the branch
+ might have a gap there.) */
+ SVN_ERR(branch_history_intersect_range(&contiguous_source,
+ source_branch, yca->rev,
+ base_rev,
+ scratch_pool, scratch_pool));
+ SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source,
+ result_pool, scratch_pool));
}
return SVN_NO_ERROR;
@@ -11911,10 +12225,10 @@ find_last_merged_location(svn_client__pa
* S_T->target
*
* Set *BASE_P to BASE, the youngest location in the history of S_T->source
- * (at or after the YCA) at which all revisions up to BASE are recorded as
+ * (at or after the YCA) at which all revisions up to BASE are effectively
* merged into S_T->target.
*
- * If no locations on the history of S_T->source are recorded as merged to
+ * If no locations on the history of S_T->source are effectively merged to
* S_T->target, set *BASE_P to the YCA.
*/
static svn_error_t *
@@ -11927,7 +12241,7 @@ find_base_on_source(svn_client__pathrev_
SVN_ERR(find_last_merged_location(base_p,
s_t->yca,
&s_t->source_branch,
- s_t->target_mergeinfo,
+ s_t->target_branch.tip,
ctx, result_pool, scratch_pool));
return SVN_NO_ERROR;
}
@@ -11944,10 +12258,10 @@ find_base_on_source(svn_client__pathrev_
* BASE S_T->target
*
* Set *BASE_P to BASE, the youngest location in the history of S_T->target
- * (at or after the YCA) at which all revisions up to BASE are recorded as
+ * (at or after the YCA) at which all revisions up to BASE are effectively
* merged into S_T->source.
*
- * If no locations on the history of S_T->target are recorded as merged to
+ * If no locations on the history of S_T->target are effectively merged to
* S_T->source, set *BASE_P to the YCA.
*/
static svn_error_t *
@@ -11960,13 +12274,13 @@ find_base_on_target(svn_client__pathrev_
SVN_ERR(find_last_merged_location(base_p,
s_t->yca,
&s_t->target_branch,
- s_t->source_mergeinfo,
+ s_t->source,
ctx, result_pool, scratch_pool));
return SVN_NO_ERROR;
}
-/* The body of svn_client_find_automatic_merge(), which see.
+/* The body of client_find_automatic_merge(), which see.
*/
static svn_error_t *
find_automatic_merge(svn_client__pathrev_t **base_p,
@@ -11978,32 +12292,6 @@ find_automatic_merge(svn_client__pathrev
{
svn_client__pathrev_t *base_on_source, *base_on_target;
- /* Fetch mergeinfo of source branch (tip) and target branch (working). */
- SVN_ERR(svn_client__get_repos_mergeinfo(&s_t->source_mergeinfo,
- s_t->source_ra_session,
- s_t->source->url,
- s_t->source->rev,
- svn_mergeinfo_inherited,
- FALSE /* squelch_incapable */,
- scratch_pool));
- if (! s_t->target->abspath)
- SVN_ERR(svn_client__get_repos_mergeinfo(&s_t->target_mergeinfo,
- s_t->target_ra_session,
- s_t->target->loc.url,
- s_t->target->loc.rev,
- svn_mergeinfo_inherited,
- FALSE /* squelch_incapable */,
- scratch_pool));
- else
- SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(&s_t->target_mergeinfo,
- NULL /* inherited */,
- NULL /* from_repos */,
- FALSE /* repos_only */,
- svn_mergeinfo_inherited,
- s_t->target_ra_session,
- s_t->target->abspath,
- ctx, scratch_pool));
-
/* Get the location-history of each branch. */
s_t->source_branch.tip = s_t->source;
SVN_ERR(svn_client__get_history_as_mergeinfo(
@@ -12046,28 +12334,26 @@ find_automatic_merge(svn_client__pathrev
return SVN_NO_ERROR;
}
-/* Details of an automatic merge. */
-struct svn_client_automatic_merge_t
-{
- svn_client__pathrev_t *yca, *base, *right, *target;
- svn_boolean_t is_reintegrate_like;
- svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
-};
-
-svn_error_t *
-svn_client_find_automatic_merge_no_wc(
- svn_client_automatic_merge_t **merge_p,
- const char *source_path_or_url,
- const svn_opt_revision_t *source_revision,
- const char *target_path_or_url,
- const svn_opt_revision_t *target_revision,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+/** Find out what kind of automatic merge would be needed, when the target
+ * is only known as a repository location rather than a WC.
+ *
+ * Like find_automatic_merge() except that the target is
+ * specified by @a target_path_or_url at @a target_revision, which must
+ * refer to a repository location, instead of by a WC path argument.
+ */
+static svn_error_t *
+find_automatic_merge_no_wc(automatic_merge_t **merge_p,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_path_or_url,
+ const svn_opt_revision_t *target_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
svn_client__pathrev_t *target_loc;
- svn_client_automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
+ automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
/* Source */
SVN_ERR(svn_client__ra_session_from_path2(
@@ -12095,23 +12381,39 @@ svn_client_find_automatic_merge_no_wc(
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_client_find_automatic_merge(svn_client_automatic_merge_t **merge_p,
- const char *source_path_or_url,
- const svn_opt_revision_t *source_revision,
- const char *target_wcpath,
- svn_boolean_t allow_mixed_rev,
- svn_boolean_t allow_local_mods,
- svn_boolean_t allow_switched_subtrees,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+/* Find the information needed to merge all unmerged changes from a source
+ * branch into a target branch.
+ *
+ * Set @a *merge_p to the information needed to merge all unmerged changes
+ * (up to @a source_revision) from the source branch @a source_path_or_url
+ * at @a source_revision into the target WC at @a target_abspath.
+ *
+ * The flags @a allow_mixed_rev, @a allow_local_mods and
+ * @a allow_switched_subtrees enable merging into a WC that is in any or all
+ * of the states described by their names, but only if this function decides
+ * that the merge will be in the same direction as the last automatic merge.
+ * If, on the other hand, the last automatic merge was in the opposite
+ * direction, then such states of the WC are not allowed regardless
+ * of these flags. This function merely records these flags in the
+ * @a *merge_p structure; do_automatic_merge_locked() checks the WC
+ * state for compliance.
+ *
+ * Allocate the @a *merge_p structure in @a result_pool.
+ */
+static svn_error_t *
+client_find_automatic_merge(automatic_merge_t **merge_p,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_abspath,
+ svn_boolean_t allow_mixed_rev,
+ svn_boolean_t allow_local_mods,
+ svn_boolean_t allow_switched_subtrees,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- const char *target_abspath;
source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
- svn_client_automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
-
- SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath, scratch_pool));
+ automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
/* "Open" the target WC. We're not going to check the target WC for
* mixed-rev, local mods or switched subtrees yet. After we find out
@@ -12136,7 +12438,7 @@ svn_client_find_automatic_merge(svn_clie
/* Check source is in same repos as target. */
SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
- &s_t->target->loc, target_wcpath,
+ &s_t->target->loc, target_abspath,
TRUE /* strict_urls */, scratch_pool));
SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
@@ -12154,7 +12456,8 @@ svn_client_find_automatic_merge(svn_clie
return SVN_NO_ERROR;
}
-/* The body of svn_client_do_automatic_merge(), which see.
+/* Perform an automatic merge, given the information in MERGE which
+ * must have come from calling client_find_automatic_merge().
*
* Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
* depending on whether the base is on the source branch or the target
@@ -12183,7 +12486,7 @@ svn_client_find_automatic_merge(svn_clie
*/
static svn_error_t *
do_automatic_merge_locked(conflict_report_t **conflict_report,
- const svn_client_automatic_merge_t *merge,
+ const automatic_merge_t *merge,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t diff_ignore_ancestry,
@@ -12311,65 +12614,42 @@ do_automatic_merge_locked(conflict_repor
}
svn_error_t *
-svn_client_do_automatic_merge(const svn_client_automatic_merge_t *merge,
- const char *target_wcpath,
- svn_depth_t depth,
- svn_boolean_t diff_ignore_ancestry,
- svn_boolean_t force_delete,
- svn_boolean_t record_only,
- svn_boolean_t dry_run,
- const apr_array_header_t *merge_options,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
+ const char **yca_url, svn_revnum_t *yca_rev,
+ const char **base_url, svn_revnum_t *base_rev,
+ const char **right_url, svn_revnum_t *right_rev,
+ const char **target_url, svn_revnum_t *target_rev,
+ const char **repos_root_url,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_revision,
+ const char *target_path_or_url,
+ const svn_opt_revision_t *target_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- const char *target_abspath, *lock_abspath;
- conflict_report_t *conflict_report;
-
- SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
- target_wcpath, ctx, pool));
+ svn_boolean_t target_is_wc;
+ automatic_merge_t *merge;
- if (!dry_run)
- SVN_WC__CALL_WITH_WRITE_LOCK(
- do_automatic_merge_locked(&conflict_report,
- merge,
- target_abspath, depth,
- diff_ignore_ancestry,
- force_delete, record_only, dry_run,
- merge_options, ctx, pool, pool),
- ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
+ target_is_wc = (! svn_path_is_url(target_path_or_url))
+ && (target_revision->kind == svn_opt_revision_unspecified
+ || target_revision->kind == svn_opt_revision_working);
+ if (target_is_wc)
+ SVN_ERR(client_find_automatic_merge(
+ &merge,
+ source_path_or_url, source_revision,
+ target_path_or_url,
+ TRUE, TRUE, TRUE, /* allow_* */
+ ctx, scratch_pool, scratch_pool));
else
- SVN_ERR(do_automatic_merge_locked(&conflict_report,
- merge,
- target_abspath, depth,
- diff_ignore_ancestry,
- force_delete, record_only, dry_run,
- merge_options, ctx, pool, pool));
-
- SVN_ERR(make_merge_conflict_error(conflict_report, pool));
- return SVN_NO_ERROR;
-}
-
-svn_boolean_t
-svn_client_automatic_merge_is_reintegrate_like(
- const svn_client_automatic_merge_t *merge)
-{
- return merge->is_reintegrate_like;
-}
+ SVN_ERR(find_automatic_merge_no_wc(
+ &merge,
+ source_path_or_url, source_revision,
+ target_path_or_url, target_revision,
+ ctx, scratch_pool, scratch_pool));
-svn_error_t *
-svn_client_automatic_merge_get_locations(
- const char **yca_url,
- svn_revnum_t *yca_rev,
- const char **base_url,
- svn_revnum_t *base_rev,
- const char **right_url,
- svn_revnum_t *right_rev,
- const char **target_url,
- svn_revnum_t *target_rev,
- const char **repos_root_url,
- const svn_client_automatic_merge_t *merge,
- apr_pool_t *result_pool)
-{
+ if (needs_reintegration)
+ *needs_reintegration = merge->is_reintegrate_like;
if (yca_url)
*yca_url = apr_pstrdup(result_pool, merge->yca->url);
if (yca_rev)
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.c Fri May 10 14:58:47 2013
@@ -161,6 +161,9 @@ svn_client__record_wc_mergeinfo_catalog(
apr_array_header_t *sorted_cat =
svn_sort__hash(result_catalog, svn_sort_compare_items_as_paths,
scratch_pool);
+
+ /* Write the mergeinfo out in sorted order of the paths (presumably just
+ * so that the notifications are in a predictable, convenient order). */
for (i = 0; i < sorted_cat->nelts; i++)
{
svn_sort__item_t elt = APR_ARRAY_IDX(sorted_cat, i,
@@ -402,9 +405,8 @@ svn_client__get_wc_mergeinfo_catalog(svn
if (mergeinfo)
{
*mergeinfo_cat = apr_hash_make(result_pool);
- apr_hash_set(*mergeinfo_cat,
- apr_pstrdup(result_pool, target_repos_relpath),
- APR_HASH_KEY_STRING, mergeinfo);
+ svn_hash_sets(*mergeinfo_cat,
+ apr_pstrdup(result_pool, target_repos_relpath), mergeinfo);
}
/* If LOCAL_ABSPATH is a directory and we want the subtree mergeinfo too,
@@ -448,8 +450,7 @@ svn_client__get_wc_mergeinfo_catalog(svn
if (*mergeinfo_cat == NULL)
*mergeinfo_cat = apr_hash_make(result_pool);
- apr_hash_set(*mergeinfo_cat, repos_relpath,
- APR_HASH_KEY_STRING, subtree_mergeinfo);
+ svn_hash_sets(*mergeinfo_cat, repos_relpath, subtree_mergeinfo);
}
}
@@ -654,8 +655,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
|| (inherit == svn_mergeinfo_explicit)
|| (repos_relpath
&& target_mergeinfo_cat_wc
- && apr_hash_get(target_mergeinfo_cat_wc, repos_relpath,
- APR_HASH_KEY_STRING))))
+ && svn_hash_gets(target_mergeinfo_cat_wc, repos_relpath))))
{
repos_only = TRUE;
/* We already have any subtree mergeinfo from the working copy, no
@@ -677,8 +677,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
SVN_ERR(svn_wc_get_pristine_props(&original_props,
ctx->wc_ctx, local_abspath,
result_pool, scratch_pool));
- if (!apr_hash_get(original_props, SVN_PROP_MERGEINFO,
- APR_HASH_KEY_STRING))
+ if (!svn_hash_gets(original_props, SVN_PROP_MERGEINFO))
{
apr_pool_t *sesspool = NULL;
@@ -697,9 +696,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
result_pool, scratch_pool));
if (target_mergeinfo_cat_repos
- && apr_hash_get(target_mergeinfo_cat_repos,
- repos_relpath,
- APR_HASH_KEY_STRING))
+ && svn_hash_gets(target_mergeinfo_cat_repos, repos_relpath))
{
if (inherited_p)
*inherited_p = TRUE;
@@ -1146,10 +1143,8 @@ svn_client__elide_mergeinfo_catalog(svn_
if (path_suffix)
{
SVN_ERR(should_elide_mergeinfo(&elides,
- apr_hash_get(mergeinfo_catalog, top,
- APR_HASH_KEY_STRING),
- apr_hash_get(mergeinfo_catalog, path,
- APR_HASH_KEY_STRING),
+ svn_hash_gets(mergeinfo_catalog, top),
+ svn_hash_gets(mergeinfo_catalog, path),
path_suffix,
iterpool));
@@ -1166,7 +1161,7 @@ svn_client__elide_mergeinfo_catalog(svn_
for (i = 0; i < elidable_paths->nelts; i++)
{
const char *path = APR_ARRAY_IDX(elidable_paths, i, const char *);
- apr_hash_set(mergeinfo_catalog, path, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(mergeinfo_catalog, path, NULL);
}
return SVN_NO_ERROR;
@@ -1399,8 +1394,8 @@ filter_log_entry_with_rangelist(void *ba
if (ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
&& (change->action != 'M'))
{
- svn_rangelist_t *rangelist = apr_hash_get(
- nearest_ancestor_mergeinfo, path, APR_HASH_KEY_STRING);
+ svn_rangelist_t *rangelist =
+ svn_hash_gets(nearest_ancestor_mergeinfo, path);
svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
rangelist, rangelist->nelts - 1, svn_merge_range_t *);
@@ -1493,6 +1488,7 @@ logs_for_mergeinfo_rangelist(const char
const apr_array_header_t *merge_source_fspaths,
svn_boolean_t filtering_merged,
const svn_rangelist_t *rangelist,
+ svn_boolean_t oldest_revs_first,
svn_mergeinfo_catalog_t target_mergeinfo_catalog,
const char *target_fspath,
svn_boolean_t discover_changed_paths,
@@ -1511,10 +1507,6 @@ logs_for_mergeinfo_rangelist(const char
if (! rangelist->nelts)
return SVN_NO_ERROR;
- /* Sort the rangelist. */
- qsort(rangelist->elts, rangelist->nelts,
- rangelist->elt_size, svn_sort_compare_ranges);
-
/* Build a single-member log target list using SOURCE_URL. */
target = apr_array_make(scratch_pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(target, const char *) = source_url;
@@ -1554,8 +1546,12 @@ logs_for_mergeinfo_rangelist(const char
/* Drive the log. */
revision_ranges = apr_array_make(scratch_pool, 1,
sizeof(svn_opt_revision_range_t *));
- APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
- = svn_opt__revision_range_create(&oldest_rev, &youngest_rev, scratch_pool);
+ if (oldest_revs_first)
+ APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
+ = svn_opt__revision_range_create(&oldest_rev, &youngest_rev, scratch_pool);
+ else
+ APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
+ = svn_opt__revision_range_create(&youngest_rev, &oldest_rev, scratch_pool);
SVN_ERR(svn_client_log5(target, &youngest_rev, revision_ranges,
0, discover_changed_paths, FALSE, FALSE, revprops,
filter_log_entry_with_rangelist, &fleb, ctx,
@@ -1594,10 +1590,10 @@ mergeinfo_relpaths_to_urls(apr_hash_t **
const char *key = svn__apr_hash_index_key(hi);
void *val = svn__apr_hash_index_val(hi);
- apr_hash_set(full_path_mergeinfo,
- svn_path_url_add_component2(repos_root_url, key + 1,
- result_pool),
- APR_HASH_KEY_STRING, val);
+ svn_hash_sets(full_path_mergeinfo,
+ svn_path_url_add_component2(repos_root_url, key + 1,
+ result_pool),
+ val);
}
*out_mergeinfo = full_path_mergeinfo;
}
@@ -1639,8 +1635,7 @@ svn_client_mergeinfo_get_merged(apr_hash
SVN_ERR_ASSERT(repos_relpath != NULL); /* Or get_mergeinfo failed */
}
- mergeinfo = apr_hash_get(mergeinfo_cat, repos_relpath,
- APR_HASH_KEY_STRING);
+ mergeinfo = svn_hash_gets(mergeinfo_cat, repos_relpath);
}
else
{
@@ -1652,11 +1647,11 @@ svn_client_mergeinfo_get_merged(apr_hash
return SVN_NO_ERROR;
}
-
svn_error_t *
-svn_client_mergeinfo_log2(svn_boolean_t finding_merged,
+svn_client__mergeinfo_log(svn_boolean_t finding_merged,
const char *target_path_or_url,
const svn_opt_revision_t *target_peg_revision,
+ svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
const char *source_path_or_url,
const svn_opt_revision_t *source_peg_revision,
const svn_opt_revision_t *source_start_revision,
@@ -1667,12 +1662,15 @@ svn_client_mergeinfo_log2(svn_boolean_t
svn_depth_t depth,
const apr_array_header_t *revprops,
svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *log_target = NULL;
const char *repos_root;
const char *target_repos_relpath;
svn_mergeinfo_catalog_t target_mergeinfo_cat;
+ svn_ra_session_t *target_session = NULL;
+ svn_client__pathrev_t *pathrev;
/* A hash of paths, at or under TARGET_PATH_OR_URL, mapped to
rangelists. Not technically mergeinfo, so not using the
@@ -1688,6 +1686,8 @@ svn_client_mergeinfo_log2(svn_boolean_t
apr_hash_index_t *hi_catalog;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
+ svn_boolean_t oldest_revs_first = TRUE;
+ apr_pool_t *subpool;
/* We currently only support depth = empty | infinity. */
if (depth != svn_depth_infinity && depth != svn_depth_empty)
@@ -1706,9 +1706,14 @@ svn_client_mergeinfo_log2(svn_boolean_t
(source_end_revision->kind == svn_opt_revision_date) ||
(source_end_revision->kind == svn_opt_revision_head)))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
+ if ((source_end_revision->kind != svn_opt_revision_unspecified)
+ && (source_start_revision->kind == svn_opt_revision_unspecified))
+ return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
if ((source_end_revision->kind == svn_opt_revision_unspecified)
&& (source_start_revision->kind != svn_opt_revision_unspecified))
- source_end_revision = source_start_revision;
+ return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
+
+ subpool = svn_pool_create(scratch_pool);
/* We need the union of TARGET_PATH_OR_URL@TARGET_PEG_REVISION's mergeinfo
and MERGE_SOURCE_URL's history. It's not enough to do path
@@ -1717,10 +1722,45 @@ svn_client_mergeinfo_log2(svn_boolean_t
the target, that vastly simplifies matters (we'll have nothing to
do). */
/* This get_mergeinfo() call doubles as a mergeinfo capabilities check. */
- SVN_ERR(get_mergeinfo(&target_mergeinfo_cat, &repos_root,
- target_path_or_url, target_peg_revision,
- depth == svn_depth_infinity, TRUE,
- ctx, scratch_pool, scratch_pool));
+ if (target_mergeinfo_catalog)
+ {
+ if (*target_mergeinfo_catalog)
+ {
+ /* The caller provided the mergeinfo catalog for
+ TARGET_PATH_OR_URL, so we don't need to accquire
+ it ourselves. We do need to get the repos_root
+ though, because get_mergeinfo() won't do it for us. */
+ target_mergeinfo_cat = *target_mergeinfo_catalog;
+ SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
+ target_path_or_url, NULL,
+ target_peg_revision,
+ target_peg_revision,
+ ctx, subpool));
+ SVN_ERR(svn_ra_get_repos_root2(target_session, &repos_root,
+ scratch_pool));
+ }
+ else
+ {
+ /* The caller didn't provide the mergeinfo catalog for
+ TARGET_PATH_OR_URL, but wants us to pass a copy back
+ when we get it, so use RESULT_POOL. */
+ SVN_ERR(get_mergeinfo(target_mergeinfo_catalog, &repos_root,
+ target_path_or_url, target_peg_revision,
+ depth == svn_depth_infinity, TRUE,
+ ctx, result_pool, scratch_pool));
+ target_mergeinfo_cat = *target_mergeinfo_catalog;
+ }
+ }
+ else
+ {
+ /* The caller didn't provide the mergeinfo catalog for
+ TARGET_PATH_OR_URL, nor does it want a copy, so we can use
+ nothing but SCRATCH_POOL. */
+ SVN_ERR(get_mergeinfo(&target_mergeinfo_cat, &repos_root,
+ target_path_or_url, target_peg_revision,
+ depth == svn_depth_infinity, TRUE,
+ ctx, scratch_pool, scratch_pool));
+ }
if (!svn_path_is_url(target_path_or_url))
{
@@ -1739,7 +1779,7 @@ svn_client_mergeinfo_log2(svn_boolean_t
/* TARGET_REPOS_REL should be non-NULL, else get_mergeinfo
should have failed. */
- SVN_ERR_ASSERT(target_repos_relpath != NULL);
+ SVN_ERR_ASSERT(target_repos_relpath != NULL);
}
if (!target_mergeinfo_cat)
@@ -1752,15 +1792,14 @@ svn_client_mergeinfo_log2(svn_boolean_t
history. */
if (finding_merged)
{
+ svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
else
{
target_mergeinfo_cat = apr_hash_make(scratch_pool);
- apr_hash_set(target_mergeinfo_cat,
- target_repos_relpath,
- APR_HASH_KEY_STRING,
- apr_hash_make(scratch_pool));
+ svn_hash_sets(target_mergeinfo_cat, target_repos_relpath,
+ apr_hash_make(scratch_pool));
}
}
@@ -1771,18 +1810,17 @@ svn_client_mergeinfo_log2(svn_boolean_t
* ### TODO: As the source and target must be in the same repository, we
* should share a single session, tracking the two URLs separately. */
{
- apr_pool_t *sesspool = svn_pool_create(scratch_pool);
- svn_ra_session_t *source_session, *target_session;
- svn_client__pathrev_t *pathrev;
+ svn_ra_session_t *source_session;
svn_revnum_t start_rev, end_rev, youngest_rev = SVN_INVALID_REVNUM;
-
+
if (! finding_merged)
{
- SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
- target_path_or_url, NULL,
- target_peg_revision,
- target_peg_revision,
- ctx, sesspool));
+ if (!target_session)
+ SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
+ target_path_or_url, NULL,
+ target_peg_revision,
+ target_peg_revision,
+ ctx, subpool));
SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
pathrev,
SVN_INVALID_REVNUM,
@@ -1790,29 +1828,33 @@ svn_client_mergeinfo_log2(svn_boolean_t
target_session, ctx,
scratch_pool));
}
-
+
SVN_ERR(svn_client__ra_session_from_path2(&source_session, &pathrev,
source_path_or_url, NULL,
source_peg_revision,
source_peg_revision,
- ctx, sesspool));
+ ctx, subpool));
SVN_ERR(svn_client__get_revision_number(&start_rev, &youngest_rev,
ctx->wc_ctx, source_path_or_url,
source_session,
source_start_revision,
- sesspool));
+ subpool));
SVN_ERR(svn_client__get_revision_number(&end_rev, &youngest_rev,
ctx->wc_ctx, source_path_or_url,
source_session,
source_end_revision,
- sesspool));
+ subpool));
SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
- pathrev, end_rev, start_rev,
+ pathrev,
+ MAX(end_rev, start_rev),
+ MIN(end_rev, start_rev),
source_session, ctx,
scratch_pool));
+ if (start_rev > end_rev)
+ oldest_revs_first = FALSE;
/* Close the source and target sessions. */
- svn_pool_destroy(sesspool);
+ svn_pool_destroy(subpool);
}
/* Separate the explicit or inherited mergeinfo on TARGET_PATH_OR_URL,
@@ -1930,18 +1972,17 @@ svn_client_mergeinfo_log2(svn_boolean_t
SVN_ERR(svn_rangelist__merge_many(subtree_merged_rangelist,
merged, scratch_pool, iterpool));
- apr_hash_set(inheritable_subtree_merges, subtree_path,
- APR_HASH_KEY_STRING, subtree_merged_rangelist);
+ svn_hash_sets(inheritable_subtree_merges, subtree_path,
+ subtree_merged_rangelist);
}
else
{
/* Map SUBTREE_PATH to an empty rangelist if there was nothing
fully merged. e.g. Only empty or non-inheritable mergeinfo
on the subtree or mergeinfo unrelated to the source. */
- apr_hash_set(inheritable_subtree_merges, subtree_path,
- APR_HASH_KEY_STRING,
- apr_array_make(scratch_pool, 0,
- sizeof(svn_merge_range_t *)));
+ svn_hash_sets(inheritable_subtree_merges, subtree_path,
+ apr_array_make(scratch_pool, 0,
+ sizeof(svn_merge_range_t *)));
}
}
@@ -2073,6 +2114,7 @@ svn_client_mergeinfo_log2(svn_boolean_t
SVN_ERR(logs_for_mergeinfo_rangelist(log_target, merge_source_fspaths,
finding_merged,
master_inheritable_rangelist,
+ oldest_revs_first,
target_mergeinfo_cat,
svn_fspath__join("/",
target_repos_relpath,
@@ -2085,6 +2127,31 @@ svn_client_mergeinfo_log2(svn_boolean_t
}
svn_error_t *
+svn_client_mergeinfo_log2(svn_boolean_t finding_merged,
+ const char *target_path_or_url,
+ const svn_opt_revision_t *target_peg_revision,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_peg_revision,
+ const svn_opt_revision_t *source_start_revision,
+ const svn_opt_revision_t *source_end_revision,
+ svn_log_entry_receiver_t log_receiver,
+ void *log_receiver_baton,
+ svn_boolean_t discover_changed_paths,
+ svn_depth_t depth,
+ const apr_array_header_t *revprops,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ return svn_client__mergeinfo_log(finding_merged, target_path_or_url,
+ target_peg_revision, NULL,
+ source_path_or_url, source_peg_revision,
+ source_start_revision, source_end_revision,
+ log_receiver, log_receiver_baton,
+ discover_changed_paths, depth, revprops,
+ ctx, scratch_pool, scratch_pool);
+}
+
+svn_error_t *
svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.h?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/mergeinfo.h Fri May 10 14:58:47 2013
@@ -339,7 +339,12 @@ svn_client__record_wc_mergeinfo(const ch
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
-/* Write mergeinfo into the WC. RESULT_CATALOG maps (const char *) WC paths
+/* Write mergeinfo into the WC.
+ *
+ * For each path in RESULT_CATALOG, set the SVN_PROP_MERGEINFO
+ * property to represent the given mergeinfo, or remove the property
+ * if the given mergeinfo is null, and notify the change. Leave
+ * other paths unchanged. RESULT_CATALOG maps (const char *) WC paths
* to (svn_mergeinfo_t) mergeinfo. */
svn_error_t *
svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog,
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/patch.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/patch.c Fri May 10 14:58:47 2013
@@ -313,8 +313,7 @@ obtain_eol_and_keywords_for_file(apr_has
SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, local_abspath,
scratch_pool, scratch_pool));
- keywords_val = apr_hash_get(props, SVN_PROP_KEYWORDS,
- APR_HASH_KEY_STRING);
+ keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS);
if (keywords_val)
{
svn_revnum_t changed_rev;
@@ -322,6 +321,7 @@ obtain_eol_and_keywords_for_file(apr_has
const char *rev_str;
const char *author;
const char *url;
+ const char *root_url;
SVN_ERR(svn_wc__node_get_changed_info(&changed_rev,
&changed_date,
@@ -333,14 +333,16 @@ obtain_eol_and_keywords_for_file(apr_has
SVN_ERR(svn_wc__node_get_url(&url, wc_ctx,
local_abspath,
scratch_pool, scratch_pool));
- SVN_ERR(svn_subst_build_keywords2(keywords,
+ SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, &root_url, NULL,
+ wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_subst_build_keywords3(keywords,
keywords_val->data,
- rev_str, url, changed_date,
+ rev_str, url, root_url, changed_date,
author, result_pool));
}
- eol_style_val = apr_hash_get(props, SVN_PROP_EOL_STYLE,
- APR_HASH_KEY_STRING);
+ eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
if (eol_style_val)
{
svn_subst_eol_style_from_value(eol_style,
@@ -1143,8 +1145,7 @@ init_patch_target(patch_target_t **patch
prop_patch->operation,
wc_ctx, target->local_abspath,
result_pool, scratch_pool));
- apr_hash_set(target->prop_targets, prop_name,
- APR_HASH_KEY_STRING, prop_target);
+ svn_hash_sets(target->prop_targets, prop_name, prop_target);
}
}
}
@@ -2188,8 +2189,7 @@ apply_one_patch(patch_target_t **patch_t
target->is_special = TRUE;
/* We'll store matched hunks in prop_content. */
- prop_target = apr_hash_get(target->prop_targets, prop_name,
- APR_HASH_KEY_STRING);
+ prop_target = svn_hash_gets(target->prop_targets, prop_name);
for (i = 0; i < prop_patch->hunks->nelts; i++)
{
@@ -2276,7 +2276,7 @@ apply_one_patch(patch_target_t **patch_t
* will be closed later in write_out_rejected_hunks(). */
if (target->kind_on_disk == svn_node_file)
SVN_ERR(svn_io_file_close(target->file, scratch_pool));
-
+
SVN_ERR(svn_io_file_close(target->patched_file, scratch_pool));
}
@@ -2826,13 +2826,13 @@ check_ancestor_delete(const char *delete
cb.local_abspath = dir_abspath;
cb.must_keep = FALSE;
cb.targets_info = targets_info;
-
+
err = svn_wc_walk_status(ctx->wc_ctx, dir_abspath, svn_depth_infinity,
TRUE, FALSE, FALSE, NULL,
can_delete_callback, &cb,
ctx->cancel_func, ctx->cancel_baton,
iterpool);
-
+
if (err)
{
if (err->apr_err != SVN_ERR_CEASE_INVOCATION)