You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/09/15 21:32:38 UTC
svn commit: r997472 [12/41] - in /subversion/branches/py-tests-as-modules:
./ build/ build/ac-macros/ build/generator/ build/generator/templates/
contrib/server-side/ notes/ notes/tree-conflicts/ notes/wc-ng/
subversion/bindings/javahl/native/ subversi...
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/prop_commands.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/prop_commands.c Wed Sep 15 19:32:26 2010
@@ -97,7 +97,7 @@ struct propset_walk_baton
void *notify_baton;
};
-/* An node-walk callback for svn_client_propset3.
+/* An node-walk callback for svn_client_propset4.
*
* For LOCAL_ABSPATH, set the property named wb->PROPNAME to the value
* wb->PROPVAL, where "wb" is the WALK_BATON of type "struct
@@ -118,7 +118,8 @@ propset_walk_cb(const char *local_abspat
err = svn_wc_prop_set4(wb->wc_ctx, local_abspath, wb->propname, wb->propval,
wb->force, wb->notify_func, wb->notify_baton, pool);
- if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET)
+ if (err && (err->apr_err == SVN_ERR_ILLEGAL_TARGET
+ || err->apr_err == SVN_ERR_WC_INVALID_SCHEDULE))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
@@ -189,13 +190,14 @@ do_url_propset(const char *propname,
}
static svn_error_t *
-propset_on_url(svn_commit_info_t **commit_info_p,
- const char *propname,
+propset_on_url(const char *propname,
const svn_string_t *propval,
const char *target,
svn_boolean_t skip_checks,
svn_revnum_t base_revision_for_url,
const apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -204,7 +206,7 @@ propset_on_url(svn_commit_info_t **commi
svn_node_kind_t node_kind;
const char *message;
const svn_delta_editor_t *editor;
- void *commit_baton, *edit_baton;
+ void *edit_baton;
apr_hash_t *commit_revprops;
svn_error_t *err;
@@ -215,7 +217,7 @@ propset_on_url(svn_commit_info_t **commi
/* Open an RA session for the URL. Note that we don't have a local
directory, nor a place to put temp files. */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, target,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, target,
NULL, NULL, FALSE, TRUE,
ctx, pool));
@@ -268,10 +270,9 @@ propset_on_url(svn_commit_info_t **commi
message, ctx, pool));
/* Fetch RA commit editor. */
- SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
- svn_client__commit_callback,
+ commit_callback,
commit_baton,
NULL, TRUE, /* No lock tokens */
pool));
@@ -290,10 +291,56 @@ propset_on_url(svn_commit_info_t **commi
return editor->close_edit(edit_baton, pool);
}
+/* Baton for set_props_cb */
+struct set_props_baton
+{
+ svn_client_ctx_t *ctx;
+ const char *local_abspath;
+ svn_depth_t depth;
+ svn_node_kind_t kind;
+ const char *propname;
+ const svn_string_t *propval;
+ svn_boolean_t skip_checks;
+ apr_hash_t *changelist_hash;
+};
+
+/* Working copy lock callback for svn_client_propset4 */
+static svn_error_t *
+set_props_cb(void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct set_props_baton *bt = baton;
+
+ if (bt->depth >= svn_depth_files && bt->kind == svn_node_dir)
+ {
+ struct propset_walk_baton wb;
+
+ wb.wc_ctx = bt->ctx->wc_ctx;
+ wb.propname = bt->propname;
+ wb.propval = bt->propval;
+ wb.force = bt->skip_checks;
+ wb.changelist_hash = bt->changelist_hash;
+ wb.notify_func = bt->ctx->notify_func2;
+ wb.notify_baton = bt->ctx->notify_baton2;
+ SVN_ERR(svn_wc__node_walk_children(bt->ctx->wc_ctx, bt->local_abspath,
+ FALSE, propset_walk_cb, &wb,
+ bt->depth, bt->ctx->cancel_func,
+ bt->ctx->cancel_baton, scratch_pool));
+ }
+ else if (svn_wc__changelist_match(bt->ctx->wc_ctx, bt->local_abspath,
+ bt->changelist_hash, scratch_pool))
+ {
+ SVN_ERR(svn_wc_prop_set4(bt->ctx->wc_ctx, bt->local_abspath,
+ bt->propname, bt->propval, bt->skip_checks,
+ bt->ctx->notify_func2, bt->ctx->notify_baton2,
+ scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
svn_error_t *
-svn_client_propset3(svn_commit_info_t **commit_info_p,
- const char *propname,
+svn_client_propset4(const char *propname,
const svn_string_t *propval,
const char *target,
svn_depth_t depth,
@@ -301,6 +348,8 @@ svn_client_propset3(svn_commit_info_t **
svn_revnum_t base_revision_for_url,
const apr_array_header_t *changelists,
const apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -351,9 +400,9 @@ svn_client_propset3(svn_commit_info_t **
_("Setting property '%s' on non-local target "
"'%s' is not supported"), propname, target);
- return propset_on_url(commit_info_p, propname, propval, target,
- skip_checks, base_revision_for_url, revprop_table,
- ctx, pool);
+ return propset_on_url(propname, propval, target, skip_checks,
+ base_revision_for_url, revprop_table,
+ commit_callback, commit_baton, ctx, pool);
}
else
{
@@ -361,6 +410,7 @@ svn_client_propset3(svn_commit_info_t **
apr_hash_t *changelist_hash = NULL;
const char *target_abspath;
svn_error_t *err;
+ struct set_props_baton baton;
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, pool));
@@ -383,29 +433,18 @@ svn_client_propset3(svn_commit_info_t **
else
SVN_ERR(err);
- if (depth >= svn_depth_files && kind == svn_node_dir)
- {
- struct propset_walk_baton wb;
-
- wb.wc_ctx = ctx->wc_ctx;
- wb.propname = propname;
- wb.propval = propval;
- wb.force = skip_checks;
- wb.changelist_hash = changelist_hash;
- wb.notify_func = ctx->notify_func2;
- wb.notify_baton = ctx->notify_baton2;
- SVN_ERR(svn_wc__node_walk_children(ctx->wc_ctx, target_abspath,
- FALSE, propset_walk_cb, &wb,
- depth, ctx->cancel_func,
- ctx->cancel_baton, pool));
- }
- else if (svn_wc__changelist_match(ctx->wc_ctx, target_abspath,
- changelist_hash, pool))
- {
- SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, target_abspath, propname,
- propval, skip_checks, ctx->notify_func2,
- ctx->notify_baton2, pool));
- }
+ baton.ctx = ctx;
+ baton.local_abspath = target_abspath;
+ baton.depth = depth;
+ baton.kind = kind;
+ baton.propname = propname;
+ baton.propval = propval;
+ baton.skip_checks = skip_checks;
+ baton.changelist_hash = changelist_hash;
+
+ SVN_ERR(svn_wc__call_with_write_lock(set_props_cb, &baton,
+ ctx->wc_ctx, target_abspath, FALSE,
+ pool, pool));
return SVN_NO_ERROR;
}
@@ -438,7 +477,7 @@ svn_client_revprop_set2(const char *prop
/* Open an RA session for the URL. Note that we don't have a local
directory, nor a place to put temp files. */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL, NULL,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL, NULL,
NULL, FALSE, TRUE, ctx, pool));
/* Resolve the revision into something real, and return that to the
@@ -930,7 +969,7 @@ svn_client_revprop_get(const char *propn
/* Open an RA session for the URL. Note that we don't have a local
directory, nor a place to put temp files. */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL, NULL,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL, NULL,
NULL, FALSE, TRUE, ctx, pool));
/* Resolve the revision into something real, and return that to the
@@ -1274,7 +1313,7 @@ svn_client_revprop_list(apr_hash_t **pro
/* Open an RA session for the URL. Note that we don't have a local
directory, nor a place to put temp files. */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL, NULL,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL, NULL,
NULL, FALSE, TRUE, ctx, pool));
/* Resolve the revision into something real, and return that to the
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/ra.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/ra.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/ra.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/ra.c Wed Sep 15 19:32:26 2010
@@ -280,8 +280,12 @@ get_client_string(void *baton,
return SVN_NO_ERROR;
}
+
+#define SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 3 /* ### TODO: Make configurable. */
+
svn_error_t *
svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
+ const char **corrected_url,
const char *base_url,
const char *base_dir_abspath,
const apr_array_header_t *commit_items,
@@ -332,9 +336,63 @@ svn_client__open_ra_session_internal(svn
SVN_ERR(err);
}
- return svn_error_return(svn_ra_open3(ra_session, base_url, uuid, cbtable, cb,
- ctx->config, pool));
-}
+ /* If the caller allows for auto-following redirections, and the
+ RA->open() call above reveals a CORRECTED_URL, try the new URL.
+ We'll do this in a loop up to some maximum number follow-and-retry
+ attempts. */
+ if (corrected_url)
+ {
+ apr_hash_t *attempted = apr_hash_make(pool);
+ int attempts_left = SVN_CLIENT__MAX_REDIRECT_ATTEMPTS;
+
+ *corrected_url = NULL;
+ while (attempts_left--)
+ {
+ const char *corrected = NULL;
+
+ /* Try to open the RA session. If this is our last attempt,
+ don't accept corrected URLs from the RA provider. */
+ SVN_ERR(svn_ra_open4(ra_session,
+ attempts_left == 0 ? NULL : &corrected,
+ base_url, uuid, cbtable, cb, ctx->config, pool));
+
+ /* No error and no corrected URL? We're done here. */
+ if (! corrected)
+ break;
+
+ /* Notify the user that a redirect is being followed. */
+ if (ctx->notify_func2 != NULL)
+ {
+ svn_wc_notify_t *notify =
+ svn_wc_create_notify_url(corrected,
+ svn_wc_notify_url_redirect, pool);
+ (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ }
+
+ /* Our caller will want to know what our final corrected URL was. */
+ *corrected_url = corrected;
+
+ /* Make sure we've not attempted this URL before. */
+ if (apr_hash_get(attempted, corrected, APR_HASH_KEY_STRING))
+ return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL,
+ _("Redirect cycle detected for URL '%s'"),
+ corrected);
+
+ /* Remember this CORRECTED_URL so we don't wind up in a loop. */
+ apr_hash_set(attempted, corrected, APR_HASH_KEY_STRING, (void *)1);
+ base_url = corrected;
+ }
+ }
+ else
+ {
+ SVN_ERR(svn_ra_open4(ra_session, NULL, base_url,
+ uuid, cbtable, cb, ctx->config, pool));
+ }
+
+ return SVN_NO_ERROR;
+ }
+#undef SVN_CLIENT__MAX_REDIRECT_ATTEMPTS
+
svn_error_t *
svn_client_open_ra_session(svn_ra_session_t **session,
@@ -342,9 +400,10 @@ svn_client_open_ra_session(svn_ra_sessio
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- return svn_error_return(svn_client__open_ra_session_internal(session, url,
- NULL, NULL,
- FALSE, TRUE, ctx, pool));
+ return svn_error_return(
+ svn_client__open_ra_session_internal(session, NULL, url,
+ NULL, NULL, FALSE, TRUE,
+ ctx, pool));
}
@@ -358,7 +417,7 @@ svn_client_uuid_from_url(const char **uu
apr_pool_t *subpool = svn_pool_create(pool);
/* use subpool to create a temporary RA session */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, url,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url,
NULL, /* no base dir */
NULL, FALSE, TRUE,
ctx, subpool));
@@ -416,7 +475,7 @@ svn_client__ra_session_from_path(svn_ra_
svn_opt_revision_t dead_end_rev;
svn_opt_revision_t *ignored_rev, *new_rev;
svn_revnum_t rev;
- const char *ignored_url;
+ const char *ignored_url, *corrected_url;
SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool,
pool));
@@ -431,11 +490,17 @@ svn_client__ra_session_from_path(svn_ra_
TRUE,
pool));
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, initial_url,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
+ initial_url,
base_dir_abspath, NULL,
base_dir_abspath != NULL,
FALSE, ctx, pool));
+ /* If we got a CORRECTED_URL, we'll want to refer to that as the
+ URL-ized form of PATH_OR_URL from now on. */
+ if (corrected_url && svn_path_is_url(path_or_url))
+ path_or_url = corrected_url;
+
dead_end_rev.kind = svn_opt_revision_unspecified;
/* Run the history function to get the object's (possibly
@@ -588,7 +653,8 @@ svn_client__repos_locations(const char *
SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, subpool));
SVN_ERR(svn_wc__node_get_url(&node_url, ctx->wc_ctx,
local_abspath_or_url, pool, subpool));
- SVN_ERR(svn_wc__node_get_copyfrom_info(©from_url, ©from_rev,
+ SVN_ERR(svn_wc__node_get_copyfrom_info(NULL, NULL,
+ ©from_url, ©from_rev,
NULL, ctx->wc_ctx,
local_abspath_or_url,
pool, subpool));
@@ -625,7 +691,7 @@ svn_client__repos_locations(const char *
/* Open a RA session to this URL if we don't have one already. */
if (! ra_session)
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, url, NULL,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url, NULL,
NULL, FALSE, TRUE,
ctx, subpool));
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/relocate.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/relocate.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/relocate.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/relocate.c Wed Sep 15 19:32:26 2010
@@ -66,6 +66,7 @@ validator_func(void *baton,
{
struct validator_baton_t *b = baton;
struct url_uuid_t *url_uuid = NULL;
+ const char *disable_checks;
apr_array_header_t *uuids = b->url_uuids;
int i;
@@ -81,6 +82,16 @@ validator_func(void *baton,
}
}
+ disable_checks = getenv("SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_RELOCATE_VALIDATION");
+ if (disable_checks && (strcmp(disable_checks, "yes") == 0))
+ {
+ /* Lie about URL_UUID's components, claiming they match the
+ expectations of the validation code below. */
+ url_uuid = apr_pcalloc(pool, sizeof(*url_uuid));
+ url_uuid->root = apr_pstrdup(pool, root_url);
+ url_uuid->uuid = apr_pstrdup(pool, uuid);
+ }
+
/* We use an RA session in a subpool to get the UUID of the
repository at the new URL so we can force the RA session to close
by destroying the subpool. */
@@ -88,7 +99,7 @@ validator_func(void *baton,
{
apr_pool_t *sesspool = svn_pool_create(pool);
svn_ra_session_t *ra_session;
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, url, NULL,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url, NULL,
NULL, FALSE, TRUE,
b->ctx, sesspool));
url_uuid = &APR_ARRAY_PUSH(uuids, struct url_uuid_t);
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff.c Wed Sep 15 19:32:26 2010
@@ -89,6 +89,17 @@ struct edit_baton {
svn_wc_notify_func2_t notify_func;
void *notify_baton;
+ /* TRUE if the operation needs to walk deleted dirs on the "old" side.
+ FALSE otherwise. */
+ svn_boolean_t walk_deleted_repos_dirs;
+
+ /* A callback used to see if the client wishes to cancel the running
+ operation. */
+ svn_cancel_func_t cancel_func;
+
+ /* A baton to pass to the cancellation callback. */
+ void *cancel_baton;
+
apr_pool_t *pool;
};
@@ -230,11 +241,10 @@ make_dir_baton(const char *path,
static struct file_baton *
make_file_baton(const char *path,
svn_boolean_t added,
- void *edit_baton,
+ struct edit_baton *edit_baton,
apr_pool_t *pool)
{
struct file_baton *file_baton = apr_pcalloc(pool, sizeof(*file_baton));
- struct edit_baton *eb = edit_baton;
file_baton->edit_baton = edit_baton;
file_baton->added = added;
@@ -242,7 +252,7 @@ make_file_baton(const char *path,
file_baton->skip = FALSE;
file_baton->pool = pool;
file_baton->path = apr_pstrdup(pool, path);
- file_baton->wcpath = svn_dirent_join(eb->target, path, pool);
+ file_baton->wcpath = svn_dirent_join(edit_baton->target, path, pool);
file_baton->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
return file_baton;
@@ -291,9 +301,11 @@ get_file_mime_types(const char **mimetyp
}
-/* Get the repository version of a file. This makes an RA request to
- * retrieve the file contents. A pool cleanup handler is installed to
- * delete this file.
+/* Get revision REVISION of the file described by B from the repository.
+ * Set B->path_start_revision to the path of a new temporary file containing
+ * the file's text. Set B->pristine_props to a new hash containing the
+ * file's properties. Install a pool cleanup handler on B->pool to delete
+ * the file.
*/
static svn_error_t *
get_file_from_ra(struct file_baton *b, svn_revnum_t revision)
@@ -443,6 +455,89 @@ open_root(void *edit_baton,
return SVN_NO_ERROR;
}
+/* Recursively walk tree rooted at DIR (at REVISION) in the repository,
+ * reporting all files as deleted. Part of a workaround for issue 2333.
+ *
+ * DIR is a repository path relative to the URL in RA_SESSION. REVISION
+ * must be a valid revision number, not SVN_INVALID_REVNUM. EB is the
+ * overall crawler editor baton. If CANCEL_FUNC is not NULL, then it
+ * should refer to a cancellation function (along with CANCEL_BATON).
+ */
+/* ### TODO: Handle depth. */
+static svn_error_t *
+diff_deleted_dir(const char *dir,
+ svn_revnum_t revision,
+ svn_ra_session_t *ra_session,
+ struct edit_baton *eb,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool)
+{
+ apr_hash_t *dirents;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ apr_hash_index_t *hi;
+
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ SVN_ERR(svn_ra_get_dir2(ra_session,
+ &dirents,
+ NULL, NULL,
+ dir,
+ revision,
+ SVN_DIRENT_KIND,
+ pool));
+
+ for (hi = apr_hash_first(pool, dirents); hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *path;
+ const char *name = svn__apr_hash_index_key(hi);
+ svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
+
+ svn_pool_clear(iterpool);
+
+ path = svn_relpath_join(dir, name, iterpool);
+
+ if (dirent->kind == svn_node_file)
+ {
+ struct file_baton *b;
+ const char *mimetype1, *mimetype2;
+
+ /* Compare a file being deleted against an empty file */
+ b = make_file_baton(path, FALSE, eb, iterpool);
+ SVN_ERR(get_file_from_ra(b, revision));
+
+ SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
+
+ get_file_mime_types(&mimetype1, &mimetype2, b);
+
+ SVN_ERR(eb->diff_callbacks->file_deleted(
+ NULL, NULL, NULL, b->wcpath,
+ b->path_start_revision,
+ b->path_end_revision,
+ mimetype1, mimetype2,
+ b->pristine_props,
+ b->edit_baton->diff_cmd_baton,
+ pool));
+ }
+
+ if (dirent->kind == svn_node_dir)
+ SVN_ERR(diff_deleted_dir(path,
+ revision,
+ ra_session,
+ eb,
+ cancel_func,
+ cancel_baton,
+ iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
/* An editor function. */
static svn_error_t *
delete_entry(const char *path,
@@ -500,6 +595,20 @@ delete_entry(const char *path,
(local_dir_abspath, &state, &tree_conflicted,
svn_dirent_join(eb->target, path, pool),
eb->diff_cmd_baton, pool));
+
+ if (eb->walk_deleted_repos_dirs)
+ {
+ /* A workaround for issue 2333. The "old" dir will be
+ skipped by the repository report. Crawl it recursively,
+ diffing each file against the empty file. */
+ SVN_ERR(diff_deleted_dir(path,
+ eb->revision,
+ eb->ra_session,
+ eb,
+ eb->cancel_func,
+ eb->cancel_baton,
+ pool));
+ }
break;
}
default:
@@ -1057,6 +1166,27 @@ change_file_prop(void *file_baton,
if (b->skip)
return SVN_NO_ERROR;
+ /* Issue #3657 'phantom svn:eol-style changes cause spurious merge text
+ conflicts'. When communicating with the repository via ra_serf and
+ ra_neon, the change_dir_prop and change_file_prop svn_delta_editor_t
+ callbacks are called (obviously) when a directory or file property has
+ changed between the start and end of the edit. Less obvious however,
+ is that these callbacks may be made describing *all* of the properties
+ on FILE_BATON->PATH when using the DAV providers, not just the change(s).
+ (Specifically ra_neon does this for diff/merge and ra_serf does it
+ for diff/merge/update/switch).
+
+ Normally this is fairly harmless, but if it appears that the
+ svn:eol-style property has changed on a file, then we can get spurious
+ text conflicts (i.e. Issue #3657). To prevent this, we populate
+ FILE_BATON->PRISTINE_PROPS only with actual property changes. */
+ if (value)
+ {
+ const char *current_prop = svn_prop_get_value(b->pristine_props, name);
+ if (current_prop && strcmp(current_prop, value->data) == 0)
+ return SVN_NO_ERROR;
+ }
+
propchange = apr_array_push(b->propchanges);
propchange->name = apr_pstrdup(b->pool, name);
propchange->value = value ? svn_string_dup(value, b->pool) : NULL;
@@ -1192,6 +1322,9 @@ svn_client__get_diff_editor(const char *
eb->pool = subpool;
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
+ eb->walk_deleted_repos_dirs = TRUE;
+ eb->cancel_func = cancel_func;
+ eb->cancel_baton = cancel_baton;
tree_editor->set_target_revision = set_target_revision;
tree_editor->open_root = open_root;
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff_summarize.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff_summarize.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff_summarize.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/repos_diff_summarize.c Wed Sep 15 19:32:26 2010
@@ -23,6 +23,7 @@
*/
+#include "svn_dirent_uri.h"
#include "svn_props.h"
#include "svn_pools.h"
@@ -45,6 +46,18 @@ struct edit_baton {
/* The start revision for the comparison */
svn_revnum_t revision;
+
+ /* TRUE if the operation needs to walk deleted dirs on the "old" side.
+ FALSE otherwise. */
+ svn_boolean_t walk_deleted_repos_dirs;
+
+ /* A callback used to see if the client wishes to cancel the running
+ operation. */
+ svn_cancel_func_t cancel_func;
+
+ /* A baton to pass to the cancellation callback. */
+ void *cancel_baton;
+
};
@@ -97,7 +110,7 @@ create_item_baton(struct edit_baton *edi
/* Make sure that this item baton contains a summarize struct.
* If it doesn't before this call, allocate a new struct in the item's pool,
* initializing the diff kind to normal.
- * All other fields are also initialized from IB to to NULL/invalid values. */
+ * All other fields are also initialized from IB or to NULL/invalid values. */
static void
ensure_summarize(struct item_baton *ib)
{
@@ -129,6 +142,83 @@ open_root(void *edit_baton,
return SVN_NO_ERROR;
}
+/* Recursively walk the tree rooted at DIR (at REVISION) in the
+ * repository, reporting all files as deleted. Part of a workaround
+ * for issue 2333.
+ *
+ * DIR is a repository path relative to the URL in RA_SESSION. REVISION
+ * may be NULL, in which case it defaults to HEAD. EDIT_BATON is the
+ * overall crawler editor baton. If CANCEL_FUNC is not NULL, then it
+ * should refer to a cancellation function (along with CANCEL_BATON).
+ */
+/* ### TODO: Handle depth. */
+static svn_error_t *
+diff_deleted_dir(const char *dir,
+ svn_revnum_t revision,
+ svn_ra_session_t *ra_session,
+ void *edit_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool)
+{
+ struct edit_baton *eb = edit_baton;
+ apr_hash_t *dirents;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ apr_hash_index_t *hi;
+
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ SVN_ERR(svn_ra_get_dir2(ra_session,
+ &dirents,
+ NULL, NULL,
+ dir,
+ revision,
+ SVN_DIRENT_KIND,
+ pool));
+
+ for (hi = apr_hash_first(pool, dirents); hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *path;
+ const char *name = svn__apr_hash_index_key(hi);
+ svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
+ svn_node_kind_t kind;
+ svn_client_diff_summarize_t *sum;
+
+ svn_pool_clear(iterpool);
+
+ path = svn_relpath_join(dir, name, iterpool);
+
+ SVN_ERR(svn_ra_check_path(eb->ra_session,
+ path,
+ eb->revision,
+ &kind,
+ iterpool));
+
+ sum = apr_pcalloc(pool, sizeof(*sum));
+ sum->summarize_kind = svn_client_diff_summarize_kind_deleted;
+ sum->path = path;
+ sum->node_kind = kind;
+
+ SVN_ERR(eb->summarize_func(sum,
+ eb->summarize_func_baton,
+ iterpool));
+
+ if (dirent->kind == svn_node_dir)
+ SVN_ERR(diff_deleted_dir(path,
+ revision,
+ ra_session,
+ eb,
+ cancel_func,
+ cancel_baton,
+ iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
/* An editor function. */
static svn_error_t *
delete_entry(const char *path,
@@ -153,7 +243,18 @@ delete_entry(const char *path,
sum->path = path;
sum->node_kind = kind;
- return eb->summarize_func(sum, eb->summarize_func_baton, pool);
+ SVN_ERR(eb->summarize_func(sum, eb->summarize_func_baton, pool));
+
+ if (kind == svn_node_dir)
+ SVN_ERR(diff_deleted_dir(path,
+ eb->revision,
+ eb->ra_session,
+ eb,
+ eb->cancel_func,
+ eb->cancel_baton,
+ pool));
+
+ return SVN_NO_ERROR;
}
/* An editor function. */
@@ -298,7 +399,9 @@ change_prop(void *entry_baton,
if (svn_property_kind(NULL, name) == svn_prop_regular_kind)
{
ensure_summarize(ib);
- ib->summarize->prop_changed = TRUE;
+
+ if (ib->summarize->summarize_kind != svn_client_diff_summarize_kind_added)
+ ib->summarize->prop_changed = TRUE;
}
return SVN_NO_ERROR;
@@ -309,7 +412,7 @@ svn_error_t *
svn_client__get_diff_summarize_editor(const char *target,
svn_client_diff_summarize_func_t
summarize_func,
- void *item_baton,
+ void *summarize_baton,
svn_ra_session_t *ra_session,
svn_revnum_t revision,
svn_cancel_func_t cancel_func,
@@ -323,9 +426,12 @@ svn_client__get_diff_summarize_editor(co
eb->target = target;
eb->summarize_func = summarize_func;
- eb->summarize_func_baton = item_baton;
+ eb->summarize_func_baton = summarize_baton;
eb->ra_session = ra_session;
eb->revision = revision;
+ eb->walk_deleted_repos_dirs = TRUE;
+ eb->cancel_func = cancel_func;
+ eb->cancel_baton = cancel_baton;
tree_editor->open_root = open_root;
tree_editor->delete_entry = delete_entry;
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/revert.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/revert.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/revert.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/revert.c Wed Sep 15 19:32:26 2010
@@ -151,7 +151,7 @@ svn_client_revert2(const apr_array_heade
baton.changelists = changelists;
baton.ctx = ctx;
err = svn_wc__call_with_write_lock(revert, &baton, ctx->wc_ctx,
- local_abspath, pool, pool);
+ local_abspath, TRUE, pool, pool);
if (err)
goto errorful;
}
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/status.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/status.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/status.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/status.c Wed Sep 15 19:32:26 2010
@@ -121,6 +121,7 @@ typedef struct report_baton_t {
/* The common ancestor URL of all paths included in the report. */
char *ancestor;
void *set_locks_baton;
+ svn_depth_t depth;
svn_client_ctx_t *ctx;
/* Pool to store locks in. */
apr_pool_t *pool;
@@ -161,14 +162,17 @@ reporter_link_path(void *report_baton, c
const char *ancestor;
apr_size_t len;
- ancestor = svn_dirent_get_longest_ancestor(url, rb->ancestor, pool);
+ ancestor = svn_uri_get_longest_ancestor(url, rb->ancestor, pool);
/* If we got a shorter ancestor, truncate our current ancestor.
Note that svn_dirent_get_longest_ancestor will allocate its return
value even if it identical to one of its arguments. */
len = strlen(ancestor);
if (len < strlen(rb->ancestor))
- rb->ancestor[len] = '\0';
+ {
+ rb->ancestor[len] = '\0';
+ rb->depth = svn_depth_infinity;
+ }
return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url,
revision, depth, start_empty,
@@ -188,14 +192,14 @@ reporter_finish_report(void *report_bato
/* Open an RA session to our common ancestor and grab the locks under it.
*/
- SVN_ERR(svn_client__open_ra_session_internal(&ras, rb->ancestor, NULL,
+ SVN_ERR(svn_client__open_ra_session_internal(&ras, NULL, rb->ancestor, NULL,
NULL, FALSE, TRUE,
rb->ctx, subpool));
/* The locks need to live throughout the edit. Note that if the
server doesn't support lock discovery, we'll just not do locky
stuff. */
- err = svn_ra_get_locks(ras, &locks, "", rb->pool);
+ err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool);
if (err && ((err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
|| (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)))
{
@@ -249,6 +253,7 @@ svn_client_status5(svn_revnum_t *result_
svn_boolean_t update,
svn_boolean_t no_ignore,
svn_boolean_t ignore_externals,
+ svn_boolean_t depth_as_sticky,
const apr_array_header_t *changelists,
svn_client_status_func_t status_func,
void *status_baton,
@@ -390,7 +395,7 @@ svn_client_status5(svn_revnum_t *result_
pool, pool));
/* Open a repository session to the URL. */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL,
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL,
dir_abspath,
NULL, FALSE, TRUE,
ctx, pool));
@@ -435,6 +440,7 @@ svn_client_status5(svn_revnum_t *result_
{
svn_revnum_t revnum;
report_baton_t rb;
+ svn_depth_t status_depth;
if (revision->kind == svn_opt_revision_head)
{
@@ -452,11 +458,16 @@ svn_client_status5(svn_revnum_t *result_
pool));
}
+ if (depth_as_sticky)
+ status_depth = depth;
+ else
+ status_depth = svn_depth_unknown; /* Use depth from WC */
+
/* Do the deed. Let the RA layer drive the status editor. */
SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
&rb.wrapped_report_baton,
- target_basename, revnum, depth, editor,
- edit_baton, pool));
+ target_basename, revnum, status_depth,
+ editor, edit_baton, pool));
/* Init the report baton. */
rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */
@@ -464,6 +475,11 @@ svn_client_status5(svn_revnum_t *result_
rb.ctx = ctx;
rb.pool = pool;
+ if (depth == svn_depth_unknown)
+ rb.depth = svn_depth_infinity;
+ else
+ rb.depth = depth;
+
SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
SVN_RA_CAPABILITY_DEPTH, pool));
@@ -605,8 +621,8 @@ create_client_status(svn_client_status_t
(*cst)->switched = status->switched;
if (status->kind == svn_node_dir)
- SVN_ERR(svn_wc__temp_get_wclocked(&(*cst)->locked, wc_ctx, local_abspath,
- scratch_pool));
+ SVN_ERR(svn_wc_locked2(NULL, &(*cst)->locked, wc_ctx, local_abspath,
+ scratch_pool));
(*cst)->copied = status->copied;
(*cst)->revision = status->revision;
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/switch.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/switch.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/switch.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/switch.c Wed Sep 15 19:32:26 2010
@@ -294,8 +294,9 @@ svn_client__switch_internal(svn_revnum_t
/* Rely on svn_wc__acquire_write_lock setting ANCHOR_ABSPATH even
when it returns SVN_ERR_WC_LOCKED */
- err = svn_wc__acquire_write_lock(&anchor_abspath, ctx->wc_ctx,
- local_abspath, pool, pool);
+ err = svn_wc__acquire_write_lock(&anchor_abspath,
+ ctx->wc_ctx, local_abspath, TRUE,
+ pool, pool);
if (err && err->apr_err != SVN_ERR_WC_LOCKED)
return svn_error_return(err);
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/update.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/update.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/update.c Wed Sep 15 19:32:26 2010
@@ -70,7 +70,7 @@ file_fetcher(void *baton,
struct ff_baton *ffb = (struct ff_baton *)baton;
const char *dirpath, *base_name, *session_url, *old_session_url;
- svn_relpath_split(path, &dirpath, &base_name, pool);
+ svn_relpath_split(&dirpath, &base_name, path, pool);
session_url = svn_path_url_add_component2(ffb->repos_root,
dirpath, pool);
@@ -78,8 +78,9 @@ file_fetcher(void *baton,
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ffb->session,
session_url, ffb->pool));
else
- SVN_ERR(svn_client__open_ra_session_internal(&(ffb->session), session_url,
- NULL, NULL, FALSE, TRUE,
+ SVN_ERR(svn_client__open_ra_session_internal(&(ffb->session), NULL,
+ session_url, NULL, NULL,
+ FALSE, TRUE,
ffb->ctx, ffb->pool));
return svn_ra_get_file(ffb->session, base_name, revision, stream,
@@ -107,6 +108,7 @@ update_internal(svn_revnum_t *result_rev
const svn_ra_reporter3_t *reporter;
void *report_baton;
const char *anchor_url;
+ const char *corrected_url;
const char *target;
const char *repos_root;
svn_error_t *err;
@@ -156,9 +158,6 @@ update_internal(svn_revnum_t *result_rev
pool));
/* Target excluded, we are done now */
- SVN_ERR(svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath,
- pool));
-
return SVN_NO_ERROR;
}
@@ -191,9 +190,19 @@ update_internal(svn_revnum_t *result_rev
: NULL;
/* Open an RA session for the URL */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, anchor_url,
- anchor_abspath, NULL, TRUE, TRUE,
- ctx, pool));
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
+ anchor_url,
+ anchor_abspath, NULL, TRUE,
+ TRUE, ctx, pool));
+
+ /* If we got a corrected URL from the RA subsystem, we'll need to
+ relocate our working copy first. */
+ if (corrected_url)
+ {
+ SVN_ERR(svn_client_relocate(anchor_abspath, anchor_url, corrected_url,
+ TRUE, ctx, pool));
+ anchor_url = corrected_url;
+ }
/* ### todo: shouldn't svn_client__get_revision_number be able
to take a URL as easily as a local path? */
@@ -282,8 +291,6 @@ update_internal(svn_revnum_t *result_rev
if (sleep_here)
svn_io_sleep_for_timestamps(local_abspath, pool);
- SVN_ERR(svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool));
-
/* Let everyone know we're finished here. */
if (ctx->notify_func2)
{
@@ -320,31 +327,30 @@ svn_client__update_internal(svn_revnum_t
apr_pool_t *pool)
{
const char *anchor_abspath;
- svn_error_t *err1, *err2;
+ svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (!innerupdate)
- {
- SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath, ctx->wc_ctx,
- local_abspath, pool, pool));
- }
+ SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath,
+ ctx->wc_ctx, local_abspath, TRUE,
+ pool, pool));
else
- {
- SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx,
- local_abspath, pool, pool));
- anchor_abspath = local_abspath;
- }
+ SVN_ERR(svn_wc__acquire_write_lock(&anchor_abspath,
+ ctx->wc_ctx, local_abspath, FALSE,
+ pool, pool));
- err1 = update_internal(result_rev, local_abspath, anchor_abspath,
+ err = update_internal(result_rev, local_abspath, anchor_abspath,
revision, depth, depth_is_sticky,
ignore_externals, allow_unver_obstructions,
timestamp_sleep, send_copyfrom_args,
innerupdate, ctx, pool);
- err2 = svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool);
+ err = svn_error_compose_create(
+ err,
+ svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool));
- return svn_error_compose_create(err1, err2);
+ return svn_error_return(err);
}
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_client/url.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_client/url.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_client/url.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_client/url.c Wed Sep 15 19:32:26 2010
@@ -116,9 +116,10 @@ svn_client__derive_location(const char *
{
if (ra_session == NULL)
{
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, *url, NULL,
- NULL, FALSE, TRUE, ctx,
- scratch_pool));
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL,
+ *url, NULL, NULL,
+ FALSE, TRUE,
+ ctx, scratch_pool));
}
SVN_ERR(svn_client__get_revision_number(peg_revnum, NULL, ctx->wc_ctx,
NULL, ra_session, peg_revision,
@@ -146,7 +147,8 @@ svn_client__entry_location(const char **
|| peg_rev_kind == svn_opt_revision_head)
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
- SVN_ERR(svn_wc__node_get_copyfrom_info(©from_url, ©from_rev,
+ SVN_ERR(svn_wc__node_get_copyfrom_info(NULL, NULL,
+ ©from_url, ©from_rev,
NULL, wc_ctx, local_abspath,
result_pool, scratch_pool));
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_delta/path_driver.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_delta/path_driver.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_delta/path_driver.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_delta/path_driver.c Wed Sep 15 19:32:26 2010
@@ -215,9 +215,9 @@ svn_delta_path_driver(const svn_delta_ed
/*** Step C - Open any directories between the common ancestor
and the parent of the current path. ***/
if (*path == '/')
- svn_uri_split(path, &pdir, &bname, iterpool);
+ svn_uri_split(&pdir, &bname, path, iterpool);
else
- svn_relpath_split(path, &pdir, &bname, iterpool);
+ svn_relpath_split(&pdir, &bname, path, iterpool);
if (strlen(pdir) > common_len)
{
const char *piece = pdir + common_len + 1;
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_delta/svndiff.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_delta/svndiff.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_delta/svndiff.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_delta/svndiff.c Wed Sep 15 19:32:26 2010
@@ -31,10 +31,12 @@
#include "svn_private_config.h"
#include <zlib.h>
-/* This macro is taken from zlib, and was originally the function
- compressBound. It shouldn't ever change, but once every millenium,
- it may be useful for someone to make sure. */
+/* The zlib compressBound function was not exported until 1.2.0. */
+#if ZLIB_VERNUM >= 0x1200
+#define svnCompressBound(LEN) compressBound(LEN)
+#else
#define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
+#endif
/* For svndiff1, address/instruction/new data under this size will not
be compressed using zlib as a secondary compressor. */
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_delta/text_delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_delta/text_delta.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_delta/text_delta.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_delta/text_delta.c Wed Sep 15 19:32:26 2010
@@ -564,6 +564,73 @@ size_buffer(char **buf, apr_size_t *buf_
return SVN_NO_ERROR;
}
+/* Copy LEN bytes from SOURCE to TARGET, optimizing for the case where LEN
+ * is often very small. Return a pointer to the first byte after the copied
+ * target range, unlike standard memcpy(), as a potential further
+ * optimization for the caller.
+ *
+ * memcpy() is hard to tune for a wide range of buffer lengths. Therefore,
+ * it is often tuned for high throughput on large buffers and relatively
+ * low latency for mid-sized buffers (tens of bytes). However, the overhead
+ * for very small buffers (<10 bytes) is still high. Even passing the
+ * parameters, for instance, may take as long as copying 3 bytes.
+ *
+ * Because short copy sequences seem to be a common case, at least in
+ * "format 2" FSFS repositories, we copy them directly. Larger buffer sizes
+ * aren't hurt measurably by the exta 'if' clause. */
+static APR_INLINE char *
+fast_memcpy(char *target, const char *source, apr_size_t len)
+{
+ if (len > 7)
+ {
+ memcpy(target, source, len);
+ target += len;
+ }
+ else
+ {
+ /* memcpy is not exactly fast for small block sizes.
+ * Since they are common, let's run optimized code for them. */
+ const char *end = source + len;
+ for (; source != end; source++)
+ *(target++) = *source;
+ }
+
+ return target;
+}
+
+/* Copy LEN bytes from SOURCE to TARGET. Unlike memmove() or memcpy(),
+ * create repeating patterns if the source and target ranges overlap.
+ * Return a pointer to the first byte after the copied target range. */
+static APR_INLINE char *
+patterning_copy(char *target, const char *source, apr_size_t len)
+{
+ const char *end = source + len;
+
+ /* On the majority of machines (x86 / x64), unaligned access is much
+ * cheaper than repeated aligned access. Therefore, use chunky copies on
+ * these machines when feasible.
+ * For those machines, GCC, ICC and MSC will define one of the following: */
+#if defined(_M_IX86) || defined(_M_X64) || defined(i386) || defined(__x86_64)
+
+ if (end + sizeof(apr_uint32_t) <= target)
+ {
+ /* Source and target are at least 4 bytes apart, so we can copy in
+ * 4-byte chunks. */
+ for (; source + sizeof(apr_uint32_t) <= end;
+ source += sizeof(apr_uint32_t),
+ target += sizeof(apr_uint32_t))
+ *(apr_uint32_t *)(target) = *(apr_uint32_t *)(source);
+ }
+
+#endif
+
+ /* fall through to byte-wise copy (either for the below-chunk-size tail
+ * or the whole copy) */
+ for (; source != end; source++)
+ *(target++) = *source;
+
+ return target;
+}
void
svn_txdelta_apply_instructions(svn_txdelta_window_t *window,
@@ -571,7 +638,7 @@ svn_txdelta_apply_instructions(svn_txdel
apr_size_t *tlen)
{
const svn_txdelta_op_t *op;
- apr_size_t i, j, tpos = 0;
+ apr_size_t tpos = 0;
for (op = window->ops; op < window->ops + window->num_ops; op++)
{
@@ -586,25 +653,26 @@ svn_txdelta_apply_instructions(svn_txdel
case svn_txdelta_source:
/* Copy from source area. */
assert(op->offset + op->length <= window->sview_len);
- memcpy(tbuf + tpos, sbuf + op->offset, buf_len);
+ fast_memcpy(tbuf + tpos, sbuf + op->offset, buf_len);
break;
case svn_txdelta_target:
- /* Copy from target area. Don't use memcpy() since its
- semantics aren't guaranteed for overlapping memory areas,
- and target copies are allowed to overlap to generate
- repeated data. */
+ /* Copy from target area. We can't use memcpy() or the like
+ * since we need a specific semantics for overlapping copies:
+ * they must result in repeating patterns.
+ * Note that most copies won't have overlapping source and
+ * target ranges (they are just a result of self-compressed
+ * data) but a small percentage will. */
assert(op->offset < tpos);
- for (i = op->offset, j = tpos; i < op->offset + buf_len; i++)
- tbuf[j++] = tbuf[i];
+ patterning_copy(tbuf + tpos, tbuf + op->offset, buf_len);
break;
case svn_txdelta_new:
/* Copy from window new area. */
assert(op->offset + op->length <= window->new_data->len);
- memcpy(tbuf + tpos,
- window->new_data->data + op->offset,
- buf_len);
+ fast_memcpy(tbuf + tpos,
+ window->new_data->data + op->offset,
+ buf_len);
break;
default:
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_file.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_file.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_file.c Wed Sep 15 19:32:26 2010
@@ -860,7 +860,7 @@ output_unified_line(svn_diff__file_outpu
{
if (type != svn_diff__file_output_unified_skip)
{
- svn_stringbuf_appendbytes(baton->hunk, curp, 1);
+ svn_stringbuf_appendbyte(baton->hunk, *curp);
}
/* We don't append the LF to extra_context, since it would
* just be stripped anyway. */
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_memory.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_memory.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_memory.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_diff/diff_memory.c Wed Sep 15 19:32:26 2010
@@ -353,6 +353,10 @@ typedef struct unified_output_baton_t
apr_off_t hunk_length[2]; /* 0 == original; 1 == modified */
apr_off_t hunk_start[2]; /* 0 == original; 1 == modified */
+ /* The delimiters of the hunk header, '@@' for text hunks and '##' for
+ * property hunks. */
+ const char *hunk_delimiter;
+
/* Pool for allocation of temporary memory in the callbacks
Should be cleared on entry of each iteration of a callback */
apr_pool_t *pool;
@@ -515,8 +519,13 @@ output_unified_diff_modified(void *baton
targ_orig = (targ_orig < 0) ? 0 : targ_orig;
targ_mod = modified_start;
+ /* If the changed ranges are far enough apart (no overlapping or
+ * connecting context), flush the current hunk. */
if (btn->next_token + SVN_DIFF__UNIFIED_CONTEXT_SIZE < targ_orig)
- SVN_ERR(output_unified_flush_hunk(btn, NULL));
+ SVN_ERR(output_unified_flush_hunk(btn, btn->hunk_delimiter));
+ /* Adjust offset if it's not the first hunk. */
+ else if (btn->hunk_length[0] != 0)
+ targ_orig = btn->next_token;
if (btn->hunk_length[0] == 0
&& btn->hunk_length[1] == 0)
@@ -569,6 +578,7 @@ svn_diff_mem_string_output_unified2(svn_
baton.pool = svn_pool_create(pool);
baton.header_encoding = header_encoding;
baton.hunk = svn_stringbuf_create("", pool);
+ baton.hunk_delimiter = hunk_delimiter;
SVN_ERR(svn_utf_cstring_from_utf8_ex2
(&(baton.prefix_str[unified_output_context]), " ",