diff options
Diffstat (limited to 'subversion/libsvn_client/conflicts.c')
-rw-r--r-- | subversion/libsvn_client/conflicts.c | 355 |
1 files changed, 187 insertions, 168 deletions
diff --git a/subversion/libsvn_client/conflicts.c b/subversion/libsvn_client/conflicts.c index f571f3c..0f899af 100644 --- a/subversion/libsvn_client/conflicts.c +++ b/subversion/libsvn_client/conflicts.c @@ -806,18 +806,20 @@ map_deleted_path_to_move(const char *deleted_relpath, if (closest_move) { const char *relpath; - const char *moved_along_path; - struct repos_move_info *move; /* See if we can find an even closer move for this moved-along path. */ relpath = svn_relpath_skip_ancestor(closest_move->moved_to_repos_relpath, deleted_relpath); - moved_along_path = - svn_relpath_join(closest_move->moved_from_repos_relpath, relpath, - scratch_pool); - move = map_deleted_path_to_move(moved_along_path, moves, scratch_pool); - if (move) - return move; + if (relpath && relpath[0] != '\0') + { + struct repos_move_info *move; + const char *moved_along_path = + svn_relpath_join(closest_move->moved_from_repos_relpath, relpath, + scratch_pool); + move = map_deleted_path_to_move(moved_along_path, moves, scratch_pool); + if (move) + return move; + } } return closest_move; @@ -1059,6 +1061,9 @@ find_deleted_rev(void *baton, { apr_array_header_t *moves; + if (b->moves_table == NULL) + return SVN_NO_ERROR; + moves = apr_hash_get(b->moves_table, &log_entry->revision, sizeof(svn_revnum_t)); if (moves) @@ -2097,33 +2102,6 @@ trace_moved_node_backwards(apr_hash_t *moves_table, return SVN_NO_ERROR; } -static svn_error_t * -reparent_session_and_fetch_node_kind(svn_node_kind_t *node_kind, - svn_ra_session_t *ra_session, - const char *url, - svn_revnum_t peg_rev, - apr_pool_t *scratch_pool) -{ - svn_error_t *err; - - err = svn_ra_reparent(ra_session, url, scratch_pool); - if (err) - { - if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) - { - svn_error_clear(err); - *node_kind = svn_node_unknown; - return SVN_NO_ERROR; - } - - return svn_error_trace(err); - } - - SVN_ERR(svn_ra_check_path(ra_session, "", peg_rev, node_kind, scratch_pool)); - - return SVN_NO_ERROR; -} - /* Scan MOVES_TABLE for moves which affect a particular deleted node, and * build a set of new move information for this node. * Return heads of all possible move chains in *MOVES. @@ -2170,22 +2148,29 @@ find_operative_moves(apr_array_header_t **moves, svn_pool_clear(iterpool); move = APR_ARRAY_IDX(moves_in_deleted_rev, i, struct repos_move_info *); - relpath = svn_relpath_skip_ancestor(move->moved_from_repos_relpath, + if (strcmp(move->moved_from_repos_relpath, deleted_repos_relpath) == 0) + { + APR_ARRAY_PUSH(*moves, struct repos_move_info *) = move; + continue; + } + + /* Test for an operative nested move. */ + relpath = svn_relpath_skip_ancestor(move->moved_to_repos_relpath, deleted_repos_relpath); if (relpath && relpath[0] != '\0') { - svn_node_kind_t node_kind; - - url = svn_path_url_add_component2(repos_root_url, - deleted_repos_relpath, - iterpool); - SVN_ERR(reparent_session_and_fetch_node_kind(&node_kind, - ra_session, url, - rev_below(deleted_rev), - iterpool)); - move = new_path_adjusted_move(move, relpath, node_kind, result_pool); + struct repos_move_info *nested_move; + const char *actual_deleted_repos_relpath; + + actual_deleted_repos_relpath = + svn_relpath_join(move->moved_from_repos_relpath, relpath, + iterpool); + nested_move = map_deleted_path_to_move(actual_deleted_repos_relpath, + moves_in_deleted_rev, + iterpool); + if (nested_move) + APR_ARRAY_PUSH(*moves, struct repos_move_info *) = nested_move; } - APR_ARRAY_PUSH(*moves, struct repos_move_info *) = move; } if (url != NULL) @@ -2223,8 +2208,8 @@ find_operative_moves(apr_array_header_t **moves, * If the node was replaced rather than deleted, set *REPLACING_NODE_KIND to * the node kind of the replacing node. Else, set it to svn_node_unknown. * Only request the log for revisions up to END_REV from the server. - * If the deleted node was moved, provide heads of move chains in *MOVES. - * If the node was not moved,set *MOVES to NULL. + * If MOVES it not NULL, and the deleted node was moved, provide heads of + * move chains in *MOVES, or, if the node was not moved, set *MOVES to NULL. */ static svn_error_t * find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev, @@ -2261,10 +2246,11 @@ find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev, scratch_pool)); victim_abspath = svn_client_conflict_get_local_abspath(conflict); - SVN_ERR(find_moves_in_revision_range(&moves_table, parent_repos_relpath, - repos_root_url, repos_uuid, - victim_abspath, start_rev, end_rev, - ctx, result_pool, scratch_pool)); + if (moves) + SVN_ERR(find_moves_in_revision_range(&moves_table, parent_repos_relpath, + repos_root_url, repos_uuid, + victim_abspath, start_rev, end_rev, + ctx, result_pool, scratch_pool)); url = svn_path_url_add_component2(repos_root_url, parent_repos_relpath, scratch_pool); @@ -2289,7 +2275,8 @@ find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev, b.repos_root_url = repos_root_url; b.repos_uuid = repos_uuid; b.ctx = ctx; - b.moves_table = moves_table; + if (moves) + b.moves_table = moves_table; b.result_pool = result_pool; SVN_ERR(svn_ra__dup_session(&b.extra_ra_session, ra_session, NULL, scratch_pool, scratch_pool)); @@ -2319,7 +2306,7 @@ find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev, { struct repos_move_info *move = b.move; - if (move) + if (moves && move) { *deleted_rev = move->rev; *deleted_rev_author = move->rev_author; @@ -2337,7 +2324,8 @@ find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev, *deleted_rev = SVN_INVALID_REVNUM; *deleted_rev_author = NULL; *replacing_node_kind = svn_node_unknown; - *moves = NULL; + if (moves) + *moves = NULL; } return SVN_NO_ERROR; } @@ -2346,10 +2334,11 @@ find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev, *deleted_rev = b.deleted_rev; *deleted_rev_author = b.deleted_rev_author; *replacing_node_kind = b.replacing_node_kind; - SVN_ERR(find_operative_moves(moves, moves_table, - b.deleted_repos_relpath, b.deleted_rev, - ra_session, repos_root_url, - result_pool, scratch_pool)); + if (moves) + SVN_ERR(find_operative_moves(moves, moves_table, + b.deleted_repos_relpath, b.deleted_rev, + ra_session, repos_root_url, + result_pool, scratch_pool)); } return SVN_NO_ERROR; @@ -2693,7 +2682,8 @@ conflict_tree_get_details_local_missing(svn_client_conflict_t *conflict, end_rev = 0; /* ### We might walk through all of history... */ SVN_ERR(find_revision_for_suspected_deletion( - &deleted_rev, &deleted_rev_author, &replacing_node_kind, &moves, + &deleted_rev, &deleted_rev_author, &replacing_node_kind, + yca_loc ? &moves : NULL, conflict, deleted_basename, parent_repos_relpath, parent_peg_rev, end_rev, related_repos_relpath, related_peg_rev, ctx, conflict->pool, scratch_pool)); @@ -5069,7 +5059,7 @@ conflict_tree_get_details_incoming_add(svn_client_conflict_t *conflict, const char *repos_root_url; svn_revnum_t old_rev; svn_revnum_t new_rev; - struct conflict_tree_incoming_add_details *details; + struct conflict_tree_incoming_add_details *details = NULL; svn_wc_operation_t operation; SVN_ERR(svn_client_conflict_get_incoming_old_repos_location( @@ -5162,7 +5152,8 @@ conflict_tree_get_details_incoming_add(svn_client_conflict_t *conflict, } } } - else if (operation == svn_wc_operation_merge) + else if (operation == svn_wc_operation_merge && + strcmp(old_repos_relpath, new_repos_relpath) == 0) { if (old_rev < new_rev) { @@ -5213,7 +5204,7 @@ conflict_tree_get_details_incoming_add(svn_client_conflict_t *conflict, details->deleted_rev = SVN_INVALID_REVNUM; details->deleted_rev_author = NULL; } - else + else if (old_rev > new_rev) { /* The merge operation was a reverse-merge. * This addition is in fact a deletion, applied in reverse, @@ -5253,10 +5244,6 @@ conflict_tree_get_details_incoming_add(svn_client_conflict_t *conflict, details->moves = moves; } } - else - { - details = NULL; - } conflict->tree_conflict_incoming_details = details; @@ -5719,7 +5706,10 @@ find_modified_rev(void *baton, if (log_item->copyfrom_path) b->repos_relpath = apr_pstrdup(b->scratch_pool, - log_item->copyfrom_path); + /* ### remove leading slash */ + svn_relpath_canonicalize( + log_item->copyfrom_path, + iterpool)); } else if (b->node_kind == svn_node_dir && svn_relpath_skip_ancestor(b->repos_relpath, path) != NULL) @@ -8004,6 +7994,112 @@ resolve_merge_incoming_added_dir_replace_and_merge( scratch_pool)); } +/* Ensure the conflict victim is a copy of itself from before it was deleted. + * Update and switch are supposed to set this up when flagging the conflict. */ +static svn_error_t * +ensure_local_edit_vs_incoming_deletion_copied_state( + struct conflict_tree_incoming_delete_details *details, + svn_wc_operation_t operation, + const char *wcroot_abspath, + svn_client_conflict_t *conflict, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + + svn_boolean_t is_copy; + svn_revnum_t copyfrom_rev; + const char *copyfrom_repos_relpath; + + SVN_ERR_ASSERT(operation == svn_wc_operation_update || + operation == svn_wc_operation_switch); + + SVN_ERR(svn_wc__node_get_origin(&is_copy, ©from_rev, + ©from_repos_relpath, + NULL, NULL, NULL, NULL, + ctx->wc_ctx, conflict->local_abspath, + FALSE, scratch_pool, scratch_pool)); + if (!is_copy) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Cannot resolve tree conflict on '%s' " + "(expected a copied item, but the item " + "is not a copy)"), + svn_dirent_local_style( + svn_dirent_skip_ancestor( + wcroot_abspath, + conflict->local_abspath), + scratch_pool)); + else if (details->deleted_rev != SVN_INVALID_REVNUM && + copyfrom_rev >= details->deleted_rev) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Cannot resolve tree conflict on '%s' " + "(expected an item copied from a revision " + "smaller than r%ld, but the item was " + "copied from r%ld)"), + svn_dirent_local_style( + svn_dirent_skip_ancestor( + wcroot_abspath, conflict->local_abspath), + scratch_pool), + details->deleted_rev, copyfrom_rev); + else if (details->added_rev != SVN_INVALID_REVNUM && + copyfrom_rev < details->added_rev) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Cannot resolve tree conflict on '%s' " + "(expected an item copied from a revision " + "larger than r%ld, but the item was " + "copied from r%ld)"), + svn_dirent_local_style( + svn_dirent_skip_ancestor( + wcroot_abspath, conflict->local_abspath), + scratch_pool), + details->added_rev, copyfrom_rev); + else if (operation == svn_wc_operation_update) + { + const char *old_repos_relpath; + + SVN_ERR(svn_client_conflict_get_incoming_old_repos_location( + &old_repos_relpath, NULL, NULL, conflict, + scratch_pool, scratch_pool)); + if (strcmp(copyfrom_repos_relpath, details->repos_relpath) != 0 && + strcmp(copyfrom_repos_relpath, old_repos_relpath) != 0) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Cannot resolve tree conflict on '%s' " + "(expected an item copied from '^/%s' " + "or from '^/%s' but the item was " + "copied from '^/%s@%ld')"), + svn_dirent_local_style( + svn_dirent_skip_ancestor( + wcroot_abspath, conflict->local_abspath), + scratch_pool), + details->repos_relpath, + old_repos_relpath, + copyfrom_repos_relpath, copyfrom_rev); + } + else if (operation == svn_wc_operation_switch) + { + const char *old_repos_relpath; + + SVN_ERR(svn_client_conflict_get_incoming_old_repos_location( + &old_repos_relpath, NULL, NULL, conflict, + scratch_pool, scratch_pool)); + + if (strcmp(copyfrom_repos_relpath, old_repos_relpath) != 0) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Cannot resolve tree conflict on '%s' " + "(expected an item copied from '^/%s', " + "but the item was copied from " + "'^/%s@%ld')"), + svn_dirent_local_style( + svn_dirent_skip_ancestor( + wcroot_abspath, + conflict->local_abspath), + scratch_pool), + old_repos_relpath, + copyfrom_repos_relpath, copyfrom_rev); + } + + return SVN_NO_ERROR; +} + /* Verify the local working copy state matches what we expect when an * incoming deletion tree conflict exists. * We assume update/merge/switch operations leave the working copy in a @@ -8014,26 +8110,25 @@ resolve_merge_incoming_added_dir_replace_and_merge( static svn_error_t * verify_local_state_for_incoming_delete(svn_client_conflict_t *conflict, svn_client_conflict_option_t *option, - svn_client_ctx_t *ctx, + svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { const char *local_abspath; const char *wcroot_abspath; svn_wc_operation_t operation; + svn_wc_conflict_reason_t local_change; local_abspath = svn_client_conflict_get_local_abspath(conflict); SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, local_abspath, scratch_pool, scratch_pool)); operation = svn_client_conflict_get_operation(conflict); + local_change = svn_client_conflict_get_local_change(conflict); if (operation == svn_wc_operation_update || operation == svn_wc_operation_switch) { struct conflict_tree_incoming_delete_details *details; - svn_boolean_t is_copy; - svn_revnum_t copyfrom_rev; - const char *copyfrom_repos_relpath; details = conflict->tree_conflict_incoming_details; if (details == NULL) @@ -8045,26 +8140,8 @@ verify_local_state_for_incoming_delete(svn_client_conflict_t *conflict, svn_dirent_local_style(local_abspath, scratch_pool)); - /* Ensure that the item is a copy of itself from before it was deleted. - * Update and switch are supposed to set this up when flagging the - * conflict. */ - SVN_ERR(svn_wc__node_get_origin(&is_copy, ©from_rev, - ©from_repos_relpath, - NULL, NULL, NULL, NULL, - ctx->wc_ctx, local_abspath, FALSE, - scratch_pool, scratch_pool)); - if (!is_copy) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot resolve tree conflict on '%s' " - "(expected a copied item, but the item " - "is not a copy)"), - svn_dirent_local_style( - svn_dirent_skip_ancestor( - wcroot_abspath, - conflict->local_abspath), - scratch_pool)); - else if (details->deleted_rev == SVN_INVALID_REVNUM && - details->added_rev == SVN_INVALID_REVNUM) + if (details->deleted_rev == SVN_INVALID_REVNUM && + details->added_rev == SVN_INVALID_REVNUM) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("Could not find the revision in which '%s' " "was deleted from the repository"), @@ -8073,75 +8150,11 @@ verify_local_state_for_incoming_delete(svn_client_conflict_t *conflict, wcroot_abspath, conflict->local_abspath), scratch_pool)); - else if (details->deleted_rev != SVN_INVALID_REVNUM && - copyfrom_rev >= details->deleted_rev) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot resolve tree conflict on '%s' " - "(expected an item copied from a revision " - "smaller than r%ld, but the item was " - "copied from r%ld)"), - svn_dirent_local_style( - svn_dirent_skip_ancestor( - wcroot_abspath, conflict->local_abspath), - scratch_pool), - details->deleted_rev, copyfrom_rev); - else if (details->added_rev != SVN_INVALID_REVNUM && - copyfrom_rev < details->added_rev) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot resolve tree conflict on '%s' " - "(expected an item copied from a revision " - "larger than r%ld, but the item was " - "copied from r%ld)"), - svn_dirent_local_style( - svn_dirent_skip_ancestor( - wcroot_abspath, conflict->local_abspath), - scratch_pool), - details->added_rev, copyfrom_rev); - else if (operation == svn_wc_operation_update) - { - const char *old_repos_relpath; - - SVN_ERR(svn_client_conflict_get_incoming_old_repos_location( - &old_repos_relpath, NULL, NULL, conflict, - scratch_pool, scratch_pool)); - if (strcmp(copyfrom_repos_relpath, details->repos_relpath) != 0 && - strcmp(copyfrom_repos_relpath, old_repos_relpath) != 0) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot resolve tree conflict on '%s' " - "(expected an item copied from '^/%s' " - "or from '^/%s' but the item was " - "copied from '^/%s@%ld')"), - svn_dirent_local_style( - svn_dirent_skip_ancestor( - wcroot_abspath, conflict->local_abspath), - scratch_pool), - details->repos_relpath, - old_repos_relpath, - copyfrom_repos_relpath, copyfrom_rev); - } - else if (operation == svn_wc_operation_switch) - { - const char *old_repos_relpath; - - SVN_ERR(svn_client_conflict_get_incoming_old_repos_location( - &old_repos_relpath, NULL, NULL, conflict, - scratch_pool, scratch_pool)); - - if (strcmp(copyfrom_repos_relpath, old_repos_relpath) != 0) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot resolve tree conflict on '%s' " - "(expected an item copied from '^/%s', " - "but the item was copied from " - "'^/%s@%ld')"), - svn_dirent_local_style( - svn_dirent_skip_ancestor( - wcroot_abspath, - conflict->local_abspath), - scratch_pool), - old_repos_relpath, - copyfrom_repos_relpath, copyfrom_rev); - } + if (local_change == svn_wc_conflict_reason_edited) + SVN_ERR(ensure_local_edit_vs_incoming_deletion_copied_state( + details, operation, wcroot_abspath, conflict, ctx, + scratch_pool)); } else if (operation == svn_wc_operation_merge) { @@ -9519,13 +9532,18 @@ configure_option_incoming_added_dir_merge(svn_client_conflict_t *conflict, conflict->local_abspath, scratch_pool, scratch_pool)); if (operation == svn_wc_operation_merge) - description = - apr_psprintf(scratch_pool, _("merge '^/%s@%ld' into '%s'"), - incoming_new_repos_relpath, incoming_new_pegrev, - svn_dirent_local_style( - svn_dirent_skip_ancestor(wcroot_abspath, - conflict->local_abspath), - scratch_pool)); + { + if (conflict->tree_conflict_incoming_details == NULL) + return SVN_NO_ERROR; + + description = + apr_psprintf(scratch_pool, _("merge '^/%s@%ld' into '%s'"), + incoming_new_repos_relpath, incoming_new_pegrev, + svn_dirent_local_style( + svn_dirent_skip_ancestor(wcroot_abspath, + conflict->local_abspath), + scratch_pool)); + } else description = apr_psprintf(scratch_pool, _("merge local '%s' and '^/%s@%ld'"), @@ -10167,6 +10185,7 @@ svn_client_conflict_option_set_moved_to_repos_relpath( if (strcmp(move_target_repos_relpath, repos_relpath) == 0) { details->move_target_repos_relpath = repos_relpath; + details->wc_move_target_idx = 0; /* Update option description. */ SVN_ERR(describe_incoming_move_merge_conflict_option( &option->description, |