You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/02/04 21:48:13 UTC
svn commit: r1442344 [9/39] - in /subversion/branches/fsfs-format7: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/
contrib/server-side/fsfsfixer/fixer/ contrib/server-side/svncutter/ doc/ ...
Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.c Mon Feb 4 20:48:05 2013
@@ -911,18 +911,15 @@ elide_mergeinfo(svn_mergeinfo_t parent_m
svn_error_t *
-svn_client__elide_mergeinfo(const char *target_wcpath,
- const char *wc_elision_limit_path,
+svn_client__elide_mergeinfo(const char *target_abspath,
+ const char *wc_elision_limit_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- const char *target_abspath;
- const char *limit_abspath = NULL;
+ const char *limit_abspath = wc_elision_limit_abspath;
- SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath, pool));
- if (wc_elision_limit_path)
- SVN_ERR(svn_dirent_get_absolute(&limit_abspath, wc_elision_limit_path,
- pool));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
+ SVN_ERR_ASSERT(!wc_elision_limit_abspath || svn_dirent_is_absolute(wc_elision_limit_abspath));
/* Check for first easy out: We are already at the limit path. */
if (!limit_abspath
@@ -985,12 +982,12 @@ svn_client__elide_mergeinfo(const char *
/* If TARGET_WCPATH inherited no mergeinfo from the WC and we are
not limiting our search to the working copy then check if it
inherits any from the repos. */
- if (!mergeinfo && !wc_elision_limit_path)
+ if (!mergeinfo && !wc_elision_limit_abspath)
{
err = svn_client__get_wc_or_repos_mergeinfo(
&mergeinfo, NULL, NULL, TRUE,
svn_mergeinfo_nearest_ancestor,
- NULL, target_wcpath, ctx, pool);
+ NULL, target_abspath, ctx, pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -1009,7 +1006,7 @@ svn_client__elide_mergeinfo(const char *
/* If there is nowhere to elide TARGET_WCPATH's mergeinfo to and
the elision is limited, then we are done.*/
- if (!mergeinfo && wc_elision_limit_path)
+ if (!mergeinfo && wc_elision_limit_abspath)
return SVN_NO_ERROR;
SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_abspath,
Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/mergeinfo.h Mon Feb 4 20:48:05 2013
@@ -292,9 +292,10 @@ svn_client__get_wc_or_repos_mergeinfo_ca
/* Set *MERGEINFO_P to a mergeinfo constructed solely from the
natural history of PATHREV.
- If RANGE_YOUNGEST and RANGE_OLDEST are valid, use them to bound the
- revision ranges of returned mergeinfo. They are governed by the same
- rules as the PEG_REVISION, START_REV, and END_REV parameters of
+ If RANGE_YOUNGEST and RANGE_OLDEST are valid, use them as inclusive
+ bounds on the revision ranges of returned mergeinfo. PATHREV->rev,
+ RANGE_YOUNGEST and RANGE_OLDEST are governed by the same rules as the
+ PEG_REVISION, START_REV, and END_REV parameters (respectively) of
svn_ra_get_location_segments().
If HAS_REV_ZERO_HISTORY is not NULL, then set *HAS_REV_ZERO_HISTORY to
@@ -345,42 +346,42 @@ svn_client__record_wc_mergeinfo_catalog(
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
-/* Elide any svn:mergeinfo set on TARGET_WCPATH to its nearest working
+/* Elide any svn:mergeinfo set on TARGET_ABSPATH to its nearest working
copy (or possibly repository) ancestor with equivalent mergeinfo.
- If WC_ELISION_LIMIT_PATH is NULL check up to the root of the
+ If WC_ELISION_LIMIT_ABSPATH is NULL check up to the root of the
working copy or the nearest switched parent for an elision
destination, if none is found check the repository, otherwise check
- as far as WC_ELISION_LIMIT_PATH within the working copy.
- TARGET_WCPATH and WC_ELISION_LIMIT_PATH, if it exists, must both be
+ as far as WC_ELISION_LIMIT_ABSPATH within the working copy.
+ TARGET_WCPATH and WC_ELISION_LIMIT_ABSPATH, if it exists, must both be
absolute or relative to the working directory.
Elision occurs if:
- A) TARGET_WCPATH has empty mergeinfo and no parent path with
+ A) TARGET_ABSPATH has empty mergeinfo and no parent path with
explicit mergeinfo can be found in either the WC or the
repository (WC_ELISION_LIMIT_PATH must be NULL for this to
occur).
- B) TARGET_WCPATH has empty mergeinfo and its nearest parent also
+ B) TARGET_ABSPATH has empty mergeinfo and its nearest parent also
has empty mergeinfo.
- C) TARGET_WCPATH has the same mergeinfo as its nearest parent
+ C) TARGET_ABSPATH has the same mergeinfo as its nearest parent
when that parent's mergeinfo is adjusted for the path
difference between the two, e.g.:
- TARGET_WCPATH = A_COPY/D/H
- TARGET_WCPATH's mergeinfo = '/A/D/H:3'
- TARGET_WCPATH nearest parent = A_COPY
- Parent's mergeinfo = '/A:3'
- Path differece = 'D/H'
- Parent's adjusted mergeinfo = '/A/D/H:3'
+ TARGET_ABSPATH = A_COPY/D/H
+ TARGET_ABSPATH's mergeinfo = '/A/D/H:3'
+ TARGET_ABSPATH nearest parent = A_COPY
+ Parent's mergeinfo = '/A:3'
+ Path difference = 'D/H'
+ Parent's adjusted mergeinfo = '/A/D/H:3'
If Elision occurs remove the svn:mergeinfo property from
- TARGET_WCPATH. */
+ TARGET_ABSPATH. */
svn_error_t *
-svn_client__elide_mergeinfo(const char *target_wcpath,
- const char *wc_elision_limit_path,
+svn_client__elide_mergeinfo(const char *target_abspath,
+ const char *wc_elision_limit_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *pool);
Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/patch.c Mon Feb 4 20:48:05 2013
@@ -175,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.
@@ -437,48 +440,37 @@ resolve_target_path(patch_target_t *targ
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));
if (target->locally_deleted)
{
@@ -498,8 +490,10 @@ resolve_target_path(patch_target_t *targ
/* As far as we are concerned this target is not locally deleted. */
target->locally_deleted = FALSE;
- SVN_ERR(svn_io_check_path(target->local_abspath,
- &target->kind_on_disk, scratch_pool));
+ 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)
{
@@ -523,6 +517,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,
@@ -536,17 +532,15 @@ readline_prop(void *baton, svn_stringbuf
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 ((apr_uint64_t)b->offset >= (apr_uint64_t)b->value->len)
{
*eol_str = NULL;
*eof = TRUE;
- *line = str;
+ *line = NULL;
return SVN_NO_ERROR;
}
@@ -578,7 +572,11 @@ readline_prop(void *baton, svn_stringbuf
}
}
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;
@@ -652,7 +650,7 @@ init_prop_target(prop_patch_target_t **p
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;
@@ -675,7 +673,7 @@ init_prop_target(prop_patch_target_t **p
/* 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;
@@ -696,6 +694,8 @@ init_prop_target(prop_patch_target_t **p
* 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.
@@ -708,13 +708,11 @@ readline_file(void *baton, svn_stringbuf
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;
@@ -761,7 +759,11 @@ readline_file(void *baton, svn_stringbuf
}
}
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;
@@ -807,6 +809,118 @@ write_file(void *baton, const char *buf,
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
@@ -912,10 +1026,24 @@ init_patch_target(patch_target_t **patch
/* 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,
@@ -950,17 +1078,34 @@ init_patch_target(patch_target_t **patch
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));
+
+ content->write_baton = (void*)target->patched_path;
- /* Put the write callback in place. */
- content->write = write_file;
- content->write_baton = target->patched_file;
+ content->write = write_symlink;
+ }
/* Open a temporary file to write rejected hunks to. */
SVN_ERR(svn_io_open_unique_file3(&target->reject_file,
@@ -1009,7 +1154,9 @@ init_patch_target(patch_target_t **patch
}
/* 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 *
@@ -1044,14 +1191,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;
}
@@ -1383,26 +1538,45 @@ get_hunk_info(hunk_info_t **hi, patch_ta
* 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)
+ {
+ matched_line = 1; /* Matched an on-disk empty file */
+ }
+ else
{
- svn_boolean_t file_matches;
+ 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;
@@ -1518,7 +1692,7 @@ get_hunk_info(hunk_info_t **hi, patch_ta
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);
@@ -2094,13 +2268,17 @@ apply_one_patch(patch_target_t **patch_t
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)
{
@@ -2111,10 +2289,10 @@ apply_one_patch(patch_target_t **patch_t
* 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;
@@ -2288,9 +2466,10 @@ create_missing_parents(patch_target_t *t
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));
}
}
}
@@ -2402,8 +2581,9 @@ install_patched_target(patch_target_t *t
* 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. */
@@ -2494,10 +2674,11 @@ install_patched_prop_targets(patch_targe
{
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;
}
@@ -2571,265 +2752,129 @@ install_patched_prop_targets(patch_targe
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;
+ struct can_delete_baton_t *cb = baton;
+ int i;
- if (status->node_status != svn_wc_status_none
- && status->node_status != svn_wc_status_deleted
- && strcmp(abspath, btn->parent_path))
+ switch(status->node_status)
{
- APR_ARRAY_PUSH(btn->existing_targets,
- const char *) = apr_pstrdup(btn->result_pool,
- abspath);
- }
+ case svn_wc_status_none:
+ case svn_wc_status_deleted:
+ return SVN_NO_ERROR;
- return SVN_NO_ERROR;
-}
+ default:
+ if (! strcmp(cb->local_abspath, abspath))
+ return SVN_NO_ERROR; /* Only interested in descendants */
-/* 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;
- int i;
+ 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 *);
- /* 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)
- {
- *empty = FALSE;
- return SVN_NO_ERROR;
- }
+ if (! strcmp(target_info->local_abspath, abspath))
+ {
+ if (target_info->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;
-
- deleted = FALSE;
- found = APR_ARRAY_IDX(btn.existing_targets, i, const char *);
-
- for (j = 0; j < deleted_targets->nelts; j++)
- {
- patch_target_info_t *target_info;
-
- 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;
+ cb->must_keep = TRUE;
- abspath = svn__apr_hash_index_key(hi);
- if (! svn_path_compare_paths(found, abspath))
- {
- deleted = TRUE;
- break;
- }
- }
- }
- if (! deleted)
- {
- *empty = FALSE;
- break;
- }
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
-
- return SVN_NO_ERROR;
}
-/* 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);
-
- 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;
+ struct can_delete_baton_t cb;
+ svn_error_t *err;
+ apr_pool_t *iterpool = svn_pool_create(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, "");
- }
+ const char *dir_abspath = svn_dirent_dirname(deleted_target, scratch_pool);
- /* 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));
-
- /* 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));
-
- for (hi = apr_hash_first(iterpool, empty_dirs_copy);
- hi;
- hi = apr_hash_next(hi))
- {
- svn_boolean_t parent_empty;
- const char *empty_dir;
- const char *parent;
+ cb.local_abspath = dir_abspath;
+ cb.must_keep = FALSE;
+ cb.targets_info = targets_info;
+
+ 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);
+
+ if (err)
+ {
+ 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;
@@ -2894,7 +2939,7 @@ apply_patches(/* The path to the patch f
{
/* 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;
@@ -2917,14 +2962,19 @@ apply_patches(/* The path to the patch f
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));
+ }
}
}
}
while (patch);
- /* Delete directories which are empty after patching, if any. */
- SVN_ERR(delete_empty_dirs(targets_info, ctx, dry_run, scratch_pool));
-
SVN_ERR(svn_diff_close_patch_file(patch_file, iterpool));
svn_pool_destroy(iterpool);
Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/prop_commands.c Mon Feb 4 20:48:05 2013
@@ -48,29 +48,6 @@
/*** Code. ***/
-/* Check whether NAME is a revision property name.
- *
- * Return TRUE if it is.
- * Return FALSE if it is not.
- */
-static svn_boolean_t
-is_revision_prop_name(const char *name)
-{
- apr_size_t i;
- static const char *revision_props[] =
- {
- SVN_PROP_REVISION_ALL_PROPS
- };
-
- for (i = 0; i < sizeof(revision_props) / sizeof(revision_props[0]); i++)
- {
- if (strcmp(name, revision_props[i]) == 0)
- return TRUE;
- }
- return FALSE;
-}
-
-
/* Return an SVN_ERR_CLIENT_PROPERTY_NAME error if NAME is a wcprop,
else return SVN_NO_ERROR. */
static svn_error_t *
@@ -285,7 +262,7 @@ static svn_error_t *
check_prop_name(const char *propname,
const svn_string_t *propval)
{
- if (is_revision_prop_name(propname))
+ if (svn_prop_is_known_svn_rev_prop(propname))
return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
_("Revision property '%s' not allowed "
"in this context"), propname);
@@ -659,11 +636,19 @@ remote_propget(apr_hash_t *props,
if (inherited_props)
{
+ const char *repos_root_url;
+
/* We will filter out all but PROPNAME later, making a final copy
- in RESULT_POOL, so pass SCRATCH_POOL for both pools. */
+ in RESULT_POOL, so pass SCRATCH_POOL for all pools. */
SVN_ERR(svn_ra_get_inherited_props(ra_session, inherited_props,
target_relative, revnum,
scratch_pool, scratch_pool));
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+ scratch_pool));
+ SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
+ repos_root_url,
+ scratch_pool,
+ scratch_pool));
}
/* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */
@@ -913,9 +898,20 @@ svn_client_propget5(apr_hash_t **props,
return svn_error_trace(err);
if (inherited_props && local_iprops)
- SVN_ERR(svn_wc__get_iprops(inherited_props, ctx->wc_ctx,
- target, propname,
- result_pool, scratch_pool));
+ {
+ const char *repos_root_url;
+
+ SVN_ERR(svn_wc__get_iprops(inherited_props, ctx->wc_ctx,
+ target, propname,
+ result_pool, scratch_pool));
+ SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL,
+ target, ctx, scratch_pool,
+ scratch_pool));
+ SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
+ repos_root_url,
+ result_pool,
+ scratch_pool));
+ }
SVN_ERR(get_prop_from_wc(props, propname, target,
pristine, kind,
@@ -1147,11 +1143,23 @@ remote_proplist(const char *target_prefi
}
if (get_target_inherited_props)
- SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
- target_relative, revnum,
- result_pool, scratch_pool));
+ {
+ const char *repos_root_url;
+
+ SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
+ target_relative, revnum,
+ result_pool, scratch_pool));
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+ scratch_pool));
+ SVN_ERR(svn_client__iprop_relpaths_to_urls(inherited_props,
+ repos_root_url,
+ result_pool,
+ scratch_pool));
+ }
else
- inherited_props = NULL;
+ {
+ inherited_props = NULL;
+ }
if (get_explicit_props)
{
@@ -1431,9 +1439,15 @@ get_local_props(const char *path_or_url,
if (get_target_inherited_props)
{
apr_array_header_t *iprops;
+ const char *repos_root_url;
SVN_ERR(svn_wc__get_iprops(&iprops, ctx->wc_ctx, local_abspath,
NULL, scratch_pool, scratch_pool));
+ SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL, local_abspath,
+ ctx, scratch_pool, scratch_pool));
+ SVN_ERR(svn_client__iprop_relpaths_to_urls(iprops, repos_root_url,
+ scratch_pool,
+ scratch_pool));
SVN_ERR(call_receiver(path_or_url, NULL, iprops, receiver,
receiver_baton, scratch_pool));
}