Modified: subversion/branches/addremove/subversion/libsvn_client/merge.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/merge.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/merge.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/merge.c Sat May 23 14:16:56 2020 @@ -215,6 +215,21 @@ /*** Repos-Diff Editor Callbacks ***/ +struct merge_cmd_baton_t; + +struct notify_begin_state_t +{ + /* Cache of which abspath was last notified. */ + const char *last_abspath; + + /* Reference to the main merge baton */ + struct merge_cmd_baton_t *merge_b; + + /* the wrapped notification callback */ + svn_wc_notify_func2_t notify_func2; + void *notify_baton2; +}; + typedef struct merge_cmd_baton_t { svn_boolean_t force_delete; /* Delete a file/dir even if modified */ svn_boolean_t dry_run; @@ -242,11 +257,15 @@ typedef struct merge_cmd_baton_t { /* Rangelist containing single range which describes the gap, if any, in the natural history of the merge source currently being processed. - See http://subversion.tigris.org/issues/show_bug.cgi?id=3432. + See https://issues.apache.org/jira/browse/SVN-3432. Updated during each call to do_directory_merge(). May be NULL if there is no gap. */ svn_rangelist_t *implicit_src_gap; + /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global + comment) or a similar list for single-file-merges */ + const apr_array_header_t *children_with_mergeinfo; + svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */ /* The list of any paths which remained in conflict after a @@ -319,17 +338,10 @@ typedef struct merge_cmd_baton_t { or do_file_merge() in do_merge(). */ apr_pool_t *pool; - - /* 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; + /* Our notification callback, that adds a 'begin' notification */ + svn_wc_notify_func2_t notify_func; + void *notify_baton; + struct notify_begin_state_t notify_begin; } merge_cmd_baton_t; @@ -340,17 +352,25 @@ typedef struct merge_cmd_baton_t { merge source is an ancestor of the right-side (or vice-versa), the merge source is in the same repository as the merge target, and we are not ignoring mergeinfo. */ -#define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable \ - && (merge_b)->merge_source.ancestral \ - && (merge_b)->same_repos \ - && (! (merge_b)->ignore_mergeinfo)) +static svn_boolean_t +HONOR_MERGEINFO(const merge_cmd_baton_t *merge_b) +{ + return (merge_b->mergeinfo_capable + && merge_b->merge_source.ancestral + && merge_b->same_repos + && (!merge_b->ignore_mergeinfo)); +} /* Return TRUE iff we should be recording mergeinfo for the merge described by MERGE_B. Specifically, that is if we are honoring mergeinfo and the merge is not a dry run. */ -#define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \ - && !(merge_b)->dry_run) +static svn_boolean_t +RECORD_MERGEINFO(const merge_cmd_baton_t *merge_b) +{ + return (HONOR_MERGEINFO(merge_b) + && !merge_b->dry_run); +} /*-----------------------------------------------------------------------*/ @@ -1226,13 +1246,6 @@ struct merge_file_baton_t svn_boolean_t add_is_replace; /* Add is second part of replace */ }; -/* Forward declaration */ -static svn_error_t * -notify_merge_begin(merge_cmd_baton_t *merge_b, - const char *local_abspath, - svn_boolean_t delete_action, - apr_pool_t *scratch_pool); - /* Record the skip for future processing and (later) produce the skip notification */ static svn_error_t * @@ -1253,18 +1266,16 @@ record_skip(merge_cmd_baton_t *merge_b, store_path(merge_b->skipped_abspaths, local_abspath); } - if (merge_b->ctx->notify_func2) + if (merge_b->notify_func) { svn_wc_notify_t *notify; - SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); - notify = svn_wc_create_notify(local_abspath, action, scratch_pool); 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->notify_func(merge_b->notify_baton, notify, + scratch_pool); } return SVN_NO_ERROR; } @@ -1364,7 +1375,7 @@ record_tree_conflict(merge_cmd_baton_t * * 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, + merge_b->children_with_mergeinfo, action != svn_wc_conflict_action_delete, local_abspath); loc1 = svn_client__pathrev_dup(merge_b->merge_source.loc1, @@ -1423,18 +1434,16 @@ record_tree_conflict(merge_cmd_baton_t * } /* On a replacement we currently get two tree conflicts */ - if (merge_b->ctx->notify_func2 && notify_tc) + if (merge_b->notify_func && notify_tc) { svn_wc_notify_t *notify; - SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); - notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict, scratch_pool); notify->kind = local_node_kind; - merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify, - scratch_pool); + merge_b->notify_func(merge_b->notify_baton, notify, + scratch_pool); } return SVN_NO_ERROR; @@ -1455,21 +1464,19 @@ record_update_add(merge_cmd_baton_t *mer store_path(merge_b->merged_abspaths, local_abspath); } - if (merge_b->ctx->notify_func2) + if (merge_b->notify_func) { svn_wc_notify_t *notify; svn_wc_notify_action_t action = svn_wc_notify_update_add; - SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); - if (notify_replaced) action = svn_wc_notify_update_replace; 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->notify_func(merge_b->notify_baton, notify, + scratch_pool); } return SVN_NO_ERROR; @@ -1490,20 +1497,18 @@ record_update_update(merge_cmd_baton_t * store_path(merge_b->merged_abspaths, local_abspath); } - if (merge_b->ctx->notify_func2) + if (merge_b->notify_func) { svn_wc_notify_t *notify; - SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); - notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update, scratch_pool); notify->kind = kind; 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->notify_func(merge_b->notify_baton, notify, + scratch_pool); } return SVN_NO_ERROR; @@ -1529,8 +1534,6 @@ record_update_delete(merge_cmd_baton_t * store_path(merge_b->merged_abspaths, local_abspath); } - SVN_ERR(notify_merge_begin(merge_b, local_abspath, TRUE, scratch_pool)); - if (parent_db) { const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath); @@ -1552,7 +1555,7 @@ handle_pending_notifications(merge_cmd_b struct merge_dir_baton_t *db, apr_pool_t *scratch_pool) { - if (merge_b->ctx->notify_func2 && db->pending_deletes) + if (merge_b->notify_func && db->pending_deletes) { apr_hash_index_t *hi; @@ -1569,8 +1572,8 @@ handle_pending_notifications(merge_cmd_b notify->kind = svn_node_kind_from_word( apr_hash_this_val(hi)); - merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, - notify, scratch_pool); + merge_b->notify_func(merge_b->notify_baton, + notify, scratch_pool); } db->pending_deletes = NULL; @@ -1620,13 +1623,10 @@ mark_dir_edited(merge_cmd_baton_t *merge for clarity we produce a skip for this node that most likely isn't touched by the merge itself */ - if (merge_b->ctx->notify_func2) + if (merge_b->notify_func) { svn_wc_notify_t *notify; - SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, - scratch_pool)); - notify = svn_wc_create_notify( local_abspath, (db->tree_conflict_reason == CONFLICT_REASON_SKIP) @@ -1636,9 +1636,9 @@ mark_dir_edited(merge_cmd_baton_t *merge 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->notify_func(merge_b->notify_baton, + notify, + scratch_pool); } if (merge_b->merge_source.ancestral @@ -1706,21 +1706,18 @@ mark_file_edited(merge_cmd_baton_t *merg for clarity we produce a skip for this node that most likely isn't touched by the merge itself */ - if (merge_b->ctx->notify_func2) + if (merge_b->notify_func) { svn_wc_notify_t *notify; - SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, - scratch_pool)); - notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip, scratch_pool); 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->notify_func(merge_b->notify_baton, + notify, + scratch_pool); } if (merge_b->merge_source.ancestral @@ -3390,7 +3387,7 @@ merge_dir_closed(const char *relpath, We register a skipped path, which will make parent mergeinfo non- inheritable. This ensures that a future merge might see these skipped - changes as eligable for merging. + changes as eligible for merging. For legacy reasons we also notify the path as skipped. */ @@ -3413,6 +3410,49 @@ merge_node_absent(const char *relpath, return SVN_NO_ERROR; } +/* Return a diff processor that will apply the merge to the WC. + */ +static svn_diff_tree_processor_t * +merge_apply_processor(merge_cmd_baton_t *merge_cmd_baton, + apr_pool_t *result_pool) +{ + svn_diff_tree_processor_t *merge_processor; + + merge_processor = svn_diff__tree_processor_create(merge_cmd_baton, + result_pool); + + merge_processor->dir_opened = merge_dir_opened; + merge_processor->dir_changed = merge_dir_changed; + merge_processor->dir_added = merge_dir_added; + merge_processor->dir_deleted = merge_dir_deleted; + merge_processor->dir_closed = merge_dir_closed; + + merge_processor->file_opened = merge_file_opened; + merge_processor->file_changed = merge_file_changed; + merge_processor->file_added = merge_file_added; + merge_processor->file_deleted = merge_file_deleted; + /* Not interested in file_closed() */ + + merge_processor->node_absent = merge_node_absent; + + return merge_processor; +} + +/* Initialize minimal dir baton to allow calculating 'R'eplace + from 'D'elete + 'A'dd. */ +static void * +open_dir_for_replace_single_file(apr_pool_t *result_pool) +{ + struct merge_dir_baton_t *dir_baton = apr_pcalloc(result_pool, sizeof(*dir_baton)); + + dir_baton->pool = result_pool; + dir_baton->tree_conflict_reason = CONFLICT_REASON_NONE; + dir_baton->tree_conflict_action = svn_wc_conflict_action_edit; + dir_baton->skip_reason = svn_wc_notify_state_unknown; + + return dir_baton; +} + /*-----------------------------------------------------------------------*/ /*** Merge Notification ***/ @@ -3608,20 +3648,9 @@ notify_merge_completed(const char *targe } } -/* Is the notification the result of a real operative merge? */ -#define IS_OPERATIVE_NOTIFICATION(notify) \ - (notify->content_state == svn_wc_notify_state_conflicted \ - || notify->content_state == svn_wc_notify_state_merged \ - || notify->content_state == svn_wc_notify_state_changed \ - || notify->prop_state == svn_wc_notify_state_conflicted \ - || notify->prop_state == svn_wc_notify_state_merged \ - || notify->prop_state == svn_wc_notify_state_changed \ - || notify->action == svn_wc_notify_update_add \ - || notify->action == svn_wc_notify_tree_conflict) - /* Remove merge source gaps from range used for merge notifications. - See http://subversion.tigris.org/issues/show_bug.cgi?id=4138 + See https://issues.apache.org/jira/browse/SVN-4138 If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a single range (see the implicit_src_gap member of merge_cmd_baton_t). @@ -3656,27 +3685,28 @@ remove_source_gap(svn_merge_range_t *ran * This calls the client's notification receiver (as found in the client * context), with a WC abspath. */ -static svn_error_t * -notify_merge_begin(merge_cmd_baton_t *merge_b, +static void +notify_merge_begin(struct notify_begin_state_t *notify_begin_state, const char *local_abspath, svn_boolean_t delete_action, apr_pool_t *scratch_pool) { + merge_cmd_baton_t *merge_b = notify_begin_state->merge_b; svn_wc_notify_t *notify; svn_merge_range_t n_range = {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE}; const char *notify_abspath; - if (! merge_b->ctx->notify_func2) - return SVN_NO_ERROR; + if (! notify_begin_state->notify_func2) + return; /* If our merge sources are ancestors of one another... */ if (merge_b->merge_source.ancestral) { const svn_client__merge_path_t *child; - /* Find NOTIFY->PATH's nearest ancestor in - NOTIFY->CHILDREN_WITH_MERGEINFO. Normally we consider a child in - NOTIFY->CHILDREN_WITH_MERGEINFO representing PATH to be an + /* Find LOCAL_ABSPATH's nearest ancestor in + CHILDREN_WITH_MERGEINFO. Normally we consider a child in + CHILDREN_WITH_MERGEINFO representing PATH to be an ancestor of PATH, but if this is a deletion of PATH then the notification must be for a proper ancestor of PATH. This ensures we don't get notifications like: @@ -3692,47 +3722,47 @@ notify_merge_begin(merge_cmd_baton_t *me child = find_nearest_ancestor_with_intersecting_ranges( &(n_range.start), &(n_range.end), - merge_b->notify_begin.nodes_with_mergeinfo, + merge_b->children_with_mergeinfo, ! delete_action, local_abspath); if (!child && delete_action) { /* Triggered by file replace in single-file-merge */ - child = find_nearest_ancestor(merge_b->notify_begin.nodes_with_mergeinfo, + child = find_nearest_ancestor(merge_b->children_with_mergeinfo, TRUE, local_abspath); } assert(child != NULL); /* Should always find the merge anchor */ if (! child) - return SVN_NO_ERROR; + return; - if (merge_b->notify_begin.last_abspath != NULL - && strcmp(child->abspath, merge_b->notify_begin.last_abspath) == 0) + if (notify_begin_state->last_abspath != NULL + && strcmp(child->abspath, notify_begin_state->last_abspath) == 0) { /* Don't notify the same merge again */ - return SVN_NO_ERROR; + return; } - merge_b->notify_begin.last_abspath = child->abspath; + notify_begin_state->last_abspath = child->abspath; if (child->absent || child->remaining_ranges->nelts == 0 || !SVN_IS_VALID_REVNUM(n_range.start)) { /* No valid information for an header */ - return SVN_NO_ERROR; + return; } notify_abspath = child->abspath; } else { - if (merge_b->notify_begin.last_abspath) - return SVN_NO_ERROR; /* already notified */ + if (notify_begin_state->last_abspath) + return; /* already notified */ 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_begin_state->last_abspath = merge_b->target->abspath; } notify = svn_wc_create_notify(notify_abspath, @@ -3753,10 +3783,23 @@ notify_merge_begin(merge_cmd_baton_t *me notify->merge_range = NULL; } - merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify, - scratch_pool); + notify_begin_state->notify_func2(notify_begin_state->notify_baton2, notify, + scratch_pool); +} - return SVN_NO_ERROR; +/* Our notification callback, that adds a 'begin' notification */ +static void +notify_merging(void *baton, + const svn_wc_notify_t *notify, + apr_pool_t *pool) +{ + struct notify_begin_state_t *b = baton; + + notify_merge_begin(b, notify->path, + notify->action == svn_wc_notify_update_delete, + pool); + + b->notify_func2(b->notify_baton2, notify, pool); } /* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple @@ -5445,7 +5488,7 @@ record_skips_in_mergeinfo(const char *me ### TODO: An empty range is fine if the skipped path doesn't ### 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. */ + ### https://issues.apache.org/jira/browse/SVN-3440. */ svn_hash_sets(merges, skipped_abspath, apr_array_make(scratch_pool, 0, sizeof(svn_merge_range_t *))); @@ -5554,7 +5597,7 @@ svn_client__make_merge_conflict_error(sv defined in get_mergeinfo_paths(). Remove any paths absent from disk or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to or are descendants of TARGET_WCPATH by setting those children to NULL. */ -static void +static svn_error_t * remove_absent_children(const char *target_wcpath, apr_array_header_t *children_with_mergeinfo) { @@ -5569,9 +5612,10 @@ remove_absent_children(const char *targe if ((child->absent || child->scheduled_for_deletion) && svn_dirent_is_ancestor(target_wcpath, child->abspath)) { - svn_sort__array_delete(children_with_mergeinfo, i--, 1); + SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1)); } } + return SVN_NO_ERROR; } /* Helper for do_directory_merge() to handle the case where a merge editor @@ -5586,14 +5630,14 @@ remove_absent_children(const char *targe MERGE_B->target->abspath, this must always be present in CHILDREN_WITH_MERGEINFO so this is never removed by this function. */ -static void +static svn_error_t * remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b, apr_array_header_t *children_with_mergeinfo) { int i; if (!merge_b->paths_with_deleted_mergeinfo) - return; + return SVN_NO_ERROR; /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target so start at the first child. */ @@ -5604,9 +5648,10 @@ remove_children_with_deleted_mergeinfo(m if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath)) { - svn_sort__array_delete(children_with_mergeinfo, i--, 1); + SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1)); } } + return SVN_NO_ERROR; } /* Helper for do_directory_merge(). @@ -5932,7 +5977,7 @@ get_most_inclusive_rev(const apr_array_h remaining_ranges is inclusive of END_REV, Slice the first range in to two at END_REV. All the allocations are persistent and allocated from POOL. */ -static void +static svn_error_t * slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo, svn_boolean_t is_rollback, svn_revnum_t end_rev, apr_pool_t *pool) @@ -5962,10 +6007,12 @@ slice_remaining_ranges(apr_array_header_ split_range2->start = end_rev; APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *) = split_range1; - svn_sort__array_insert(child->remaining_ranges, &split_range2, 1); + SVN_ERR(svn_sort__array_insert2(child->remaining_ranges, + &split_range2, 1)); } } } + return SVN_NO_ERROR; } /* Helper for do_directory_merge(). @@ -5977,7 +6024,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. */ -static void +static svn_error_t * remove_first_range_from_remaining_ranges(svn_revnum_t revision, apr_array_header_t *children_with_mergeinfo, @@ -5998,10 +6045,11 @@ remove_first_range_from_remaining_ranges APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *); if (first_range->end == revision) { - svn_sort__array_delete(child->remaining_ranges, 0, 1); + SVN_ERR(svn_sort__array_delete2(child->remaining_ranges, 0, 1)); } } } + return SVN_NO_ERROR; } /* Get a file's content and properties from the repository. @@ -6087,7 +6135,7 @@ get_child_with_mergeinfo(const apr_array out of order and then sort afterwards. (One caller is doing a qsort after calling this anyway.) */ -static void +static svn_error_t * insert_child_to_merge(apr_array_header_t *children_with_mergeinfo, const svn_client__merge_path_t *insert_element, apr_pool_t *pool) @@ -6101,7 +6149,9 @@ insert_child_to_merge(apr_array_header_t compare_merge_path_t_as_paths); new_element = svn_client__merge_path_dup(insert_element, pool); - svn_sort__array_insert(children_with_mergeinfo, &new_element, insert_index); + SVN_ERR(svn_sort__array_insert2(children_with_mergeinfo, + &new_element, insert_index)); + return SVN_NO_ERROR; } /* Helper for get_mergeinfo_paths(). @@ -6162,7 +6212,7 @@ insert_parent_and_sibs_of_sw_absent_del_ parent->missing_child = child->absent; parent->switched_child = child->switched; /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */ - insert_child_to_merge(children_with_mergeinfo, parent, pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, parent, pool)); /* Increment for loop index so we don't process the inserted element. */ (*curr_index)++; } /*(parent == NULL) */ @@ -6199,8 +6249,8 @@ insert_parent_and_sibs_of_sw_absent_del_ sibling_of_missing = svn_client__merge_path_create(child_abspath, pool); - insert_child_to_merge(children_with_mergeinfo, sibling_of_missing, - pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, + sibling_of_missing, pool)); } } @@ -6541,8 +6591,8 @@ get_mergeinfo_paths(apr_array_header_t * svn_client__merge_path_t *switched_child = svn_client__merge_path_create(wc_path, result_pool); switched_child->switched = TRUE; - insert_child_to_merge(children_with_mergeinfo, switched_child, - result_pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, + switched_child, result_pool)); } } } @@ -6594,8 +6644,8 @@ get_mergeinfo_paths(apr_array_header_t * } if (new_shallow_child) - insert_child_to_merge(children_with_mergeinfo, shallow_child, - result_pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, + shallow_child, result_pool)); } } @@ -6624,8 +6674,8 @@ get_mergeinfo_paths(apr_array_header_t * svn_client__merge_path_t *absent_child = svn_client__merge_path_create(wc_path, result_pool); absent_child->absent = TRUE; - insert_child_to_merge(children_with_mergeinfo, absent_child, - result_pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, + absent_child, result_pool)); } } } @@ -6638,8 +6688,8 @@ get_mergeinfo_paths(apr_array_header_t * svn_client__merge_path_t *target_child = svn_client__merge_path_create(target->abspath, result_pool); - insert_child_to_merge(children_with_mergeinfo, target_child, - result_pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, target_child, + result_pool)); } /* Case 8: Path is an immediate *directory* child of @@ -6682,8 +6732,8 @@ get_mergeinfo_paths(apr_array_header_t * && depth == svn_depth_immediates) immediate_child->immediate_child_dir = TRUE; - insert_child_to_merge(children_with_mergeinfo, - immediate_child, result_pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, + immediate_child, result_pool)); } } } @@ -6775,9 +6825,9 @@ get_mergeinfo_paths(apr_array_header_t * child_of_noninheritable = svn_client__merge_path_create(child_abspath, result_pool); child_of_noninheritable->child_of_noninheritable = TRUE; - insert_child_to_merge(children_with_mergeinfo, - child_of_noninheritable, - result_pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, + child_of_noninheritable, + result_pool)); if (!dry_run && same_repos) { svn_mergeinfo_t mergeinfo; @@ -7201,7 +7251,7 @@ normalize_merge_sources_internal(apr_arr new_segment->path = original_repos_relpath; new_segment->range_start = original_revision; new_segment->range_end = original_revision; - svn_sort__array_insert(segments, &new_segment, 0); + SVN_ERR(svn_sort__array_insert2(segments, &new_segment, 0)); } } } @@ -7596,16 +7646,15 @@ do_file_merge(svn_mergeinfo_catalog_t re /* ### Create a fake copy of merge_target as we don't keep remaining_ranges in sync (yet). */ - target_info = apr_pcalloc(scratch_pool, sizeof(*target_info)); - - target_info->abspath = merge_target->abspath; + target_info = svn_client__merge_path_create(merge_target->abspath, + scratch_pool); target_info->remaining_ranges = ranges_to_merge; APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *) = target_info; /* And store in baton to allow using it from notify_merge_begin() */ - merge_b->notify_begin.nodes_with_mergeinfo = child_with_mergeinfo; + merge_b->children_with_mergeinfo = child_with_mergeinfo; } while (ranges_to_merge->nelts > 0) @@ -7648,19 +7697,10 @@ do_file_merge(svn_mergeinfo_catalog_t re do a text-n-props merge; otherwise, do a delete-n-add merge. */ if (! (merge_b->diff_ignore_ancestry || sources_related)) { - struct merge_dir_baton_t dir_baton; + void *dir_baton = open_dir_for_replace_single_file(iterpool); void *file_baton; svn_boolean_t skip; - /* Initialize minimal dir baton to allow calculating 'R'eplace - from 'D'elete + 'A'dd. */ - - memset(&dir_baton, 0, sizeof(dir_baton)); - dir_baton.pool = iterpool; - dir_baton.tree_conflict_reason = CONFLICT_REASON_NONE; - dir_baton.tree_conflict_action = svn_wc_conflict_action_edit; - dir_baton.skip_reason = svn_wc_notify_state_unknown; - /* Delete... */ file_baton = NULL; skip = FALSE; @@ -7668,7 +7708,7 @@ do_file_merge(svn_mergeinfo_catalog_t re left_source, NULL /* right_source */, NULL /* copyfrom_source */, - &dir_baton, + dir_baton, processor, iterpool, iterpool)); if (! skip) @@ -7687,7 +7727,7 @@ do_file_merge(svn_mergeinfo_catalog_t re NULL /* left_source */, right_source, NULL /* copyfrom_source */, - &dir_baton, + dir_baton, processor, iterpool, iterpool)); if (! skip) @@ -7758,7 +7798,7 @@ do_file_merge(svn_mergeinfo_catalog_t re (This list is used from notify_merge_begin) Directory merges use remove_first_range_from_remaining_ranges() */ - svn_sort__array_delete(ranges_to_merge, 0, 1); + SVN_ERR(svn_sort__array_delete2(ranges_to_merge, 0, 1)); } merge_b->notify_begin.last_abspath = NULL; } /* !merge_b->record_only */ @@ -7819,7 +7859,7 @@ do_file_merge(svn_mergeinfo_catalog_t re } } - merge_b->notify_begin.nodes_with_mergeinfo = NULL; + merge_b->children_with_mergeinfo = NULL; svn_pool_destroy(iterpool); @@ -7878,7 +7918,7 @@ process_children_with_new_mergeinfo(merg was added (with preexisting mergeinfo) by the merge. That's actually more correct, since the inherited mergeinfo likely describes non-existent or unrelated merge history, but it's not quite so simple - as that, see http://subversion.tigris.org/issues/show_bug.cgi?id=4309 + as that, see https://issues.apache.org/jira/browse/SVN-4309 */ /* Get the path's new explicit mergeinfo... */ @@ -7945,7 +7985,8 @@ process_children_with_new_mergeinfo(merg /* Set the path's remaining_ranges equal to its parent's. */ new_child->remaining_ranges = svn_rangelist_dup( parent->remaining_ranges, pool); - insert_child_to_merge(children_with_mergeinfo, new_child, pool); + SVN_ERR(insert_child_to_merge(children_with_mergeinfo, + new_child, pool)); } } } @@ -8269,7 +8310,7 @@ flag_subtrees_needing_mergeinfo(svn_bool merge_b->target->abspath, depth, merge_b->ctx->wc_ctx, merge_b->ra_session1, scratch_pool, iterpool)); - /* Issue #4056: Walk NOTIFY_B->CHILDREN_WITH_MERGEINFO reverse depth-first + /* Issue #4056: Walk CHILDREN_WITH_MERGEINFO reverse depth-first order. This way each child knows if it has operative missing/switched children which necessitates non-inheritable mergeinfo. */ for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--) @@ -8432,7 +8473,7 @@ flag_subtrees_needing_mergeinfo(svn_bool } else /* child->record_mergeinfo */ { - /* If CHILD is in NOTIFY_B->CHILDREN_WITH_MERGEINFO simply + /* If CHILD is in CHILDREN_WITH_MERGEINFO simply because it had no explicit mergeinfo of its own at the start of the merge but is the child of of some path with non-inheritable mergeinfo, then the explicit mergeinfo it @@ -8457,7 +8498,7 @@ flag_subtrees_needing_mergeinfo(svn_bool If RESULT_CATALOG is NULL then record mergeinfo describing a merge of MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described - by NOTIFY_B->CHILDREN_WITH_MERGEINFO -- see the global comment + by CHILDREN_WITH_MERGEINFO -- see the global comment 'THE CHILDREN_WITH_MERGEINFO ARRAY'. Obviously this should only be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO(). @@ -8508,10 +8549,10 @@ record_mergeinfo_for_dir_merge(svn_merge range.inheritable = TRUE; /* Remove absent children at or under MERGE_B->target->abspath from - NOTIFY_B->CHILDREN_WITH_MERGEINFO + CHILDREN_WITH_MERGEINFO before we calculate the merges performed. */ - remove_absent_children(merge_b->target->abspath, - children_with_mergeinfo); + SVN_ERR(remove_absent_children(merge_b->target->abspath, + children_with_mergeinfo)); /* Determine which subtrees of interest need mergeinfo recorded... */ SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range, @@ -9334,7 +9375,7 @@ do_mergeinfo_aware_dir_merge(svn_mergein /* Point our RA_SESSION to the URL of our youngest merge source side. */ ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2; - /* Fill NOTIFY_B->CHILDREN_WITH_MERGEINFO with child paths (const + /* Fill CHILDREN_WITH_MERGEINFO with child paths (const svn_client__merge_path_t *) which might have intersecting merges because they meet one or more of the criteria described in get_mergeinfo_paths(). Here the paths are arranged in a depth @@ -9344,13 +9385,13 @@ do_mergeinfo_aware_dir_merge(svn_mergein merge_b->dry_run, merge_b->same_repos, merge_b->ctx, scratch_pool, scratch_pool)); - /* The first item from the NOTIFY_B->CHILDREN_WITH_MERGEINFO is always + /* The first item from the CHILDREN_WITH_MERGEINFO is always the target thanks to depth-first ordering. */ target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *); /* If we are honoring mergeinfo, then for each item in - NOTIFY_B->CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be + CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be merged, and then merge it. Otherwise, we just merge what we were asked to merge across the whole tree. */ SVN_ERR(populate_remaining_ranges(children_with_mergeinfo, @@ -9370,7 +9411,7 @@ do_mergeinfo_aware_dir_merge(svn_mergein /* The merge target TARGET_ABSPATH and/or its subtrees may not need all of SOURCE->rev1:rev2 applied. So examine - NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest starting + CHILDREN_WITH_MERGEINFO to find the oldest starting revision that actually needs to be merged (for reverse merges this is the youngest starting revision). @@ -9408,7 +9449,7 @@ do_mergeinfo_aware_dir_merge(svn_mergein /* Is there anything to merge? */ if (SVN_IS_VALID_REVNUM(start_rev)) { - /* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest + /* Now examine CHILDREN_WITH_MERGEINFO to find the oldest ending revision that actually needs to be merged (for reverse merges this is the youngest ending revision). */ svn_revnum_t end_rev = @@ -9417,7 +9458,7 @@ do_mergeinfo_aware_dir_merge(svn_mergein /* While END_REV is valid, do the following: - 1. Tweak each NOTIFY_B->CHILDREN_WITH_MERGEINFO element so that + 1. Tweak each CHILDREN_WITH_MERGEINFO element so that the element's remaining_ranges member has as its first element a range that ends with end_rev. @@ -9425,17 +9466,17 @@ do_mergeinfo_aware_dir_merge(svn_mergein on MERGE_B->target->abspath for start_rev:end_rev. 3. Remove the first element from each - NOTIFY_B->CHILDREN_WITH_MERGEINFO element's remaining_ranges + CHILDREN_WITH_MERGEINFO element's remaining_ranges member. - 4. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most + 4. Again examine CHILDREN_WITH_MERGEINFO to find the most inclusive starting revision that actually needs to be merged and update start_rev. This prevents us from needlessly contacting the repository and doing a diff where we describe the entire target tree as *not* needing any of the requested range. This can happen whenever we have mergeinfo with gaps in it for the merge source. - 5. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most + 5. Again examine CHILDREN_WITH_MERGEINFO to find the most inclusive ending revision that actually needs to be merged and update end_rev. @@ -9477,8 +9518,9 @@ do_mergeinfo_aware_dir_merge(svn_mergein svn_pool_clear(iterpool); - slice_remaining_ranges(children_with_mergeinfo, - is_rollback, end_rev, scratch_pool); + SVN_ERR(slice_remaining_ranges(children_with_mergeinfo, + is_rollback, end_rev, + scratch_pool)); /* Reset variables that must be reset for every drive */ merge_b->notify_begin.last_abspath = NULL; @@ -9496,23 +9538,23 @@ do_mergeinfo_aware_dir_merge(svn_mergein /* If any paths picked up explicit mergeinfo as a result of the merge we need to make sure any mergeinfo those paths inherited is recorded and then add these paths to - NOTIFY_B->CHILDREN_WITH_MERGEINFO.*/ + CHILDREN_WITH_MERGEINFO.*/ SVN_ERR(process_children_with_new_mergeinfo( merge_b, children_with_mergeinfo, scratch_pool)); /* If any subtrees had their explicit mergeinfo deleted as a result of the merge then remove these paths from - NOTIFY_B->CHILDREN_WITH_MERGEINFO since there is no need + CHILDREN_WITH_MERGEINFO since there is no need to consider these subtrees for subsequent editor drives nor do we want to record mergeinfo on them describing the merge itself. */ - remove_children_with_deleted_mergeinfo( - merge_b, children_with_mergeinfo); + SVN_ERR(remove_children_with_deleted_mergeinfo( + merge_b, children_with_mergeinfo)); /* Prepare for the next iteration (if any). */ - remove_first_range_from_remaining_ranges( - end_rev, children_with_mergeinfo, scratch_pool); + SVN_ERR(remove_first_range_from_remaining_ranges( + end_rev, children_with_mergeinfo, scratch_pool)); /* If we raised any conflicts, break out and report how much we have merged. */ @@ -9634,7 +9676,7 @@ do_directory_merge(svn_mergeinfo_catalog apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *)); /* And make it read-only accessible from the baton */ - merge_b->notify_begin.nodes_with_mergeinfo = children_with_mergeinfo; + merge_b->children_with_mergeinfo = children_with_mergeinfo; /* If we are not honoring mergeinfo we can skip right to the business of merging changes! */ @@ -9652,7 +9694,7 @@ do_directory_merge(svn_mergeinfo_catalog processor, depth, merge_b, result_pool, scratch_pool)); - merge_b->notify_begin.nodes_with_mergeinfo = NULL; + merge_b->children_with_mergeinfo = NULL; return SVN_NO_ERROR; } @@ -9889,28 +9931,13 @@ do_merge(apr_hash_t **modified_subtrees, merge_cmd_baton.added_abspaths = apr_hash_make(result_pool); merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool); - { - svn_diff_tree_processor_t *merge_processor; - - merge_processor = svn_diff__tree_processor_create(&merge_cmd_baton, - scratch_pool); - - merge_processor->dir_opened = merge_dir_opened; - merge_processor->dir_changed = merge_dir_changed; - merge_processor->dir_added = merge_dir_added; - merge_processor->dir_deleted = merge_dir_deleted; - merge_processor->dir_closed = merge_dir_closed; - - merge_processor->file_opened = merge_file_opened; - merge_processor->file_changed = merge_file_changed; - merge_processor->file_added = merge_file_added; - merge_processor->file_deleted = merge_file_deleted; - /* Not interested in file_closed() */ - - merge_processor->node_absent = merge_node_absent; + merge_cmd_baton.notify_func = notify_merging; + merge_cmd_baton.notify_baton = &merge_cmd_baton.notify_begin; + merge_cmd_baton.notify_begin.merge_b = &merge_cmd_baton; + merge_cmd_baton.notify_begin.notify_func2 = ctx->notify_func2; + merge_cmd_baton.notify_begin.notify_baton2 = ctx->notify_baton2; - processor = merge_processor; - } + processor = merge_apply_processor(&merge_cmd_baton, scratch_pool); if (src_session) {
Modified: subversion/branches/addremove/subversion/libsvn_client/mergeinfo.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/mergeinfo.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/mergeinfo.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/mergeinfo.c Sat May 23 14:16:56 2020 @@ -1749,7 +1749,7 @@ svn_client__mergeinfo_log(svn_boolean_t if (*target_mergeinfo_catalog) { /* The caller provided the mergeinfo catalog for - TARGET_PATH_OR_URL, so we don't need to accquire + TARGET_PATH_OR_URL, so we don't need to acquire it ourselves. We do need to get the repos_root though, because get_mergeinfo() won't do it for us. */ target_mergeinfo_cat = *target_mergeinfo_catalog; Modified: subversion/branches/addremove/subversion/libsvn_client/mergeinfo.h URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/mergeinfo.h?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/mergeinfo.h (original) +++ subversion/branches/addremove/subversion/libsvn_client/mergeinfo.h Sat May 23 14:16:56 2020 @@ -316,7 +316,7 @@ svn_client__get_history_as_mergeinfo(svn /* Parse any explicit mergeinfo on LOCAL_ABSPATH and store it in *MERGEINFO. If no record of any mergeinfo exists, set *MERGEINFO to NULL. - Does not acount for inherited mergeinfo. + Does not account for inherited mergeinfo. Allocate the result deeply in @a result_pool. */ svn_error_t * Modified: subversion/branches/addremove/subversion/libsvn_client/mtcc.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/mtcc.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/mtcc.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/mtcc.c Sat May 23 14:16:56 2020 @@ -453,7 +453,8 @@ mtcc_verify_create(svn_client__mtcc_t *m if (op) return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, - _("Path '%s' already exists"), + _("Path '%s' already exists, or was created " + "by an earlier operation"), new_relpath); SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE, @@ -604,7 +605,7 @@ mtcc_op_contains_non_delete(const mtcc_o static svn_error_t * mtcc_add_delete(const char *relpath, svn_boolean_t for_move, - svn_client__mtcc_t *mtcc, + svn_client__mtcc_t *mtcc, apr_pool_t *scratch_pool) { mtcc_op_t *op; @@ -636,7 +637,7 @@ mtcc_add_delete(const char *relpath, { /* Allow deleting directories, that are unmodified except for one or more deleted descendants */ - + SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE, FALSE, mtcc->pool, scratch_pool)); Modified: subversion/branches/addremove/subversion/libsvn_client/patch.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/patch.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/patch.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/patch.c Sat May 23 14:16:56 2020 @@ -343,7 +343,9 @@ strip_path(const char **result, const ch components = svn_path_decompose(path, scratch_pool); if (strip_count > components->nelts) return svn_error_createf(SVN_ERR_CLIENT_PATCH_BAD_STRIP_COUNT, NULL, - _("Cannot strip %u components from '%s'"), + Q_("Cannot strip %u component from '%s'", + "Cannot strip %u components from '%s'", + strip_count), strip_count, svn_dirent_local_style(path, scratch_pool)); @@ -1018,6 +1020,7 @@ init_patch_target(patch_target_t **patch target_content_t *content; svn_boolean_t has_text_changes = FALSE; svn_boolean_t follow_moves; + const char *tempdir_abspath; has_text_changes = ((patch->hunks && patch->hunks->nelts > 0) || patch->binary_patch); @@ -1223,8 +1226,10 @@ init_patch_target(patch_target_t **patch } /* Open a temporary file to write the patched result to. */ + SVN_ERR(svn_wc__get_tmpdir(&tempdir_abspath, wc_ctx, + target->local_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_io_open_unique_file3(&target->patched_file, - &target->patched_path, NULL, + &target->patched_path, tempdir_abspath, remove_tempfiles ? svn_io_file_del_on_pool_cleanup : svn_io_file_del_none, @@ -1236,7 +1241,7 @@ init_patch_target(patch_target_t **patch /* Open a temporary stream to write rejected hunks to. */ SVN_ERR(svn_stream_open_unique(&target->reject_stream, - &target->reject_path, NULL, + &target->reject_path, tempdir_abspath, remove_tempfiles ? svn_io_file_del_on_pool_cleanup : svn_io_file_del_none, @@ -2145,8 +2150,8 @@ reject_hunk(patch_target_t *target, targ if (prop_name) { /* ### Print 'Added', 'Deleted' or 'Modified' instead of 'Property'. */ - svn_stream_printf(target->reject_stream, - pool, "Property: %s" APR_EOL_STR, prop_name); + SVN_ERR(svn_stream_printf(target->reject_stream, + pool, "Property: %s" APR_EOL_STR, prop_name)); atat = prop_atat; } else @@ -2465,7 +2470,7 @@ send_patch_notification(const patch_targ /* Implements the callback for svn_sort__array. Puts hunks that match before hunks that do not match, puts hunks that match in order - based on postion matched, puts hunks that do not match in order + based on position matched, puts hunks that do not match in order based on original position. */ static int sort_matched_hunks(const void *a, const void *b) @@ -2511,7 +2516,8 @@ sort_matched_hunks(const void *a, const * in RESULT_POOL. Use WC_CTX as the working copy context. * STRIP_COUNT specifies the number of leading path components * which should be stripped from target paths in the patch. - * REMOVE_TEMPFILES, PATCH_FUNC, and PATCH_BATON as in svn_client_patch(). + * REMOVE_TEMPFILES is as in svn_client_patch(). + * TARGETS_INFO is for preserving info across calls. * IGNORE_WHITESPACE tells whether whitespace should be considered when * doing the matching. * Call cancel CANCEL_FUNC with baton CANCEL_BATON to trigger cancellation. Modified: subversion/branches/addremove/subversion/libsvn_client/ra.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/ra.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/ra.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/ra.c Sat May 23 14:16:56 2020 @@ -402,8 +402,7 @@ svn_client__open_ra_session_internal(svn } } - /* If the caller allows for auto-following redirections, and the - RA->open() call above reveals a CORRECTED_URL, try the new URL. + /* If the caller allows for auto-following redirections, try the new URL. We'll do this in a loop up to some maximum number follow-and-retry attempts. */ if (corrected_url) @@ -414,12 +413,14 @@ svn_client__open_ra_session_internal(svn *corrected_url = NULL; while (attempts_left--) { - const char *corrected = NULL; + const char *corrected = NULL; /* canonicalized version */ + const char *redirect_url = NULL; /* non-canonicalized version */ /* Try to open the RA session. If this is our last attempt, don't accept corrected URLs from the RA provider. */ - SVN_ERR(svn_ra_open4(ra_session, + SVN_ERR(svn_ra_open5(ra_session, attempts_left == 0 ? NULL : &corrected, + attempts_left == 0 ? NULL : &redirect_url, base_url, uuid, cbtable, cb, ctx->config, result_pool)); @@ -441,19 +442,28 @@ svn_client__open_ra_session_internal(svn *corrected_url = corrected; /* Make sure we've not attempted this URL before. */ - if (svn_hash_gets(attempted, corrected)) + if (svn_hash_gets(attempted, redirect_url)) return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL, _("Redirect cycle detected for URL '%s'"), - corrected); + redirect_url); + + /* + * Remember this redirect URL so we don't wind up in a loop. + * + * Store the non-canonicalized version of the URL. The canonicalized + * version is insufficient for loop detection because we might not get + * an exact match against URLs used by the RA protocol-layer (the URL + * used by the protocol may contain trailing slashes, for example, + * which are stripped during canonicalization). + */ + svn_hash_sets(attempted, redirect_url, (void *)1); - /* Remember this CORRECTED_URL so we don't wind up in a loop. */ - svn_hash_sets(attempted, corrected, (void *)1); base_url = corrected; } } else { - SVN_ERR(svn_ra_open4(ra_session, NULL, base_url, + SVN_ERR(svn_ra_open5(ra_session, NULL, NULL, base_url, uuid, cbtable, cb, ctx->config, result_pool)); } Modified: subversion/branches/addremove/subversion/libsvn_client/repos_diff.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/repos_diff.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/repos_diff.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/repos_diff.c Sat May 23 14:16:56 2020 @@ -51,6 +51,7 @@ #include "private/svn_subr_private.h" #include "private/svn_wc_private.h" #include "private/svn_editor.h" +#include "private/svn_sorts_private.h" /* Overall crawler editor baton. */ struct edit_baton { @@ -380,10 +381,10 @@ get_file_from_ra(struct file_baton *fb, way. Hence this little hack: We populate FILE_BATON->PROPCHANGES only with *actual* property changes. - See http://subversion.tigris.org/issues/show_bug.cgi?id=3657#desc9 and + See https://issues.apache.org/jira/browse/SVN-3657#desc9 and http://svn.haxx.se/dev/archive-2010-08/0351.shtml for more details. */ -static void +static svn_error_t * remove_non_prop_changes(apr_hash_t *pristine_props, apr_array_header_t *changes) { @@ -391,7 +392,7 @@ remove_non_prop_changes(apr_hash_t *pris /* For added nodes, there is nothing to filter. */ if (apr_hash_count(pristine_props) == 0) - return; + return SVN_NO_ERROR; for (i = 0; i < changes->nelts; i++) { @@ -404,18 +405,13 @@ remove_non_prop_changes(apr_hash_t *pris if (old_val && svn_string_compare(old_val, change->value)) { - int j; - - /* Remove the matching change by shifting the rest */ - for (j = i; j < changes->nelts - 1; j++) - { - APR_ARRAY_IDX(changes, j, svn_prop_t) - = APR_ARRAY_IDX(changes, j+1, svn_prop_t); - } - changes->nelts--; + /* Remove the matching change and re-check the current index */ + SVN_ERR(svn_sort__array_delete2(changes, i, 1)); + i--; } } } + return SVN_NO_ERROR; } /* Get the empty file associated with the edit baton. This is cached so @@ -1015,7 +1011,7 @@ close_file(void *file_baton, } if (fb->pristine_props) - remove_non_prop_changes(fb->pristine_props, fb->propchanges); + SVN_ERR(remove_non_prop_changes(fb->pristine_props, fb->propchanges)); right_props = svn_prop__patch(fb->pristine_props, fb->propchanges, fb->pool); @@ -1088,7 +1084,7 @@ close_directory(void *dir_baton, if (db->propchanges->nelts > 0) { - remove_non_prop_changes(pristine_props, db->propchanges); + SVN_ERR(remove_non_prop_changes(pristine_props, db->propchanges)); } if (db->propchanges->nelts > 0 || db->added) Modified: subversion/branches/addremove/subversion/libsvn_client/revert.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/revert.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/revert.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/revert.c Sat May 23 14:16:56 2020 @@ -51,42 +51,32 @@ struct revert_with_write_lock_baton { const apr_array_header_t *changelists; svn_boolean_t clear_changelists; svn_boolean_t metadata_only; + svn_boolean_t added_keep_local; svn_client_ctx_t *ctx; }; /* (Note: All arguments are in the baton above.) - Attempt to revert LOCAL_ABSPATH. + Attempt to revert LOCAL_ABSPATH by calling svn_wc_revert6(), which + see for further details. - If DEPTH is svn_depth_empty, revert just the properties on the - directory; else if svn_depth_files, revert the properties and any - files immediately under the directory; else if - svn_depth_immediates, revert all of the preceding plus properties - on immediate subdirectories; else if svn_depth_infinity, revert - path and everything under it fully recursively. - - CHANGELISTS is an array of const char * changelist names, used as a - restrictive filter on items reverted; that is, don't revert any - item unless it's a member of one of those changelists. If - CHANGELISTS is empty (or altogether NULL), no changelist filtering occurs. - - Consult CTX to determine whether or not to revert timestamp to the - time of last commit ('use-commit-times = yes'). - - If PATH is unversioned, return SVN_ERR_UNVERSIONED_RESOURCE. */ + If the target isn't versioned, send a 'skip' notification and return + no error. + */ static svn_error_t * revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct revert_with_write_lock_baton *b = baton; svn_error_t *err; - err = svn_wc_revert5(b->ctx->wc_ctx, + err = svn_wc_revert6(b->ctx->wc_ctx, b->local_abspath, b->depth, b->use_commit_times, b->changelists, b->clear_changelists, b->metadata_only, + b->added_keep_local, b->ctx->cancel_func, b->ctx->cancel_baton, b->ctx->notify_func2, b->ctx->notify_baton2, scratch_pool); @@ -123,11 +113,12 @@ revert(void *baton, apr_pool_t *result_p svn_error_t * -svn_client_revert3(const apr_array_header_t *paths, +svn_client_revert4(const apr_array_header_t *paths, svn_depth_t depth, const apr_array_header_t *changelists, svn_boolean_t clear_changelists, svn_boolean_t metadata_only, + svn_boolean_t added_keep_local, svn_client_ctx_t *ctx, apr_pool_t *pool) { @@ -183,6 +174,7 @@ svn_client_revert3(const apr_array_heade baton.changelists = changelists; baton.clear_changelists = clear_changelists; baton.metadata_only = metadata_only; + baton.added_keep_local = added_keep_local; baton.ctx = ctx; err = svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, iterpool); Modified: subversion/branches/addremove/subversion/libsvn_client/revisions.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/revisions.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/revisions.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/revisions.c Sat May 23 14:16:56 2020 @@ -146,7 +146,14 @@ svn_client__get_revision_number(svn_revn scratch_pool)); if (revision->kind == svn_opt_revision_previous) - (*revnum)--; + { + if (*revnum == 0) + return svn_error_createf( + SVN_ERR_CLIENT_BAD_REVISION, NULL, + _("Path '%s' has no previous revision"), + svn_dirent_local_style(local_abspath, scratch_pool)); + --(*revnum); + } } break; Modified: subversion/branches/addremove/subversion/libsvn_client/status.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/status.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/status.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/status.c Sat May 23 14:16:56 2020 @@ -23,6 +23,9 @@ /* ==================================================================== */ +/* We define this here to remove any further warnings about the usage of + experimental functions in this file. */ +#define SVN_EXPERIMENTAL /*** Includes. ***/ @@ -41,6 +44,7 @@ #include "svn_error.h" #include "svn_hash.h" +#include "private/svn_client_shelf.h" #include "private/svn_client_private.h" #include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" @@ -329,6 +333,79 @@ do_external_status(svn_client_ctx_t *ctx return SVN_NO_ERROR; } + +/* Run status on shelf SHELF_NAME, if it exists. + */ +static svn_error_t * +shelf_status(const char *shelf_name, + const char *target_abspath, + svn_wc_status_func4_t status_func, + void *status_baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + svn_client__shelf_t *shelf; + svn_client__shelf_version_t *shelf_version; + const char *wc_relpath; + + err = svn_client__shelf_open_existing(&shelf, + shelf_name, target_abspath, + ctx, scratch_pool); + if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + else + SVN_ERR(err); + + SVN_ERR(svn_client__shelf_version_open(&shelf_version, + shelf, shelf->max_version, + scratch_pool, scratch_pool)); + wc_relpath = svn_dirent_skip_ancestor(shelf->wc_root_abspath, target_abspath); + SVN_ERR(svn_client__shelf_version_status_walk(shelf_version, wc_relpath, + status_func, status_baton, + scratch_pool)); + SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Run status on all shelves named in CHANGELISTS by a changelist name + * of the form "svn:shelf:SHELF_NAME", if they exist. + */ +static svn_error_t * +shelves_status(const apr_array_header_t *changelists, + const char *target_abspath, + svn_wc_status_func4_t status_func, + void *status_baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + static const char PREFIX[] = "svn:shelf:"; + static const int PREFIX_LEN = 10; + int i; + + if (! changelists) + return SVN_NO_ERROR; + for (i = 0; i < changelists->nelts; i++) + { + const char *cl = APR_ARRAY_IDX(changelists, i, const char *); + + if (strncmp(cl, PREFIX, PREFIX_LEN) == 0) + { + const char *shelf_name = cl + PREFIX_LEN; + + SVN_ERR(shelf_status(shelf_name, target_abspath, + status_func, status_baton, + ctx, scratch_pool)); + } + } + + return SVN_NO_ERROR; +} + /*** Public Interface. ***/ @@ -586,6 +663,9 @@ svn_client_status6(svn_revnum_t *result_ } else { + SVN_ERR(shelves_status(changelists, target_abspath, + tweak_status, &sb, + ctx, pool)); err = svn_wc_walk_status(ctx->wc_ctx, target_abspath, depth, get_all, no_ignore, FALSE, ignores, tweak_status, &sb, Modified: subversion/branches/addremove/subversion/libsvn_client/switch.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/switch.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/switch.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/switch.c Sat May 23 14:16:56 2020 @@ -216,7 +216,7 @@ switch_internal(svn_revnum_t *result_rev anchor_url, switch_loc->repos_root_url); /* If we're not ignoring ancestry, then error out if the switch - source and target don't have a common ancestory. + source and target don't have a common ancestry. ### We're acting on the anchor here, not the target. Is that ### okay? */ @@ -279,7 +279,7 @@ switch_internal(svn_revnum_t *result_rev /* If LOCAL_ABSPATH will be unswitched relative to its parent, then it doesn't need an iprop cache. Note: It doesn't matter if - LOCAL_ABSPATH is withing a switched subtree, only if it's the + LOCAL_ABSPATH is within a switched subtree, only if it's the *root* of a switched subtree.*/ if (strcmp(unswitched_url, switch_loc->url) == 0) needs_iprop_cache = FALSE; Modified: subversion/branches/addremove/subversion/libsvn_client/update.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/update.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/update.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/update.c Sat May 23 14:16:56 2020 @@ -96,7 +96,7 @@ svn_client__dirent_fetcher(void *baton, folder. ANCHOR_ABSPATH is the w/c root and LOCAL_ABSPATH will still be considered empty, if it is equal to ANCHOR_ABSPATH and only contains the admin sub-folder. - If the w/c folder already exists but cannot be openend, we return + If the w/c folder already exists but cannot be opened, we return "unclean" - just in case. Most likely, the caller will have to bail out later due to the same error we got here. */ @@ -182,6 +182,88 @@ record_conflict(svn_wc_conflict_result_t return SVN_NO_ERROR; } +/* Perform post-update processing of externals defined below LOCAL_ABSPATH. */ +static svn_error_t * +handle_externals(svn_boolean_t *timestamp_sleep, + const char *local_abspath, + svn_depth_t depth, + const char *repos_root_url, + svn_ra_session_t *ra_session, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + apr_hash_t *new_externals; + apr_hash_t *new_depths; + + SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, + &new_depths, + ctx->wc_ctx, local_abspath, + depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_client__handle_externals(new_externals, + new_depths, + repos_root_url, local_abspath, + depth, timestamp_sleep, ra_session, + ctx, scratch_pool)); + return SVN_NO_ERROR; +} + +/* Try to reuse the RA session by reparenting it to the anchor_url. + * This code is probably overly cautious since we only use this + * currently when parents are missing and so all the anchor_urls + * have to be in the same repo. + * Note that ra_session_p is an (optional) input parameter as well + * as an output parameter. */ +static svn_error_t * +reuse_ra_session(svn_ra_session_t **ra_session_p, + const char **corrected_url, + const char *anchor_url, + const char *anchor_abspath, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_session_t *ra_session = *ra_session_p; + + if (ra_session) + { + svn_error_t *err = svn_ra_reparent(ra_session, anchor_url, scratch_pool); + if (err) + { + if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) + { + /* session changed repos, can't reuse it */ + svn_error_clear(err); + ra_session = NULL; + } + else + { + return svn_error_trace(err); + } + } + else + { + *corrected_url = NULL; + } + } + + /* Open an RA session for the URL if one isn't already available */ + if (!ra_session) + { + SVN_ERR(svn_client__open_ra_session_internal(&ra_session, corrected_url, + anchor_url, + anchor_abspath, NULL, + TRUE /* write_dav_props */, + TRUE /* read_dav_props */, + ctx, + result_pool, scratch_pool)); + *ra_session_p = ra_session; + } + + return SVN_NO_ERROR; +} + /* This is a helper for svn_client__update_internal(), which see for an explanation of most of these parameters. Some stuff that's unique is as follows: @@ -320,6 +402,18 @@ update_internal(svn_revnum_t *result_rev ctx->notify_func2, ctx->notify_baton2, scratch_pool)); + if (!ignore_externals) + { + /* We may now be able to remove externals below LOCAL_ABSPATH. */ + SVN_ERR(reuse_ra_session(ra_session_p, &corrected_url, + anchor_url, anchor_abspath, + ctx, result_pool, scratch_pool)); + ra_session = *ra_session_p; + SVN_ERR(handle_externals(timestamp_sleep, local_abspath, depth, + repos_root_url, ra_session, ctx, + scratch_pool)); + } + /* Target excluded, we are done now */ return SVN_NO_ERROR; } @@ -373,44 +467,9 @@ update_internal(svn_revnum_t *result_rev ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); } - /* Try to reuse the RA session by reparenting it to the anchor_url. - * This code is probably overly cautious since we only use this - * currently when parents are missing and so all the anchor_urls - * have to be in the same repo. */ - if (ra_session) - { - svn_error_t *err = svn_ra_reparent(ra_session, anchor_url, scratch_pool); - if (err) - { - if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) - { - /* session changed repos, can't reuse it */ - svn_error_clear(err); - ra_session = NULL; - } - else - { - return svn_error_trace(err); - } - } - else - { - corrected_url = NULL; - } - } - - /* Open an RA session for the URL if one isn't already available */ - if (!ra_session) - { - SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, - anchor_url, - anchor_abspath, NULL, - TRUE /* write_dav_props */, - TRUE /* read_dav_props */, - ctx, - result_pool, scratch_pool)); - *ra_session_p = ra_session; - } + SVN_ERR(reuse_ra_session(ra_session_p, &corrected_url, anchor_url, + anchor_abspath, ctx, result_pool, scratch_pool)); + ra_session = *ra_session_p; /* If we got a corrected URL from the RA subsystem, we'll need to relocate our working copy first. */ @@ -513,19 +572,8 @@ update_internal(svn_revnum_t *result_rev if ((SVN_DEPTH_IS_RECURSIVE(depth) || cropping_target) && (! ignore_externals)) { - apr_hash_t *new_externals; - apr_hash_t *new_depths; - SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, - &new_depths, - ctx->wc_ctx, local_abspath, - depth, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_client__handle_externals(new_externals, - new_depths, - repos_root_url, local_abspath, - depth, timestamp_sleep, ra_session, - ctx, scratch_pool)); + SVN_ERR(handle_externals(timestamp_sleep, local_abspath, depth, + repos_root_url, ra_session, ctx, scratch_pool)); } /* Let everyone know we're finished here (unless we're asked not to). */ @@ -567,7 +615,7 @@ svn_client__update_internal(svn_revnum_t { const char *anchor_abspath, *lockroot_abspath; svn_error_t *err; - svn_opt_revision_t peg_revision = *revision; + svn_opt_revision_t opt_rev = *revision; /* operative revision */ apr_hash_t *conflicted_paths = ctx->conflict_func2 ? apr_hash_make(pool) : NULL; @@ -620,7 +668,7 @@ svn_client__update_internal(svn_revnum_t err = update_internal(result_rev, timestamp_sleep, conflicted_paths, &ra_session, missing_parent, - anchor_abspath, &peg_revision, svn_depth_empty, + anchor_abspath, &opt_rev, svn_depth_empty, FALSE, ignore_externals, allow_unver_obstructions, adds_as_modification, FALSE, ctx, pool, iterpool); @@ -631,8 +679,8 @@ svn_client__update_internal(svn_revnum_t /* If we successfully updated a missing parent, let's re-use the returned revision number for future updates for the sake of consistency. */ - peg_revision.kind = svn_opt_revision_number; - peg_revision.value.number = *result_rev; + opt_rev.kind = svn_opt_revision_number; + opt_rev.value.number = *result_rev; } svn_pool_destroy(iterpool); @@ -648,7 +696,7 @@ svn_client__update_internal(svn_revnum_t err = update_internal(result_rev, timestamp_sleep, conflicted_paths, &ra_session, local_abspath, anchor_abspath, - &peg_revision, depth, depth_is_sticky, + &opt_rev, depth, depth_is_sticky, ignore_externals, allow_unver_obstructions, adds_as_modification, TRUE, ctx, pool, pool); Modified: subversion/branches/addremove/subversion/libsvn_client/upgrade.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/upgrade.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/upgrade.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/upgrade.c Sat May 23 14:16:56 2020 @@ -303,7 +303,7 @@ upgrade_externals_from_properties(svn_cl { apr_hash_index_t *hi; apr_pool_t *iterpool; - apr_pool_t *iterpool2; + apr_pool_t *inner_iterpool; apr_hash_t *externals; svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; @@ -317,7 +317,7 @@ upgrade_externals_from_properties(svn_cl scratch_pool, scratch_pool)); iterpool = svn_pool_create(scratch_pool); - iterpool2 = svn_pool_create(scratch_pool); + inner_iterpool = svn_pool_create(scratch_pool); for (hi = apr_hash_first(scratch_pool, externals); hi; hi = apr_hash_next(hi)) @@ -351,14 +351,12 @@ upgrade_externals_from_properties(svn_cl iterpool, iterpool); if (!err) - externals_parent_url = svn_path_url_add_component2( - externals_parent_repos_root_url, - externals_parent_repos_relpath, - iterpool); - if (!err) - err = svn_wc_parse_externals_description3( - &externals_p, svn_dirent_dirname(local_abspath, iterpool), - external_desc->data, FALSE, iterpool); + { + err = svn_wc_parse_externals_description3( + &externals_p, svn_dirent_dirname(local_abspath, iterpool), + external_desc->data, FALSE, iterpool); + } + if (err) { svn_wc_notify_t *notify = @@ -376,24 +374,29 @@ upgrade_externals_from_properties(svn_cl continue; } + externals_parent_url = svn_path_url_add_component2( + externals_parent_repos_root_url, + externals_parent_repos_relpath, + iterpool); + for (i = 0; i < externals_p->nelts; i++) { svn_wc_external_item2_t *item; item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); - svn_pool_clear(iterpool2); + svn_pool_clear(inner_iterpool); err = upgrade_external_item(ctx, externals_parent_abspath, externals_parent_url, externals_parent_repos_root_url, - item, info_baton, iterpool2); + item, info_baton, inner_iterpool); if (err) { svn_wc_notify_t *notify = svn_wc_create_notify(svn_dirent_join(externals_parent_abspath, item->target_dir, - iterpool2), + inner_iterpool), svn_wc_notify_failed_external, scratch_pool); notify->err = err; @@ -405,8 +408,8 @@ upgrade_externals_from_properties(svn_cl } } + svn_pool_destroy(inner_iterpool); svn_pool_destroy(iterpool); - svn_pool_destroy(iterpool2); return SVN_NO_ERROR; } Modified: subversion/branches/addremove/subversion/libsvn_client/util.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_client/util.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_client/util.c (original) +++ subversion/branches/addremove/subversion/libsvn_client/util.c Sat May 23 14:16:56 2020 @@ -93,6 +93,7 @@ svn_client__pathrev_create_with_session( pathrev->rev = rev; pathrev->url = apr_pstrdup(result_pool, url); *pathrev_p = pathrev; + SVN_ERR_ASSERT(svn_uri__is_ancestor(pathrev->repos_root_url, url)); return SVN_NO_ERROR; } Modified: subversion/branches/addremove/subversion/libsvn_delta/branch.c URL: http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/libsvn_delta/branch.c?rev=1878061&r1=1878060&r2=1878061&view=diff ============================================================================== --- subversion/branches/addremove/subversion/libsvn_delta/branch.c (original) +++ subversion/branches/addremove/subversion/libsvn_delta/branch.c Sat May 23 14:16:56 2020 @@ -157,7 +157,7 @@ branch_txn_delete_branch(svn_branch__txn if (strcmp(b->bid, bid) == 0) { - svn_sort__array_delete(txn->priv->branches, i, 1); + SVN_ERR(svn_sort__array_delete2(txn->priv->branches, i, 1)); break; } }