diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_client/patch.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_client/patch.c')
-rw-r--r-- | subversion/libsvn_client/patch.c | 320 |
1 files changed, 246 insertions, 74 deletions
diff --git a/subversion/libsvn_client/patch.c b/subversion/libsvn_client/patch.c index b7fbf06..6d8d0a4 100644 --- a/subversion/libsvn_client/patch.c +++ b/subversion/libsvn_client/patch.c @@ -48,6 +48,7 @@ #include "private/svn_dep_compat.h" #include "private/svn_string_private.h" #include "private/svn_subr_private.h" +#include "private/svn_sorts_private.h" typedef struct hunk_info_t { /* The hunk. */ @@ -208,9 +209,6 @@ typedef struct patch_target_t { /* True if the target had to be skipped for some reason. */ svn_boolean_t skipped; - /* True if the target has been filtered by the patch callback. */ - svn_boolean_t filtered; - /* True if at least one hunk was rejected. */ svn_boolean_t had_rejects; @@ -232,6 +230,10 @@ typedef struct patch_target_t { * (i.e. a new file was added on top locally deleted node). */ svn_boolean_t replaced; + /* Set if the target is supposed to be moved by the patch. + * This applies to --git diffs which carry "rename from/to" headers. */ + const char *move_target_abspath; + /* True if the target has the executable bit set. */ svn_boolean_t executable; @@ -321,7 +323,8 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords, const char *rev_str; const char *author; const char *url; - const char *root_url; + const char *repos_root_url; + const char *repos_relpath; SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, &changed_date, @@ -330,15 +333,17 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords, scratch_pool, scratch_pool)); rev_str = apr_psprintf(scratch_pool, "%ld", changed_rev); - SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, - local_abspath, - scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, &root_url, NULL, + SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, &repos_root_url, + NULL, wc_ctx, local_abspath, scratch_pool, scratch_pool)); + url = svn_path_url_add_component2(repos_root_url, repos_relpath, + scratch_pool); + SVN_ERR(svn_subst_build_keywords3(keywords, keywords_val->data, - rev_str, url, root_url, changed_date, + rev_str, url, repos_root_url, + changed_date, author, result_pool)); } @@ -481,6 +486,8 @@ resolve_target_path(patch_target_t *target, SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL, wc_ctx, target->local_abspath, result_pool, scratch_pool)); + /* ### BUG: moved_to_abspath contains the target where the op-root was + ### moved to... not the target itself! */ if (moved_to_abspath) { target->local_abspath = moved_to_abspath; @@ -942,6 +949,12 @@ choose_target_filename(const svn_patch_t *patch) if (strcmp(patch->new_filename, "/dev/null") == 0) return patch->old_filename; + /* If the patch renames the target, use the old name while + * applying hunks. The target will be renamed to the new name + * after hunks have been applied. */ + if (patch->operation == svn_diff_op_moved) + return patch->old_filename; + old = svn_path_component_count(patch->old_filename); new = svn_path_component_count(patch->new_filename); @@ -991,7 +1004,7 @@ init_patch_target(patch_target_t **patch_target, hi; hi = apr_hash_next(hi)) { - svn_prop_patch_t *prop_patch = svn__apr_hash_index_val(hi); + svn_prop_patch_t *prop_patch = apr_hash_this_val(hi); if (! has_prop_changes) has_prop_changes = prop_patch->hunks->nelts > 0; else @@ -1021,6 +1034,7 @@ init_patch_target(patch_target_t **patch_target, SVN_ERR(resolve_target_path(target, choose_target_filename(patch), wcroot_abspath, strip_count, prop_changes_only, wc_ctx, result_pool, scratch_pool)); + *patch_target = target; if (! target->skipped) { const char *diff_header; @@ -1079,6 +1093,64 @@ init_patch_target(patch_target_t **patch_target, target->added = TRUE; else if (patch->operation == svn_diff_op_deleted) target->deleted = TRUE; + else if (patch->operation == svn_diff_op_moved) + { + const char *move_target_path; + const char *move_target_relpath; + svn_boolean_t under_root; + svn_node_kind_t kind_on_disk; + svn_node_kind_t wc_kind; + + move_target_path = svn_dirent_internal_style(patch->new_filename, + scratch_pool); + + if (strip_count > 0) + SVN_ERR(strip_path(&move_target_path, move_target_path, + strip_count, scratch_pool, scratch_pool)); + + if (svn_dirent_is_absolute(move_target_path)) + { + move_target_relpath = svn_dirent_is_child(wcroot_abspath, + move_target_path, + scratch_pool); + if (! move_target_relpath) + { + /* The move target path is either outside of the working + * copy or it is the working copy itself. Skip it. */ + target->skipped = TRUE; + target->local_abspath = NULL; + return SVN_NO_ERROR; + } + } + else + move_target_relpath = move_target_path; + + /* Make sure the move target path is secure to use. */ + SVN_ERR(svn_dirent_is_under_root(&under_root, + &target->move_target_abspath, + wcroot_abspath, + move_target_relpath, result_pool)); + if (! under_root) + { + /* The target path is outside of the working copy. Skip it. */ + target->skipped = TRUE; + target->local_abspath = NULL; + return SVN_NO_ERROR; + } + + SVN_ERR(svn_io_check_path(target->move_target_abspath, + &kind_on_disk, scratch_pool)); + SVN_ERR(svn_wc_read_kind2(&wc_kind, wc_ctx, + target->move_target_abspath, + FALSE, FALSE, scratch_pool)); + if (kind_on_disk != svn_node_none || wc_kind != svn_node_none) + { + /* The move target path already exists on disk. Skip target. */ + target->skipped = TRUE; + target->move_target_abspath = NULL; + return SVN_NO_ERROR; + } + } if (! target->is_symlink) { @@ -1136,8 +1208,8 @@ init_patch_target(patch_target_t **patch_target, hi; hi = apr_hash_next(hi)) { - const char *prop_name = svn__apr_hash_index_key(hi); - svn_prop_patch_t *prop_patch = svn__apr_hash_index_val(hi); + const char *prop_name = apr_hash_this_key(hi); + svn_prop_patch_t *prop_patch = apr_hash_this_val(hi); prop_patch_target_t *prop_target; SVN_ERR(init_prop_target(&prop_target, @@ -1150,7 +1222,6 @@ init_patch_target(patch_target_t **patch_target, } } - *patch_target = target; return SVN_NO_ERROR; } @@ -1510,7 +1581,8 @@ match_existing_target(svn_boolean_t *match, /* Determine the line at which a HUNK applies to CONTENT of the TARGET * file, and return an appropriate hunk_info object in *HI, allocated from * RESULT_POOL. Use fuzz factor FUZZ. Set HI->FUZZ to FUZZ. If no correct - * line can be determined, set HI->REJECTED to TRUE. + * line can be determined, set HI->REJECTED to TRUE. PREVIOUS_OFFSET + * is the offset at which the previous matching hunk was applied, or zero. * IGNORE_WHITESPACE tells whether whitespace should be considered when * matching. IS_PROP_HUNK indicates whether the hunk patches file content * or a property. @@ -1522,6 +1594,7 @@ static svn_error_t * get_hunk_info(hunk_info_t **hi, patch_target_t *target, target_content_t *content, svn_diff_hunk_t *hunk, svn_linenum_t fuzz, + svn_linenum_t previous_offset, svn_boolean_t ignore_whitespace, svn_boolean_t is_prop_hunk, svn_cancel_func_t cancel_func, void *cancel_baton, @@ -1531,7 +1604,7 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target, svn_linenum_t original_start; svn_boolean_t already_applied; - original_start = svn_diff_hunk_get_original_start(hunk); + original_start = svn_diff_hunk_get_original_start(hunk) + previous_offset; already_applied = FALSE; /* An original offset of zero means that this hunk wants to create @@ -1639,7 +1712,9 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target, modified_start = svn_diff_hunk_get_modified_start(hunk); if (modified_start == 0) { - /* Patch wants to delete the file. */ + /* Patch wants to delete the file. + + ### locally_deleted is always false here? */ already_applied = target->locally_deleted; } else @@ -1660,27 +1735,85 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target, if (! already_applied) { - /* Scan the whole file again from the start. */ - SVN_ERR(seek_to_line(content, 1, scratch_pool)); + int i; + svn_linenum_t search_start = 1, search_end = 0; + svn_linenum_t matched_line2; + + /* Search for closest match before or after original + start. We have no backward search so search forwards + from the previous match (or start of file) to the + original start looking for the last match. Then + search forwards from the original start looking for a + better match. Finally search forwards from the start + of file to the previous hunk if that could result in + a better match. */ + + for (i = content->hunks->nelts; i > 0; --i) + { + const hunk_info_t *prev + = APR_ARRAY_IDX(content->hunks, i - 1, const hunk_info_t *); + if (!prev->rejected) + { + svn_linenum_t length; - /* Scan forward towards the hunk's line and look for a line - * where the hunk matches. */ + length = svn_diff_hunk_get_original_length(prev->hunk); + search_start = prev->matched_line + length; + break; + } + } + + /* Search from the previous match, or start of file, + towards the original location. */ + SVN_ERR(seek_to_line(content, search_start, scratch_pool)); SVN_ERR(scan_for_match(&matched_line, content, hunk, FALSE, original_start, fuzz, ignore_whitespace, FALSE, cancel_func, cancel_baton, scratch_pool)); - /* In tie-break situations, we arbitrarily prefer early matches - * to save us from scanning the rest of the file. */ - if (matched_line == 0) + /* If a match we only need to search forwards for a + better match, otherwise to the end of the file. */ + if (matched_line) + search_end = original_start + (original_start - matched_line); + + /* Search from original location, towards the end. */ + SVN_ERR(seek_to_line(content, original_start + 1, scratch_pool)); + SVN_ERR(scan_for_match(&matched_line2, content, hunk, + TRUE, search_end, fuzz, ignore_whitespace, + FALSE, cancel_func, cancel_baton, + scratch_pool)); + + /* Chose the forward match if it is closer than the + backward match or if there is no backward match. */ + if (matched_line2 + && (!matched_line + || (matched_line2 - original_start + < original_start - matched_line))) + matched_line = matched_line2; + + /* Search from before previous hunk if there could be a + better match. */ + if (search_start > 1 + && (!matched_line + || (matched_line > original_start + && (matched_line - original_start + > original_start - search_start)))) { - /* Scan forward towards the end of the file and look - * for a line where the hunk matches. */ - SVN_ERR(scan_for_match(&matched_line, content, hunk, - TRUE, 0, fuzz, ignore_whitespace, - FALSE, cancel_func, cancel_baton, + svn_linenum_t search_start2 = 1; + + if (matched_line + && matched_line - original_start < original_start) + search_start2 + = original_start - (matched_line - original_start) + 1; + + SVN_ERR(seek_to_line(content, search_start2, scratch_pool)); + SVN_ERR(scan_for_match(&matched_line2, content, hunk, FALSE, + search_start - 1, fuzz, + ignore_whitespace, FALSE, + cancel_func, cancel_baton, scratch_pool)); + if (matched_line2) + matched_line = matched_line2; } } } @@ -1725,7 +1858,7 @@ copy_lines_to_target(target_content_t *content, svn_linenum_t line, SVN_ERR(readline(content, &target_line, iterpool, iterpool)); if (! content->eof) target_line = apr_pstrcat(iterpool, target_line, content->eol_str, - (char *)NULL); + SVN_VA_NULL); len = strlen(target_line); SVN_ERR(content->write(content->write_baton, target_line, len, iterpool)); @@ -1949,7 +2082,7 @@ send_hunk_notification(const hunk_info_t *hi, notify->hunk_fuzz = hi->fuzz; notify->prop_name = prop_name; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); return SVN_NO_ERROR; } @@ -1963,6 +2096,7 @@ send_patch_notification(const patch_target_t *target, { svn_wc_notify_t *notify; svn_wc_notify_action_t action; + const char *notify_path; if (! ctx->notify_func2) return SVN_NO_ERROR; @@ -1971,14 +2105,18 @@ send_patch_notification(const patch_target_t *target, action = svn_wc_notify_skip; else if (target->deleted) action = svn_wc_notify_delete; - else if (target->added || target->replaced) + else if (target->added || target->replaced || target->move_target_abspath) action = svn_wc_notify_add; else action = svn_wc_notify_patch; - notify = svn_wc_create_notify(target->local_abspath ? target->local_abspath - : target->local_relpath, - action, pool); + if (target->move_target_abspath) + notify_path = target->move_target_abspath; + else + notify_path = target->local_abspath ? target->local_abspath + : target->local_relpath; + + notify = svn_wc_create_notify(notify_path, action, pool); notify->kind = svn_node_file; if (action == svn_wc_notify_skip) @@ -2006,7 +2144,7 @@ send_patch_notification(const patch_target_t *target, notify->prop_state = svn_wc_notify_state_changed; } - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); if (action == svn_wc_notify_patch) { @@ -2033,7 +2171,7 @@ send_patch_notification(const patch_target_t *target, { prop_patch_target_t *prop_target; - prop_target = svn__apr_hash_index_val(hash_index); + prop_target = apr_hash_this_val(hash_index); for (i = 0; i < prop_target->content->hunks->nelts; i++) { @@ -2054,15 +2192,16 @@ send_patch_notification(const patch_target_t *target, svn_pool_destroy(iterpool); } - return SVN_NO_ERROR; -} + if (target->move_target_abspath) + { + /* Notify about deletion of move source. */ + notify = svn_wc_create_notify(target->local_abspath, + svn_wc_notify_delete, pool); + notify->kind = svn_node_file; + ctx->notify_func2(ctx->notify_baton2, notify, pool); + } -static void -svn_sort__array(apr_array_header_t *array, - int (*comparison_func)(const void *, - const void *)) -{ - qsort(array->elts, array->nelts, array->elt_size, comparison_func); + return SVN_NO_ERROR; } /* Implements the callback for svn_sort__array. Puts hunks that match @@ -2124,8 +2263,6 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, int strip_count, svn_boolean_t ignore_whitespace, svn_boolean_t remove_tempfiles, - svn_client_patch_func_t patch_func, - void *patch_baton, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) @@ -2135,6 +2272,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, int i; static const svn_linenum_t MAX_FUZZ = 2; apr_hash_index_t *hash_index; + svn_linenum_t previous_offset = 0; SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count, remove_tempfiles, result_pool, scratch_pool)); @@ -2144,19 +2282,6 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, return SVN_NO_ERROR; } - if (patch_func) - { - SVN_ERR(patch_func(patch_baton, &target->filtered, - target->canon_path_from_patchfile, - target->patched_path, target->reject_path, - scratch_pool)); - if (target->filtered) - { - *patch_target = target; - return SVN_NO_ERROR; - } - } - iterpool = svn_pool_create(scratch_pool); /* Match hunks. */ for (i = 0; i < patch->hunks->nelts; i++) @@ -2177,6 +2302,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, do { SVN_ERR(get_hunk_info(&hi, target, target->content, hunk, fuzz, + previous_offset, ignore_whitespace, FALSE /* is_prop_hunk */, cancel_func, cancel_baton, @@ -2185,6 +2311,10 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, } while (hi->rejected && fuzz <= MAX_FUZZ && ! hi->already_applied); + if (hi->matched_line) + previous_offset + = hi->matched_line - svn_diff_hunk_get_original_start(hunk); + APR_ARRAY_PUSH(target->content->hunks, hunk_info_t *) = hi; } @@ -2236,8 +2366,8 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, const char *prop_name; prop_patch_target_t *prop_target; - prop_name = svn__apr_hash_index_key(hash_index); - prop_patch = svn__apr_hash_index_val(hash_index); + prop_name = apr_hash_this_key(hash_index); + prop_patch = apr_hash_this_val(hash_index); if (! strcmp(prop_name, SVN_PROP_SPECIAL)) target->is_special = TRUE; @@ -2263,7 +2393,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, do { SVN_ERR(get_hunk_info(&hi, target, prop_target->content, - hunk, fuzz, + hunk, fuzz, 0, ignore_whitespace, TRUE /* is_prop_hunk */, cancel_func, cancel_baton, @@ -2283,7 +2413,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, { prop_patch_target_t *prop_target; - prop_target = svn__apr_hash_index_val(hash_index); + prop_target = apr_hash_this_val(hash_index); for (i = 0; i < prop_target->content->hunks->nelts; i++) { @@ -2508,8 +2638,9 @@ create_missing_parents(patch_target_t *target, if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); - SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath, + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath, NULL /*props*/, + FALSE /* skip checks */, ctx->notify_func2, ctx->notify_baton2, iterpool)); } @@ -2625,7 +2756,10 @@ install_patched_target(patch_target_t *target, const char *abs_wc_path, svn_subst_eol_style_native); SVN_ERR(svn_subst_copy_and_translate4( - target->patched_path, target->local_abspath, + target->patched_path, + target->move_target_abspath + ? target->move_target_abspath + : target->local_abspath, target->content->eol_str, repair_eol, target->content->keywords, TRUE /* expand */, FALSE /* special */, @@ -2639,15 +2773,39 @@ install_patched_target(patch_target_t *target, const char *abs_wc_path, * Suppress notification, we'll do that later (and also * during dry-run). Don't allow cancellation because * we'd rather notify about what we did before aborting. */ - SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath, + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, target->local_abspath, NULL /*props*/, + FALSE /* skip checks */, NULL, NULL, pool)); } /* Restore the target's executable bit if necessary. */ - SVN_ERR(svn_io_set_file_executable(target->local_abspath, + SVN_ERR(svn_io_set_file_executable(target->move_target_abspath + ? target->move_target_abspath + : target->local_abspath, target->executable, FALSE, pool)); + + if (target->move_target_abspath) + { + /* ### Copying the patched content to the move target location, + * performing the move in meta-data, and removing the file at + * the move source should be one atomic operation. */ + + /* ### Create missing parents. */ + + /* Perform the move in meta-data. */ + SVN_ERR(svn_wc__move2(ctx->wc_ctx, + target->local_abspath, + target->move_target_abspath, + TRUE, /* metadata_only */ + FALSE, /* allow_mixed_revisions */ + NULL, NULL, NULL, NULL, + pool)); + + /* Delete the patch target's old location from disk. */ + SVN_ERR(svn_io_remove_file2(target->local_abspath, FALSE, pool)); + } } } @@ -2694,7 +2852,7 @@ install_patched_prop_targets(patch_target_t *target, hi; hi = apr_hash_next(hi)) { - prop_patch_target_t *prop_target = svn__apr_hash_index_val(hi); + prop_patch_target_t *prop_target = apr_hash_this_val(hi); const svn_string_t *prop_val; svn_error_t *err; @@ -2730,10 +2888,11 @@ install_patched_prop_targets(patch_target_t *target, { if (! dry_run) { - SVN_ERR(svn_io_file_create(target->local_abspath, "", - scratch_pool)); - SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath, + SVN_ERR(svn_io_file_create_empty(target->local_abspath, + scratch_pool)); + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, target->local_abspath, NULL /*props*/, + FALSE /* skip checks */, /* suppress notification */ NULL, NULL, iterpool)); @@ -2869,10 +3028,13 @@ check_ancestor_delete(const char *deleted_target, { struct can_delete_baton_t cb; svn_error_t *err; + apr_array_header_t *ignores; apr_pool_t *iterpool = svn_pool_create(scratch_pool); const char *dir_abspath = svn_dirent_dirname(deleted_target, scratch_pool); + SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool)); + while (svn_dirent_is_child(apply_root, dir_abspath, iterpool)) { svn_pool_clear(iterpool); @@ -2882,7 +3044,7 @@ check_ancestor_delete(const char *deleted_target, cb.targets_info = targets_info; err = svn_wc_walk_status(ctx->wc_ctx, dir_abspath, svn_depth_infinity, - TRUE, FALSE, FALSE, NULL, + TRUE, FALSE, FALSE, ignores, can_delete_callback, &cb, ctx->cancel_func, ctx->cancel_baton, iterpool); @@ -2986,14 +3148,23 @@ apply_patches(/* The path to the patch file. */ if (patch) { patch_target_t *target; + svn_boolean_t filtered = FALSE; SVN_ERR(apply_one_patch(&target, patch, abs_wc_path, ctx->wc_ctx, strip_count, ignore_whitespace, remove_tempfiles, - patch_func, patch_baton, ctx->cancel_func, ctx->cancel_baton, iterpool, iterpool)); - if (! target->filtered) + + if (!target->skipped && patch_func) + { + SVN_ERR(patch_func(patch_baton, &filtered, + target->canon_path_from_patchfile, + target->patched_path, target->reject_path, + iterpool)); + } + + if (! filtered) { /* Save info we'll still need when we're done patching. */ patch_target_info_t *target_info = @@ -3009,6 +3180,7 @@ apply_patches(/* The path to the patch file. */ if (target->has_text_changes || target->added + || target->move_target_abspath || target->deleted) SVN_ERR(install_patched_target(target, abs_wc_path, ctx, dry_run, iterpool)); |