summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/patch.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/patch.c')
-rw-r--r--subversion/libsvn_client/patch.c1000
1 files changed, 549 insertions, 451 deletions
diff --git a/subversion/libsvn_client/patch.c b/subversion/libsvn_client/patch.c
index 24a4c5f..b7fbf06 100644
--- a/subversion/libsvn_client/patch.c
+++ b/subversion/libsvn_client/patch.c
@@ -47,6 +47,7 @@
#include "private/svn_wc_private.h"
#include "private/svn_dep_compat.h"
#include "private/svn_string_private.h"
+#include "private/svn_subr_private.h"
typedef struct hunk_info_t {
/* The hunk. */
@@ -174,6 +175,9 @@ typedef struct patch_target_t {
* CONTENT->existed). */
apr_file_t *file;
+ /* The target file is a symlink */
+ svn_boolean_t is_symlink;
+
/* The patched file.
* This is equivalent to the target, except that in appropriate
* places it contains the modified text as it appears in the patch file.
@@ -309,8 +313,7 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, local_abspath,
scratch_pool, scratch_pool));
- keywords_val = apr_hash_get(props, SVN_PROP_KEYWORDS,
- APR_HASH_KEY_STRING);
+ keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS);
if (keywords_val)
{
svn_revnum_t changed_rev;
@@ -318,6 +321,7 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
const char *rev_str;
const char *author;
const char *url;
+ const char *root_url;
SVN_ERR(svn_wc__node_get_changed_info(&changed_rev,
&changed_date,
@@ -329,14 +333,16 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
SVN_ERR(svn_wc__node_get_url(&url, wc_ctx,
local_abspath,
scratch_pool, scratch_pool));
- SVN_ERR(svn_subst_build_keywords2(keywords,
+ SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, &root_url, NULL,
+ wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_subst_build_keywords3(keywords,
keywords_val->data,
- rev_str, url, changed_date,
+ rev_str, url, root_url, changed_date,
author, result_pool));
}
- eol_style_val = apr_hash_get(props, SVN_PROP_EOL_STYLE,
- APR_HASH_KEY_STRING);
+ eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
if (eol_style_val)
{
svn_subst_eol_style_from_value(eol_style,
@@ -365,7 +371,7 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
static svn_error_t *
resolve_target_path(patch_target_t *target,
const char *path_from_patchfile,
- const char *local_abspath,
+ const char *wcroot_abspath,
int strip_count,
svn_boolean_t prop_changes_only,
svn_wc_context_t *wc_ctx,
@@ -398,7 +404,8 @@ resolve_target_path(patch_target_t *target,
if (svn_dirent_is_absolute(stripped_path))
{
- target->local_relpath = svn_dirent_is_child(local_abspath, stripped_path,
+ target->local_relpath = svn_dirent_is_child(wcroot_abspath,
+ stripped_path,
result_pool);
if (! target->local_relpath)
@@ -419,7 +426,7 @@ resolve_target_path(patch_target_t *target,
/* Make sure the path is secure to use. We want the target to be inside
* of the working copy and not be fooled by symlinks it might contain. */
SVN_ERR(svn_dirent_is_under_root(&under_root,
- &target->local_abspath, local_abspath,
+ &target->local_abspath, wcroot_abspath,
target->local_relpath, result_pool));
if (! under_root)
@@ -435,54 +442,66 @@ resolve_target_path(patch_target_t *target,
result_pool, scratch_pool);
if (err)
{
- if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- svn_error_clear(err);
- else
+ if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
+
+ svn_error_clear(err);
+
+ target->locally_deleted = TRUE;
+ target->db_kind = svn_node_none;
+ status = NULL;
}
else if (status->node_status == svn_wc_status_ignored ||
status->node_status == svn_wc_status_unversioned ||
status->node_status == svn_wc_status_missing ||
- status->node_status == svn_wc_status_obstructed)
+ status->node_status == svn_wc_status_obstructed ||
+ status->conflicted)
{
target->skipped = TRUE;
return SVN_NO_ERROR;
}
-
- SVN_ERR(svn_io_check_path(target->local_abspath,
- &target->kind_on_disk, scratch_pool));
- err = svn_wc__node_is_status_deleted(&target->locally_deleted,
- wc_ctx, target->local_abspath,
- scratch_pool);
- if (err)
+ else if (status->node_status == svn_wc_status_deleted)
{
- if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- {
- svn_error_clear(err);
- target->locally_deleted = FALSE;
- }
- else
- return svn_error_trace(err);
+ target->locally_deleted = TRUE;
}
- SVN_ERR(svn_wc_read_kind(&target->db_kind, wc_ctx, target->local_abspath,
- FALSE, scratch_pool));
- /* If the target is a versioned directory present on disk,
- * and there are only property changes in the patch, we accept
- * a directory target. Else, we skip directories. */
- if (target->db_kind == svn_node_dir && ! prop_changes_only)
- {
- /* ### We cannot yet replace a locally deleted dir with a file,
- * ### but some day we might want to allow it. */
- target->skipped = TRUE;
- return SVN_NO_ERROR;
- }
+ if (status && (status->kind != svn_node_unknown))
+ target->db_kind = status->kind;
+ else
+ target->db_kind = svn_node_none;
+
+ SVN_ERR(svn_io_check_special_path(target->local_abspath,
+ &target->kind_on_disk, &target->is_symlink,
+ scratch_pool));
- /* ### Shouldn't libsvn_wc flag an obstruction in this case? */
- if (target->locally_deleted && target->kind_on_disk != svn_node_none)
+ if (target->locally_deleted)
{
- target->skipped = TRUE;
- return SVN_NO_ERROR;
+ const char *moved_to_abspath;
+
+ SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
+ wc_ctx, target->local_abspath,
+ result_pool, scratch_pool));
+ if (moved_to_abspath)
+ {
+ target->local_abspath = moved_to_abspath;
+ target->local_relpath = svn_dirent_skip_ancestor(wcroot_abspath,
+ moved_to_abspath);
+ SVN_ERR_ASSERT(target->local_relpath &&
+ target->local_relpath[0] != '\0');
+
+ /* As far as we are concerned this target is not locally deleted. */
+ target->locally_deleted = FALSE;
+
+ SVN_ERR(svn_io_check_special_path(target->local_abspath,
+ &target->kind_on_disk,
+ &target->is_symlink,
+ scratch_pool));
+ }
+ else if (target->kind_on_disk != svn_node_none)
+ {
+ target->skipped = TRUE;
+ return SVN_NO_ERROR;
+ }
}
return SVN_NO_ERROR;
@@ -500,6 +519,8 @@ typedef struct prop_read_baton_t {
* the property value runs out in which case *EOF is set to TRUE.
* The line-terminator is not stored in *STRINGBUF.
*
+ * If the line is empty or could not be read, *line is set to NULL.
+ *
* The line-terminator is detected automatically and stored in *EOL
* if EOL is not NULL. If the end of the property value is reached
* and does not end with a newline character, and EOL is not NULL,
@@ -513,17 +534,15 @@ readline_prop(void *baton, svn_stringbuf_t **line, const char **eol_str,
apr_pool_t *scratch_pool)
{
prop_read_baton_t *b = (prop_read_baton_t *)baton;
- svn_stringbuf_t *str;
+ svn_stringbuf_t *str = NULL;
const char *c;
svn_boolean_t found_eof;
- str = svn_stringbuf_create_ensure(80, result_pool);
-
- if (b->offset >= b->value->len)
+ if ((apr_uint64_t)b->offset >= (apr_uint64_t)b->value->len)
{
*eol_str = NULL;
*eof = TRUE;
- *line = str;
+ *line = NULL;
return SVN_NO_ERROR;
}
@@ -555,7 +574,11 @@ readline_prop(void *baton, svn_stringbuf_t **line, const char **eol_str,
}
}
else
- svn_stringbuf_appendbyte(str, *c);
+ {
+ if (str == NULL)
+ str = svn_stringbuf_create_ensure(80, result_pool);
+ svn_stringbuf_appendbyte(str, *c);
+ }
if (*eol_str)
break;
@@ -629,7 +652,7 @@ init_prop_target(prop_patch_target_t **prop_target,
content->hunks = apr_array_make(result_pool, 0, sizeof(hunk_info_t *));
content->keywords = apr_hash_make(result_pool);
- new_prop_target = apr_palloc(result_pool, sizeof(*new_prop_target));
+ new_prop_target = apr_pcalloc(result_pool, sizeof(*new_prop_target));
new_prop_target->name = apr_pstrdup(result_pool, prop_name);
new_prop_target->operation = operation;
new_prop_target->content = content;
@@ -648,11 +671,11 @@ init_prop_target(prop_patch_target_t **prop_target,
}
content->existed = (value != NULL);
new_prop_target->value = value;
- new_prop_target->patched_value = svn_stringbuf_create("", result_pool);
+ new_prop_target->patched_value = svn_stringbuf_create_empty(result_pool);
/* Wire up the read and write callbacks. */
- prop_read_baton = apr_palloc(result_pool, sizeof(*prop_read_baton));
+ prop_read_baton = apr_pcalloc(result_pool, sizeof(*prop_read_baton));
prop_read_baton->value = value;
prop_read_baton->offset = 0;
content->readline = readline_prop;
@@ -673,6 +696,8 @@ init_prop_target(prop_patch_target_t **prop_target,
* or if EOF is reached in which case *EOF is set to TRUE.
* The line-terminator is not stored in *STRINGBUF.
*
+ * If the line is empty or could not be read, *line is set to NULL.
+ *
* The line-terminator is detected automatically and stored in *EOL
* if EOL is not NULL. If EOF is reached and FILE does not end
* with a newline character, and EOL is not NULL, *EOL is set to NULL.
@@ -685,13 +710,11 @@ readline_file(void *baton, svn_stringbuf_t **line, const char **eol_str,
apr_pool_t *scratch_pool)
{
apr_file_t *file = (apr_file_t *)baton;
- svn_stringbuf_t *str;
+ svn_stringbuf_t *str = NULL;
apr_size_t numbytes;
char c;
svn_boolean_t found_eof;
- str = svn_stringbuf_create_ensure(80, result_pool);
-
/* Read bytes into STR up to and including, but not storing,
* the next EOL sequence. */
*eol_str = NULL;
@@ -738,7 +761,11 @@ readline_file(void *baton, svn_stringbuf_t **line, const char **eol_str,
}
}
else
- svn_stringbuf_appendbyte(str, c);
+ {
+ if (str == NULL)
+ str = svn_stringbuf_create_ensure(80, result_pool);
+ svn_stringbuf_appendbyte(str, c);
+ }
if (*eol_str)
break;
@@ -784,6 +811,118 @@ write_file(void *baton, const char *buf, apr_size_t len,
return SVN_NO_ERROR;
}
+/* Handling symbolic links:
+ *
+ * In Subversion, symlinks can be represented on disk in two distinct ways.
+ * On systems which support symlinks, a symlink is created on disk.
+ * On systems which do not support symlink, a file is created on disk
+ * which contains the "normal form" of the symlink, which looks like:
+ * link TARGET
+ * where TARGET is the file the symlink points to.
+ *
+ * When reading symlinks (i.e. the link itself, not the file the symlink
+ * is pointing to) through the svn_subst_create_specialfile() function
+ * into a buffer, the buffer always contains the "normal form" of the symlink.
+ * Due to this representation symlinks always contain a single line of text.
+ *
+ * The functions below are needed to deal with the case where a patch
+ * wants to change the TARGET that a symlink points to.
+ */
+
+/* Baton for the (readline|tell|seek|write)_symlink functions. */
+struct symlink_baton_t
+{
+ /* The path to the symlink on disk (not the path to the target of the link) */
+ const char *local_abspath;
+
+ /* Indicates whether the "normal form" of the symlink has been read. */
+ svn_boolean_t at_eof;
+};
+
+/* Allocate *STRINGBUF in RESULT_POOL, and store into it the "normal form"
+ * of the symlink accessed via BATON.
+ *
+ * Otherwise behaves like readline_file(), which see.
+ */
+static svn_error_t *
+readline_symlink(void *baton, svn_stringbuf_t **line, const char **eol_str,
+ svn_boolean_t *eof, apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct symlink_baton_t *sb = baton;
+
+ if (eof)
+ *eof = TRUE;
+ if (eol_str)
+ *eol_str = NULL;
+
+ if (sb->at_eof)
+ {
+ *line = NULL;
+ }
+ else
+ {
+ svn_string_t *dest;
+
+ SVN_ERR(svn_io_read_link(&dest, sb->local_abspath, scratch_pool));
+ *line = svn_stringbuf_createf(result_pool, "link %s", dest->data);
+ sb->at_eof = TRUE;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Set *OFFSET to 1 or 0 depending on whether the "normal form" of
+ * the symlink has already been read. */
+static svn_error_t *
+tell_symlink(void *baton, apr_off_t *offset, apr_pool_t *scratch_pool)
+{
+ struct symlink_baton_t *sb = baton;
+
+ *offset = sb->at_eof ? 1 : 0;
+ return SVN_NO_ERROR;
+}
+
+/* If offset is non-zero, mark the symlink as having been read in its
+ * "normal form". Else, mark the symlink as not having been read yet. */
+static svn_error_t *
+seek_symlink(void *baton, apr_off_t offset, apr_pool_t *scratch_pool)
+{
+ struct symlink_baton_t *sb = baton;
+
+ sb->at_eof = (offset != 0);
+ return SVN_NO_ERROR;
+}
+
+
+/* Set the target of the symlink accessed via BATON.
+ * The contents of BUF must be a valid "normal form" of a symlink. */
+static svn_error_t *
+write_symlink(void *baton, const char *buf, apr_size_t len,
+ apr_pool_t *scratch_pool)
+{
+ const char *target_abspath = baton;
+ const char *new_name;
+ const char *link = apr_pstrndup(scratch_pool, buf, len);
+
+ if (strncmp(link, "link ", 5) != 0)
+ return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL,
+ _("Invalid link representation"));
+
+ link += 5; /* Skip "link " */
+
+ /* We assume the entire symlink is written at once, as the patch
+ format is line based */
+
+ SVN_ERR(svn_io_create_unique_link(&new_name, target_abspath, link,
+ ".tmp", scratch_pool));
+
+ SVN_ERR(svn_io_file_rename(new_name, target_abspath, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+
/* Return a suitable filename for the target of PATCH.
* Examine the ``old'' and ``new'' file names, and choose the file name
* with the fewest path components, the shortest basename, and the shortest
@@ -835,7 +974,7 @@ choose_target_filename(const svn_patch_t *patch)
static svn_error_t *
init_patch_target(patch_target_t **patch_target,
const svn_patch_t *patch,
- const char *base_dir,
+ const char *wcroot_abspath,
svn_wc_context_t *wc_ctx, int strip_count,
svn_boolean_t remove_tempfiles,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
@@ -880,7 +1019,7 @@ init_patch_target(patch_target_t **patch_target,
target->prop_targets = apr_hash_make(result_pool);
SVN_ERR(resolve_target_path(target, choose_target_filename(patch),
- base_dir, strip_count, prop_changes_only,
+ wcroot_abspath, strip_count, prop_changes_only,
wc_ctx, result_pool, scratch_pool));
if (! target->skipped)
{
@@ -889,10 +1028,24 @@ init_patch_target(patch_target_t **patch_target,
/* Create a temporary file to write the patched result to.
* Also grab various bits of information about the file. */
- if (target->kind_on_disk == svn_node_file)
+ if (target->is_symlink)
+ {
+ struct symlink_baton_t *sb = apr_pcalloc(result_pool, sizeof(*sb));
+ content->existed = TRUE;
+
+ sb->local_abspath = target->local_abspath;
+
+ /* Wire up the read callbacks. */
+ content->read_baton = sb;
+
+ content->readline = readline_symlink;
+ content->seek = seek_symlink;
+ content->tell = tell_symlink;
+ }
+ else if (target->kind_on_disk == svn_node_file)
{
SVN_ERR(svn_io_file_open(&target->file, target->local_abspath,
- APR_READ | APR_BINARY | APR_BUFFERED,
+ APR_READ | APR_BUFFERED,
APR_OS_DEFAULT, result_pool));
SVN_ERR(svn_wc_text_modified_p2(&target->local_mods, wc_ctx,
target->local_abspath, FALSE,
@@ -927,17 +1080,34 @@ init_patch_target(patch_target_t **patch_target,
else if (patch->operation == svn_diff_op_deleted)
target->deleted = TRUE;
- /* Open a temporary file to write the patched result to. */
- SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
- &target->patched_path, NULL,
- remove_tempfiles ?
- svn_io_file_del_on_pool_cleanup :
- svn_io_file_del_none,
- result_pool, scratch_pool));
+ if (! target->is_symlink)
+ {
+ /* Open a temporary file to write the patched result to. */
+ SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
+ &target->patched_path, NULL,
+ remove_tempfiles ?
+ svn_io_file_del_on_pool_cleanup :
+ svn_io_file_del_none,
+ result_pool, scratch_pool));
+
+ /* Put the write callback in place. */
+ content->write = write_file;
+ content->write_baton = target->patched_file;
+ }
+ else
+ {
+ /* Put the write callback in place. */
+ SVN_ERR(svn_io_open_unique_file3(NULL,
+ &target->patched_path, NULL,
+ remove_tempfiles ?
+ svn_io_file_del_on_pool_cleanup :
+ svn_io_file_del_none,
+ result_pool, scratch_pool));
- /* Put the write callback in place. */
- content->write = write_file;
- content->write_baton = target->patched_file;
+ content->write_baton = (void*)target->patched_path;
+
+ content->write = write_symlink;
+ }
/* Open a temporary file to write rejected hunks to. */
SVN_ERR(svn_io_open_unique_file3(&target->reject_file,
@@ -975,8 +1145,7 @@ init_patch_target(patch_target_t **patch_target,
prop_patch->operation,
wc_ctx, target->local_abspath,
result_pool, scratch_pool));
- apr_hash_set(target->prop_targets, prop_name,
- APR_HASH_KEY_STRING, prop_target);
+ svn_hash_sets(target->prop_targets, prop_name, prop_target);
}
}
}
@@ -986,7 +1155,9 @@ init_patch_target(patch_target_t **patch_target,
}
/* Read a *LINE from CONTENT. If the line has not been read before
- * mark the line in CONTENT->LINES. Allocate *LINE in RESULT_POOL.
+ * mark the line in CONTENT->LINES.
+ * If a line could be read successfully, increase CONTENT->CURRENT_LINE,
+ * and allocate *LINE in RESULT_POOL.
* Do temporary allocations in SCRATCH_POOL.
*/
static svn_error_t *
@@ -997,6 +1168,7 @@ readline(target_content_t *content,
{
svn_stringbuf_t *line_raw;
const char *eol_str;
+ svn_linenum_t max_line = (svn_linenum_t)content->lines->nelts + 1;
if (content->eof || content->readline == NULL)
{
@@ -1004,8 +1176,8 @@ readline(target_content_t *content,
return SVN_NO_ERROR;
}
- SVN_ERR_ASSERT(content->current_line <= content->lines->nelts + 1);
- if (content->current_line == content->lines->nelts + 1)
+ SVN_ERR_ASSERT(content->current_line <= max_line);
+ if (content->current_line == max_line)
{
apr_off_t offset;
@@ -1020,14 +1192,22 @@ readline(target_content_t *content,
if (content->eol_style == svn_subst_eol_style_none)
content->eol_str = eol_str;
- /* Contract keywords. */
- SVN_ERR(svn_subst_translate_cstring2(line_raw->data, line,
- NULL, FALSE,
- content->keywords, FALSE,
- result_pool));
- if (! content->eof)
+ if (line_raw)
+ {
+ /* Contract keywords. */
+ SVN_ERR(svn_subst_translate_cstring2(line_raw->data, line,
+ NULL, FALSE,
+ content->keywords, FALSE,
+ result_pool));
+ }
+ else
+ *line = "";
+
+ if ((line_raw && line_raw->len > 0) || eol_str)
content->current_line++;
+ SVN_ERR_ASSERT(content->current_line > 0);
+
return SVN_NO_ERROR;
}
@@ -1050,7 +1230,7 @@ seek_to_line(target_content_t *content, svn_linenum_t line,
saved_line = content->current_line;
saved_eof = content->eof;
- if (line <= content->lines->nelts)
+ if (line <= (svn_linenum_t)content->lines->nelts)
{
apr_off_t offset;
@@ -1359,26 +1539,45 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target,
* the hunk applies at line 1. If the file already exists, the hunk
* is rejected, unless the file is versioned and its content matches
* the file the patch wants to create. */
- if (original_start == 0 && ! is_prop_hunk)
+ if (original_start == 0 && fuzz > 0)
+ {
+ matched_line = 0; /* reject any fuzz for new files */
+ }
+ else if (original_start == 0 && ! is_prop_hunk)
{
if (target->kind_on_disk == svn_node_file)
{
- if (target->db_kind == svn_node_file)
+ const svn_io_dirent2_t *dirent;
+ SVN_ERR(svn_io_stat_dirent2(&dirent, target->local_abspath, FALSE,
+ TRUE, scratch_pool, scratch_pool));
+
+ if (dirent->kind == svn_node_file
+ && !dirent->special
+ && dirent->filesize == 0)
{
- svn_boolean_t file_matches;
+ matched_line = 1; /* Matched an on-disk empty file */
+ }
+ else
+ {
+ if (target->db_kind == svn_node_file)
+ {
+ svn_boolean_t file_matches;
- SVN_ERR(match_existing_target(&file_matches, content, hunk,
+ /* ### I can't reproduce anything but a no-match here.
+ The content is already at eof, so any hunk fails */
+ SVN_ERR(match_existing_target(&file_matches, content, hunk,
scratch_pool));
- if (file_matches)
- {
- matched_line = 1;
- already_applied = TRUE;
+ if (file_matches)
+ {
+ matched_line = 1;
+ already_applied = TRUE;
+ }
+ else
+ matched_line = 0; /* reject */
}
else
matched_line = 0; /* reject */
}
- else
- matched_line = 0; /* reject */
}
else
matched_line = 1;
@@ -1494,7 +1693,7 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target,
matched_line = 0;
}
- (*hi) = apr_palloc(result_pool, sizeof(hunk_info_t));
+ (*hi) = apr_pcalloc(result_pool, sizeof(hunk_info_t));
(*hi)->hunk = hunk;
(*hi)->matched_line = matched_line;
(*hi)->rejected = (matched_line == 0);
@@ -1858,6 +2057,56 @@ send_patch_notification(const patch_target_t *target,
return SVN_NO_ERROR;
}
+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);
+}
+
+/* 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 original position. */
+static int
+sort_matched_hunks(const void *a, const void *b)
+{
+ const hunk_info_t *item1 = *((const hunk_info_t * const *)a);
+ const hunk_info_t *item2 = *((const hunk_info_t * const *)b);
+ svn_boolean_t matched1 = !item1->rejected && !item1->already_applied;
+ svn_boolean_t matched2 = !item2->rejected && !item2->already_applied;
+ svn_linenum_t original1, original2;
+
+ if (matched1 && matched2)
+ {
+ /* Both match so use order matched in file. */
+ if (item1->matched_line > item2->matched_line)
+ return 1;
+ else if (item1->matched_line == item2->matched_line)
+ return 0;
+ else
+ return -1;
+ }
+ else if (matched2)
+ /* Only second matches, put it before first. */
+ return 1;
+ else if (matched1)
+ /* Only first matches, put it before second. */
+ return -1;
+
+ /* Neither matches, sort by original_start. */
+ original1 = svn_diff_hunk_get_original_start(item1->hunk);
+ original2 = svn_diff_hunk_get_original_start(item2->hunk);
+ if (original1 > original2)
+ return 1;
+ else if (original1 == original2)
+ return 0;
+ else
+ return -1;
+}
+
+
/* Apply a PATCH to a working copy at ABS_WC_PATH and put the result
* into temporary files, to be installed in the working copy later.
* Return information about the patch target in *PATCH_TARGET, allocated
@@ -1939,6 +2188,10 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
APR_ARRAY_PUSH(target->content->hunks, hunk_info_t *) = hi;
}
+ /* Hunks are applied in the order determined by the matched line and
+ this may be different from the order of the original lines. */
+ svn_sort__array(target->content->hunks, sort_matched_hunks);
+
/* Apply or reject hunks. */
for (i = 0; i < target->content->hunks->nelts; i++)
{
@@ -1990,8 +2243,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
target->is_special = TRUE;
/* We'll store matched hunks in prop_content. */
- prop_target = apr_hash_get(target->prop_targets, prop_name,
- APR_HASH_KEY_STRING);
+ prop_target = svn_hash_gets(target->prop_targets, prop_name);
for (i = 0; i < prop_patch->hunks->nelts; i++)
{
@@ -2070,13 +2322,17 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
svn_pool_destroy(iterpool);
- /* Now close files we don't need any longer to get their contents
- * flushed to disk.
- * But we're not closing the reject file -- it still needed and
- * will be closed later in write_out_rejected_hunks(). */
- if (target->kind_on_disk == svn_node_file)
- SVN_ERR(svn_io_file_close(target->file, scratch_pool));
- SVN_ERR(svn_io_file_close(target->patched_file, scratch_pool));
+ if (!target->is_symlink)
+ {
+ /* Now close files we don't need any longer to get their contents
+ * flushed to disk.
+ * But we're not closing the reject file -- it still needed and
+ * will be closed later in write_out_rejected_hunks(). */
+ if (target->kind_on_disk == svn_node_file)
+ SVN_ERR(svn_io_file_close(target->file, scratch_pool));
+
+ SVN_ERR(svn_io_file_close(target->patched_file, scratch_pool));
+ }
if (! target->skipped)
{
@@ -2087,10 +2343,10 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
* We'll need those to figure out whether we should delete the
* patched file. */
SVN_ERR(svn_io_stat(&patched_file, target->patched_path,
- APR_FINFO_SIZE, scratch_pool));
+ APR_FINFO_SIZE | APR_FINFO_LINK, scratch_pool));
if (target->kind_on_disk == svn_node_file)
SVN_ERR(svn_io_stat(&working_file, target->local_abspath,
- APR_FINFO_SIZE, scratch_pool));
+ APR_FINFO_SIZE | APR_FINFO_LINK, scratch_pool));
else
working_file.size = 0;
@@ -2159,47 +2415,36 @@ create_missing_parents(patch_target_t *target,
{
const char *component;
svn_node_kind_t wc_kind, disk_kind;
- svn_boolean_t is_deleted;
svn_pool_clear(iterpool);
component = APR_ARRAY_IDX(components, i, const char *);
local_abspath = svn_dirent_join(local_abspath, component, scratch_pool);
- SVN_ERR(svn_wc_read_kind(&wc_kind, ctx->wc_ctx, local_abspath, TRUE,
- iterpool));
+ SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, local_abspath,
+ FALSE, TRUE, iterpool));
SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, iterpool));
- if (wc_kind != svn_node_none)
- SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted,
- ctx->wc_ctx,
- local_abspath,
- iterpool));
- else
- is_deleted = FALSE;
-
- if (disk_kind == svn_node_file
- || (wc_kind == svn_node_file && !is_deleted))
+ if (disk_kind == svn_node_file || wc_kind == svn_node_file)
{
/* on-disk files and missing files are obstructions */
target->skipped = TRUE;
break;
}
- else if (wc_kind == svn_node_dir)
+ else if (disk_kind == svn_node_dir)
{
- if (is_deleted)
+ if (wc_kind == svn_node_dir)
+ present_components++;
+ else
{
target->skipped = TRUE;
break;
}
-
- /* continue one level deeper */
- present_components++;
}
- else if (disk_kind == svn_node_dir)
+ else if (wc_kind != svn_node_none)
{
- /* Obstructed. ### BH: why? We can just add a directory */
+ /* Node is missing */
target->skipped = TRUE;
break;
}
@@ -2210,7 +2455,6 @@ create_missing_parents(patch_target_t *target,
break;
}
}
-
if (! target->skipped)
{
local_abspath = abs_wc_path;
@@ -2264,9 +2508,10 @@ 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_disk(ctx->wc_ctx, local_abspath,
- ctx->notify_func2, ctx->notify_baton2,
- iterpool));
+ SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath,
+ NULL /*props*/,
+ ctx->notify_func2, ctx->notify_baton2,
+ iterpool));
}
}
}
@@ -2301,37 +2546,53 @@ install_patched_target(patch_target_t *target, const char *abs_wc_path,
else
{
svn_node_kind_t parent_db_kind;
-
- if (target->added)
+ if (target->added || target->replaced)
{
+ const char *parent_abspath;
+
+ parent_abspath = svn_dirent_dirname(target->local_abspath,
+ pool);
/* If the target's parent directory does not yet exist
* we need to create it before we can copy the patched
* result in place. */
- SVN_ERR(svn_wc_read_kind(&parent_db_kind, ctx->wc_ctx,
- svn_dirent_dirname(target->local_abspath,
- pool),
- FALSE, pool));
-
- /* We don't allow targets to be added under dirs scheduled for
- * deletion. */
- if (parent_db_kind == svn_node_dir)
+ SVN_ERR(svn_wc_read_kind2(&parent_db_kind, ctx->wc_ctx,
+ parent_abspath, FALSE, FALSE, pool));
+
+ /* We can't add targets under nodes scheduled for delete, so add
+ a new directory if needed. */
+ if (parent_db_kind == svn_node_dir
+ || parent_db_kind == svn_node_file)
{
- const char *parent_abspath;
- svn_boolean_t is_deleted;
-
- parent_abspath = svn_dirent_dirname(target->local_abspath,
- pool);
- SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx,
- parent_abspath, pool));
- if (is_deleted)
+ if (parent_db_kind != svn_node_dir)
+ target->skipped = TRUE;
+ else
{
- target->skipped = TRUE;
- return SVN_NO_ERROR;
+ svn_node_kind_t disk_kind;
+
+ SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, pool));
+ if (disk_kind != svn_node_dir)
+ target->skipped = TRUE;
}
}
else
SVN_ERR(create_missing_parents(target, abs_wc_path, ctx,
dry_run, pool));
+
+ }
+ else
+ {
+ svn_node_kind_t wc_kind;
+
+ /* The target should exist */
+ SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx,
+ target->local_abspath,
+ FALSE, FALSE, pool));
+
+ if (target->kind_on_disk == svn_node_none
+ || wc_kind != target->kind_on_disk)
+ {
+ target->skipped = TRUE;
+ }
}
if (! dry_run && ! target->skipped)
@@ -2378,8 +2639,9 @@ 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_disk(ctx->wc_ctx, target->local_abspath,
- NULL, NULL, pool));
+ SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+ NULL /*props*/,
+ NULL, NULL, pool));
}
/* Restore the target's executable bit if necessary. */
@@ -2470,10 +2732,11 @@ install_patched_prop_targets(patch_target_t *target,
{
SVN_ERR(svn_io_file_create(target->local_abspath, "",
scratch_pool));
- SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, target->local_abspath,
- /* suppress notification */
- NULL, NULL,
- iterpool));
+ SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+ NULL /*props*/,
+ /* suppress notification */
+ NULL, NULL,
+ iterpool));
}
target->added = TRUE;
}
@@ -2547,318 +2810,164 @@ install_patched_prop_targets(patch_target_t *target,
return SVN_NO_ERROR;
}
-/* Baton for find_existing_children() */
-struct status_baton
+/* Baton for can_delete_callback */
+struct can_delete_baton_t
{
- apr_array_header_t *existing_targets;
- const char *parent_path;
- apr_pool_t *result_pool;
+ svn_boolean_t must_keep;
+ const apr_array_header_t *targets_info;
+ const char *local_abspath;
};
/* Implements svn_wc_status_func4_t. */
static svn_error_t *
-find_existing_children(void *baton,
- const char *abspath,
- const svn_wc_status3_t *status,
- apr_pool_t *pool)
+can_delete_callback(void *baton,
+ const char *abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *pool)
{
- struct status_baton *btn = baton;
-
- if (status->node_status != svn_wc_status_none
- && status->node_status != svn_wc_status_deleted
- && strcmp(abspath, btn->parent_path))
- {
- APR_ARRAY_PUSH(btn->existing_targets,
- const char *) = apr_pstrdup(btn->result_pool,
- abspath);
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Indicate in *EMPTY whether the directory at LOCAL_ABSPATH has any
- * versioned or unversioned children. Consider any DELETED_TARGETS,
- * as well as paths occuring as keys of DELETED_ABSPATHS_HASH (which may
- * be NULL) as already deleted. Use WC_CTX as the working copy context.
- * Do temporary allocations in SCRATCH_POOL. */
-static svn_error_t *
-check_dir_empty(svn_boolean_t *empty, const char *local_abspath,
- svn_wc_context_t *wc_ctx,
- apr_array_header_t *deleted_targets,
- apr_hash_t *deleted_abspath_hash,
- apr_pool_t *scratch_pool)
-{
- struct status_baton btn;
- svn_boolean_t is_wc_root;
+ struct can_delete_baton_t *cb = baton;
int i;
- /* Working copy root cannot be deleted, so never consider it empty. */
- SVN_ERR(svn_wc__strictly_is_wc_root(&is_wc_root, wc_ctx, local_abspath,
- scratch_pool));
- if (is_wc_root)
+ switch(status->node_status)
{
- *empty = FALSE;
- return SVN_NO_ERROR;
- }
+ case svn_wc_status_none:
+ case svn_wc_status_deleted:
+ return SVN_NO_ERROR;
- /* Find existing children of the directory. */
- btn.existing_targets = apr_array_make(scratch_pool, 0,
- sizeof(patch_target_t *));
- btn.parent_path = local_abspath;
- btn.result_pool = scratch_pool;
- SVN_ERR(svn_wc_walk_status(wc_ctx, local_abspath, svn_depth_immediates,
- TRUE, TRUE, FALSE, NULL, find_existing_children,
- &btn, NULL, NULL, scratch_pool));
- *empty = TRUE;
-
- /* Do we delete all children? */
- for (i = 0; i < btn.existing_targets->nelts; i++)
- {
- int j;
- const char *found;
- svn_boolean_t deleted;
+ default:
+ if (! strcmp(cb->local_abspath, abspath))
+ return SVN_NO_ERROR; /* Only interested in descendants */
- deleted = FALSE;
- found = APR_ARRAY_IDX(btn.existing_targets, i, const char *);
+ for (i = 0; i < cb->targets_info->nelts; i++)
+ {
+ const patch_target_info_t *target_info =
+ APR_ARRAY_IDX(cb->targets_info, i, const patch_target_info_t *);
- for (j = 0; j < deleted_targets->nelts; j++)
- {
- patch_target_info_t *target_info;
+ if (! strcmp(target_info->local_abspath, abspath))
+ {
+ if (target_info->deleted)
+ return SVN_NO_ERROR;
- target_info = APR_ARRAY_IDX(deleted_targets, j,
- patch_target_info_t *);
- if (! svn_path_compare_paths(found, target_info->local_abspath))
- {
- deleted = TRUE;
- break;
- }
- }
- if (! deleted && deleted_abspath_hash)
- {
- apr_hash_index_t *hi;
+ break; /* Cease invocation; must keep */
+ }
+ }
- for (hi = apr_hash_first(scratch_pool, deleted_abspath_hash);
- hi;
- hi = apr_hash_next(hi))
- {
- const char *abspath;
-
- abspath = svn__apr_hash_index_key(hi);
- if (! svn_path_compare_paths(found, abspath))
- {
- deleted = TRUE;
- break;
- }
- }
- }
- if (! deleted)
- {
- *empty = FALSE;
- break;
- }
- }
+ cb->must_keep = TRUE;
- return SVN_NO_ERROR;
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+ }
}
-/* Delete all directories from the working copy which are left empty
- * by deleted TARGETS. Use client context CTX.
- * If DRY_RUN is TRUE, do not modify the working copy.
- * Do temporary allocations in SCRATCH_POOL. */
static svn_error_t *
-delete_empty_dirs(apr_array_header_t *targets_info, svn_client_ctx_t *ctx,
- svn_boolean_t dry_run, apr_pool_t *scratch_pool)
+check_ancestor_delete(const char *deleted_target,
+ apr_array_header_t *targets_info,
+ const char *apply_root,
+ svn_boolean_t dry_run,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- apr_hash_t *empty_dirs;
- apr_hash_t *non_empty_dirs;
- apr_array_header_t *deleted_targets;
- apr_pool_t *iterpool;
- svn_boolean_t again;
- int i;
- apr_hash_index_t *hi;
-
- /* Get a list of all deleted targets. */
- deleted_targets = apr_array_make(scratch_pool, 0, sizeof(patch_target_t *));
- for (i = 0; i < targets_info->nelts; i++)
- {
- patch_target_info_t *target_info;
-
- target_info = APR_ARRAY_IDX(targets_info, i, patch_target_info_t *);
- if (target_info->deleted)
- APR_ARRAY_PUSH(deleted_targets, patch_target_info_t *) = target_info;
- }
-
- /* We have nothing to do if there aren't any deleted targets. */
- if (deleted_targets->nelts == 0)
- return SVN_NO_ERROR;
-
- /* Look for empty parent directories of deleted targets. */
- empty_dirs = apr_hash_make(scratch_pool);
- non_empty_dirs = apr_hash_make(scratch_pool);
- iterpool = svn_pool_create(scratch_pool);
- for (i = 0; i < deleted_targets->nelts; i++)
- {
- svn_boolean_t parent_empty;
- patch_target_info_t *target_info;
- const char *parent;
-
- svn_pool_clear(iterpool);
-
- if (ctx->cancel_func)
- SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
-
- target_info = APR_ARRAY_IDX(deleted_targets, i, patch_target_info_t *);
-
- parent = svn_dirent_dirname(target_info->local_abspath, iterpool);
+ struct can_delete_baton_t cb;
+ svn_error_t *err;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- if (apr_hash_get(non_empty_dirs, parent, APR_HASH_KEY_STRING))
- continue;
- else if (apr_hash_get(empty_dirs, parent, APR_HASH_KEY_STRING))
- continue;
+ const char *dir_abspath = svn_dirent_dirname(deleted_target, scratch_pool);
- SVN_ERR(check_dir_empty(&parent_empty, parent, ctx->wc_ctx,
- deleted_targets, NULL, iterpool));
- if (parent_empty)
- apr_hash_set(empty_dirs, apr_pstrdup(scratch_pool, parent),
- APR_HASH_KEY_STRING, "");
- else
- apr_hash_set(non_empty_dirs, apr_pstrdup(scratch_pool, parent),
- APR_HASH_KEY_STRING, "");
- }
-
- /* We have nothing to do if there aren't any empty directories. */
- if (apr_hash_count(empty_dirs) == 0)
+ while (svn_dirent_is_child(apply_root, dir_abspath, iterpool))
{
- svn_pool_destroy(iterpool);
- return SVN_NO_ERROR;
- }
-
- /* Determine the minimal set of empty directories we need to delete. */
- do
- {
- apr_hash_t *empty_dirs_copy;
-
svn_pool_clear(iterpool);
- if (ctx->cancel_func)
- SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ cb.local_abspath = dir_abspath;
+ cb.must_keep = FALSE;
+ cb.targets_info = targets_info;
- /* Rebuild the empty dirs list, replacing empty dirs which have
- * an empty parent with their parent. */
- again = FALSE;
- empty_dirs_copy = apr_hash_copy(iterpool, empty_dirs);
- SVN_ERR(svn_hash__clear(empty_dirs, iterpool));
+ err = svn_wc_walk_status(ctx->wc_ctx, dir_abspath, svn_depth_infinity,
+ TRUE, FALSE, FALSE, NULL,
+ can_delete_callback, &cb,
+ ctx->cancel_func, ctx->cancel_baton,
+ iterpool);
- for (hi = apr_hash_first(iterpool, empty_dirs_copy);
- hi;
- hi = apr_hash_next(hi))
+ if (err)
{
- svn_boolean_t parent_empty;
- const char *empty_dir;
- const char *parent;
+ if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
+ return svn_error_trace(err);
- empty_dir = svn__apr_hash_index_key(hi);
- parent = svn_dirent_dirname(empty_dir, iterpool);
+ svn_error_clear(err);
+ }
- if (apr_hash_get(empty_dirs, parent, APR_HASH_KEY_STRING))
- continue;
+ if (cb.must_keep)
+ {
+ break;
+ }
- SVN_ERR(check_dir_empty(&parent_empty, parent, ctx->wc_ctx,
- deleted_targets, empty_dirs_copy,
- iterpool));
- if (parent_empty)
- {
- again = TRUE;
- apr_hash_set(empty_dirs, apr_pstrdup(scratch_pool, parent),
- APR_HASH_KEY_STRING, "");
- }
- else
- apr_hash_set(empty_dirs, apr_pstrdup(scratch_pool, empty_dir),
- APR_HASH_KEY_STRING, "");
+ if (! dry_run)
+ {
+ SVN_ERR(svn_wc_delete4(ctx->wc_ctx, dir_abspath, FALSE, FALSE,
+ ctx->cancel_func, ctx->cancel_baton,
+ NULL, NULL,
+ scratch_pool));
}
- }
- while (again);
- /* Finally, delete empty directories. */
- for (hi = apr_hash_first(scratch_pool, empty_dirs);
- hi;
- hi = apr_hash_next(hi))
- {
- const char *empty_dir;
+ {
+ patch_target_info_t *pti = apr_pcalloc(result_pool, sizeof(*pti));
- svn_pool_clear(iterpool);
+ pti->local_abspath = apr_pstrdup(result_pool, dir_abspath);
+ pti->deleted = TRUE;
+
+ APR_ARRAY_PUSH(targets_info, patch_target_info_t *) = pti;
+ }
- if (ctx->cancel_func)
- SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
- empty_dir = svn__apr_hash_index_key(hi);
- if (! dry_run)
- SVN_ERR(svn_wc_delete4(ctx->wc_ctx, empty_dir, FALSE, FALSE,
- ctx->cancel_func, ctx->cancel_baton,
- NULL, NULL, /* no duplicate notification */
- iterpool));
if (ctx->notify_func2)
{
svn_wc_notify_t *notify;
- notify = svn_wc_create_notify(empty_dir, svn_wc_notify_delete,
- iterpool);
- (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
+ notify = svn_wc_create_notify(dir_abspath, svn_wc_notify_delete,
+ iterpool);
+ notify->kind = svn_node_dir;
+
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
}
+
+ /* And check if we must also delete the parent */
+ dir_abspath = svn_dirent_dirname(dir_abspath, scratch_pool);
}
+
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
-/* Baton for apply_patches(). */
-typedef struct apply_patches_baton_t {
- /* The path to the patch file. */
- const char *patch_abspath;
-
- /* The abspath to the working copy the patch should be applied to. */
- const char *abs_wc_path;
-
- /* Indicates whether we're doing a dry run. */
- svn_boolean_t dry_run;
-
- /* Number of leading components to strip from patch target paths. */
- int strip_count;
-
- /* Whether to apply the patch in reverse. */
- svn_boolean_t reverse;
-
- /* Indicates whether we should ignore whitespace when matching context
- * lines */
- svn_boolean_t ignore_whitespace;
-
- /* As in svn_client_patch(). */
- svn_boolean_t remove_tempfiles;
-
- /* As in svn_client_patch(). */
- svn_client_patch_func_t patch_func;
- void *patch_baton;
-
- /* The client context. */
- svn_client_ctx_t *ctx;
-} apply_patches_baton_t;
-
-/* Callback for use with svn_wc__call_with_write_lock().
- * This function is the main entry point into the patch code. */
+/* This function is the main entry point into the patch code. */
static svn_error_t *
-apply_patches(void *baton,
- apr_pool_t *result_pool,
+apply_patches(/* The path to the patch file. */
+ const char *patch_abspath,
+ /* The abspath to the working copy the patch should be applied to. */
+ const char *abs_wc_path,
+ /* Indicates whether we're doing a dry run. */
+ svn_boolean_t dry_run,
+ /* Number of leading components to strip from patch target paths. */
+ int strip_count,
+ /* Whether to apply the patch in reverse. */
+ svn_boolean_t reverse,
+ /* Whether to ignore whitespace when matching context lines. */
+ svn_boolean_t ignore_whitespace,
+ /* As in svn_client_patch(). */
+ svn_boolean_t remove_tempfiles,
+ /* As in svn_client_patch(). */
+ svn_client_patch_func_t patch_func,
+ void *patch_baton,
+ /* The client context. */
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_patch_t *patch;
apr_pool_t *iterpool;
svn_patch_file_t *patch_file;
apr_array_header_t *targets_info;
- apply_patches_baton_t *btn = baton;
/* Try to open the patch file. */
- SVN_ERR(svn_diff_open_patch_file(&patch_file, btn->patch_abspath,
- scratch_pool));
+ SVN_ERR(svn_diff_open_patch_file(&patch_file, patch_abspath, scratch_pool));
/* Apply patches. */
targets_info = apr_array_make(scratch_pool, 0,
@@ -2868,29 +2977,27 @@ apply_patches(void *baton,
{
svn_pool_clear(iterpool);
- if (btn->ctx->cancel_func)
- SVN_ERR(btn->ctx->cancel_func(btn->ctx->cancel_baton));
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
- btn->reverse, btn->ignore_whitespace,
+ reverse, ignore_whitespace,
iterpool, iterpool));
if (patch)
{
patch_target_t *target;
- SVN_ERR(apply_one_patch(&target, patch, btn->abs_wc_path,
- btn->ctx->wc_ctx, btn->strip_count,
- btn->ignore_whitespace,
- btn->remove_tempfiles,
- btn->patch_func, btn->patch_baton,
- btn->ctx->cancel_func,
- btn->ctx->cancel_baton,
+ 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)
{
/* Save info we'll still need when we're done patching. */
patch_target_info_t *target_info =
- apr_palloc(scratch_pool, sizeof(patch_target_info_t));
+ apr_pcalloc(scratch_pool, sizeof(patch_target_info_t));
target_info->local_abspath = apr_pstrdup(scratch_pool,
target->local_abspath);
target_info->deleted = target->deleted;
@@ -2903,28 +3010,29 @@ apply_patches(void *baton,
if (target->has_text_changes
|| target->added
|| target->deleted)
- SVN_ERR(install_patched_target(target, btn->abs_wc_path,
- btn->ctx, btn->dry_run,
- iterpool));
+ SVN_ERR(install_patched_target(target, abs_wc_path,
+ ctx, dry_run, iterpool));
if (target->has_prop_changes && (!target->deleted))
- SVN_ERR(install_patched_prop_targets(target, btn->ctx,
- btn->dry_run,
- iterpool));
+ SVN_ERR(install_patched_prop_targets(target, ctx,
+ dry_run, iterpool));
- SVN_ERR(write_out_rejected_hunks(target, btn->dry_run,
- iterpool));
+ SVN_ERR(write_out_rejected_hunks(target, dry_run, iterpool));
+ }
+ SVN_ERR(send_patch_notification(target, ctx, iterpool));
+
+ if (target->deleted && !target->skipped)
+ {
+ SVN_ERR(check_ancestor_delete(target_info->local_abspath,
+ targets_info, abs_wc_path,
+ dry_run, ctx,
+ scratch_pool, iterpool));
}
- SVN_ERR(send_patch_notification(target, btn->ctx, iterpool));
}
}
}
while (patch);
- /* Delete directories which are empty after patching, if any. */
- SVN_ERR(delete_empty_dirs(targets_info, btn->ctx, btn->dry_run,
- scratch_pool));
-
SVN_ERR(svn_diff_close_patch_file(patch_file, iterpool));
svn_pool_destroy(iterpool);
@@ -2944,7 +3052,6 @@ svn_client_patch(const char *patch_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
- apply_patches_baton_t baton;
svn_node_kind_t kind;
if (strip_count < 0)
@@ -2981,19 +3088,10 @@ svn_client_patch(const char *patch_abspath,
svn_dirent_local_style(wc_dir_abspath,
scratch_pool));
- baton.patch_abspath = patch_abspath;
- baton.abs_wc_path = wc_dir_abspath;
- baton.dry_run = dry_run;
- baton.ctx = ctx;
- baton.strip_count = strip_count;
- baton.reverse = reverse;
- baton.ignore_whitespace = ignore_whitespace;
- baton.remove_tempfiles = remove_tempfiles;
- baton.patch_func = patch_func;
- baton.patch_baton = patch_baton;
-
- return svn_error_trace(
- svn_wc__call_with_write_lock(apply_patches, &baton,
- ctx->wc_ctx, wc_dir_abspath, FALSE,
- scratch_pool, scratch_pool));
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ apply_patches(patch_abspath, wc_dir_abspath, dry_run, strip_count,
+ reverse, ignore_whitespace, remove_tempfiles,
+ patch_func, patch_baton, ctx, scratch_pool),
+ ctx->wc_ctx, wc_dir_abspath, FALSE /* lock_anchor */, scratch_pool);
+ return SVN_NO_ERROR;
}