You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/02/13 07:37:56 UTC
svn commit: r1445479 [3/11] - in /subversion/branches/fsfs-format7: ./
build/generator/ build/generator/swig/ build/generator/templates/
notes/api-errata/1.7/ packages/ subversion/bindings/swig/include/
subversion/bindings/swig/perl/libsvn_swig_perl/ s...
Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/merge.c?rev=1445479&r1=1445478&r2=1445479&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/merge.c Wed Feb 13 06:37:54 2013
@@ -198,9 +198,9 @@
* merge target and any of its subtrees which have explicit mergeinfo
* or otherwise need special attention during a merge.
*
- * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO is created by
- * do_mergeinfo_unaware_dir_merge and contains only one element describing
- * a contiguous range to be merged to the WC merge target.
+ * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
+ * contains only one element (added by do_mergeinfo_unaware_dir_merge)
+ * describing a contiguous range to be merged to the WC merge target.
*
* During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
* by get_mergeinfo_paths() and outside of that function and its helpers
@@ -235,9 +235,6 @@ typedef struct merge_target_t
/* Absolute path to the WC node */
const char *abspath;
- /* Node kind of the WC node (at the start of the merge) */
- svn_node_kind_t kind;
-
/* The repository location of the base node of the target WC. If the node
* is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
* REPOS_ROOT_URL and REPOS_UUID are always valid. */
@@ -266,9 +263,6 @@ typedef struct merge_cmd_baton_t {
merge or not. */
const merge_target_t *target; /* Description of merge target node */
- const svn_merge_range_t *current_range; /* For single file merges the current
- revision range */
-
/* The left and right URLs and revs. The value of this field changes to
reflect the merge_source_t *currently* being merged by do_merge(). */
merge_source_t merge_source;
@@ -348,26 +342,18 @@ typedef struct merge_cmd_baton_t {
or do_file_merge() in do_merge(). */
apr_pool_t *pool;
- /* Boolean indicating whether the information header for the current
- operation has already been notified */
- svn_boolean_t notified_merge_begin;
-
- /* Contains any state collected while receiving path notifications. */
-
- /* Flag indicating whether it is a single file merge or not. */
- svn_boolean_t is_single_file_merge;
-
- /* Depth first ordered list of paths that needs special care while merging.
- ### And ...? This is not just a list of paths. See the global comment
- 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
- This defaults to NULL. For 'same_url' merge alone we set it to
- proper array. This is used by notification_receiver to put a
- merge notification begin lines. */
- apr_array_header_t *children_with_mergeinfo;
- /* The path in CHILDREN_WITH_MERGEINFO where we found the nearest ancestor
- for merged path. Default value is null. */
- const char *cur_ancestor_abspath;
+ /* State for notify_merge_begin() */
+ struct notify_begin_state_t
+ {
+ /* Cache of which abspath was last notified. */
+ const char *last_abspath;
+
+ /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
+ comment) or a similar list for single-file-merges */
+ const apr_array_header_t *nodes_with_mergeinfo;
+ } notify_begin;
+
} merge_cmd_baton_t;
@@ -500,7 +486,7 @@ store_path(apr_hash_t *path_hash, const
const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
local_abspath);
- apr_hash_set(path_hash, dup_path, APR_HASH_KEY_STRING, dup_path);
+ svn_hash_sets(path_hash, dup_path, dup_path);
}
/* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
@@ -516,13 +502,6 @@ alloc_and_store_path(apr_hash_t **path_h
store_path(*path_hash_p, local_abspath);
}
-/* Helper function to easy in checking if a path is in a path hash */
-static APR_INLINE svn_boolean_t
-contains_path(apr_hash_t *path_hash, const char *local_abspath)
-{
- return apr_hash_get(path_hash, local_abspath, APR_HASH_KEY_STRING) != NULL;
-}
-
/* Return whether any WC path was put in conflict by the merge
operation corresponding to MERGE_B. */
static APR_INLINE svn_boolean_t
@@ -550,10 +529,11 @@ is_path_conflicted_by_merge(merge_cmd_ba
static svn_error_t *
perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
svn_boolean_t *deleted,
+ svn_boolean_t *excluded,
svn_node_kind_t *kind,
+ svn_depth_t *parent_depth,
const merge_cmd_baton_t *merge_b,
const char *local_abspath,
- svn_node_kind_t expected_kind,
apr_pool_t *scratch_pool)
{
svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
@@ -577,24 +557,18 @@ perform_obstruction_check(svn_wc_notify_
SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
kind,
deleted,
+ excluded,
+ parent_depth,
wc_ctx, local_abspath,
check_root,
scratch_pool));
-
- if (*obstruction_state == svn_wc_notify_state_inapplicable
- && expected_kind != svn_node_unknown
- && *kind != expected_kind)
- {
- *obstruction_state = svn_wc_notify_state_obstructed;
- }
-
return SVN_NO_ERROR;
}
/* Create *LEFT and *RIGHT conflict versions for conflict victim
* at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained
* from MERGE_SOURCE and TARGET.
- * Allocate returned conflict versions in POOL. */
+ * 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,
@@ -602,7 +576,8 @@ make_conflict_versions(const svn_wc_conf
svn_node_kind_t node_kind,
const merge_source_t *merge_source,
const merge_target_t *target,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
const char *child = svn_dirent_skip_ancestor(target->abspath,
victim_abspath);
@@ -610,186 +585,25 @@ make_conflict_versions(const svn_wc_conf
SVN_ERR_ASSERT(child != NULL);
left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
- pool);
+ scratch_pool);
right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
- pool);
+ scratch_pool);
*left = svn_wc_conflict_version_create2(
merge_source->loc1->repos_root_url,
merge_source->loc1->repos_uuid,
- svn_relpath_join(left_relpath, child, pool),
- merge_source->loc1->rev, node_kind, pool);
+ svn_relpath_join(left_relpath, child, scratch_pool),
+ merge_source->loc1->rev, 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, pool),
- merge_source->loc2->rev, node_kind, pool);
-
- return SVN_NO_ERROR;
-}
-
-/* Set *CONFLICT to a new tree-conflict description allocated in POOL,
- * populated with information from the other parameters.
- * See tree_conflict() for the other parameters.
- */
-static svn_error_t*
-make_tree_conflict(svn_wc_conflict_description2_t **conflict,
- const char *victim_abspath,
- svn_node_kind_t node_kind,
- svn_wc_conflict_action_t action,
- svn_wc_conflict_reason_t reason,
- const merge_source_t *merge_source,
- const merge_target_t *target,
- apr_pool_t *pool)
-{
- const svn_wc_conflict_version_t *left;
- const svn_wc_conflict_version_t *right;
-
- SVN_ERR(make_conflict_versions(&left, &right, victim_abspath, node_kind,
- merge_source, target, pool));
-
- *conflict = svn_wc_conflict_description_create_tree2(
- victim_abspath, node_kind, svn_wc_operation_merge,
- left, right, pool);
-
- (*conflict)->action = action;
- (*conflict)->reason = reason;
-
- return SVN_NO_ERROR;
-}
-
-/* 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
- * editor drives because of a gap in the range of eligible revisions.)
- *
- * 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.
- */
-static svn_error_t*
-tree_conflict(merge_cmd_baton_t *merge_b,
- const char *victim_abspath,
- svn_node_kind_t node_kind,
- svn_wc_conflict_action_t action,
- svn_wc_conflict_reason_t reason,
- const char *moved_away_op_root_abspath)
-{
- const svn_wc_conflict_description2_t *existing_conflict;
- svn_error_t *err;
-
- if (merge_b->record_only || merge_b->dry_run)
- return SVN_NO_ERROR;
-
- err = svn_wc__get_tree_conflict(&existing_conflict, merge_b->ctx->wc_ctx,
- victim_abspath, merge_b->pool,
- merge_b->pool);
-
- if (err)
- {
- if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
- return svn_error_trace(err);
-
- svn_error_clear(err);
- existing_conflict = FALSE;
- }
-
- if (existing_conflict == NULL)
- {
- svn_wc_conflict_description2_t *conflict;
-
- /* There is no existing tree conflict so it is safe to add one. */
- SVN_ERR(make_tree_conflict(&conflict, victim_abspath,
- node_kind, action, reason,
- &merge_b->merge_source, merge_b->target,
- merge_b->pool));
- SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
- moved_away_op_root_abspath,
- merge_b->pool));
-
- alloc_and_store_path(&merge_b->conflicted_paths, victim_abspath,
- merge_b->pool);
- }
+ svn_relpath_join(right_relpath, child, scratch_pool),
+ merge_source->loc2->rev, node_kind, result_pool);
return SVN_NO_ERROR;
}
-/* Similar to tree_conflict(), but if this is an "add" action and there
- is an existing tree conflict on the victim with a "delete" action, then
- combine the two conflicts into a single conflict with a "replace" action. */
-static svn_error_t*
-tree_conflict_on_add(merge_cmd_baton_t *merge_b,
- const char *victim_abspath,
- svn_node_kind_t node_kind,
- svn_wc_conflict_action_t action,
- svn_wc_conflict_reason_t reason)
-{
- const svn_wc_conflict_description2_t *existing_conflict;
- svn_wc_conflict_description2_t *conflict;
-
- if (merge_b->record_only || merge_b->dry_run)
- return SVN_NO_ERROR;
-
- /* Construct the new conflict first compare the new conflict with
- a possibly existing one. */
- SVN_ERR(make_tree_conflict(&conflict, victim_abspath,
- node_kind, action, reason,
- &merge_b->merge_source, merge_b->target,
- merge_b->pool));
-
- SVN_ERR(svn_wc__get_tree_conflict(&existing_conflict, merge_b->ctx->wc_ctx,
- victim_abspath, merge_b->pool,
- merge_b->pool));
-
- if (existing_conflict == NULL)
- {
- /* There is no existing tree conflict so it is safe to add one. */
- SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
- NULL,
- merge_b->pool));
-
- alloc_and_store_path(&merge_b->conflicted_paths, victim_abspath,
- merge_b->pool);
- }
- else if (existing_conflict->action == svn_wc_conflict_action_delete &&
- conflict->action == svn_wc_conflict_action_add)
- {
- /* There is already a tree conflict raised by a previous incoming
- * change that attempted to delete the item (whether in this same
- * merge operation or not). Change the existing conflict to note
- * that the incoming change is replacement. */
-
- /* Remove the existing tree-conflict so we can add a new one.*/
- SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
- victim_abspath, merge_b->pool));
-
- /* Preserve the reason which caused the first conflict,
- * re-label the incoming change as 'replacement', and update
- * version info for the left version of the conflict. */
- conflict->reason = existing_conflict->reason;
- conflict->action = svn_wc_conflict_action_replace;
- conflict->src_left_version = svn_wc_conflict_version_dup(
- existing_conflict->src_left_version,
- merge_b->pool);
-
- SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
- NULL,
- merge_b->pool));
-
- alloc_and_store_path(&merge_b->conflicted_paths, victim_abspath,
- merge_b->pool);
- }
-
- /* In any other cases, we don't touch the existing conflict. */
- return SVN_NO_ERROR;
-}
-
-
/* Helper for filter_self_referential_mergeinfo()
*MERGEINFO is a non-empty, non-null collection of mergeinfo.
@@ -863,8 +677,8 @@ split_mergeinfo_on_revision(svn_mergeinf
ranges from *MERGEINFO */
if (!(*younger_mergeinfo))
*younger_mergeinfo = apr_hash_make(pool);
- apr_hash_set(*younger_mergeinfo, merge_source_path,
- APR_HASH_KEY_STRING, younger_rangelist);
+ svn_hash_sets(*younger_mergeinfo, merge_source_path,
+ younger_rangelist);
SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
*mergeinfo, TRUE, pool, iterpool));
break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
@@ -1134,8 +948,8 @@ filter_self_referential_mergeinfo(apr_ar
{
if (!filtered_younger_mergeinfo)
filtered_younger_mergeinfo = apr_hash_make(iterpool);
- apr_hash_set(filtered_younger_mergeinfo, source_path,
- APR_HASH_KEY_STRING, adjusted_rangelist);
+ svn_hash_sets(filtered_younger_mergeinfo, source_path,
+ adjusted_rangelist);
}
} /* Iteration over each merge source in younger_mergeinfo. */
@@ -1266,10 +1080,10 @@ prepare_merge_props_changed(const apr_ar
}
*prop_updates = props;
- /* If this is not a dry run then make a record in BATON if we find a
- PATH where mergeinfo is added where none existed previously or PATH
- is having its existing mergeinfo deleted. */
- if (!merge_b->dry_run && props->nelts)
+ /* Make a record in BATON if we find a PATH where mergeinfo is added
+ where none existed previously or PATH is having its existing
+ mergeinfo deleted. */
+ if (props->nelts)
{
int i;
@@ -1290,8 +1104,7 @@ prepare_merge_props_changed(const apr_ar
scratch_pool));
if (pristine_props
- && apr_hash_get(pristine_props, SVN_PROP_MERGEINFO,
- APR_HASH_KEY_STRING))
+ && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
has_pristine_mergeinfo = TRUE;
if (!has_pristine_mergeinfo && prop->value)
@@ -1311,62 +1124,24 @@ prepare_merge_props_changed(const apr_ar
return SVN_NO_ERROR;
}
-/* Set *OP_ROOT_ABSPATH if the node at LOCAL_ABSPATH was moved away
- * locally, or set *OP_ROOT_ABSPATH to NULL otherwise. Do not raise an
- * error if the node at LOCAL_ABSPATH does not exist.
- *
- * ### Currently this looks at the highest moved-to but should merge
- * ### be looking at the lowest moved-to? Or perhaps only the base
- * ### moved-to? */
-static svn_error_t *
-check_moved_away(const char **op_root_abspath,
- svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- const char *moved_to_abspath;
-
- SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, op_root_abspath,
- wc_ctx, local_abspath,
- scratch_pool, scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-/* Indicate in *MOVED_HERE whether the node at LOCAL_ABSPATH was
- * moved here locally. Do not raise an error if the node at LOCAL_ABSPATH
- * does not exist. */
-static svn_error_t *
-check_moved_here(svn_boolean_t *moved_here,
- svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- const char *moved_from_abspath;
- svn_error_t *err;
-
- *moved_here = FALSE;
-
- err = svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
- wc_ctx, local_abspath,
- scratch_pool, scratch_pool);
- if (err)
- {
- if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- svn_error_clear(err);
- else
- return svn_error_trace(err);
- }
- else if (moved_from_abspath)
- *moved_here = TRUE;
-
- return SVN_NO_ERROR;
-}
-
#define CONFLICT_REASON_NONE ((svn_wc_conflict_reason_t)-1)
-#define CONFLICT_REASON_EXCLUDED ((svn_wc_conflict_reason_t)-2)
-#define CONFLICT_REASON_SKIP ((svn_wc_conflict_reason_t)-3)
-#define CONFLICT_REASON_SKIP_WC ((svn_wc_conflict_reason_t)-4)
+#define CONFLICT_REASON_SKIP ((svn_wc_conflict_reason_t)-2)
+#define CONFLICT_REASON_SKIP_WC ((svn_wc_conflict_reason_t)-3)
+
+/* Baton used for testing trees for being editted while performing tree
+ conflict detection for incoming deletes */
+struct dir_delete_baton_t
+{
+ /* Reference to dir baton of directory that is the root of the deletion */
+ struct merge_dir_baton_t *del_root;
+
+ /* Boolean indicating that some edit is found. Allows avoiding more work */
+ svn_boolean_t found_edit;
+
+ /* A list of paths that are compared. Kept up to date until FOUND_EDIT is
+ set to TRUE */
+ apr_hash_t *compared_abspaths;
+};
/* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
struct merge_dir_baton_t
@@ -1394,10 +1169,6 @@ struct merge_dir_baton_t
conflict.
Special values:
- CONFLICT_REASON_EXCLUDED:
- The node has been excluded (or depth filtered) and the node will
- only be reported as skipped.
-
CONFLICT_REASON_SKIP:
The node will be skipped with content and property state as stored in
SKIP_REASON.
@@ -1425,6 +1196,16 @@ struct merge_dir_baton_t
notification (which may be a replaced notification if the node is not just
deleted) */
apr_hash_t *pending_deletes;
+
+ /* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to
+ a const svn_wc_conflict_description2_t * instance, describing the just
+ installed conflict */
+ apr_hash_t *new_tree_conflicts;
+
+ /* If not NULL, a reference to the information of the delete test that is
+ currently in progress. Allocated in the root-directory baton, referenced
+ from all descendants */
+ struct dir_delete_baton_t *delete_state;
};
/* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
@@ -1500,18 +1281,113 @@ record_skip(merge_cmd_baton_t *merge_b,
return SVN_NO_ERROR;
}
-/* Record the skip for future processing and (later) produce the
- tree conflict notification */
+/* 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
+ * editor drives because of a gap in the range of eligible revisions.)
+ *
+ * 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.
+ */
static svn_error_t *
record_tree_conflict(merge_cmd_baton_t *merge_b,
const char *local_abspath,
- svn_node_kind_t kind,
+ struct merge_dir_baton_t *parent_baton,
+ svn_node_kind_t node_kind,
+ svn_wc_conflict_action_t action,
+ svn_wc_conflict_reason_t reason,
+ const svn_wc_conflict_description2_t *existing_conflict,
+ svn_boolean_t notify_tc,
apr_pool_t *scratch_pool)
{
+ svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
+
+ if (merge_b->merge_source.ancestral
+ || merge_b->reintegrate_merge)
+ {
+ store_path(merge_b->tree_conflicted_abspaths, local_abspath);
+ }
+
+ alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
+ merge_b->pool);
+
+
+ if (!merge_b->record_only && !merge_b->dry_run)
+ {
+ svn_wc_conflict_description2_t *conflict;
+ const svn_wc_conflict_version_t *left;
+ const svn_wc_conflict_version_t *right;
+ apr_pool_t *result_pool = parent_baton ? parent_baton->pool
+ : scratch_pool;
+
+ if (reason == svn_wc_conflict_reason_deleted)
+ {
+ const char *moved_to_abspath;
+
+ SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
+ wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (moved_to_abspath)
+ {
+ /* Local abspath itself has been moved away. If only a
+ descendant is moved away, we call the node itself deleted */
+ reason = svn_wc_conflict_reason_moved_away;
+ }
+ }
+ else if (reason == svn_wc_conflict_reason_added)
+ {
+ const char *moved_from_abspath;
+ SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
+ wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+ if (moved_from_abspath)
+ 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));
+
+ /* 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,
+ left, right, result_pool);
+
+ conflict->action = action;
+ conflict->reason = reason;
+
+ /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
+ if (existing_conflict)
+ SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
+ scratch_pool));
+
+ if (parent_baton)
+ {
+ if (! parent_baton->new_tree_conflicts)
+ parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
+
+ svn_hash_sets(parent_baton->new_tree_conflicts,
+ apr_pstrdup(result_pool, local_abspath),
+ conflict);
+ }
+
+ /* ### TODO: Store in parent baton */
+ }
+
/* On a replacement we currently get two tree conflicts */
- if (merge_b->ctx->notify_func2
- && !contains_path(merge_b->tree_conflicted_abspaths,
- local_abspath))
+ if (merge_b->ctx->notify_func2 && notify_tc)
{
svn_wc_notify_t *notify;
@@ -1519,18 +1395,12 @@ record_tree_conflict(merge_cmd_baton_t *
notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
scratch_pool);
- notify->kind = kind;
+ notify->kind = node_kind;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
}
- if (merge_b->merge_source.ancestral
- || merge_b->reintegrate_merge)
- {
- store_path(merge_b->tree_conflicted_abspaths, local_abspath);
- }
-
return SVN_NO_ERROR;
}
@@ -1547,42 +1417,8 @@ record_update_add(merge_cmd_baton_t *mer
svn_boolean_t notify_replaced,
apr_pool_t *scratch_pool)
{
- svn_boolean_t root_of_added_subtree = TRUE;
-
if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
{
- /* Stash the root path of any added subtrees. */
- if (merge_b->added_abspaths == NULL)
- {
- /* The first added path is always a root. */
- merge_b->added_abspaths = apr_hash_make(merge_b->pool);
- }
- else
- {
- const char *added_path_dir = svn_dirent_dirname(local_abspath,
- scratch_pool);
-
- /* Is NOTIFY->PATH the root of an added subtree? */
- while (strcmp(merge_b->target->abspath, added_path_dir))
- {
- if (contains_path(merge_b->added_abspaths, added_path_dir))
- {
- root_of_added_subtree = FALSE;
- break;
- }
-
- if (svn_dirent_is_root(added_path_dir, strlen(added_path_dir)))
- break;
- added_path_dir = svn_dirent_dirname(added_path_dir,
- scratch_pool);
- }
- }
-
- if (root_of_added_subtree)
- {
- store_path(merge_b->added_abspaths, local_abspath);
- }
-
store_path(merge_b->merged_abspaths, local_abspath);
}
@@ -1656,8 +1492,7 @@ record_update_delete(merge_cmd_baton_t *
/* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
are now deleting it, then remove it from the list of added
paths. */
- apr_hash_set(merge_b->added_abspaths, local_abspath,
- APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
store_path(merge_b->merged_abspaths, local_abspath);
}
@@ -1670,8 +1505,8 @@ record_update_delete(merge_cmd_baton_t *
if (!parent_db->pending_deletes)
parent_db->pending_deletes = apr_hash_make(parent_db->pool);
- apr_hash_set(parent_db->pending_deletes, dup_abspath, APR_HASH_KEY_STRING,
- svn_node_kind_to_word(kind));
+ svn_hash_sets(parent_db->pending_deletes, dup_abspath,
+ svn_node_kind_to_word(kind));
}
return SVN_NO_ERROR;
@@ -1739,8 +1574,14 @@ mark_dir_edited(merge_cmd_baton_t *merge
if (! db->shadowed)
return SVN_NO_ERROR; /* Easy out */
- if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
- || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
+ if (db->parent_baton
+ && db->parent_baton->delete_state
+ && db->tree_conflict_reason != CONFLICT_REASON_NONE)
+ {
+ db->parent_baton->delete_state->found_edit = TRUE;
+ }
+ else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
+ || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
{
/* open_directory() decided not to flag a tree conflict, but
for clarity we produce a skip for this node that
@@ -1773,45 +1614,15 @@ mark_dir_edited(merge_cmd_baton_t *merge
store_path(merge_b->skipped_abspaths, local_abspath);
}
}
- else if (db->tree_conflict_reason == CONFLICT_REASON_EXCLUDED)
- {
- /* open_directory() decided not to flag a tree conflict, while
- there really should be one.
-
- Ok: The skip for the descendant will do the right thing anyway!
- */
-
- /* We don't register the node in merge_b->skipped_abspaths here, as the
- node itself is not skipped: the operation on a descendant is */
- }
else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
{
/* open_directory() decided that a tree conflict should be raised */
- const char *moved_away_abspath = NULL;
-
- if (db->tree_conflict_reason == svn_wc_conflict_reason_deleted)
- {
- const char *ignored_path;
- SVN_ERR(check_moved_away(&ignored_path, merge_b->ctx->wc_ctx,
- local_abspath, scratch_pool));
-
- if (ignored_path)
- {
- /* Local abspath has been moved away itself, as we only create
- tree conflict on the obstructing op-root.
- If only a descendant is moved away, we call the node itself
- deleted. */
- db->tree_conflict_reason = svn_wc_conflict_reason_moved_away;
- moved_away_abspath = local_abspath;
- }
- }
-
- SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
- db->tree_conflict_action, db->tree_conflict_reason,
- moved_away_abspath));
-
- SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir, scratch_pool));
+ SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
+ svn_node_dir, db->tree_conflict_action,
+ db->tree_conflict_reason,
+ NULL, TRUE,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -1846,8 +1657,14 @@ mark_file_edited(merge_cmd_baton_t *merg
if (! fb->shadowed)
return SVN_NO_ERROR; /* Easy out */
- if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
- || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
+ if (fb->parent_baton
+ && fb->parent_baton->delete_state
+ && fb->tree_conflict_reason != CONFLICT_REASON_NONE)
+ {
+ fb->parent_baton->delete_state->found_edit = TRUE;
+ }
+ else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
+ || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
{
/* open_directory() decided not to flag a tree conflict, but
for clarity we produce a skip for this node that
@@ -1876,35 +1693,15 @@ mark_file_edited(merge_cmd_baton_t *merg
store_path(merge_b->skipped_abspaths, local_abspath);
}
}
- else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE
- && fb->tree_conflict_reason != CONFLICT_REASON_EXCLUDED)
+ else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
{
- /* open_directory() decided that a tree conflict should be raised */
- const char *moved_away_abspath = NULL;
-
- if (fb->tree_conflict_reason == svn_wc_conflict_reason_deleted)
- {
- const char *ignored_path;
- SVN_ERR(check_moved_away(&ignored_path, merge_b->ctx->wc_ctx,
- local_abspath, scratch_pool));
-
- if (ignored_path)
- {
- /* Local abspath has been moved away itself, as we only create
- tree conflict on the obstructing op-root.
-
- If only a descendant is moved away, we call the node itself
- deleted. */
- fb->tree_conflict_reason = svn_wc_conflict_reason_moved_away;
- moved_away_abspath = local_abspath;
- }
- }
+ /* open_file() decided that a tree conflict should be raised */
- SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_file,
- fb->tree_conflict_action, fb->tree_conflict_reason,
- moved_away_abspath));
-
- SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir, scratch_pool));
+ SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
+ svn_node_file, fb->tree_conflict_action,
+ fb->tree_conflict_reason,
+ NULL, TRUE,
+ scratch_pool));
}
return SVN_NO_ERROR;
@@ -1959,13 +1756,19 @@ merge_file_opened(void **new_file_baton,
/* 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;
+
+ if (! right_source)
+ fb->tree_conflict_action = svn_wc_conflict_action_delete;
{
svn_wc_notify_state_t obstr_state;
- SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &kind,
+ SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
+ &kind, &parent_depth,
merge_b, local_abspath,
- svn_node_unknown, scratch_pool));
+ scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
@@ -1991,24 +1794,15 @@ merge_file_opened(void **new_file_baton,
Non-inheritable mergeinfo will be recorded, allowing
future merges into non-shallow working copies to merge
changes we missed this time around. */
- if (pdb != NULL)
- {
- svn_depth_t parent_depth;
- const char *dir_abspath = svn_dirent_dirname(local_abspath,
- scratch_pool);
-
- SVN_ERR(svn_wc__node_get_depth(&parent_depth,
- merge_b->ctx->wc_ctx,
- dir_abspath, scratch_pool));
- if (parent_depth != svn_depth_unknown &&
- parent_depth < svn_depth_immediates)
- {
- fb->shadowed = TRUE;
-
- fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
- fb->skip_reason = svn_wc_notify_state_missing;
- return SVN_NO_ERROR;
- }
+ if (pdb && (excluded
+ || (parent_depth != svn_depth_unknown &&
+ parent_depth < svn_depth_files)))
+ {
+ fb->shadowed = TRUE;
+
+ fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
+ fb->skip_reason = svn_wc_notify_state_missing;
+ return SVN_NO_ERROR;
}
if (is_deleted)
@@ -2046,33 +1840,73 @@ merge_file_opened(void **new_file_baton,
return SVN_NO_ERROR; /* Already set a tree conflict */
}
- /* TODO: Start comparison mode to verify for delete tree conflict */
+ /* Comparison mode to verify for delete tree conflicts? */
+ if (pdb && pdb->delete_state
+ && pdb->delete_state->found_edit)
+ {
+ /* Earlier nodes found a conflict. Done. */
+ *skip = TRUE;
+ }
}
}
else
{
+ const svn_wc_conflict_description2_t *old_tc = NULL;
+
/* The node doesn't exist pre-merge: We have an addition */
fb->added = TRUE;
fb->tree_conflict_action = svn_wc_conflict_action_add;
if (pdb && pdb->pending_deletes
- && contains_path(pdb->pending_deletes, local_abspath))
+ && svn_hash_gets(pdb->pending_deletes, local_abspath))
{
fb->add_is_replace = TRUE;
+ fb->tree_conflict_action = svn_wc_conflict_action_replace;
- apr_hash_set(pdb->pending_deletes, local_abspath,
- APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
}
- if (! (merge_b->dry_run && ((pdb && pdb->added) || fb->add_is_replace)))
+ if (pdb
+ && pdb->new_tree_conflicts
+ && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
+ {
+ fb->tree_conflict_action = svn_wc_conflict_action_replace;
+ fb->tree_conflict_reason = old_tc->reason;
+
+ /* Update the tree conflict to store that this is a replace */
+ SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ svn_node_file,
+ fb->tree_conflict_action,
+ fb->tree_conflict_reason,
+ old_tc, FALSE,
+ scratch_pool));
+
+ if (old_tc->reason == svn_wc_conflict_reason_deleted
+ || old_tc->reason == svn_wc_conflict_reason_moved_away)
+ {
+ /* Issue #3806: Incoming replacements on local deletes produce
+ inconsistent result.
+
+ In this specific case we can continue applying the add part
+ of the replacement. */
+ }
+ else
+ {
+ *skip = TRUE;
+
+ return SVN_NO_ERROR;
+ }
+ }
+ else if (! (merge_b->dry_run
+ && ((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, &kind,
+ SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
+ &kind, NULL,
merge_b, local_abspath,
- svn_node_unknown,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
@@ -2161,7 +1995,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, merge_b->pool));
+ 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 */
if ((merge_b->record_only || !left_file) && prop_changes->nelts)
@@ -2298,14 +2133,18 @@ merge_file_added(const char *relpath,
return SVN_NO_ERROR;
}
+ if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
+ && ( !fb->parent_baton || !fb->parent_baton->added))
+ {
+ /* Store the roots of added subtrees */
+ store_path(merge_b->added_abspaths, local_abspath);
+ }
if (!merge_b->dry_run)
{
const char *copyfrom_url;
svn_revnum_t copyfrom_rev;
svn_stream_t *new_contents, *pristine_contents;
- svn_boolean_t existing_tree_conflict;
- svn_error_t *err;
/* If this is a merge from the same repository as our
working copy, we handle adds as add-with-history.
@@ -2331,8 +2170,7 @@ merge_file_added(const char *relpath,
pristine_props = right_props; /* Includes last_* information */
new_props = NULL; /* No local changes */
- if (apr_hash_get(pristine_props, SVN_PROP_MERGEINFO,
- APR_HASH_KEY_STRING))
+ if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
{
alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
local_abspath, merge_b->pool);
@@ -2360,68 +2198,22 @@ merge_file_added(const char *relpath,
new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
/* Issue #3383: We don't want mergeinfo from a foreign repository. */
- apr_hash_set(new_props, SVN_PROP_MERGEINFO, APR_HASH_KEY_STRING,
- NULL);
+ svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
}
- err = svn_wc_conflicted_p3(NULL, NULL, &existing_tree_conflict,
- merge_b->ctx->wc_ctx, local_abspath,
- merge_b->pool);
+ /* Do everything like if we had called 'svn cp PATH1 PATH2'. */
+ SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
+ local_abspath,
+ pristine_contents,
+ new_contents,
+ pristine_props, new_props,
+ copyfrom_url, copyfrom_rev,
+ merge_b->ctx->cancel_func,
+ merge_b->ctx->cancel_baton,
+ scratch_pool));
- if (err)
- {
- if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
- return svn_error_trace(err);
-
- svn_error_clear(err);
- existing_tree_conflict = FALSE;
- }
-
- if (existing_tree_conflict)
- {
- svn_boolean_t moved_here;
- svn_wc_conflict_reason_t reason;
-
- /* Possibly collapse the existing conflict into a 'replace'
- * tree conflict. The conflict reason is 'added' because
- * the now-deleted tree conflict victim must have been
- * added in the history of the merge target. */
- SVN_ERR(check_moved_here(&moved_here, merge_b->ctx->wc_ctx,
- local_abspath, scratch_pool));
- reason = moved_here ? svn_wc_conflict_reason_moved_here
- : svn_wc_conflict_reason_added;
- SVN_ERR(tree_conflict_on_add(merge_b, local_abspath,
- svn_node_file,
- svn_wc_conflict_action_add,
- reason));
-
- SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_file,
- scratch_pool));
- return SVN_NO_ERROR;
- }
- else
- {
- /* Since 'mine' doesn't exist, and this is
- 'merge_file_added', I hope it's safe to assume that
- 'older' is empty, and 'yours' is the full file. Merely
- copying 'yours' to 'mine', isn't enough; we need to get
- the whole text-base and props installed too, just as if
- we had called 'svn cp wc wc'. */
- SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
- local_abspath,
- pristine_contents,
- new_contents,
- pristine_props, new_props,
- copyfrom_url, copyfrom_rev,
- merge_b->ctx->cancel_func,
- merge_b->ctx->cancel_baton,
- scratch_pool));
- }
- }
- else /* dry_run */
- {
- /* ### Should we do this?
- store_path(merge_b->dry_run_added, local_abspath); */
+ /* Caller must call svn_sleep_for_timestamps() */
+ *merge_b->use_sleep = TRUE;
}
SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
@@ -2553,21 +2345,45 @@ merge_file_deleted(const char *relpath,
}
/* If the files are identical, attempt deletion */
- SVN_ERR(files_same_p(&same, left_file, left_props,
- local_abspath, merge_b->ctx->wc_ctx,
- scratch_pool));
- if (same || merge_b->force_delete || merge_b->record_only /* ### why? */)
- {
- /* Passing NULL for the notify_func and notify_baton because
- repos_diff.c:delete_entry() will do it for us. */
- SVN_ERR(svn_client__wc_delete(local_abspath, TRUE,
- merge_b->dry_run, FALSE, NULL, NULL,
- merge_b->ctx, scratch_pool));
+ if (merge_b->force_delete)
+ same = TRUE;
+ else
+ SVN_ERR(files_same_p(&same, left_file, left_props,
+ local_abspath, merge_b->ctx->wc_ctx,
+ scratch_pool));
+
+ if (fb->parent_baton
+ && fb->parent_baton->delete_state)
+ {
+ if (same)
+ {
+ /* Note that we checked this file */
+ store_path(fb->parent_baton->delete_state->compared_abspaths,
+ local_abspath);
+ }
+ else
+ {
+ /* We found some modification. Parent should raise a tree conflict */
+ fb->parent_baton->delete_state->found_edit = TRUE;
+ }
+
+ return SVN_NO_ERROR;
+ }
+ else if (same)
+ {
+ if (!merge_b->dry_run)
+ SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
+ FALSE /* keep_local */, FALSE /* unversioned */,
+ merge_b->ctx->cancel_func,
+ merge_b->ctx->cancel_baton,
+ NULL, NULL /* no notify */,
+ scratch_pool));
/* Record that we might have deleted mergeinfo */
alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
local_abspath, merge_b->pool);
+ /* And notify the deletion */
SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
svn_node_file, scratch_pool));
}
@@ -2575,14 +2391,14 @@ merge_file_deleted(const char *relpath,
{
/* The files differ, so raise a conflict instead of deleting */
- /* This might be use case 5 described in the paper attached to issue
+ /* This is use case 5 described in the paper attached to issue
* #2282. See also notes/tree-conflicts/detection.txt
*/
- SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_file,
- svn_wc_conflict_action_delete,
- svn_wc_conflict_reason_edited, NULL));
-
- SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+ SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
+ svn_node_file,
+ svn_wc_conflict_action_delete,
+ svn_wc_conflict_reason_edited,
+ NULL, TRUE,
scratch_pool));
}
@@ -2630,9 +2446,6 @@ merge_dir_opened(void **new_dir_baton,
*new_dir_baton = db;
- if (! right_source)
- *skip_children = TRUE; /* ### Compatibility with walk_children = FALSE */
-
if (pdb)
{
db->parent_baton = pdb;
@@ -2651,13 +2464,18 @@ merge_dir_opened(void **new_dir_baton,
/* 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;
+
+ if (! right_source)
+ db->tree_conflict_action = svn_wc_conflict_action_delete;
/* Check for an obstructed or missing node on disk. */
{
svn_wc_notify_state_t obstr_state;
- SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &kind,
+ SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
+ &kind, &parent_depth,
merge_b, local_abspath,
- svn_node_unknown,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
@@ -2681,6 +2499,14 @@ merge_dir_opened(void **new_dir_baton,
db->tree_conflict_reason = CONFLICT_REASON_SKIP;
db->skip_reason = obstr_state;
+
+ if (! right_source)
+ {
+ *skip = *skip_children = TRUE;
+ SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
+ scratch_pool));
+ }
+
return SVN_NO_ERROR;
}
@@ -2700,25 +2526,16 @@ merge_dir_opened(void **new_dir_baton,
Non-inheritable mergeinfo will be recorded, allowing
future merges into non-shallow working copies to merge
changes we missed this time around. */
- if (pdb != NULL)
+ if (pdb && (excluded
+ || (parent_depth != svn_depth_unknown &&
+ parent_depth < svn_depth_immediates)))
{
- svn_depth_t parent_depth;
- const char *dir_abspath = svn_dirent_dirname(local_abspath,
- scratch_pool);
-
- SVN_ERR(svn_wc__node_get_depth(&parent_depth,
- merge_b->ctx->wc_ctx,
- dir_abspath, scratch_pool));
- if (parent_depth != svn_depth_unknown &&
- parent_depth < svn_depth_immediates)
- {
- db->shadowed = TRUE;
+ db->shadowed = TRUE;
- db->tree_conflict_reason = CONFLICT_REASON_SKIP;
- db->skip_reason = svn_wc_notify_state_missing;
+ db->tree_conflict_reason = CONFLICT_REASON_SKIP;
+ db->skip_reason = svn_wc_notify_state_missing;
- return SVN_NO_ERROR;
- }
+ return SVN_NO_ERROR;
}
if (is_deleted)
@@ -2756,36 +2573,94 @@ merge_dir_opened(void **new_dir_baton,
if (db->shadowed)
{
+ *skip_children = TRUE;
return SVN_NO_ERROR; /* Already set a tree conflict */
}
- /* TODO: Start comparison mode to verify for delete tree conflict */
+ db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
+
+ if (db->delete_state && db->delete_state->found_edit)
+ {
+ /* A sibling found a conflict. Done. */
+ *skip = TRUE;
+ *skip_children = TRUE;
+ }
+ else if (merge_b->force_delete)
+ {
+ /* No comparison necessary */
+ *skip_children = TRUE;
+ }
+ else if (! db->delete_state)
+ {
+ /* Start descendant comparison */
+ db->delete_state = apr_pcalloc(db->pool,
+ sizeof(*db->delete_state));
+
+ db->delete_state->del_root = db;
+ db->delete_state->compared_abspaths = apr_hash_make(db->pool);
+ }
}
}
else
{
+ const svn_wc_conflict_description2_t *old_tc = NULL;
+
/* The node doesn't exist pre-merge: We have an addition */
db->added = TRUE;
db->tree_conflict_action = svn_wc_conflict_action_add;
if (pdb && pdb->pending_deletes
- && contains_path(pdb->pending_deletes, local_abspath))
+ && svn_hash_gets(pdb->pending_deletes, local_abspath))
+ {
+ db->add_is_replace = TRUE;
+ db->tree_conflict_action = svn_wc_conflict_action_replace;
+
+ svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
+ }
+
+ if (pdb
+ && pdb->new_tree_conflicts
+ && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
{
- db->add_is_replace = TRUE;
+ db->tree_conflict_action = svn_wc_conflict_action_replace;
+ db->tree_conflict_reason = old_tc->reason;
- apr_hash_set(pdb->pending_deletes, local_abspath,
- APR_HASH_KEY_STRING, NULL);
- }
+ if (old_tc->reason == svn_wc_conflict_reason_deleted
+ || old_tc->reason == svn_wc_conflict_reason_moved_away)
+ {
+ /* Issue #3806: Incoming replacements on local deletes produce
+ inconsistent result.
+
+ In this specific case we can continue applying the add part
+ of the replacement. */
+ }
+ else
+ {
+ *skip = TRUE;
+ *skip_children = TRUE;
+
+ /* Update the tree conflict to store that this is a replace */
+ SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ svn_node_dir,
+ db->tree_conflict_action,
+ db->tree_conflict_reason,
+ old_tc, FALSE,
+ scratch_pool));
- if (! (merge_b->dry_run && ((pdb && pdb->added) || db->add_is_replace)))
+ return SVN_NO_ERROR;
+ }
+ }
+
+ if (! (merge_b->dry_run
+ && ((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, &kind,
+ SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
+ &kind, NULL,
merge_b, local_abspath,
- svn_node_unknown,
scratch_pool));
/* In this case of adding a directory, we have an exception to the
@@ -2844,6 +2719,19 @@ merge_dir_opened(void **new_dir_baton,
SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
scratch_pool));
+ if (old_tc)
+ {
+ /* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node
+ over an existing tree conflict */
+
+ /* ### These functions should take some tree conflict argument
+ and allow overwriting the tc when one is passed */
+
+ SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
+ local_abspath,
+ scratch_pool));
+ }
+
if (merge_b->same_repos)
{
const char *original_url;
@@ -2876,6 +2764,17 @@ merge_dir_opened(void **new_dir_baton,
NULL, NULL /* no notify! */,
scratch_pool));
}
+
+ if (old_tc != NULL)
+ {
+ /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
+ SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ svn_node_dir,
+ db->tree_conflict_action,
+ db->tree_conflict_reason,
+ old_tc, FALSE,
+ scratch_pool));
+ }
}
if (! db->shadowed && !merge_b->record_only)
@@ -2939,7 +2838,8 @@ merge_dir_changed(const char *relpath,
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
svn_node_dir, &merge_b->merge_source,
- merge_b->target, merge_b->pool));
+ merge_b->target,
+ scratch_pool, scratch_pool));
SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
left, right,
@@ -3015,6 +2915,13 @@ merge_dir_added(const char *relpath,
&& ! merge_b->record_only /* Skip details from merge_open_dir() */
);
+ if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
+ && ( !db->parent_baton || !db->parent_baton->added))
+ {
+ /* Store the roots of added subtrees */
+ store_path(merge_b->added_abspaths, local_abspath);
+ }
+
if (merge_b->same_repos)
{
/* When the directory was added in merge_dir_added() we didn't update its
@@ -3058,14 +2965,11 @@ merge_dir_added(const char *relpath,
scratch_pool));
}
- if (apr_hash_get(new_pristine_props, SVN_PROP_MERGEINFO,
- APR_HASH_KEY_STRING))
+ if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
{
alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
local_abspath, merge_b->pool);
}
-
- return SVN_NO_ERROR;
}
else
{
@@ -3079,7 +2983,7 @@ merge_dir_added(const char *relpath,
new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
- apr_hash_set(new_props, SVN_PROP_MERGEINFO, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
/* ### What is the easiest way to set new_props on LOCAL_ABSPATH?
@@ -3103,6 +3007,31 @@ merge_dir_added(const char *relpath,
return SVN_NO_ERROR;
}
+/* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */
+static svn_error_t *
+verify_touched_by_del_check(void *baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct dir_delete_baton_t *delb = baton;
+
+ if (svn_hash_gets(delb->compared_abspaths, local_abspath))
+ return SVN_NO_ERROR;
+
+ switch (status->node_status)
+ {
+ case svn_wc_status_deleted:
+ case svn_wc_status_ignored:
+ case svn_wc_status_none:
+ return SVN_NO_ERROR;
+
+ default:
+ delb->found_edit = TRUE;
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+ }
+}
+
/* An svn_diff_tree_processor_t function.
*
* Called after merge_dir_opened() when a node existed only in the left source.
@@ -3110,9 +3039,6 @@ merge_dir_added(const char *relpath,
* After the merge_dir_opened() but before the call to this merge_dir_deleted()
* function all descendants that existed in left_source will have been deleted.
*
- * ### By default the diff drivers report all descendants of a delete,
- * ### but this feature is currently disabled by a SKIP on merge_dir_opened()
- *
* If this node is replaced, an _opened() followed by a matching _add() will
* be invoked after this function.
*/
@@ -3128,7 +3054,9 @@ merge_dir_deleted(const char *relpath,
struct merge_dir_baton_t *db = dir_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
- svn_error_t *err;
+ struct dir_delete_baton_t *delb;
+ svn_boolean_t same;
+ apr_hash_t *working_props;
SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
@@ -3152,38 +3080,119 @@ merge_dir_deleted(const char *relpath,
return SVN_NO_ERROR;
}
- /* ### TODO: Before deleting, we should ensure that this dir
- tree is equal to the one we're being asked to delete.
- If not, mark this directory as a tree conflict victim,
- because this could be use case 5 as described in
- notes/tree-conflicts/detection.txt.
- */
+ SVN_ERR(svn_wc_prop_list2(&working_props,
+ merge_b->ctx->wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
- /* Passing NULL for the notify_func and notify_baton because
- repos_diff.c:delete_entry() will do it for us. */
- err = svn_client__wc_delete(local_abspath, merge_b->force_delete,
- merge_b->dry_run, FALSE,
- NULL, NULL,
- merge_b->ctx, scratch_pool);
- if (err)
+ if (merge_b->force_delete)
+ same = TRUE;
+ else
{
- svn_error_clear(err);
+ /* Compare the properties */
+ SVN_ERR(properties_same_p(&same, left_props, working_props,
+ scratch_pool));
+ }
+
+ delb = db->delete_state;
+ assert(delb != NULL);
+
+ if (! same)
+ {
+ delb->found_edit = TRUE;
+ }
+ else
+ {
+ store_path(delb->compared_abspaths, local_abspath);
+ }
+
+ if (delb->del_root != db)
+ return SVN_NO_ERROR;
+
+ if (delb->found_edit)
+ same = FALSE;
+ else if (merge_b->force_delete)
+ same = TRUE;
+ else
+ {
+ apr_array_header_t *ignores;
+ svn_error_t *err;
+ same = TRUE;
+ SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
+ scratch_pool));
+
+ /* None of the descendants was modified, but maybe there are
+ descendants we haven't walked?
+
+ Note that we aren't interested in changes, as we already verified
+ changes in the paths touched by the merge. And the existance of
+ other paths is enough to mark the directory edited */
+ err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
+ svn_depth_infinity, TRUE /* get-all */,
+ FALSE /* no-ignore */,
+ TRUE /* ignore-text-mods */, ignores,
+ verify_touched_by_del_check, delb,
+ merge_b->ctx->cancel_func,
+ merge_b->ctx->cancel_baton,
+ scratch_pool);
+
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ }
+
+ same = ! delb->found_edit;
+ }
+
+ if (same && !merge_b->dry_run)
+ {
+ svn_error_t *err;
+
+ err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
+ FALSE /* keep_local */, FALSE /* unversioned */,
+ merge_b->ctx->cancel_func,
+ merge_b->ctx->cancel_baton,
+ NULL, NULL /* no notify */,
+ scratch_pool);
+
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ same = FALSE;
+ }
+ }
+
+ if (! same)
+ {
/* If the attempt to delete an existing directory failed,
* the directory has local modifications (e.g. locally added
* files, or property changes). Flag a tree conflict. */
- SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
- svn_wc_conflict_action_delete,
- svn_wc_conflict_reason_edited, NULL));
- SVN_ERR(record_tree_conflict(merge_b, local_abspath, svn_node_dir,
+ /* This handles use case 5 described in the paper attached to issue
+ * #2282. See also notes/tree-conflicts/detection.txt
+ */
+ SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
+ svn_node_dir,
+ svn_wc_conflict_action_delete,
+ svn_wc_conflict_reason_edited,
+ NULL, TRUE,
scratch_pool));
}
else
{
/* Record that we might have deleted mergeinfo */
- alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
- local_abspath, merge_b->pool);
+ if (working_props
+ && svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
+ {
+ alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
+ local_abspath, merge_b->pool);
+ }
SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
svn_node_dir, scratch_pool));
@@ -3240,8 +3249,9 @@ merge_node_absent(const char *relpath,
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
- record_skip(merge_b, local_abspath, svn_node_unknown,
- svn_wc_notify_skip, svn_wc_notify_state_missing, scratch_pool);
+ SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
+ svn_wc_notify_skip, svn_wc_notify_state_missing,
+ scratch_pool));
return SVN_NO_ERROR;
}
@@ -3280,6 +3290,125 @@ find_nearest_ancestor(const apr_array_he
return NULL;
}
+/* Find the highest level path in a merge target (possibly the merge target
+ itself) to use in a merge notification header.
+
+ Return the svn_client__merge_path_t * representing the most distant
+ ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
+ ancestor's first remaining ranges element (per the REMAINING_RANGES
+ member of the ancestor) intersect with the first remaining ranges element
+ for every intermediate ancestor svn_client__merge_path_t * of
+ LOCAL_ABSPATH. If no such ancestor is found return NULL.
+
+ If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
+ represent a forward merge, then set *START to the oldest revision found
+ in any of the intersecting ancestors and *END to the youngest revision
+ found. If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
+ represent a reverse merge, then set *START to the youngest revision
+ found and *END to the oldest revision found. If no ancestors are found
+ then set *START and *END to SVN_INVALID_REVNUM.
+
+ If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
+ where child->abspath == PATH is considered PATH's ancestor. If FALSE,
+ then child->abspath must be a proper ancestor of PATH.
+
+ See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
+ information. */
+static svn_client__merge_path_t *
+find_nearest_ancestor_with_intersecting_ranges(
+ svn_revnum_t *start,
+ svn_revnum_t *end,
+ const apr_array_header_t *children_with_mergeinfo,
+ svn_boolean_t path_is_own_ancestor,
+ const char *local_abspath)
+{
+ int i;
+ svn_client__merge_path_t *nearest_ancestor = NULL;
+
+ *start = SVN_INVALID_REVNUM;
+ *end = SVN_INVALID_REVNUM;
+
+ SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
+
+ for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
+ {
+ svn_client__merge_path_t *child =
+ APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
+
+ if (svn_dirent_is_ancestor(child->abspath, local_abspath)
+ && (path_is_own_ancestor
+ || strcmp(child->abspath, local_abspath) != 0))
+ {
+ if (nearest_ancestor == NULL)
+ {
+ /* Found an ancestor. */
+ nearest_ancestor = child;
+
+ if (child->remaining_ranges)
+ {
+ svn_merge_range_t *r1 = APR_ARRAY_IDX(
+ child->remaining_ranges, 0, svn_merge_range_t *);
+ *start = r1->start;
+ *end = r1->end;
+ }
+ else
+ {
+ /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
+ is inside an absent subtree in the merge target. */
+ *start = SVN_INVALID_REVNUM;
+ *end = SVN_INVALID_REVNUM;
+ break;
+ }
+ }
+ else
+ {
+ /* We'e found another ancestor for LOCAL_ABSPATH. Do its
+ first remaining range intersect with the previously
+ found ancestor? */
+ svn_merge_range_t *r1 =
+ APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
+ svn_merge_range_t *);
+ svn_merge_range_t *r2 =
+ APR_ARRAY_IDX(child->remaining_ranges, 0,
+ svn_merge_range_t *);
+
+ if (r1 && r2)
+ {
+ svn_merge_range_t range1;
+ svn_merge_range_t range2;
+ svn_boolean_t reverse_merge = r1->start > r2->end;
+
+ /* Flip endpoints if this is a reverse merge. */
+ if (reverse_merge)
+ {
+ range1.start = r1->end;
+ range1.end = r1->start;
+ range2.start = r2->end;
+ range2.end = r2->start;
+ }
+ else
+ {
+ range1.start = r1->start;
+ range1.end = r1->end;
+ range2.start = r2->start;
+ range2.end = r2->end;
+ }
+
+ if (range1.start < range2.end && range2.start < range1.end)
+ {
+ *start = reverse_merge ?
+ MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
+ *end = reverse_merge ?
+ MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
+ nearest_ancestor = child;
+ }
+ }
+ }
+ }
+ }
+ return nearest_ancestor;
+}
+
/* Notify that we're starting to record mergeinfo for the merge of the
* revision range RANGE into TARGET_ABSPATH. RANGE should be null if the
* merge sources are not from the same URL.
@@ -3346,19 +3475,15 @@ notify_merge_begin(merge_cmd_baton_t *me
apr_pool_t *scratch_pool)
{
svn_wc_notify_t *notify;
- svn_merge_range_t *n_range = NULL;
+ svn_merge_range_t n_range =
+ {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
const char *notify_abspath;
- if (merge_b->notified_merge_begin
- || ! merge_b->ctx->notify_func2)
+ if (! merge_b->ctx->notify_func2)
return SVN_NO_ERROR;
- notify_abspath = merge_b->target->abspath;
-
/* If our merge sources are ancestors of one another... */
- /* single file merge updates current_range */
- if (merge_b->merge_source.ancestral
- && ! merge_b->is_single_file_merge)
+ if (merge_b->merge_source.ancestral)
{
const svn_client__merge_path_t *child;
/* Find NOTIFY->PATH's nearest ancestor in
@@ -3377,38 +3502,49 @@ notify_merge_begin(merge_cmd_baton_t *me
D PARENT/CHILD
*/
- child = find_nearest_ancestor(merge_b->children_with_mergeinfo,
- ! delete_action, local_abspath);
+ child = find_nearest_ancestor_with_intersecting_ranges(
+ &(n_range.start), &(n_range.end),
+ merge_b->notify_begin.nodes_with_mergeinfo,
+ ! delete_action, local_abspath);
+
+ if (!child && delete_action)
+ {
+ /* Triggered by file replace in single-file-merge */
+ child = find_nearest_ancestor(merge_b->notify_begin.nodes_with_mergeinfo,
+ TRUE, local_abspath);
+ }
assert(child != NULL); /* Should always find the merge anchor */
if (! child)
return SVN_NO_ERROR;
- if (merge_b->cur_ancestor_abspath != NULL
- && strcmp(child->abspath, merge_b->cur_ancestor_abspath) == 0)
+ if (merge_b->notify_begin.last_abspath != NULL
+ && strcmp(child->abspath, merge_b->notify_begin.last_abspath) == 0)
{
/* Don't notify the same merge again */
return SVN_NO_ERROR;
}
- merge_b->cur_ancestor_abspath = child->abspath;
+ merge_b->notify_begin.last_abspath = child->abspath;
- if (child->absent || child->remaining_ranges->nelts == 0)
+ if (child->absent || child->remaining_ranges->nelts == 0
+ || !SVN_IS_VALID_REVNUM(n_range.start))
{
/* No valid information for an header */
return SVN_NO_ERROR;
}
notify_abspath = child->abspath;
- n_range = APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
}
- else if (merge_b->merge_source.ancestral)
+ else
{
- merge_b->notified_merge_begin = TRUE;
+ if (merge_b->notify_begin.last_abspath)
+ return SVN_NO_ERROR; /* already notified */
- if (merge_b->current_range)
- n_range = svn_merge_range_dup(merge_b->current_range, scratch_pool);
+ notify_abspath = merge_b->target->abspath;
+ /* Store something in last_abspath. Any value would do */
+ merge_b->notify_begin.last_abspath = merge_b->target->abspath;
}
notify = svn_wc_create_notify(notify_abspath,
@@ -3416,7 +3552,11 @@ notify_merge_begin(merge_cmd_baton_t *me
? svn_wc_notify_merge_begin
: svn_wc_notify_foreign_merge_begin,
scratch_pool);
- notify->merge_range = n_range;
+
+ if (SVN_IS_VALID_REVNUM(n_range.start))
+ notify->merge_range = &n_range;
+ else
+ notify->merge_range = NULL;
(*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
scratch_pool);
@@ -4225,9 +4365,8 @@ filter_merged_revisions(svn_client__merg
result_pool,
scratch_pool));
- target_implicit_rangelist = apr_hash_get(child->implicit_mergeinfo,
- mergeinfo_path,
- APR_HASH_KEY_STRING);
+ target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
+ mergeinfo_path);
if (target_implicit_rangelist)
SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
@@ -4311,9 +4450,8 @@ filter_merged_revisions(svn_client__merg
result_pool,
scratch_pool));
- target_implicit_rangelist = apr_hash_get(child->implicit_mergeinfo,
- mergeinfo_path,
- APR_HASH_KEY_STRING);
+ target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
+ mergeinfo_path);
if (target_implicit_rangelist)
SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
target_implicit_rangelist,
@@ -4408,8 +4546,7 @@ calculate_remaining_ranges(svn_client__m
### [JAF] In fact, that may still leave some ranges that lie entirely
outside the range of SOURCE; it seems we don't care about that. */
if (target_mergeinfo)
- target_rangelist = apr_hash_get(target_mergeinfo, mergeinfo_path,
- APR_HASH_KEY_STRING);
+ target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path);
else
target_rangelist = NULL;
if (implicit_src_gap && target_rangelist)
@@ -4458,7 +4595,7 @@ calculate_remaining_ranges(svn_client__m
So in the name of user friendliness, return an error suggesting a helpful
course of action.
*/
- SVN_ERR(svn_wc__node_get_base(&child_base_revision, NULL, NULL, NULL,
+ SVN_ERR(svn_wc__node_get_base(&child_base_revision, NULL, NULL, NULL, NULL,
ctx->wc_ctx, child->abspath,
scratch_pool, scratch_pool));
/* If CHILD has no base revision then it hasn't been committed yet, so it
@@ -4560,9 +4697,7 @@ find_gaps_in_merge_source_history(svn_re
ra_session,
ctx, scratch_pool));
- rangelist = apr_hash_get(implicit_src_mergeinfo,
- merge_src_fspath,
- APR_HASH_KEY_STRING);
+ rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath);
if (!rangelist) /* ### Can we ever not find a rangelist? */
return SVN_NO_ERROR;
@@ -4999,7 +5134,7 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
fspath = svn_fspath__join(source_fspath,
local_abspath_rel_to_target,
iterpool);
- rangelist = apr_hash_get(mergeinfo, fspath, APR_HASH_KEY_STRING);
+ rangelist = svn_hash_gets(mergeinfo, fspath);
if (rangelist == NULL)
rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
@@ -5016,7 +5151,7 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool));
}
/* Update the mergeinfo by adjusting the path's rangelist. */
- apr_hash_set(mergeinfo, fspath, APR_HASH_KEY_STRING, rangelist);
+ svn_hash_sets(mergeinfo, fspath, rangelist);
if (is_rollback && apr_hash_count(mergeinfo) == 0)
mergeinfo = NULL;
@@ -5026,16 +5161,15 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
if (result_catalog)
{
svn_mergeinfo_t existing_mergeinfo =
- apr_hash_get(result_catalog, local_abspath, APR_HASH_KEY_STRING);
+ svn_hash_gets(result_catalog, local_abspath);
apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
if (existing_mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo,
result_catalog_pool, scratch_pool));
- apr_hash_set(result_catalog,
- apr_pstrdup(result_catalog_pool, local_abspath),
- APR_HASH_KEY_STRING,
- svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
+ svn_hash_sets(result_catalog,
+ apr_pstrdup(result_catalog_pool, local_abspath),
+ svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
}
else
{
@@ -5108,8 +5242,9 @@ record_skips_in_mergeinfo(const char *me
/* Before we override, make sure this is a versioned path, it might
be an external or missing from disk due to authz restrictions. */
SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL,
+ NULL, NULL,
merge_b, skipped_abspath,
- svn_node_unknown, iterpool));
+ iterpool));
if (obstruction_state == svn_wc_notify_state_obstructed
|| obstruction_state == svn_wc_notify_state_missing)
continue;
@@ -5125,10 +5260,9 @@ record_skips_in_mergeinfo(const char *me
### inherit any mergeinfo from a parent, but if it does
### we need to account for that. See issue #3440
### http://subversion.tigris.org/issues/show_bug.cgi?id=3440. */
- apr_hash_set(merges, skipped_abspath,
- APR_HASH_KEY_STRING,
- apr_array_make(scratch_pool, 0,
- sizeof(svn_merge_range_t *)));
+ svn_hash_sets(merges, skipped_abspath,
+ apr_array_make(scratch_pool, 0,
+ sizeof(svn_merge_range_t *)));
/* if (nbr_skips < notify_b->nbr_notifications)
### Use RANGELIST as the mergeinfo for all children of
@@ -5190,10 +5324,11 @@ remove_absent_children(const char *targe
drive removes explicit mergeinfo from a subtree of the merge target.
MERGE_B is cascaded from the argument of the same name in
- do_directory_merge(). If MERGE_B->DRY_RUN is true do nothing, if it is
- false then for each path (if any) in MERGE_B->PATHS_WITH_DELETED_MERGEINFO
- remove that path from CHILDREN_WITH_MERGEINFO by setting that
- child to NULL. The one exception is for the merge target itself,
+ do_directory_merge(). For each path (if any) in
+ MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from
+ CHILDREN_WITH_MERGEINFO.
+
+ The one exception is for the merge target itself,
MERGE_B->target->abspath, this must always be present in
CHILDREN_WITH_MERGEINFO so this is never removed by this
function. */
@@ -5203,7 +5338,7 @@ remove_children_with_deleted_mergeinfo(m
{
int i;
- if (merge_b->dry_run || !merge_b->paths_with_deleted_mergeinfo)
+ if (!merge_b->paths_with_deleted_mergeinfo)
return;
/* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
@@ -5212,8 +5347,8 @@ remove_children_with_deleted_mergeinfo(m
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
- if (apr_hash_get(merge_b->paths_with_deleted_mergeinfo,
- child->abspath, APR_HASH_KEY_STRING))
+
+ if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
{
svn_sort__array_delete(children_with_mergeinfo, i--, 1);
}
@@ -5492,9 +5627,6 @@ drive_merge_report_editor(const char *ta
SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool));
SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
- /* Caller must call svn_sleep_for_timestamps() */
- *(merge_b->use_sleep) = TRUE;
-
return SVN_NO_ERROR;
}
@@ -5590,13 +5722,7 @@ slice_remaining_ranges(apr_array_header_
If a range is removed from a child's remaining_ranges array, allocate the
new remaining_ranges array in POOL.
-
- ### TODO: We should have remaining_ranges in reverse order to avoid
- ### recreating and reallocating the remaining_ranges every time we want
- ### to remove the first range. If the ranges were reversed we could simply
- ### pop the last element in the array. Alternatively we might be able to
- ### make svn_sort__array_delete() efficient: it could increment 'elts'
- ### (and decrement 'nelts' and 'nalloc') instead of moving elements. */
+ */
static void
remove_first_range_from_remaining_ranges(svn_revnum_t revision,
apr_array_header_t
@@ -5861,28 +5987,22 @@ pre_merge_status_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct pre_merge_status_baton_t *pmsb = baton;
- const char *dup_abspath = NULL;
if (status->switched && !status->file_external)
{
- if (!dup_abspath)
- dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
-
- apr_hash_set(pmsb->switched_subtrees,
- dup_abspath, APR_HASH_KEY_STRING, dup_abspath);
+ store_path(pmsb->switched_subtrees, local_abspath);
}
if (status->depth == svn_depth_empty
|| status->depth == svn_depth_files)
{
+ const char *dup_abspath;
svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
sizeof *depth);
- if (!dup_abspath)
- dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
+ dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
- apr_hash_set(pmsb->shallow_subtrees,
- dup_abspath, APR_HASH_KEY_STRING, depth);
+ svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth);
}
if (status->node_status == svn_wc_status_missing)
@@ -5890,9 +6010,6 @@ pre_merge_status_cb(void *baton,
svn_boolean_t new_missing_root = TRUE;
apr_hash_index_t *hi;
- if (!dup_abspath)
- dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
-
for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
hi;
hi = apr_hash_next(hi))
@@ -5900,7 +6017,7 @@ pre_merge_status_cb(void *baton,
const char *missing_root_path = svn__apr_hash_index_key(hi);
if (svn_dirent_is_ancestor(missing_root_path,
- dup_abspath))
+ local_abspath))
{
new_missing_root = FALSE;
break;
@@ -5908,8 +6025,7 @@ pre_merge_status_cb(void *baton,
}
if (new_missing_root)
- apr_hash_set(pmsb->missing_subtrees,
- dup_abspath, APR_HASH_KEY_STRING, dup_abspath);
+ store_path(pmsb->missing_subtrees, local_abspath);
}
return SVN_NO_ERROR;
@@ -5963,10 +6079,9 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
/* svn_client_propget5 picks up file externals with
mergeinfo, but we don't want those. */
- if (apr_hash_get(externals, wc_path, APR_HASH_KEY_STRING))
+ if (svn_hash_gets(externals, wc_path))
{
- apr_hash_set(*subtrees_with_mergeinfo, wc_path,
- APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL);
continue;
}
@@ -5986,8 +6101,7 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
}
return svn_error_trace(err);
}
- apr_hash_set(*subtrees_with_mergeinfo, wc_path, APR_HASH_KEY_STRING,
- mergeinfo);
+ svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo);
}
svn_pool_destroy(iterpool);
@@ -7000,8 +7114,7 @@ filter_natural_history_from_mergeinfo(sv
&& (requested_range->start < requested_range->end))
{
svn_rangelist_t *implied_rangelist =
- apr_hash_get(implicit_mergeinfo, source_rel_path,
- APR_HASH_KEY_STRING);
+ svn_hash_gets(implicit_mergeinfo, source_rel_path);
if (implied_rangelist)
SVN_ERR(svn_rangelist_remove(filtered_rangelist,
@@ -7073,25 +7186,27 @@ subrange_source(const merge_source_t *so
and the value is the new mergeinfo for that path. Allocate additions
to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
+ CONFLICTED_RANGE is as documented for do_directory_merge().
+
Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
*/
static svn_error_t *
do_file_merge(svn_mergeinfo_catalog_t result_catalog,
+ svn_merge_range_t **conflicted_range,
const merge_source_t *source,
const char *target_abspath,
const svn_diff_tree_processor_t *processor,
svn_boolean_t sources_related,
svn_boolean_t squelch_mergeinfo_notifications,
- svn_boolean_t abort_on_conflicts,
merge_cmd_baton_t *merge_b,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_rangelist_t *remaining_ranges;
svn_client_ctx_t *ctx = merge_b->ctx;
svn_merge_range_t range;
svn_mergeinfo_t target_mergeinfo;
- svn_merge_range_t *conflicted_range = NULL;
svn_boolean_t inherited = FALSE;
svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
const svn_client__pathrev_t *primary_src
@@ -7102,9 +7217,9 @@ do_file_merge(svn_mergeinfo_catalog_t re
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
- /* Note that this is a single-file merge. */
- merge_b->is_single_file_merge = TRUE;
+ *conflicted_range = NULL;
+ /* Note that this is a single-file merge. */
range.start = source->loc1->rev;
range.end = source->loc2->rev;
[... 1272 lines stripped ...]