summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/merge.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/merge.c')
-rw-r--r--subversion/libsvn_client/merge.c677
1 files changed, 426 insertions, 251 deletions
diff --git a/subversion/libsvn_client/merge.c b/subversion/libsvn_client/merge.c
index f0ff9a2..aaab4e0 100644
--- a/subversion/libsvn_client/merge.c
+++ b/subversion/libsvn_client/merge.c
@@ -54,13 +54,12 @@
#include "client.h"
#include "mergeinfo.h"
-#include "private/svn_opt_private.h"
-#include "private/svn_wc_private.h"
-#include "private/svn_mergeinfo_private.h"
#include "private/svn_fspath.h"
-#include "private/svn_ra_private.h"
+#include "private/svn_mergeinfo_private.h"
#include "private/svn_client_private.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_subr_private.h"
+#include "private/svn_wc_private.h"
#include "svn_private_config.h"
@@ -570,14 +569,16 @@ perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
}
/* Create *LEFT and *RIGHT conflict versions for conflict victim
- * at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained
- * from MERGE_SOURCE and TARGET.
+ * at VICTIM_ABSPATH, with merge-left node kind MERGE_LEFT_NODE_KIND
+ * and merge-right node kind MERGE_RIGHT_NODE_KIND, using information
+ * obtained from MERGE_SOURCE and TARGET.
* Allocate returned conflict versions in RESULT_POOL. */
static svn_error_t *
make_conflict_versions(const svn_wc_conflict_version_t **left,
const svn_wc_conflict_version_t **right,
const char *victim_abspath,
- svn_node_kind_t node_kind,
+ svn_node_kind_t merge_left_node_kind,
+ svn_node_kind_t merge_right_node_kind,
const merge_source_t *merge_source,
const merge_target_t *target,
apr_pool_t *result_pool,
@@ -597,13 +598,15 @@ make_conflict_versions(const svn_wc_conflict_version_t **left,
merge_source->loc1->repos_root_url,
merge_source->loc1->repos_uuid,
svn_relpath_join(left_relpath, child, scratch_pool),
- merge_source->loc1->rev, node_kind, result_pool);
+ merge_source->loc1->rev,
+ merge_left_node_kind, result_pool);
*right = svn_wc_conflict_version_create2(
merge_source->loc2->repos_root_url,
merge_source->loc2->repos_uuid,
svn_relpath_join(right_relpath, child, scratch_pool),
- merge_source->loc2->rev, node_kind, result_pool);
+ merge_source->loc2->rev,
+ merge_right_node_kind, result_pool);
return SVN_NO_ERROR;
}
@@ -633,8 +636,8 @@ split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
{
int i;
- const char *merge_source_path = svn__apr_hash_index_key(hi);
- svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
+ const char *merge_source_path = apr_hash_this_key(hi);
+ svn_rangelist_t *rangelist = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
@@ -764,7 +767,7 @@ filter_self_referential_mergeinfo(apr_array_header_t **props,
/* If PATH itself has been added there is no need to filter. */
SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath,
&target_base.repos_root_url,
- &target_base.repos_uuid, NULL,
+ &target_base.repos_uuid, NULL, NULL,
ctx->wc_ctx, target_abspath, FALSE,
pool, pool));
@@ -863,8 +866,8 @@ filter_self_referential_mergeinfo(apr_array_header_t **props,
hi; hi = apr_hash_next(hi))
{
int j;
- const char *source_path = svn__apr_hash_index_key(hi);
- svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
+ const char *source_path = apr_hash_this_key(hi);
+ svn_rangelist_t *rangelist = apr_hash_this_val(hi);
const char *merge_source_url;
svn_rangelist_t *adjusted_rangelist =
apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
@@ -1182,6 +1185,9 @@ struct merge_dir_baton_t
*/
svn_wc_conflict_reason_t tree_conflict_reason;
svn_wc_conflict_action_t tree_conflict_action;
+ svn_node_kind_t tree_conflict_local_node_kind;
+ svn_node_kind_t tree_conflict_merge_left_node_kind;
+ svn_node_kind_t tree_conflict_merge_right_node_kind;
/* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
add to the notification */
@@ -1233,6 +1239,9 @@ struct merge_file_baton_t
merge_tree_baton_t for an explanation. */
svn_wc_conflict_reason_t tree_conflict_reason;
svn_wc_conflict_action_t tree_conflict_action;
+ svn_node_kind_t tree_conflict_local_node_kind;
+ svn_node_kind_t tree_conflict_merge_left_node_kind;
+ svn_node_kind_t tree_conflict_merge_right_node_kind;
/* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
add to the notification */
@@ -1280,12 +1289,21 @@ record_skip(merge_cmd_baton_t *merge_b,
notify->kind = kind;
notify->content_state = notify->prop_state = state;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
}
+/* Forward declaration */
+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);
+
/* Record a tree conflict in the WC, unless this is a dry run or a record-
* only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
* (The latter can happen if a merge-tracking-aware merge is doing multiple
@@ -1294,8 +1312,6 @@ record_skip(merge_cmd_baton_t *merge_b,
* The tree conflict, with its victim specified by VICTIM_PATH, is
* assumed to have happened during a merge using merge baton MERGE_B.
*
- * NODE_KIND must be the node kind of "old" and "theirs" and "mine";
- * this function cannot cope with node kind clashes.
* ACTION and REASON correspond to the fields
* of the same names in svn_wc_tree_conflict_description_t.
*/
@@ -1303,7 +1319,9 @@ static svn_error_t *
record_tree_conflict(merge_cmd_baton_t *merge_b,
const char *local_abspath,
struct merge_dir_baton_t *parent_baton,
- svn_node_kind_t node_kind,
+ svn_node_kind_t local_node_kind,
+ svn_node_kind_t merge_left_node_kind,
+ svn_node_kind_t merge_right_node_kind,
svn_wc_conflict_action_t action,
svn_wc_conflict_reason_t reason,
const svn_wc_conflict_description2_t *existing_conflict,
@@ -1357,16 +1375,53 @@ record_tree_conflict(merge_cmd_baton_t *merge_b,
reason = svn_wc_conflict_reason_moved_here;
}
- SVN_ERR(make_conflict_versions(&left, &right, local_abspath, node_kind,
- &merge_b->merge_source, merge_b->target,
- result_pool, scratch_pool));
+ if (HONOR_MERGEINFO(merge_b) && merge_b->merge_source.ancestral)
+ {
+ struct merge_source_t *source;
+ svn_client__pathrev_t *loc1;
+ svn_client__pathrev_t *loc2;
+ svn_merge_range_t range =
+ {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
+
+ /* We are honoring mergeinfo so do not blindly record
+ * a conflict describing the merge of
+ * SOURCE->LOC1->URL@SOURCE->LOC1->REV through
+ * SOURCE->LOC2->URL@SOURCE->LOC2->REV
+ * but figure out the actual revision range merged. */
+ (void)find_nearest_ancestor_with_intersecting_ranges(
+ &(range.start), &(range.end),
+ merge_b->notify_begin.nodes_with_mergeinfo,
+ action != svn_wc_conflict_action_delete,
+ local_abspath);
+ loc1 = svn_client__pathrev_dup(merge_b->merge_source.loc1,
+ scratch_pool);
+ loc2 = svn_client__pathrev_dup(merge_b->merge_source.loc2,
+ scratch_pool);
+ loc1->rev = range.start;
+ loc2->rev = range.end;
+ source = merge_source_create(loc1, loc2,
+ merge_b->merge_source.ancestral,
+ scratch_pool);
+ SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
+ merge_left_node_kind,
+ merge_right_node_kind,
+ source, merge_b->target,
+ result_pool, scratch_pool));
+ }
+ else
+ SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
+ merge_left_node_kind,
+ merge_right_node_kind,
+ &merge_b->merge_source, merge_b->target,
+ result_pool, scratch_pool));
/* Fix up delete of file, add of dir replacement (or other way around) */
if (existing_conflict != NULL && existing_conflict->src_left_version)
left = existing_conflict->src_left_version;
conflict = svn_wc_conflict_description_create_tree2(
- local_abspath, node_kind, svn_wc_operation_merge,
+ local_abspath, local_node_kind,
+ svn_wc_operation_merge,
left, right, result_pool);
conflict->action = action;
@@ -1402,10 +1457,10 @@ record_tree_conflict(merge_cmd_baton_t *merge_b,
notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
scratch_pool);
- notify->kind = node_kind;
+ notify->kind = local_node_kind;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
@@ -1439,8 +1494,8 @@ record_update_add(merge_cmd_baton_t *merge_b,
notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
notify->kind = kind;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
@@ -1473,8 +1528,8 @@ record_update_update(merge_cmd_baton_t *merge_b,
notify->content_state = content_state;
notify->prop_state = prop_state;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
@@ -1531,17 +1586,17 @@ handle_pending_notifications(merge_cmd_baton_t *merge_b,
hi;
hi = apr_hash_next(hi))
{
- const char *del_abspath = svn__apr_hash_index_key(hi);
+ const char *del_abspath = apr_hash_this_key(hi);
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(del_abspath,
svn_wc_notify_update_delete,
scratch_pool);
notify->kind = svn_node_kind_from_word(
- svn__apr_hash_index_val(hi));
+ apr_hash_this_val(hi));
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
- notify, scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
+ notify, scratch_pool);
}
db->pending_deletes = NULL;
@@ -1607,9 +1662,9 @@ mark_dir_edited(merge_cmd_baton_t *merge_b,
notify->kind = svn_node_dir;
notify->content_state = notify->prop_state = db->skip_reason;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
- notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
+ notify,
+ scratch_pool);
}
if (merge_b->merge_source.ancestral
@@ -1623,7 +1678,10 @@ mark_dir_edited(merge_cmd_baton_t *merge_b,
/* open_directory() decided that a tree conflict should be raised */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
- svn_node_dir, db->tree_conflict_action,
+ db->tree_conflict_local_node_kind,
+ db->tree_conflict_merge_left_node_kind,
+ db->tree_conflict_merge_right_node_kind,
+ db->tree_conflict_action,
db->tree_conflict_reason,
NULL, TRUE,
scratch_pool));
@@ -1686,9 +1744,9 @@ mark_file_edited(merge_cmd_baton_t *merge_b,
notify->kind = svn_node_file;
notify->content_state = notify->prop_state = fb->skip_reason;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
- notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
+ notify,
+ scratch_pool);
}
if (merge_b->merge_source.ancestral
@@ -1702,7 +1760,10 @@ mark_file_edited(merge_cmd_baton_t *merge_b,
/* open_file() decided that a tree conflict should be raised */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
- svn_node_file, fb->tree_conflict_action,
+ fb->tree_conflict_local_node_kind,
+ fb->tree_conflict_merge_left_node_kind,
+ fb->tree_conflict_merge_right_node_kind,
+ fb->tree_conflict_action,
fb->tree_conflict_reason,
NULL, TRUE,
scratch_pool));
@@ -1742,6 +1803,16 @@ merge_file_opened(void **new_file_baton,
fb->tree_conflict_action = svn_wc_conflict_action_edit;
fb->skip_reason = svn_wc_notify_state_unknown;
+ if (left_source)
+ fb->tree_conflict_merge_left_node_kind = svn_node_file;
+ else
+ fb->tree_conflict_merge_left_node_kind = svn_node_none;
+
+ if (right_source)
+ fb->tree_conflict_merge_right_node_kind = svn_node_file;
+ else
+ fb->tree_conflict_merge_right_node_kind = svn_node_none;
+
*new_file_baton = fb;
if (pdb)
@@ -1758,7 +1829,6 @@ merge_file_opened(void **new_file_baton,
else if (left_source != NULL)
{
/* Node is expected to be a file, which will be changed or deleted. */
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
svn_boolean_t excluded;
svn_depth_t parent_depth;
@@ -1770,7 +1840,8 @@ merge_file_opened(void **new_file_baton,
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
- &kind, &parent_depth,
+ &fb->tree_conflict_local_node_kind,
+ &parent_depth,
merge_b, local_abspath,
scratch_pool));
@@ -1783,10 +1854,10 @@ merge_file_opened(void **new_file_baton,
}
if (is_deleted)
- kind = svn_node_none;
+ fb->tree_conflict_local_node_kind = svn_node_none;
}
- if (kind == svn_node_none)
+ if (fb->tree_conflict_local_node_kind == svn_node_none)
{
fb->shadowed = TRUE;
@@ -1820,11 +1891,16 @@ merge_file_opened(void **new_file_baton,
return SVN_NO_ERROR;
/* ### /Similar */
}
- else if (kind != svn_node_file)
+ else if (fb->tree_conflict_local_node_kind != svn_node_file)
{
+ svn_boolean_t added;
fb->shadowed = TRUE;
- fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
+
+ fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
/* ### Similar to directory */
*skip = TRUE;
@@ -1879,6 +1955,8 @@ merge_file_opened(void **new_file_baton,
/* Update the tree conflict to store that this is a replace */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ old_tc->node_kind,
+ old_tc->src_left_version->node_kind,
svn_node_file,
fb->tree_conflict_action,
fb->tree_conflict_reason,
@@ -1905,12 +1983,11 @@ merge_file_opened(void **new_file_baton,
&& ((pdb && pdb->added) || fb->add_is_replace)))
{
svn_wc_notify_state_t obstr_state;
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
- &kind, NULL,
- merge_b, local_abspath,
+ &fb->tree_conflict_local_node_kind,
+ NULL, merge_b, local_abspath,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
@@ -1920,11 +1997,18 @@ merge_file_opened(void **new_file_baton,
fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
fb->skip_reason = obstr_state;
}
- else if (kind != svn_node_none && !is_deleted)
+ else if (fb->tree_conflict_local_node_kind != svn_node_none
+ && !is_deleted)
{
/* Set a tree conflict */
+ svn_boolean_t added;
+
fb->shadowed = TRUE;
- fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
+
+ fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
}
}
@@ -2000,7 +2084,8 @@ merge_file_changed(const char *relpath,
scratch_pool, scratch_pool));
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
- svn_node_file, &merge_b->merge_source, merge_b->target,
+ svn_node_file, svn_node_file,
+ &merge_b->merge_source, merge_b->target,
scratch_pool, scratch_pool));
/* Do property merge now, if we are not going to perform a text merge */
@@ -2307,17 +2392,47 @@ files_same_p(svn_boolean_t *same,
{
svn_stream_t *mine_stream;
svn_stream_t *older_stream;
- svn_opt_revision_t working_rev = { svn_opt_revision_working, { 0 } };
+ svn_string_t *special = svn_hash_gets(working_props, SVN_PROP_SPECIAL);
+ svn_string_t *eol_style = svn_hash_gets(working_props, SVN_PROP_EOL_STYLE);
+ svn_string_t *keywords = svn_hash_gets(working_props, SVN_PROP_KEYWORDS);
/* Compare the file content, translating 'mine' to 'normal' form. */
- if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL)
+ if (special != NULL)
SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
scratch_pool, scratch_pool));
else
- SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx,
- mine_abspath, &working_rev,
- FALSE, TRUE, NULL, NULL,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_open_readonly(&mine_stream, mine_abspath,
+ scratch_pool, scratch_pool));
+
+ if (!special && (eol_style || keywords))
+ {
+ apr_hash_t *kw = NULL;
+ const char *eol = NULL;
+ svn_subst_eol_style_t style;
+
+ /* We used to use svn_client__get_normalized_stream() here, but
+ that doesn't work in 100% of the cases because it doesn't
+ convert EOLs to the repository form; just to '\n'.
+ */
+
+ if (eol_style)
+ {
+ svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
+
+ if (style == svn_subst_eol_style_native)
+ eol = SVN_SUBST_NATIVE_EOL_STR;
+ else if (style != svn_subst_eol_style_fixed
+ && style != svn_subst_eol_style_none)
+ return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
+ }
+
+ if (keywords)
+ SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, "", "",
+ "", 0, "", scratch_pool));
+
+ mine_stream = svn_subst_stream_translated(
+ mine_stream, eol, FALSE, kw, FALSE, scratch_pool);
+ }
SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
scratch_pool, scratch_pool));
@@ -2427,6 +2542,8 @@ merge_file_deleted(const char *relpath,
*/
SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
svn_node_file,
+ svn_node_file,
+ svn_node_none,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited,
NULL, TRUE,
@@ -2447,7 +2564,7 @@ merge_file_deleted(const char *relpath,
When *SKIP is TRUE, the diff driver avoids work on getting the details
for the closing callbacks.
- The SKIP and SKIP_DESCENDANTS work independantly.
+ The SKIP and SKIP_DESCENDANTS work independently.
*/
static svn_error_t *
merge_dir_opened(void **new_dir_baton,
@@ -2477,6 +2594,16 @@ merge_dir_opened(void **new_dir_baton,
*new_dir_baton = db;
+ if (left_source)
+ db->tree_conflict_merge_left_node_kind = svn_node_dir;
+ else
+ db->tree_conflict_merge_left_node_kind = svn_node_none;
+
+ if (right_source)
+ db->tree_conflict_merge_right_node_kind = svn_node_dir;
+ else
+ db->tree_conflict_merge_right_node_kind = svn_node_none;
+
if (pdb)
{
db->parent_baton = pdb;
@@ -2493,7 +2620,6 @@ merge_dir_opened(void **new_dir_baton,
else if (left_source != NULL)
{
/* Node is expected to be a directory. */
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
svn_boolean_t excluded;
svn_depth_t parent_depth;
@@ -2505,9 +2631,9 @@ merge_dir_opened(void **new_dir_baton,
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
- &kind, &parent_depth,
- merge_b, local_abspath,
- scratch_pool));
+ &db->tree_conflict_local_node_kind,
+ &parent_depth, merge_b,
+ local_abspath, scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
@@ -2542,10 +2668,10 @@ merge_dir_opened(void **new_dir_baton,
}
if (is_deleted)
- kind = svn_node_none;
+ db->tree_conflict_local_node_kind = svn_node_none;
}
- if (kind == svn_node_none)
+ if (db->tree_conflict_local_node_kind == svn_node_none)
{
db->shadowed = TRUE;
@@ -2581,11 +2707,16 @@ merge_dir_opened(void **new_dir_baton,
return SVN_NO_ERROR;
/* ### /avoid breaking tests */
}
- else if (kind != svn_node_dir)
+ else if (db->tree_conflict_local_node_kind != svn_node_dir)
{
+ svn_boolean_t added;
+
db->shadowed = TRUE;
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
- db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
+ db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
/* ### To avoid breaking tests */
*skip = TRUE;
@@ -2672,6 +2803,8 @@ merge_dir_opened(void **new_dir_baton,
/* Update the tree conflict to store that this is a replace */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ old_tc->node_kind,
+ old_tc->src_left_version->node_kind,
svn_node_dir,
db->tree_conflict_action,
db->tree_conflict_reason,
@@ -2686,12 +2819,11 @@ merge_dir_opened(void **new_dir_baton,
&& ((pdb && pdb->added) || db->add_is_replace)))
{
svn_wc_notify_state_t obstr_state;
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
- &kind, NULL,
- merge_b, local_abspath,
+ &db->tree_conflict_local_node_kind,
+ NULL, merge_b, local_abspath,
scratch_pool));
/* In this case of adding a directory, we have an exception to the
@@ -2701,7 +2833,8 @@ merge_dir_opened(void **new_dir_baton,
* versioned but unexpectedly missing from disk, or is unversioned
* but obstructed by a node of the wrong kind. */
if (obstr_state == svn_wc_notify_state_obstructed
- && (is_deleted || kind == svn_node_none))
+ && (is_deleted ||
+ db->tree_conflict_local_node_kind == svn_node_none))
{
svn_node_kind_t disk_kind;
@@ -2722,17 +2855,18 @@ merge_dir_opened(void **new_dir_baton,
db->tree_conflict_reason = CONFLICT_REASON_SKIP;
db->skip_reason = obstr_state;
}
- else if (kind != svn_node_none && !is_deleted)
+ else if (db->tree_conflict_local_node_kind != svn_node_none
+ && !is_deleted)
{
/* Set a tree conflict */
+ svn_boolean_t added;
db->shadowed = TRUE;
- db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
- if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
- && !(pdb && pdb->shadowed))
- {
- store_path(merge_b->skipped_abspaths, local_abspath);
- }
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
+
+ db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
}
}
@@ -2758,7 +2892,7 @@ merge_dir_opened(void **new_dir_baton,
if (old_tc)
{
- /* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node
+ /* svn_wc_add4 and svn_wc_add_from_disk3 can't add a node
over an existing tree conflict */
/* ### These functions should take some tree conflict argument
@@ -2796,8 +2930,9 @@ merge_dir_opened(void **new_dir_baton,
}
else
{
- SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath,
+ SVN_ERR(svn_wc_add_from_disk3(merge_b->ctx->wc_ctx, local_abspath,
apr_hash_make(scratch_pool),
+ FALSE /* skip checks */,
NULL, NULL /* no notify! */,
scratch_pool));
}
@@ -2806,6 +2941,8 @@ merge_dir_opened(void **new_dir_baton,
{
/* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ old_tc->node_kind,
+ svn_node_none,
svn_node_dir,
db->tree_conflict_action,
db->tree_conflict_reason,
@@ -2875,7 +3012,8 @@ merge_dir_changed(const char *relpath,
svn_wc_notify_state_t prop_state;
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
- svn_node_dir, &merge_b->merge_source,
+ svn_node_dir, svn_node_dir,
+ &merge_b->merge_source,
merge_b->target,
scratch_pool, scratch_pool));
@@ -3225,6 +3363,8 @@ merge_dir_deleted(const char *relpath,
*/
SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
svn_node_dir,
+ svn_node_dir,
+ svn_node_none,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited,
NULL, TRUE,
@@ -3643,8 +3783,8 @@ notify_merge_begin(merge_cmd_baton_t *merge_b,
notify->merge_range = NULL;
}
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
return SVN_NO_ERROR;
}
@@ -3792,111 +3932,96 @@ adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
younger_rev, older_rev, ctx,
scratch_pool);
- /* If PRIMARY_URL@peg_rev doesn't exist then
- svn_client__repos_location_segments() typically returns an
- SVN_ERR_FS_NOT_FOUND error, but if it doesn't exist for a
- forward merge over ra_neon then we get SVN_ERR_RA_DAV_REQUEST_FAILED.
- http://subversion.tigris.org/issues/show_bug.cgi?id=3137 fixed some of
- the cases where different RA layers returned different error codes to
- signal the "path not found"...but it looks like there is more to do.
-
- ### Do we still need to special case for ra_neon (since it no longer
- exists)? */
if (err)
{
- if (err->apr_err == SVN_ERR_FS_NOT_FOUND
- || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
+ 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)
{
- /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
- exists, if neither exist then the editor can simply ignore this
- subtree. */
- const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
- svn_node_kind_t kind;
+ /* 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;
- svn_error_clear(err);
- err = NULL;
+ /* 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));
- SVN_ERR(svn_ra_get_path_relative_to_session(
- ra_session, &rel_source_path, primary_url, 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));
- SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
- older_rev, &kind, scratch_pool));
- if (kind == svn_node_none)
+ /* 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)
{
- /* 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);
+ /* 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));
}
- 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));
- /* 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));
+ /* 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));
- }
+ /* 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
- {
- return svn_error_trace(err);
- }
}
else /* PRIMARY_URL@peg_rev exists. */
{
@@ -4684,7 +4809,6 @@ calculate_remaining_ranges(svn_client__merge_path_t *parent,
NULL, NULL, NULL, NULL,
ctx->wc_ctx, child->abspath,
TRUE /* ignore_enoent */,
- FALSE /* show_hidden */,
scratch_pool, scratch_pool));
/* If CHILD has no base revision then it hasn't been committed yet, so it
can't have any "future" history. */
@@ -4779,7 +4903,7 @@ find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
*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)
+ if (labs(source->loc1->rev - source->loc2->rev) == 1)
return SVN_NO_ERROR;
/* Get SOURCE as mergeinfo. */
@@ -5174,8 +5298,8 @@ update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
the WC with its on-disk mergeinfo. */
for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
{
- const char *local_abspath = svn__apr_hash_index_key(hi);
- svn_rangelist_t *ranges = svn__apr_hash_index_val(hi);
+ const char *local_abspath = apr_hash_this_key(hi);
+ svn_rangelist_t *ranges = apr_hash_this_val(hi);
svn_rangelist_t *rangelist;
svn_error_t *err;
const char *local_abspath_rel_to_target;
@@ -5326,7 +5450,7 @@ record_skips_in_mergeinfo(const char *mergeinfo_path,
for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
hi = apr_hash_next(hi))
{
- const char *skipped_abspath = svn__apr_hash_index_key(hi);
+ const char *skipped_abspath = apr_hash_this_key(hi);
svn_wc_notify_state_t obstruction_state;
svn_pool_clear(iterpool);
@@ -5882,7 +6006,7 @@ slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
split_range2->start = end_rev;
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *) = split_range1;
- svn_sort__array_insert(&split_range2, child->remaining_ranges, 1);
+ svn_sort__array_insert(child->remaining_ranges, &split_range2, 1);
}
}
}
@@ -6017,11 +6141,11 @@ insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
/* Find where to insert the new element */
insert_index =
- svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo,
+ svn_sort__bsearch_lower_bound(children_with_mergeinfo, &insert_element,
compare_merge_path_t_as_paths);
new_element = svn_client__merge_path_dup(insert_element, pool);
- svn_sort__array_insert(&new_element, children_with_mergeinfo, insert_index);
+ svn_sort__array_insert(children_with_mergeinfo, &new_element, insert_index);
}
/* Helper for get_mergeinfo_paths().
@@ -6088,8 +6212,9 @@ insert_parent_and_sibs_of_sw_absent_del_subtree(
} /*(parent == NULL) */
/* Add all of PARENT's non-missing children that are not already present.*/
- SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx,
- parent_abspath, FALSE, pool, pool));
+ SVN_ERR(svn_wc__node_get_children_of_working_node(&children, ctx->wc_ctx,
+ parent_abspath,
+ pool, pool));
iterpool = svn_pool_create(pool);
for (i = 0; i < children->nelts; i++)
{
@@ -6188,7 +6313,7 @@ pre_merge_status_cb(void *baton,
hi;
hi = apr_hash_next(hi))
{
- const char *missing_root_path = svn__apr_hash_index_key(hi);
+ const char *missing_root_path = apr_hash_this_key(hi);
if (svn_dirent_is_ancestor(missing_root_path,
local_abspath))
@@ -6246,8 +6371,8 @@ get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
- svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
+ const char *wc_path = apr_hash_this_key(hi);
+ svn_string_t *mergeinfo_string = apr_hash_this_val(hi);
svn_mergeinfo_t mergeinfo;
svn_error_t *err;
@@ -6340,6 +6465,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_pool_t *swmi_pool;
apr_hash_t *subtrees_with_mergeinfo;
apr_hash_t *excluded_subtrees;
apr_hash_t *switched_subtrees;
@@ -6348,10 +6474,13 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
struct pre_merge_status_baton_t pre_merge_status_baton;
/* Case 1: Subtrees with explicit mergeinfo. */
+ /* Use a subpool for subtrees_with_mergeinfo, as it can be very large
+ and is temporary. */
+ swmi_pool = svn_pool_create(scratch_pool);
SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
target->abspath,
depth, ctx,
- result_pool, scratch_pool));
+ swmi_pool, swmi_pool));
if (subtrees_with_mergeinfo)
{
apr_hash_index_t *hi;
@@ -6360,8 +6489,8 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
- svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
+ const char *wc_path = apr_hash_this_key(hi);
+ svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi);
svn_client__merge_path_t *mergeinfo_child =
svn_client__merge_path_create(wc_path, result_pool);
@@ -6388,6 +6517,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
children_with_mergeinfo->elt_size,
compare_merge_path_t_as_paths);
}
+ svn_pool_destroy(swmi_pool);
/* Case 2: Switched subtrees
Case 10: Paths at depths of 'empty' or 'files'
@@ -6429,7 +6559,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
svn_pool_clear(iterpool);
svn_stringbuf_appendcstr(missing_subtree_err_buf,
svn_dirent_local_style(
- svn__apr_hash_index_key(hi), iterpool));
+ apr_hash_this_key(hi), iterpool));
svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
}
@@ -6445,7 +6575,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
+ const char *wc_path = apr_hash_this_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
@@ -6473,8 +6603,8 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi = apr_hash_next(hi))
{
svn_boolean_t new_shallow_child = FALSE;
- const char *wc_path = svn__apr_hash_index_key(hi);
- svn_depth_t *child_depth = svn__apr_hash_index_val(hi);
+ const char *wc_path = apr_hash_this_key(hi);
+ svn_depth_t *child_depth = apr_hash_this_val(hi);
svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
@@ -6528,7 +6658,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
+ const char *wc_path = apr_hash_this_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
@@ -6571,7 +6701,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
SVN_ERR(svn_wc__node_get_children_of_working_node(
&immediate_children, ctx->wc_ctx,
- target->abspath, FALSE, scratch_pool, scratch_pool));
+ target->abspath, scratch_pool, scratch_pool));
for (j = 0; j < immediate_children->nelts; j++)
{
@@ -6655,9 +6785,10 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
const apr_array_header_t *children;
int j;
- SVN_ERR(svn_wc__node_get_children(&children,
+ SVN_ERR(svn_wc__node_get_children_of_working_node(
+ &children,
ctx->wc_ctx,
- child->abspath, FALSE,
+ child->abspath,
iterpool, iterpool));
for (j = 0; j < children->nelts; j++)
{
@@ -7104,7 +7235,8 @@ normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
&original_revision,
segment_url,
- &range_start_rev, ctx,
+ &range_start_rev,
+ ra_session, ctx,
result_pool, scratch_pool));
/* Got copyfrom data? Fix up the first segment to cover
back to COPYFROM_REV + 1, and then prepend a new
@@ -7117,7 +7249,7 @@ normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
new_segment->path = original_repos_relpath;
new_segment->range_start = original_revision;
new_segment->range_end = original_revision;
- svn_sort__array_insert(&new_segment, segments, 0);
+ svn_sort__array_insert(segments, &new_segment, 0);
}
}
}
@@ -7783,7 +7915,7 @@ process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
hi;
hi = apr_hash_next(hi))
{
- const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi);
+ const char *abspath_with_new_mergeinfo = apr_hash_this_key(hi);
svn_mergeinfo_t path_inherited_mergeinfo;
svn_mergeinfo_t path_explicit_mergeinfo;
svn_client__merge_path_t *new_child;
@@ -7886,7 +8018,7 @@ path_is_subtree(const char *local_abspath,
for (hi = apr_hash_first(pool, subtrees);
hi; hi = apr_hash_next(hi))
{
- const char *path_touched_by_merge = svn__apr_hash_index_key(hi);
+ const char *path_touched_by_merge = apr_hash_this_key(hi);
if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
return TRUE;
}
@@ -8001,8 +8133,8 @@ log_find_operative_subtree_revs(void *baton,
hi;
hi = apr_hash_next(hi))
{
- const char *path = svn__apr_hash_index_key(hi);
- svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
+ const char *path = apr_hash_this_key(hi);
+ svn_log_changed_path2_t *change = apr_hash_this_val(hi);
{
const char *child;
@@ -8638,7 +8770,7 @@ record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
/* Allow mergeinfo on switched subtrees to elide to the
repository. Otherwise limit elision to the merge target
- for now. do_directory_merge() will eventually try to
+ for now. do_merge() will eventually try to
elide that when the merge is complete. */
SVN_ERR(svn_client__elide_mergeinfo(
child->abspath,
@@ -8687,7 +8819,7 @@ record_mergeinfo_for_added_subtrees(
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
{
- const char *added_abspath = svn__apr_hash_index_key(hi);
+ const char *added_abspath = apr_hash_this_key(hi);
const char *dir_abspath;
svn_mergeinfo_t parent_mergeinfo;
svn_mergeinfo_t added_path_mergeinfo;
@@ -8896,7 +9028,7 @@ log_noop_revs(void *baton,
hi;
hi = apr_hash_next(hi))
{
- const char *fspath = svn__apr_hash_index_key(hi);
+ const char *fspath = apr_hash_this_key(hi);
const char *rel_path;
const char *cwmi_abspath;
svn_rangelist_t *paths_explicit_rangelist = NULL;
@@ -9053,24 +9185,23 @@ remove_noop_subtree_ranges(const merge_source_t *source,
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);
@@ -9464,7 +9595,7 @@ do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
{
if (!merge_b->record_only)
{
- /* Reset cur_ancestor_abspath to null so that subsequent cherry
+ /* Reset the last notification path so that subsequent cherry
picked revision ranges will be notified upon subsequent
operative merge. */
merge_b->notify_begin.last_abspath = NULL;
@@ -10226,7 +10357,7 @@ ensure_wc_is_suitable_merge_target(const char *target_abspath,
svn_boolean_t is_modified;
SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
- target_abspath,
+ target_abspath, TRUE,
ctx->cancel_func,
ctx->cancel_baton,
scratch_pool));
@@ -10724,7 +10855,7 @@ log_find_operative_revs(void *baton,
hi = apr_hash_next(hi))
{
const char *subtree_missing_this_rev;
- const char *path = svn__apr_hash_index_key(hi);
+ const char *path = apr_hash_this_key(hi);
const char *rel_path;
const char *source_rel_path;
svn_boolean_t in_catalog;
@@ -10852,7 +10983,7 @@ find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
hi_catalog;
hi_catalog = apr_hash_next(hi_catalog))
{
- svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog);
+ svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi_catalog);
SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
mergeinfo,
@@ -10873,7 +11004,7 @@ find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
potentially_unmerged_ranges->nelts - 1,
svn_merge_range_t *))->end;
log_find_operative_baton_t log_baton;
- const char *old_session_url;
+ const char *old_session_url = NULL;
svn_error_t *err;
log_baton.merged_catalog = merged_catalog;
@@ -10884,14 +11015,22 @@ find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
= svn_client__pathrev_fspath(target_loc, scratch_pool);
log_baton.result_pool = result_pool;
- SVN_ERR(svn_client__ensure_ra_session_url(
- &old_session_url, ra_session, target_loc->url, scratch_pool));
+ /* Reparent the session to TARGET_LOC if this target location
+ * exists within the unmerged revision range. */
+ if (target_loc->rev <= youngest_rev && target_loc->rev >= oldest_rev)
+ SVN_ERR(svn_client__ensure_ra_session_url(
+ &old_session_url, ra_session, target_loc->url, scratch_pool));
+
err = get_log(ra_session, "", youngest_rev, oldest_rev,
TRUE, /* discover_changed_paths */
log_find_operative_revs, &log_baton,
scratch_pool);
- SVN_ERR(svn_error_compose_create(
- err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
+ if (old_session_url)
+ err = svn_error_compose_create(err,
+ svn_ra_reparent(ra_session,
+ old_session_url,
+ scratch_pool));
+ SVN_ERR(err);
}
return SVN_NO_ERROR;
@@ -11058,8 +11197,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
hi;
hi = apr_hash_next(hi))
{
- const char *target_path = svn__apr_hash_index_key(hi);
- svn_mergeinfo_t target_history_as_mergeinfo = svn__apr_hash_index_val(hi);
+ const char *target_path = apr_hash_this_key(hi);
+ svn_mergeinfo_t target_history_as_mergeinfo = apr_hash_this_val(hi);
const char *path_rel_to_session
= svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
const char *source_path;
@@ -11143,11 +11282,11 @@ find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
hi;
hi = apr_hash_next(hi))
{
- const char *source_path = svn__apr_hash_index_key(hi);
+ const char *source_path = apr_hash_this_key(hi);
const char *path_rel_to_session =
svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
const char *source_url;
- svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi);
+ svn_mergeinfo_t source_mergeinfo = apr_hash_this_val(hi);
svn_mergeinfo_t filtered_mergeinfo;
svn_client__pathrev_t *target_pathrev;
svn_mergeinfo_t target_history_as_mergeinfo;
@@ -11296,7 +11435,7 @@ calculate_left_hand_side(svn_client__pathrev_t **left_p,
hi;
hi = apr_hash_next(hi))
{
- const char *local_abspath = svn__apr_hash_index_key(hi);
+ const char *local_abspath = apr_hash_this_key(hi);
svn_client__pathrev_t *target_child;
const char *repos_relpath;
svn_mergeinfo_t target_history_as_mergeinfo;
@@ -11523,7 +11662,7 @@ find_reintegrate_merge(merge_source_t **source_p,
SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
&source_mergeinfo_cat_string,
final_unmerged_catalog,
- " ", " Missing ranges: ", scratch_pool));
+ " ", _(" Missing ranges: "), scratch_pool));
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
NULL,
_("Reintegrate can only be used if "
@@ -11583,9 +11722,6 @@ open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
SVN_ERR(open_target_wc(&target, target_abspath,
FALSE, FALSE, FALSE,
ctx, scratch_pool, scratch_pool));
- SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
- target->loc.url, target->abspath,
- ctx, result_pool, scratch_pool));
if (! target->loc.url)
return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
_("Can't reintegrate into '%s' because it is "
@@ -11594,6 +11730,10 @@ open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
svn_dirent_local_style(target->abspath,
scratch_pool));
+ SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
+ target->loc.url, target->abspath,
+ ctx, result_pool, scratch_pool));
+
SVN_ERR(svn_client__ra_session_from_path2(
source_ra_session_p, &source_loc,
source_path_or_url, NULL, source_peg_revision, source_peg_revision,
@@ -11647,6 +11787,7 @@ merge_reintegrate_locked(conflict_report_t **conflict_report,
if (! source)
{
+ *conflict_report = NULL;
return SVN_NO_ERROR;
}
@@ -11943,8 +12084,8 @@ location_on_branch_at_rev(const branch_history_t *branch_history,
for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
hi = apr_hash_next(hi))
{
- const char *fspath = svn__apr_hash_index_key(hi);
- svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
+ const char *fspath = apr_hash_this_key(hi);
+ svn_rangelist_t *rangelist = apr_hash_this_val(hi);
int i;
for (i = 0; i < rangelist->nelts; i++)
@@ -12195,6 +12336,10 @@ find_last_merged_location(svn_client__pathrev_t **base_p,
svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
+ /* Using a local subpool for 'target_mergeinfo_cat' can make a big
+ reduction in overall memory usage. */
+ apr_pool_t *tmic_pool = svn_pool_create(scratch_pool);
+
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;
@@ -12215,7 +12360,7 @@ find_last_merged_location(svn_client__pathrev_t **base_p,
operative_rev_receiver,
&youngest_merged_rev,
ctx, ra_session,
- result_pool, scratch_pool));
+ tmic_pool, tmic_pool));
if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
{
@@ -12251,7 +12396,7 @@ find_last_merged_location(svn_client__pathrev_t **base_p,
operative_rev_receiver,
&oldest_eligible_rev,
ctx, ra_session,
- scratch_pool, scratch_pool));
+ tmic_pool, tmic_pool));
/* If there are revisions eligible for merging, use the oldest one
to calculate the base. Otherwise there are no operative revisions
@@ -12273,6 +12418,7 @@ find_last_merged_location(svn_client__pathrev_t **base_p,
result_pool, scratch_pool));
}
+ svn_pool_destroy(tmic_pool);
return SVN_NO_ERROR;
}
@@ -12347,7 +12493,16 @@ find_base_on_target(svn_client__pathrev_t **base_p,
return SVN_NO_ERROR;
}
-/* The body of client_find_automatic_merge(), which see.
+/* Find the last point at which the branch at S_T->source was completely
+ * merged to the branch at S_T->target or vice-versa.
+ *
+ * Fill in S_T->source_branch and S_T->target_branch and S_T->yca.
+ * Set *BASE_P to the merge base. Set *IS_REINTEGRATE_LIKE to true if
+ * an automatic merge from source to target would be a reintegration
+ * merge: that is, if the last automatic merge was in the opposite
+ * direction; or to false otherwise.
+ *
+ * If there is no youngest common ancestor, throw an error.
*/
static svn_error_t *
find_automatic_merge(svn_client__pathrev_t **base_p,
@@ -12417,6 +12572,9 @@ find_automatic_merge(svn_client__pathrev_t **base_p,
* 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.
+ *
+ * Set *MERGE_P to a new structure with all fields filled in except the
+ * 'allow_*' flags.
*/
static svn_error_t *
find_automatic_merge_no_wc(automatic_merge_t **merge_p,
@@ -12492,6 +12650,8 @@ client_find_automatic_merge(automatic_merge_t **merge_p,
source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
+
/* "Open" the target WC. Check the target WC for mixed-rev, local mods and
* switched subtrees yet to faster exit and notify user before contacting
* with server. After we find out what kind of merge is required, then if a
@@ -12503,12 +12663,19 @@ client_find_automatic_merge(automatic_merge_t **merge_p,
allow_switched_subtrees,
ctx, result_pool, scratch_pool));
+ if (!s_t->target->loc.url)
+ return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("Can't perform automatic merge into '%s' "
+ "because it is locally added and therefore "
+ "not related to the merge source"),
+ svn_dirent_local_style(target_abspath,
+ scratch_pool));
+
/* Open RA sessions to the source and target trees. */
SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
s_t->target->loc.url,
s_t->target->abspath,
ctx, result_pool, scratch_pool));
- /* ### check for null URL (i.e. added path) here, like in reintegrate? */
SVN_ERR(svn_client__ra_session_from_path2(
&s_t->source_ra_session, &s_t->source,
source_path_or_url, NULL, source_revision, source_revision,
@@ -12523,6 +12690,7 @@ client_find_automatic_merge(automatic_merge_t **merge_p,
ctx, result_pool, scratch_pool));
merge->yca = s_t->yca;
merge->right = s_t->source;
+ merge->target = &s_t->target->loc;
merge->allow_mixed_rev = allow_mixed_rev;
merge->allow_local_mods = allow_local_mods;
merge->allow_switched_subtrees = allow_switched_subtrees;
@@ -12711,14 +12879,21 @@ svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
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);
+ || target_revision->kind == svn_opt_revision_working
+ || target_revision->kind == svn_opt_revision_base);
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));
+ {
+ const char *target_abspath;
+
+ SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_path_or_url,
+ scratch_pool));
+ SVN_ERR(client_find_automatic_merge(
+ &merge,
+ source_path_or_url, source_revision,
+ target_abspath,
+ TRUE, TRUE, TRUE, /* allow_* */
+ ctx, scratch_pool, scratch_pool));
+ }
else
SVN_ERR(find_automatic_merge_no_wc(
&merge,