Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id CE001E332 for ; Tue, 27 Nov 2012 22:14:33 +0000 (UTC) Received: (qmail 74640 invoked by uid 500); 27 Nov 2012 22:14:33 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 74572 invoked by uid 500); 27 Nov 2012 22:14:33 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 74508 invoked by uid 99); 27 Nov 2012 22:14:33 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 27 Nov 2012 22:14:33 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 27 Nov 2012 22:14:22 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 0B65B23889BF; Tue, 27 Nov 2012 22:14:00 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1414433 [3/4] - in /subversion/branches/issue-4194-dev: ./ build/ac-macros/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_delta/ subversion/libsvn_diff/ subversion/libsvn_fs_fs/ subversion/libs... Date: Tue, 27 Nov 2012 22:13:38 -0000 To: commits@subversion.apache.org From: cmpilato@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20121127221400.0B65B23889BF@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c (original) +++ subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c Tue Nov 27 22:13:24 2012 @@ -32,6 +32,7 @@ #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_hash.h" +#include "svn_sorts.h" #include "svn_wc.h" #include "svn_checksum.h" #include "svn_pools.h" @@ -381,7 +382,18 @@ wclock_owns_lock(svn_boolean_t *own_lock svn_boolean_t exact, apr_pool_t *scratch_pool); +/* Baton for db_is_switched */ +struct db_is_switched_baton_t +{ + svn_boolean_t *is_switched; + svn_kind_t *kind; +}; +static svn_error_t * +db_is_switched(void *baton, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool); /* Return the absolute path, in local path style, of LOCAL_RELPATH in WCROOT. */ @@ -1778,6 +1790,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d svn_boolean_t delete_working, svn_boolean_t update_actual_props, apr_hash_t *new_actual_props, + apr_array_header_t *new_iprops, svn_boolean_t keep_recorded_info, svn_boolean_t insert_base_deleted, const svn_skel_t *conflict, @@ -1821,6 +1834,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d ibb.checksum = checksum; ibb.dav_cache = dav_cache; + ibb.iprops = new_iprops; if (update_actual_props) { @@ -1863,6 +1877,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t svn_boolean_t delete_working, svn_boolean_t update_actual_props, apr_hash_t *new_actual_props, + apr_array_header_t *new_iprops, svn_boolean_t keep_recorded_info, svn_boolean_t insert_base_deleted, const svn_skel_t *conflict, @@ -1905,6 +1920,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t ibb.target = target; ibb.dav_cache = dav_cache; + ibb.iprops = new_iprops; if (update_actual_props) { @@ -7663,7 +7679,7 @@ read_info(svn_wc__db_status_t *status, if (op_depth != 0) *lock = NULL; else - *lock = lock_from_columns(stmt_info, 16, 17, 18, 19, result_pool); + *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool); } if (have_work) @@ -9014,6 +9030,45 @@ svn_wc__db_prop_retrieve_recursive(apr_h return svn_error_trace(svn_sqlite__reset(stmt)); } +/* Baton for db_read_cached_iprops */ +struct read_cached_iprops_baton_t +{ + apr_array_header_t *iprops; + apr_pool_t *result_pool; +}; + +/* Implements svn_wc__db_txn_callback_t for svn_wc__db_read_cached_iprops */ +static svn_error_t * +db_read_cached_iprops(void *baton, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + struct read_cached_iprops_baton_t *rib = baton; + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (!have_row) + { + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, + svn_sqlite__reset(stmt), + _("The node '%s' was not found."), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + } + + SVN_ERR(svn_sqlite__column_iprops(&rib->iprops, stmt, 0, + rib->result_pool, scratch_pool)); + + SVN_ERR(svn_sqlite__reset(stmt)); + + return SVN_NO_ERROR; +} + svn_error_t * svn_wc__db_read_cached_iprops(apr_array_header_t **iprops, svn_wc__db_t *db, @@ -9023,91 +9078,269 @@ svn_wc__db_read_cached_iprops(apr_array_ { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - const char *repos_root_url; - svn_revnum_t revision; - int op_depth; - const char *repos_relpath; + struct read_cached_iprops_baton_t rcib; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - SVN_ERR(svn_wc__db_read_info(NULL, NULL, - &revision, &repos_relpath, &repos_root_url, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, db, local_abspath, result_pool, - scratch_pool)); + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, + db, local_abspath, + scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + rcib.result_pool = result_pool; - if (repos_relpath && repos_relpath[0] == '\0') + /* Don't use with_txn yet, as we perform just a single transaction */ + SVN_ERR(db_read_cached_iprops(&rcib, wcroot, local_relpath, scratch_pool)); + + if (rcib.iprops) + *iprops = rcib.iprops; + else { - /* LOCAL_ABSPATH reflects the root of the repository, so there is - no parents to inherit from. */ *iprops = apr_array_make(result_pool, 0, sizeof(svn_prop_inherited_item_t *)); } - else + + return SVN_NO_ERROR; +} + +/* Remove all prop name value pairs from PROP_HASH where the property + name is not PROPNAME. */ +static void +filter_unwanted_props(apr_hash_t *prop_hash, + const char * propname, + apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, prop_hash); + hi; + hi = apr_hash_next(hi)) { - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, local_abspath, - scratch_pool, - scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_IPROPS)); - SVN_ERR(op_depth_of(&op_depth, wcroot, local_relpath)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + const char *ipropname = svn__apr_hash_index_key(hi); - if (!have_row) + if (strcmp(ipropname, propname) != 0) + apr_hash_set(prop_hash, ipropname, APR_HASH_KEY_STRING, NULL); + } + return; +} + +/* Baton for db_read_inherited_props */ +struct read_inherited_props_baton_t +{ + apr_array_header_t *iprops; + const char *propname; + apr_pool_t *result_pool; +}; + +/* Implements svn_wc__db_txn_callback_t for svn_wc__db_read_inherited_props */ +static svn_error_t * +db_read_inherited_props(void *baton, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + struct read_inherited_props_baton_t *ripb = baton; + int i; + apr_array_header_t *cached_iprops = NULL; + const char *parent_relpath = local_relpath; + svn_boolean_t is_wc_root = FALSE; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_pool_t *result_pool = ripb->result_pool; + + ripb->iprops = apr_array_make(ripb->result_pool, 1, + sizeof(svn_prop_inherited_item_t *)); + + /* Walk up to the root of the WC looking for inherited properties. When we + reach the WC root also check for cached inherited properties. */ + while (TRUE) + { + apr_hash_t *actual_props; + svn_boolean_t is_switched; + struct db_is_switched_baton_t swb; + + svn_pool_clear(iterpool); + + swb.is_switched = &is_switched; + swb.kind = NULL; + + if (*parent_relpath == '\0') { - /* No cached iprops. */ - *iprops = NULL; + is_switched = FALSE; + is_wc_root = TRUE; } else + SVN_ERR(db_is_switched(&swb, wcroot, parent_relpath, scratch_pool)); + + if (is_switched || is_wc_root) { - SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0, result_pool, - scratch_pool)); - } + struct read_cached_iprops_baton_t rib; + is_wc_root = TRUE; - SVN_ERR(svn_sqlite__reset(stmt)); + rib.result_pool = scratch_pool; + + /* If the WC root is also the root of the repository then by + definition there are no inheritable properties to be had, + but checking for that is just as expensive as fetching them + anyway. */ + + /* Grab the cached inherited properties for the WC root. */ + SVN_ERR(db_read_cached_iprops(&rib, wcroot, parent_relpath, + iterpool)); + + cached_iprops = rib.iprops; + } + + /* If PARENT_ABSPATH is a true parent of LOCAL_ABSPATH, then + LOCAL_ABSPATH can inherit properties from it. */ + if (strcmp(local_relpath, parent_relpath) != 0) + { + struct db_read_props_baton_t rpb; + rpb.result_pool = result_pool; + + SVN_ERR(db_read_props(&rpb, wcroot, parent_relpath, iterpool)); + + actual_props = rpb.props; + + if (actual_props) + { + /* If we only want PROPNAME filter out any other properties. */ + if (ripb->propname) + filter_unwanted_props(actual_props, ripb->propname, iterpool); + + if (apr_hash_count(actual_props)) + { + svn_prop_inherited_item_t *iprop_elt = + apr_pcalloc(ripb->result_pool, + sizeof(svn_prop_inherited_item_t)); + iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath, + parent_relpath, + result_pool); + + iprop_elt->prop_hash = actual_props; + /* Build the output array in depth-first order. */ + svn_sort__array_insert(&iprop_elt, ripb->iprops, 0); + } + } + } + + /* Inheritance only goes as far as the nearest WC root. */ + if (is_wc_root) + break; + + /* Keep looking for the WC root. */ + parent_relpath = svn_relpath_dirname(parent_relpath, scratch_pool); + } + + if (cached_iprops) + { + for (i = cached_iprops->nelts - 1; i >= 0; i--) + { + svn_prop_inherited_item_t *cached_iprop = + APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *); + + /* An empty property hash in the iprops cache means there are no + inherited properties. */ + if (apr_hash_count(cached_iprop->prop_hash) == 0) + continue; + + if (ripb->propname) + filter_unwanted_props(cached_iprop->prop_hash, ripb->propname, + scratch_pool); + + /* If we didn't filter everything then keep this iprop. */ + if (apr_hash_count(cached_iprop->prop_hash)) + svn_sort__array_insert(&cached_iprop, ripb->iprops, 0); + } } + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } -/* Recursive body of svn_wc__db_get_children_with_cached_iprops. */ -static svn_error_t * -get_children_with_cached_iprops(apr_hash_t *iprop_paths, - svn_depth_t depth, - const char *local_abspath, +svn_error_t * +svn_wc__db_read_inherited_props(apr_array_header_t **iprops, svn_wc__db_t *db, + const char *local_abspath, + const char *propname, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; + struct read_inherited_props_baton_t ripb; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - local_abspath, scratch_pool, - scratch_pool)); + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, + db, local_abspath, + scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - if (depth == svn_depth_empty - || depth == svn_depth_files - || depth == svn_depth_immediates) + + ripb.iprops = NULL; + ripb.propname = propname; + ripb.result_pool = result_pool; + + SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath, db_read_inherited_props, + &ripb, scratch_pool)); + + *iprops = ripb.iprops; + return SVN_NO_ERROR; +} + +/* Baton for get_children_with_cached_iprops */ +struct get_children_with_cached_baton_t +{ + svn_depth_t depth; + apr_hash_t *iprop_paths; + apr_pool_t *result_pool; +}; + +/* Implements svn_wc__db_txn_callback_t for + svn_wc__db_get_children_with_cached_iprops. */ +static svn_error_t * +get_children_with_cached_iprops(void *baton, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + struct get_children_with_cached_baton_t *cwcb = baton; + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + apr_hash_t *iprop_paths = cwcb->iprop_paths; + apr_pool_t *result_pool = cwcb->result_pool; + + /* First check if LOCAL_RELPATH itself has iprops */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_IPROPS_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + { + const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0, + NULL); + const char *abspath_with_cache = svn_dirent_join(wcroot->abspath, + relpath_with_cache, + result_pool); + apr_hash_set(iprop_paths, abspath_with_cache, APR_HASH_KEY_STRING, + svn_sqlite__column_text(stmt, 1, result_pool)); + } + SVN_ERR(svn_sqlite__reset(stmt)); + + if (cwcb->depth == svn_depth_empty) + return SVN_NO_ERROR; + + /* Now fetch information for children or all descendants */ + if (cwcb->depth == svn_depth_files + || cwcb->depth == svn_depth_immediates) { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_INODES)); + STMT_SELECT_IPROPS_CHILDREN)); } else /* Default to svn_depth_infinity. */ { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_INODES_RECURSIVE)); + STMT_SELECT_IPROPS_RECURSIVE)); } SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); @@ -9121,46 +9354,50 @@ get_children_with_cached_iprops(apr_hash relpath_with_cache, result_pool); apr_hash_set(iprop_paths, abspath_with_cache, APR_HASH_KEY_STRING, - abspath_with_cache); + svn_sqlite__column_text(stmt, 1, result_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } SVN_ERR(svn_sqlite__reset(stmt)); - if (depth == svn_depth_files || depth == svn_depth_immediates) + /* For depth files we should filter non files */ + if (cwcb->depth == svn_depth_files) { - const apr_array_header_t *rel_children; - int i; + apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(scratch_pool); - SVN_ERR(svn_wc__db_read_children_of_working_node(&rel_children, - db, local_abspath, - scratch_pool, - scratch_pool)); - for (i = 0; i < rel_children->nelts; i++) + for (hi = apr_hash_first(scratch_pool, iprop_paths); + hi; + hi = apr_hash_next(hi)) { - const char *child_abspath; + const char *child_abspath = svn__apr_hash_index_key(hi); + const char *child_relpath; + svn_kind_t child_kind; svn_pool_clear(iterpool); - child_abspath = svn_dirent_join( - local_abspath, APR_ARRAY_IDX(rel_children, i, const char *), - iterpool); - if (depth == svn_depth_files) - { - svn_kind_t child_kind; + child_relpath = svn_dirent_is_child(local_relpath, child_abspath, + NULL); - SVN_ERR(svn_wc__db_read_kind(&child_kind, db, child_abspath, - FALSE, FALSE, iterpool)); - if (child_kind != svn_kind_file) - continue; + if (! child_relpath) + { + continue; /* local_relpath itself */ } - SVN_ERR(get_children_with_cached_iprops(iprop_paths, - svn_depth_empty, - child_abspath, db, - result_pool, - iterpool)); + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + wcroot, child_relpath, + scratch_pool, + scratch_pool)); + + /* Filter if not a file */ + if (child_kind != svn_kind_file) + { + apr_hash_set(iprop_paths, child_abspath, APR_HASH_KEY_STRING, + NULL); + } } svn_pool_destroy(iterpool); @@ -9177,10 +9414,26 @@ svn_wc__db_get_children_with_cached_ipro apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - *iprop_paths = apr_hash_make(result_pool); - SVN_ERR(get_children_with_cached_iprops(*iprop_paths, depth, - local_abspath, db, result_pool, - scratch_pool)); + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + struct get_children_with_cached_baton_t cwcb; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + local_abspath, scratch_pool, + scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + cwcb.iprop_paths = apr_hash_make(result_pool); + cwcb.depth = depth; + cwcb.result_pool = result_pool; + + SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath, + get_children_with_cached_iprops, &cwcb, + scratch_pool)); + + *iprop_paths = cwcb.iprop_paths; return SVN_NO_ERROR; } @@ -9686,6 +9939,7 @@ commit_node(void *baton, svn_sqlite__stmt_t *stmt_act; svn_boolean_t have_act; svn_string_t prop_blob = { 0 }; + svn_string_t inherited_prop_blob = { 0 }; const char *changelist = NULL; const char *parent_relpath; svn_wc__db_status_t new_presence; @@ -9755,6 +10009,10 @@ commit_node(void *baton, prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len, scratch_pool); + inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16, + &inherited_prop_blob.len, + scratch_pool); + if (cb->keep_changelist && have_act) changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool); @@ -9841,6 +10099,11 @@ commit_node(void *baton, scratch_pool)); SVN_ERR(svn_sqlite__bind_properties(stmt, 15, cb->new_dav_cache, scratch_pool)); + if (inherited_prop_blob.data != NULL) + { + SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data, + inherited_prop_blob.len)); + } SVN_ERR(svn_sqlite__step_done(stmt)); @@ -12475,6 +12738,118 @@ svn_wc__db_is_wcroot(svn_boolean_t *is_r return SVN_NO_ERROR; } +/* This implements svn_wc__db_txn_callback_t for svn_wc_db__is_switched */ +static svn_error_t * +db_is_switched(void *baton, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + struct db_is_switched_baton_t *isb = baton; + svn_wc__db_status_t status; + apr_int64_t repos_id; + const char *repos_relpath; + const char *name; + const char *parent_local_relpath; + apr_int64_t parent_repos_id; + const char *parent_repos_relpath; + + SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */ + + SVN_ERR(read_info(&status, isb->kind, NULL, &repos_relpath, &repos_id, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + wcroot, local_relpath, scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_server_excluded + || status == svn_wc__db_status_excluded + || status == svn_wc__db_status_not_present) + { + return svn_error_createf( + SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' was not found."), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + } + else if (! repos_relpath) + { + /* Node is shadowed; easy out */ + if (isb->is_switched) + *isb->is_switched = FALSE; + + return SVN_NO_ERROR; + } + + if (! isb->is_switched) + return SVN_NO_ERROR; + + svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool); + + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, + &parent_repos_relpath, + &parent_repos_id, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + wcroot, parent_local_relpath, + scratch_pool, scratch_pool)); + + if (repos_id != parent_repos_id) + *isb->is_switched = TRUE; + else + { + const char *expected_relpath; + + expected_relpath = svn_relpath_join(parent_repos_relpath, name, + scratch_pool); + + *isb->is_switched = (strcmp(expected_relpath, repos_relpath) != 0); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_is_switched(svn_boolean_t *is_wcroot, + svn_boolean_t *is_switched, + svn_kind_t *kind, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + struct db_is_switched_baton_t isb; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + local_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + if (is_switched) + *is_switched = FALSE; + + if (*local_relpath == '\0') + { + /* Easy out */ + if (is_wcroot) + *is_wcroot = TRUE; + + if (kind) + *kind = svn_kind_dir; + return SVN_NO_ERROR; + } + + if (is_wcroot) + *is_wcroot = FALSE; + + isb.is_switched = is_switched; + isb.kind = kind; + + return svn_error_trace(svn_wc__db_with_txn(wcroot, local_relpath, + db_is_switched, &isb, + scratch_pool)); +} + svn_error_t * svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath, Modified: subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h (original) +++ subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h Tue Nov 27 22:13:24 2012 @@ -512,6 +512,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d svn_boolean_t delete_working, svn_boolean_t update_actual_props, apr_hash_t *new_actual_props, + apr_array_header_t *new_iprops, svn_boolean_t keep_recorded_info, svn_boolean_t insert_base_deleted, const svn_skel_t *conflict, @@ -594,6 +595,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t svn_boolean_t delete_working, svn_boolean_t update_actual_props, apr_hash_t *new_actual_props, + apr_array_header_t *new_iprops, svn_boolean_t keep_recorded_info, svn_boolean_t insert_base_deleted, const svn_skel_t *conflict, @@ -2092,6 +2094,26 @@ svn_wc__db_read_pristine_props(apr_hash_ apr_pool_t *result_pool, apr_pool_t *scratch_pool); + +/** + * Set @a *inherited_props to a depth-first ordered array of + * #svn_prop_inherited_item_t * structures representing the properties + * inherited by @a local_abspath from the ACTUAL tree above + * @a local_abspath (looking through to the WORKING or BASE tree as + * required), up to and including the root of the working copy and + * any cached inherited properties inherited by the root. + * + * Allocate @a *inherited_props in @a result_pool. Use @a scratch_pool + * for temporary allocations. + */ +svn_error_t * +svn_wc__db_read_inherited_props(apr_array_header_t **iprops, + svn_wc__db_t *db, + const char *local_abspath, + const char *propname, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /* Read a BASE node's inherited property information. Set *IPROPS to to a depth-first ordered array of @@ -2114,9 +2136,11 @@ svn_wc__db_read_cached_iprops(apr_array_ /* Find BASE nodes with cached inherited properties. Set *IPROPS_PATHS to a hash mapping const char * absolute working copy - paths to the same for each path in the working copy at or below - LOCAL_ABSPATH, limited by DEPTH, that has cached inherited properties - for the BASE node of the path. Allocate *IPROP_PATHS in RESULT_POOL. + paths to the repos_relpath of the path for each path in the working copy + at or below LOCAL_ABSPATH, limited by DEPTH, that has cached inherited + properties for the BASE node of the path. + + Allocate *IPROP_PATHS in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths, @@ -2288,6 +2312,21 @@ svn_wc__db_is_wcroot(svn_boolean_t *is_r const char *local_abspath, apr_pool_t *scratch_pool); +/* Checks if LOCAL_ABSPATH is a working copy root, switched and a directory. + With these answers we can answer all 'is anchor' questions that we need for + the different ra operations with just a single sqlite transaction and + filestat. + + All output arguments can be null to specify that the result is not + interesting to the caller + */ +svn_error_t * +svn_wc__db_is_switched(svn_boolean_t *is_wcroot, + svn_boolean_t *is_switched, + svn_kind_t *kind, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool); /* @} */ Modified: subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c (original) +++ subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c Tue Nov 27 22:13:24 2012 @@ -618,12 +618,8 @@ send_propchange(item_baton_t *b, { const char *qname; - /* Ensure that the property name is XML-safe. - apr_xml_quote_string() doesn't realloc if there is nothing to - quote, so dup the name, but only if necessary. */ - qname = apr_xml_quote_string(b->pool, name, 1); - if (qname == name) - qname = apr_pstrdup(b->pool, name); + /* Ensure that the property name is XML-safe. */ + qname = apr_xml_quote_string(pool, name, 1); if (value) { Modified: subversion/branches/issue-4194-dev/subversion/svn/cl.h URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/cl.h?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svn/cl.h (original) +++ subversion/branches/issue-4194-dev/subversion/svn/cl.h Tue Nov 27 22:13:24 2012 @@ -767,6 +767,22 @@ const char * svn_cl__operation_str_human_readable(svn_wc_operation_t operation, apr_pool_t *pool); +/* If PROPNAME looks like but is not identical to one of the svn: + * poperties, raise an error and suggest a better spelling. Names that + * raise errors look like this: + * + * - start with svn: but do not exactly match a known property; or, + * - start with a 3-letter prefix that differs in only one letter + * from "svn:", and the rest exactly matches a known propery. + * + * If REVPROP is TRUE, only check revision property names; otherwise + * only check node property names. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_cl__check_svn_prop_name(const char *propname, svn_boolean_t revprop, + apr_pool_t *scratch_pool); /* If PROPNAME is one of the svn: properties with a boolean value, and * PROPVAL looks like an attempt to turn the property off (i.e., it's Modified: subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c (original) +++ subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c Tue Nov 27 22:13:24 2012 @@ -42,9 +42,14 @@ struct print_baton { svn_boolean_t verbose; svn_client_ctx_t *ctx; + + /* To keep track of last seen external information. */ + const char *last_external_parent_url; + const char *last_external_target; + svn_boolean_t in_external; }; -/* This implements the svn_client_list_func_t API, printing a single +/* This implements the svn_client_list_func2_t API, printing a single directory entry in text format. */ static svn_error_t * print_dirent(void *baton, @@ -52,6 +57,8 @@ print_dirent(void *baton, const svn_dirent_t *dirent, const svn_lock_t *lock, const char *abs_path, + const char *external_parent_url, + const char *external_target, apr_pool_t *pool) { struct print_baton *pb = baton; @@ -59,6 +66,9 @@ print_dirent(void *baton, static const char *time_format_long = NULL; static const char *time_format_short = NULL; + SVN_ERR_ASSERT((external_parent_url == NULL && external_target == NULL) || + (external_parent_url && external_target)); + if (time_format_long == NULL) time_format_long = _("%b %d %H:%M"); if (time_format_short == NULL) @@ -79,6 +89,24 @@ print_dirent(void *baton, } else entryname = path; + + if (external_parent_url && external_target) + { + if ((pb->last_external_parent_url == NULL + && pb->last_external_target == NULL) + || (strcmp(pb->last_external_parent_url, external_parent_url) != 0 + || strcmp(pb->last_external_target, external_target) != 0)) + { + SVN_ERR(svn_cmdline_printf(pool, + _("Listing external '%s'" + " defined on '%s':\n"), + external_target, + external_parent_url)); + + pb->last_external_parent_url = external_parent_url; + pb->last_external_target = external_target; + } + } if (pb->verbose) { @@ -133,7 +161,7 @@ print_dirent(void *baton, } -/* This implements the svn_client_list_func_t API, printing a single dirent +/* This implements the svn_client_list_func2_t API, printing a single dirent in XML format. */ static svn_error_t * print_dirent_xml(void *baton, @@ -141,18 +169,21 @@ print_dirent_xml(void *baton, const svn_dirent_t *dirent, const svn_lock_t *lock, const char *abs_path, + const char *external_parent_url, + const char *external_target, apr_pool_t *pool) { struct print_baton *pb = baton; const char *entryname; - svn_stringbuf_t *sb; + svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); + + SVN_ERR_ASSERT((external_parent_url == NULL && external_target == NULL) || + (external_parent_url && external_target)); if (strcmp(path, "") == 0) { if (dirent->kind == svn_node_file) entryname = svn_dirent_basename(abs_path, pool); - else if (pb->verbose) - entryname = "."; else /* Don't bother to list if no useful information will be shown. */ return SVN_NO_ERROR; @@ -162,8 +193,32 @@ print_dirent_xml(void *baton, if (pb->ctx->cancel_func) SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton)); + + if (external_parent_url && external_target) + { + if ((pb->last_external_parent_url == NULL + && pb->last_external_target == NULL) + || (strcmp(pb->last_external_parent_url, external_parent_url) != 0 + || strcmp(pb->last_external_target, external_target) != 0)) + { + if (pb->in_external) + { + /* The external item being listed is different from the previous + one, so close the tag. */ + svn_xml_make_close_tag(&sb, pool, "external"); + pb->in_external = FALSE; + } + + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "external", + "parent_url", external_parent_url, + "target", external_target, + NULL); - sb = svn_stringbuf_create_empty(pool); + pb->last_external_parent_url = external_parent_url; + pb->last_external_target = external_target; + pb->in_external = TRUE; + } + } svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", "kind", svn_cl__node_kind_str_xml(dirent->kind), @@ -225,6 +280,8 @@ svn_cl__list(apr_getopt_t *os, struct print_baton pb; svn_boolean_t seen_nonexistent_target = FALSE; svn_error_t *err; + svn_error_t *externals_err = SVN_NO_ERROR; + struct svn_cl__check_externals_failed_notify_baton nwb; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, @@ -266,12 +323,27 @@ svn_cl__list(apr_getopt_t *os, if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_immediates; + if (opt_state->include_externals) + { + nwb.wrapped_func = ctx->notify_func2; + nwb.wrapped_baton = ctx->notify_baton2; + nwb.had_externals_error = FALSE; + ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper; + ctx->notify_baton2 = &nwb; + } + /* For each target, try to list it. */ for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); const char *truepath; svn_opt_revision_t peg_revision; + + /* Initialize the following variables for + every list target. */ + pb.last_external_parent_url = NULL; + pb.last_external_target = NULL; + pb.in_external = FALSE; svn_pool_clear(subpool); @@ -290,11 +362,12 @@ svn_cl__list(apr_getopt_t *os, SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); } - err = svn_client_list2(truepath, &peg_revision, + err = svn_client_list3(truepath, &peg_revision, &(opt_state->start_revision), opt_state->depth, dirent_fields, (opt_state->xml || opt_state->verbose), + opt_state->include_externals, opt_state->xml ? print_dirent_xml : print_dirent, &pb, ctx, subpool); @@ -316,20 +389,38 @@ svn_cl__list(apr_getopt_t *os, if (opt_state->xml) { svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); + + if (pb.in_external) + { + /* close the final external item's tag */ + svn_xml_make_close_tag(&sb, pool, "external"); + pb.in_external = FALSE; + } + svn_xml_make_close_tag(&sb, pool, "list"); SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); } } svn_pool_destroy(subpool); + + if (opt_state->include_externals && nwb.had_externals_error) + { + externals_err = svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, + NULL, + _("Failure occurred processing one or " + "more externals definitions")); + } if (opt_state->xml && ! opt_state->incremental) SVN_ERR(svn_cl__xml_print_footer("lists", pool)); if (seen_nonexistent_target) - return svn_error_create( - SVN_ERR_ILLEGAL_TARGET, NULL, - _("Could not list all targets because some targets don't exist")); + { + err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Could not list all targets because some targets don't exist")); + return svn_error_compose_create(externals_err, err); + } else - return SVN_NO_ERROR; + return svn_error_compose_create(externals_err, err); } Modified: subversion/branches/issue-4194-dev/subversion/svn/main.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/main.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svn/main.c (original) +++ subversion/branches/issue-4194-dev/subversion/svn/main.c Tue Nov 27 22:13:24 2012 @@ -131,7 +131,7 @@ typedef enum svn_cl__longopt_t { opt_include_externals, opt_show_inherited_props, opt_search, - opt_search_and, + opt_search_and } svn_cl__longopt_t; @@ -624,7 +624,9 @@ const svn_opt_subcommand_desc2_t svn_cl_ " If locked, the letter 'O'. (Use 'svn info URL' to see details)\n" " Size (in bytes)\n" " Date and time of the last commit\n"), - {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml} }, + {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml, + opt_include_externals }, + {{opt_include_externals, N_("include externals definitions")}} }, { "lock", svn_cl__lock, {0}, N_ ("Lock working copy paths or URLs in the repository, so that\n" Modified: subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c (original) +++ subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c Tue Nov 27 22:13:24 2012 @@ -84,6 +84,9 @@ svn_cl__propedit(apr_getopt_t *os, return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, _("'%s' is not a valid Subversion property name"), pname_utf8); + if (!opt_state->force) + SVN_ERR(svn_cl__check_svn_prop_name(pname_utf8, opt_state->revprop, pool)); + if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8)) return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL, Modified: subversion/branches/issue-4194-dev/subversion/svn/props.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/props.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svn/props.c (original) +++ subversion/branches/issue-4194-dev/subversion/svn/props.c Tue Nov 27 22:13:24 2012 @@ -27,6 +27,8 @@ /*** Includes. ***/ +#include + #include #include "svn_cmdline.h" #include "svn_string.h" @@ -40,10 +42,10 @@ #include "svn_base64.h" #include "cl.h" +#include "private/svn_string_private.h" #include "private/svn_cmdline_private.h" #include "svn_private_config.h" - svn_error_t * @@ -223,3 +225,191 @@ svn_cl__check_boolean_prop_val(const cha } } + +/* Context for sorting property names */ +struct simprop_context_t +{ + svn_string_t name; /* The name of the property we're comparing with */ + svn_membuf_t buffer; /* Buffer for similariry testing */ +}; + +struct simprop_t +{ + const char *propname; /* The original svn: property name */ + svn_string_t name; /* The property name without the svn: prefx */ + unsigned int score; /* The similarity score */ + apr_size_t diff; /* Number of chars different from context.name */ + struct simprop_context_t *context; /* Sorting context for qsort() */ +}; + +/* Similarity test between two property names */ +static APR_INLINE unsigned int +simprop_key_diff(const svn_string_t *key, const svn_string_t *ctx, + svn_membuf_t *buffer, apr_size_t *diff) +{ + apr_size_t lcs; + const unsigned int score = svn_string__similarity(key, ctx, buffer, &lcs); + if (key->len > ctx->len) + *diff = key->len - lcs; + else + *diff = ctx->len - lcs; + return score; +} + +/* Key comparator for qsort for simprop_t */ +static int +simprop_compare(const void *pkeya, const void *pkeyb) +{ + struct simprop_t *const keya = *(struct simprop_t *const *)pkeya; + struct simprop_t *const keyb = *(struct simprop_t *const *)pkeyb; + struct simprop_context_t *const context = keya->context; + + if (keya->score == -1) + keya->score = simprop_key_diff(&keya->name, &context->name, + &context->buffer, &keya->diff); + if (keyb->score == -1) + keyb->score = simprop_key_diff(&keyb->name, &context->name, + &context->buffer, &keyb->diff); + + return (keya->score < keyb->score ? 1 + : (keya->score > keyb->score ? -1 + : (keya->diff > keyb->diff ? 1 + : (keya->diff < keyb->diff ? -1 : 0)))); +} + +svn_error_t * +svn_cl__check_svn_prop_name(const char *propname, svn_boolean_t revprop, + apr_pool_t *scratch_pool) +{ + static const char *const nodeprops[] = + { + SVN_PROP_NODE_ALL_PROPS + }; + static const apr_size_t nodeprops_len = sizeof(nodeprops)/sizeof(*nodeprops); + + static const char *const revprops[] = + { + SVN_PROP_REVISION_ALL_PROPS + }; + static const apr_size_t revprops_len = sizeof(revprops)/sizeof(*revprops); + + const char *const *const proplist = (revprop ? revprops : nodeprops); + const apr_size_t numprops = (revprop ? revprops_len : nodeprops_len); + + struct simprop_t **propkeys; + struct simprop_t *propbuf; + apr_size_t i; + + struct simprop_context_t context; + svn_string_t prefix; + + context.name.data = propname; + context.name.len = strlen(propname); + prefix.data = SVN_PROP_PREFIX; + prefix.len = strlen(SVN_PROP_PREFIX); + + svn_membuf__create(&context.buffer, 0, scratch_pool); + + /* First, check if the name is even close to being in the svn: namespace. + It must contain a colon in the right place, and we only allow + one-char typos or a single transposition. */ + if (context.name.len < prefix.len + || context.name.data[prefix.len - 1] != prefix.data[prefix.len - 1]) + return SVN_NO_ERROR; /* Wrong prefix, ignore */ + else + { + apr_size_t lcs; + const apr_size_t name_len = context.name.len; + context.name.len = prefix.len; /* Only check up to the prefix length */ + svn_string__similarity(&context.name, &prefix, &context.buffer, &lcs); + context.name.len = name_len; /* Restore the original propname length */ + if (lcs < prefix.len - 1) + return SVN_NO_ERROR; /* Wrong prefix, ignore */ + + /* If the prefix is slightly different, the rest must be + identical in order to trigger the error. */ + if (lcs == prefix.len - 1) + { + for (i = 0; i < numprops; ++i) + { + if (0 == strcmp(proplist[i] + prefix.len, propname + prefix.len)) + return svn_error_createf( + SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("'%s' is not a valid %s property name; did you mean '%s'?" + "\n(To set the '%s' property, re-run with '--force'.)"), + propname, SVN_PROP_PREFIX, proplist[i], propname); + } + return SVN_NO_ERROR; + } + } + + /* Now find the closest match from amongst a the set of reserved + node or revision property names. Skip the prefix while matching, + we already know that it's the same and looking at it would only + skew the results. */ + propkeys = apr_palloc(scratch_pool, + numprops * sizeof(struct simprop_t*)); + propbuf = apr_palloc(scratch_pool, + numprops * sizeof(struct simprop_t)); + context.name.data += prefix.len; + context.name.len -= prefix.len; + for (i = 0; i < numprops; ++i) + { + propkeys[i] = &propbuf[i]; + propbuf[i].propname = proplist[i]; + propbuf[i].name.data = proplist[i] + prefix.len; + propbuf[i].name.len = strlen(propbuf[i].name.data); + propbuf[i].score = (unsigned int)-1; + propbuf[i].context = &context; + } + + qsort(propkeys, numprops, sizeof(*propkeys), simprop_compare); + + if (0 == propkeys[0]->diff) + return SVN_NO_ERROR; /* We found an exact match. */ + + /* See if we can suggest a sane alternative spelling */ + for (i = 0; i < numprops; ++i) + if (propkeys[i]->score < 666) /* 2/3 similarity required */ + break; + + switch (i) + { + case 0: + /* The best alternative isn't good enough */ + return svn_error_createf( + SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("'%s' is not a valid %s property name;" + " re-run with '--force' to set it"), + propname, SVN_PROP_PREFIX); + + case 1: + /* There is only one good candidate */ + return svn_error_createf( + SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("'%s' is not a valid %s property name; did you mean '%s'?\n" + "(To set the '%s' property, re-run with '--force'.)"), + propname, SVN_PROP_PREFIX, propkeys[0]->propname, propname); + + case 2: + /* Suggest a list of the most likely candidates */ + return svn_error_createf( + SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("'%s' is not a valid %s property name\n" + "Did you mean '%s' or '%s'?\n" + "(To set the '%s' property, re-run with '--force'.)"), + propname, SVN_PROP_PREFIX, + propkeys[0]->propname, propkeys[1]->propname, propname); + + default: + /* Never suggest more than three candidates */ + return svn_error_createf( + SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("'%s' is not a valid %s property name\n" + "Did you mean '%s', '%s' or '%s'?\n" + "(To set the '%s' property, re-run with '--force'.)"), + propname, SVN_PROP_PREFIX, + propkeys[0]->propname, propkeys[1]->propname, propkeys[2]->propname, + propname); + } +} Modified: subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c (original) +++ subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c Tue Nov 27 22:13:24 2012 @@ -67,6 +67,9 @@ svn_cl__propset(apr_getopt_t *os, return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, _("'%s' is not a valid Subversion property name"), pname_utf8); + if (!opt_state->force) + SVN_ERR(svn_cl__check_svn_prop_name(pname_utf8, opt_state->revprop, + scratch_pool)); /* Get the PROPVAL from either an external file, or from the command line. */ Modified: subversion/branches/issue-4194-dev/subversion/svnlook/main.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnlook/main.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svnlook/main.c (original) +++ subversion/branches/issue-4194-dev/subversion/svnlook/main.c Tue Nov 27 22:13:24 2012 @@ -52,6 +52,7 @@ #include "svn_version.h" #include "svn_xml.h" +#include "private/svn_diff_private.h" #include "private/svn_cmdline_private.h" #include "private/svn_fspath.h" @@ -95,7 +96,8 @@ enum svnlook__copy_info, svnlook__xml_opt, svnlook__ignore_properties, - svnlook__properties_only + svnlook__properties_only, + svnlook__diff_cmd }; /* @@ -127,6 +129,9 @@ static const apr_getopt_option_t options {"no-diff-deleted", svnlook__no_diff_deleted, 0, N_("do not print differences for deleted files")}, + {"diff-cmd", svnlook__diff_cmd, 1, + N_("use ARG as diff command")}, + {"ignore-properties", svnlook__ignore_properties, 0, N_("ignore properties during the operation")}, @@ -158,37 +163,23 @@ static const apr_getopt_option_t options N_("output in XML")}, {"extensions", 'x', 1, - N_("Default: '-u'. When Subversion is invoking an\n" - " " - " external diff program, ARG is simply passed along\n" - " " - " to the program. But when Subversion is using its\n" - " " - " default internal diff implementation, or when\n" - " " - " Subversion is displaying blame annotations, ARG\n" - " " - " could be any of the following:\n" - " " - " -u (--unified):\n" - " " - " Output 3 lines of unified context.\n" - " " - " -b (--ignore-space-change):\n" - " " - " Ignore changes in the amount of white space.\n" - " " - " -w (--ignore-all-space):\n" - " " - " Ignore all white space.\n" - " " - " --ignore-eol-style:\n" - " " - " Ignore changes in EOL style\n" - " " - " -p (--show-c-function):\n" - " " - " Show C function name in diff output.")}, + N_("Specify differencing options for external diff or\n" + " " + "internal diff. Default: '-u'. Options are\n" + " " + "separated by spaces. Internal diff takes:\n" + " " + " -u, --unified: Show 3 lines of unified context\n" + " " + " -b, --ignore-space-change: Ignore changes in\n" + " " + " amount of white space\n" + " " + " -w, --ignore-all-space: Ignore all white space\n" + " " + " --ignore-eol-style: Ignore changes in EOL style\n" + " " + " -p, --show-c-function: Show C function name")}, {"quiet", 'q', 0, N_("no progress (only errors) to stderr")}, @@ -226,8 +217,8 @@ static const svn_opt_subcommand_desc2_t N_("usage: svnlook diff REPOS_PATH\n\n" "Print GNU-style diffs of changed files and properties.\n"), {'r', 't', svnlook__no_diff_deleted, svnlook__no_diff_added, - svnlook__diff_copy_from, 'x', svnlook__ignore_properties, - svnlook__properties_only} }, + svnlook__diff_copy_from, svnlook__diff_cmd, 'x', + svnlook__ignore_properties, svnlook__properties_only} }, {"dirs-changed", subcommand_dirschanged, {0}, N_("usage: svnlook dirs-changed REPOS_PATH\n\n" @@ -331,6 +322,7 @@ struct svnlook_opt_state svn_boolean_t quiet; /* --quiet */ svn_boolean_t ignore_properties; /* --ignore_properties */ svn_boolean_t properties_only; /* --properties-only */ + const char *diff_cmd; /* --diff-cmd */ }; @@ -352,6 +344,7 @@ typedef struct svnlook_ctxt_t const apr_array_header_t *diff_options; svn_boolean_t ignore_properties; svn_boolean_t properties_only; + const char *diff_cmd; } svnlook_ctxt_t; @@ -791,66 +784,16 @@ generate_label(const char **label, return SVN_NO_ERROR; } -/* A helper function used by display_prop_diffs. - TOKEN is a string holding a property value. - If TOKEN is empty, or is already terminated by an EOL marker, - return TOKEN unmodified. Else, return a new string consisting - of the concatenation of TOKEN and the system's default EOL marker. - The new string is allocated from POOL. - If HAD_EOL is not NULL, indicate in *HAD_EOL if the token had a EOL. */ -static const svn_string_t * -maybe_append_eol(const svn_string_t *token, svn_boolean_t *had_eol, - apr_pool_t *pool) -{ - const char *curp; - - if (had_eol) - *had_eol = FALSE; - - if (token->len == 0) - return token; - - curp = token->data + token->len - 1; - if (*curp == '\r') - { - if (had_eol) - *had_eol = TRUE; - return token; - } - else if (*curp != '\n') - { - return svn_string_createf(pool, "%s%s", token->data, APR_EOL_STR); - } - else - { - if (had_eol) - *had_eol = TRUE; - return token; - } -} - -/* - * Constant diff output separator strings - */ -static const char equal_string[] = - "==================================================================="; -static const char under_string[] = - "___________________________________________________________________"; - /* Helper function to display differences in properties of a file */ static svn_error_t * -display_prop_diffs(const apr_array_header_t *propchanges, +display_prop_diffs(svn_stream_t *outstream, + const char *encoding, + const apr_array_header_t *propchanges, apr_hash_t *original_props, const char *path, apr_pool_t *pool) { - const char *encoding = svn_cmdline_output_encoding(pool); - svn_stream_t *outstream; - apr_pool_t *iterpool; - int i; - - SVN_ERR(svn_stream_for_stdout(&outstream, pool)); SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool, _("%sProperty changes on: %s%s"), @@ -859,90 +802,24 @@ display_prop_diffs(const apr_array_heade APR_EOL_STR)); SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool, - "%s" APR_EOL_STR, under_string)); - - iterpool = svn_pool_create(pool); - for (i = 0; i < propchanges->nelts; i++) - { - const char *action; - const svn_string_t *original_value; - const svn_prop_t *propchange - = &APR_ARRAY_IDX(propchanges, i, svn_prop_t); - - SVN_ERR(check_cancel(NULL)); + SVN_DIFF__UNDER_STRING APR_EOL_STR)); - if (original_props) - original_value = apr_hash_get(original_props, - propchange->name, APR_HASH_KEY_STRING); - else - original_value = NULL; - - svn_pool_clear(iterpool); + SVN_ERR(check_cancel(NULL)); - if (! original_value) - action = "Added"; - else if (! propchange->value) - action = "Deleted"; - else - action = "Modified"; - SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool, - "%s: %s%s", action, - propchange->name, APR_EOL_STR)); - - { - svn_diff_t *diff; - svn_diff_file_options_t options; - const svn_string_t *tmp; - const svn_string_t *orig; - const svn_string_t *val; - svn_boolean_t val_has_eol; - - /* The last character in a property is often not a newline. - An eol character is appended to prevent the diff API to add a - '\ No newline at end of file' line. We add - '\ No newline at end of property' manually if needed. */ - tmp = original_value ? original_value - : svn_string_create_empty(iterpool); - orig = maybe_append_eol(tmp, NULL, iterpool); - - tmp = propchange->value ? propchange->value : - svn_string_create_empty(iterpool); - val = maybe_append_eol(tmp, &val_has_eol, iterpool); - - SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options, - iterpool)); - - /* UNIX patch will try to apply a diff even if the diff header - * is missing. It tries to be helpful by asking the user for a - * target filename when it can't determine the target filename - * from the diff header. But there usually are no files which - * UNIX patch could apply the property diff to, so we use "##" - * instead of "@@" as the default hunk delimiter for property diffs. - * We also supress the diff header. */ - SVN_ERR(svn_diff_mem_string_output_unified2( - outstream, diff, FALSE, "##", - svn_dirent_local_style(path, iterpool), - svn_dirent_local_style(path, iterpool), - encoding, orig, val, iterpool)); - if (!val_has_eol) - { - const char *s = "\\ No newline at end of property" APR_EOL_STR; - SVN_ERR(svn_stream_puts(outstream, s)); - } - } - } - svn_pool_destroy(iterpool); + SVN_ERR(svn_diff__display_prop_diffs( + outstream, encoding, propchanges, original_props, + FALSE /* pretty_print_mergeinfo */, pool)); - SVN_ERR(svn_stream_close(outstream)); - return svn_cmdline_fflush(stdout); + return SVN_NO_ERROR; } - /* Recursively print all nodes in the tree that have been modified (do not include directories affected only by "bubble-up"). */ static svn_error_t * -print_diff_tree(svn_fs_root_t *root, +print_diff_tree(svn_stream_t *out_stream, + const char *encoding, + svn_fs_root_t *root, svn_fs_root_t *base_root, svn_repos_node_t *node, const char *path /* UTF-8! */, @@ -1063,59 +940,46 @@ print_diff_tree(svn_fs_root_t *root, if (do_diff && (! c->properties_only)) { - svn_stringbuf_appendcstr(header, equal_string); - svn_stringbuf_appendcstr(header, "\n"); + svn_stringbuf_appendcstr(header, SVN_DIFF__EQUAL_STRING "\n"); if (binary) { svn_stringbuf_appendcstr(header, _("(Binary files differ)\n\n")); - SVN_ERR(svn_cmdline_printf(pool, "%s", header->data)); + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + "%s", header->data)); } else { - svn_diff_t *diff; - svn_diff_file_options_t *opts = svn_diff_file_options_create(pool); - - if (c->diff_options) - SVN_ERR(svn_diff_file_options_parse(opts, c->diff_options, pool)); - - SVN_ERR(svn_diff_file_diff_2(&diff, orig_path, - new_path, opts, pool)); - - if (svn_diff_contains_diffs(diff)) + if (c->diff_cmd) { - svn_stream_t *ostream; - const char *orig_label, *new_label; + apr_file_t *outfile; + apr_file_t *errfile; + const char *outfilename; + const char *errfilename; + svn_stream_t *stream; + svn_stream_t *err_stream; + const char **diff_cmd_argv; + int diff_cmd_argc; + int exitcode; + const char *orig_label; + const char *new_label; + + diff_cmd_argv = NULL; + diff_cmd_argc = c->diff_options->nelts; + if (diff_cmd_argc) + { + int i; + diff_cmd_argv = apr_palloc(pool, + diff_cmd_argc * sizeof(char *)); + for (i = 0; i < diff_cmd_argc; i++) + SVN_ERR(svn_utf_cstring_to_utf8(&diff_cmd_argv[i], + APR_ARRAY_IDX(c->diff_options, i, const char *), + pool)); + } /* Print diff header. */ - SVN_ERR(svn_cmdline_printf(pool, "%s", header->data)); - - /* This fflush() might seem odd, but it was added to deal - with this bug report: - - http://subversion.tigris.org/servlets/ReadMsg?\ - list=dev&msgNo=140782 - - From: "Steve Hay" - To: - Subject: svnlook diff output in wrong order when redirected - Date: Fri, 4 Jul 2008 16:34:15 +0100 - Message-ID: <1B32FF956ABF414C9BCE5E487A1497E702014F62@\ - ukmail02.planit.group> - - Adding the fflush() fixed the bug (not everyone could - reproduce it, but those who could confirmed the fix). - Later in the thread, Daniel Shahaf speculated as to - why the fix works: - - "Because svn_cmdline_printf() uses the standard - 'FILE *stdout' to write to stdout, while - svn_stream_for_stdout() uses (through - apr_file_open_stdout()) Windows API's to get a - handle for stdout?" */ - SVN_ERR(svn_cmdline_fflush(stdout)); - - SVN_ERR(svn_stream_for_stdout(&ostream, pool)); + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + "%s", header->data)); if (orig_empty) SVN_ERR(generate_label(&orig_label, NULL, path, pool)); @@ -1123,26 +987,89 @@ print_diff_tree(svn_fs_root_t *root, SVN_ERR(generate_label(&orig_label, base_root, base_path, pool)); SVN_ERR(generate_label(&new_label, root, path, pool)); - SVN_ERR(svn_diff_file_output_unified3 - (ostream, diff, orig_path, new_path, - orig_label, new_label, - svn_cmdline_output_encoding(pool), NULL, - opts->show_c_function, pool)); - SVN_ERR(svn_stream_close(ostream)); - SVN_ERR(svn_cmdline_printf(pool, "\n")); + + /* We deal in streams, but svn_io_run_diff2() deals in file + handles, unfortunately, so we need to make these temporary + files, and then copy the contents to our stream. */ + SVN_ERR(svn_io_open_unique_file3(&outfile, &outfilename, NULL, + svn_io_file_del_on_pool_cleanup, pool, pool)); + SVN_ERR(svn_io_open_unique_file3(&errfile, &errfilename, NULL, + svn_io_file_del_on_pool_cleanup, pool, pool)); + + SVN_ERR(svn_io_run_diff2(".", + diff_cmd_argv, + diff_cmd_argc, + orig_label, new_label, + orig_path, new_path, + &exitcode, outfile, errfile, + c->diff_cmd, pool)); + + SVN_ERR(svn_io_file_close(outfile, pool)); + SVN_ERR(svn_io_file_close(errfile, pool)); + + /* Now, open and copy our files to our output streams. */ + SVN_ERR(svn_stream_for_stderr(&err_stream, pool)); + SVN_ERR(svn_stream_open_readonly(&stream, outfilename, + pool, pool)); + SVN_ERR(svn_stream_copy3(stream, + svn_stream_disown(out_stream, pool), + NULL, NULL, pool)); + SVN_ERR(svn_stream_open_readonly(&stream, errfilename, + pool, pool)); + SVN_ERR(svn_stream_copy3(stream, + svn_stream_disown(err_stream, pool), + NULL, NULL, pool)); + + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + "\n")); diff_header_printed = TRUE; } - else if (! node->prop_mod && - ((! c->no_diff_added && node->action == 'A') || - (! c->no_diff_deleted && node->action == 'D'))) + else { - /* There was an empty file added or deleted in this revision. - * We can't print a diff, but we can at least print - * a diff header since we know what happened to this file. */ - SVN_ERR(svn_cmdline_printf(pool, "%s", header->data)); + svn_diff_t *diff; + svn_diff_file_options_t *opts = svn_diff_file_options_create(pool); + + if (c->diff_options) + SVN_ERR(svn_diff_file_options_parse(opts, c->diff_options, pool)); + + SVN_ERR(svn_diff_file_diff_2(&diff, orig_path, + new_path, opts, pool)); + + if (svn_diff_contains_diffs(diff)) + { + const char *orig_label, *new_label; + + /* Print diff header. */ + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + "%s", header->data)); + + if (orig_empty) + SVN_ERR(generate_label(&orig_label, NULL, path, pool)); + else + SVN_ERR(generate_label(&orig_label, base_root, + base_path, pool)); + SVN_ERR(generate_label(&new_label, root, path, pool)); + SVN_ERR(svn_diff_file_output_unified3 + (out_stream, diff, orig_path, new_path, + orig_label, new_label, + svn_cmdline_output_encoding(pool), NULL, + opts->show_c_function, pool)); + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + "\n")); + diff_header_printed = TRUE; + } + else if (! node->prop_mod && + ((! c->no_diff_added && node->action == 'A') || + (! c->no_diff_deleted && node->action == 'D'))) + { + /* There was an empty file added or deleted in this revision. + * We can't print a diff, but we can at least print + * a diff header since we know what happened to this file. */ + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + "%s", header->data)); + } } } - SVN_ERR(svn_cmdline_fflush(stdout)); } /* Make sure we delete any temporary files. */ @@ -1182,12 +1109,17 @@ print_diff_tree(svn_fs_root_t *root, pool)); SVN_ERR(generate_label(&new_label, root, path, pool)); - SVN_ERR(svn_cmdline_printf(pool, "Index: %s\n", path)); - SVN_ERR(svn_cmdline_printf(pool, "%s\n", equal_string)); - SVN_ERR(svn_cmdline_printf(pool, "--- %s\n", orig_label)); - SVN_ERR(svn_cmdline_printf(pool, "+++ %s\n", new_label)); + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + "Index: %s\n", path)); + SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, + SVN_DIFF__EQUAL_STRING "\n")); + /* --- + * +++ */ + SVN_ERR(svn_diff__unidiff_write_header( + out_stream, encoding, orig_label, new_label, pool)); } - SVN_ERR(display_prop_diffs(props, base_proptable, path, pool)); + SVN_ERR(display_prop_diffs(out_stream, encoding, + props, base_proptable, path, pool)); } } @@ -1198,7 +1130,7 @@ print_diff_tree(svn_fs_root_t *root, /* Recursively handle the node's children. */ subpool = svn_pool_create(pool); - SVN_ERR(print_diff_tree(root, base_root, node, + SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node, svn_dirent_join(path, node->name, subpool), svn_dirent_join(base_path, node->name, subpool), c, tmpdir, subpool)); @@ -1206,7 +1138,7 @@ print_diff_tree(svn_fs_root_t *root, { svn_pool_clear(subpool); node = node->sibling; - SVN_ERR(print_diff_tree(root, base_root, node, + SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node, svn_dirent_join(path, node->name, subpool), svn_dirent_join(base_path, node->name, subpool), c, tmpdir, subpool)); @@ -1569,12 +1501,40 @@ do_diff(svnlook_ctxt_t *c, apr_pool_t *p if (tree) { const char *tmpdir; + svn_stream_t *out_stream; + const char *encoding = svn_cmdline_output_encoding(pool); SVN_ERR(svn_fs_revision_root(&base_root, c->fs, base_rev_id, pool)); SVN_ERR(svn_io_temp_dir(&tmpdir, pool)); - SVN_ERR(print_diff_tree(root, base_root, tree, "", "", - c, tmpdir, pool)); + /* This fflush() might seem odd, but it was added to deal + with this bug report: + + http://subversion.tigris.org/servlets/ReadMsg?\ + list=dev&msgNo=140782 + + From: "Steve Hay" + To: + Subject: svnlook diff output in wrong order when redirected + Date: Fri, 4 Jul 2008 16:34:15 +0100 + Message-ID: <1B32FF956ABF414C9BCE5E487A1497E702014F62@\ + ukmail02.planit.group> + + Adding the fflush() fixed the bug (not everyone could + reproduce it, but those who could confirmed the fix). + Later in the thread, Daniel Shahaf speculated as to + why the fix works: + + "Because svn_cmdline_printf() uses the standard + 'FILE *stdout' to write to stdout, while + svn_stream_for_stdout() uses (through + apr_file_open_stdout()) Windows API's to get a + handle for stdout?" */ + SVN_ERR(svn_cmdline_fflush(stdout)); + SVN_ERR(svn_stream_for_stdout(&out_stream, pool)); + + SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, tree, + "", "", c, tmpdir, pool)); } return SVN_NO_ERROR; } @@ -1945,6 +1905,7 @@ get_ctxt_baton(svnlook_ctxt_t **baton_p, " \t\n\r", TRUE, pool); baton->ignore_properties = opt_state->ignore_properties; baton->properties_only = opt_state->properties_only; + baton->diff_cmd = opt_state->diff_cmd; if (baton->txn_name) SVN_ERR(svn_fs_open_txn(&(baton->txn), baton->fs, @@ -2454,6 +2415,10 @@ main(int argc, const char *argv[]) opt_state.properties_only = TRUE; break; + case svnlook__diff_cmd: + opt_state.diff_cmd = opt_arg; + break; + default: SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); svn_pool_destroy(pool); Modified: subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c (original) +++ subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c Tue Nov 27 22:13:24 2012 @@ -926,7 +926,7 @@ usage(apr_pool_t *pool, int exit_val) " -X, --extra-args ARG append arguments from file ARG (one per line;\n" " use \"-\" to read from standard input)\n" " --config-dir ARG use ARG to override the config directory\n" - " --config-option ARG use ARG so override a configuration option\n" + " --config-option ARG use ARG to override a configuration option\n" " --no-auth-cache do not cache authentication tokens\n" " --version print version information\n"; svn_error_clear(svn_cmdline_fputs(msg, stream, pool)); Modified: subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c (original) +++ subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c Tue Nov 27 22:13:24 2012 @@ -39,6 +39,7 @@ #include "svnrdump.h" #include "private/svn_cmdline_private.h" +#include "private/svn_ra_private.h" @@ -248,6 +249,81 @@ replay_revend(svn_revnum_t revision, return SVN_NO_ERROR; } +#ifdef USE_EV2_IMPL +/* Print dumpstream-formatted information about REVISION. + * Implements the `svn_ra_replay_revstart_callback_t' interface. + */ +static svn_error_t * +replay_revstart_v2(svn_revnum_t revision, + void *replay_baton, + svn_editor_t **editor, + apr_hash_t *rev_props, + apr_pool_t *pool) +{ + struct replay_baton *rb = replay_baton; + apr_hash_t *normal_props; + svn_stringbuf_t *propstring; + svn_stream_t *stdout_stream; + svn_stream_t *revprop_stream; + + SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); + + /* Revision-number: 19 */ + SVN_ERR(svn_stream_printf(stdout_stream, pool, + SVN_REPOS_DUMPFILE_REVISION_NUMBER + ": %ld\n", revision)); + SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); + propstring = svn_stringbuf_create_ensure(0, pool); + revprop_stream = svn_stream_from_stringbuf(propstring, pool); + SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); + SVN_ERR(svn_stream_close(revprop_stream)); + + /* Prop-content-length: 13 */ + SVN_ERR(svn_stream_printf(stdout_stream, pool, + SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH + ": %" APR_SIZE_T_FMT "\n", propstring->len)); + + /* Content-length: 29 */ + SVN_ERR(svn_stream_printf(stdout_stream, pool, + SVN_REPOS_DUMPFILE_CONTENT_LENGTH + ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); + + /* Property data. */ + SVN_ERR(svn_stream_write(stdout_stream, propstring->data, + &(propstring->len))); + + SVN_ERR(svn_stream_puts(stdout_stream, "\n")); + SVN_ERR(svn_stream_close(stdout_stream)); + + SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision, + rb->stdout_stream, + rb->extra_ra_session, + check_cancel, NULL, pool, pool)); + + return SVN_NO_ERROR; +} + +/* Print progress information about the dump of REVISION. + Implements the `svn_ra_replay_revfinish_callback_t' interface. */ +static svn_error_t * +replay_revend_v2(svn_revnum_t revision, + void *replay_baton, + svn_editor_t *editor, + apr_hash_t *rev_props, + apr_pool_t *pool) +{ + /* No resources left to free. */ + struct replay_baton *rb = replay_baton; + + SVN_ERR(svn_editor_complete(editor)); + + if (! rb->quiet) + SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", + revision)); + return SVN_NO_ERROR; +} +#endif + /* Initialize the RA layer, and set *CTX to a new client context baton * allocated from POOL. Use CONFIG_DIR and pass USERNAME, PASSWORD, * CONFIG_DIR and NO_AUTH_CACHE to initialize the authorization baton. @@ -391,9 +467,16 @@ replay_revisions(svn_ra_session_t *sessi if (incremental) { +#ifndef USE_EV2_IMPL SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision, 0, TRUE, replay_revstart, replay_revend, replay_baton, pool)); +#else + SVN_ERR(svn_ra__replay_range_ev2(session, start_revision, end_revision, + 0, TRUE, replay_revstart_v2, + replay_revend_v2, replay_baton, + NULL, NULL, NULL, NULL, pool)); +#endif } else { @@ -432,9 +515,16 @@ replay_revisions(svn_ra_session_t *sessi /* Now go pick up additional revisions in the range, if any. */ if (start_revision <= end_revision) +#ifndef USE_EV2_IMPL SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision, 0, TRUE, replay_revstart, replay_revend, replay_baton, pool)); +#else + SVN_ERR(svn_ra__replay_range_ev2(session, start_revision, end_revision, + 0, TRUE, replay_revstart_v2, + replay_revend_v2, replay_baton, + NULL, NULL, NULL, NULL, pool)); +#endif } SVN_ERR(svn_stream_close(stdout_stream)); Modified: subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h (original) +++ subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h Tue Nov 27 22:13:24 2012 @@ -53,6 +53,17 @@ svn_rdump__get_dump_editor(const svn_del void *cancel_baton, apr_pool_t *pool); +/* Same as above, only returns an Ev2 editor. */ +svn_error_t * +svn_rdump__get_dump_editor_v2(svn_editor_t **editor, + svn_revnum_t revision, + svn_stream_t *stream, + svn_ra_session_t *ra_session, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool, + apr_pool_t *result_pool); + /** * Load the dumpstream carried in @a stream to the location described Modified: subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c (original) +++ subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c Tue Nov 27 22:13:24 2012 @@ -98,7 +98,7 @@ static int canonicalize_username(sasl_co static sasl_callback_t callbacks[] = { - { SASL_CB_CANON_USER, (void*)canonicalize_username, NULL }, + { SASL_CB_CANON_USER, (int (*)(void))canonicalize_username, NULL }, { SASL_CB_LIST_END, NULL, NULL } }; Modified: subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py?rev=1414433&r1=1414432&r2=1414433&view=diff ============================================================================== --- subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py (original) +++ subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py Tue Nov 27 22:13:24 2012 @@ -196,8 +196,14 @@ def make_diff_prop_added(pname, pval): def make_diff_prop_modified(pname, pval1, pval2): """Return a property diff for modification of property PNAME, old value - PVAL1, new value PVAL2. PVAL is a single string with no embedded - newlines. Return the result as a list of newline-terminated strings.""" + PVAL1, new value PVAL2. + + PVAL is a single string with no embedded newlines. A newline at the + end is significant: without it, we add an extra line saying '\ No + newline at end of property'. + + Return the result as a list of newline-terminated strings. + """ return [ "Modified: " + pname + "\n", "## -1 +1 ##\n", @@ -4075,6 +4081,43 @@ def diff_properties_only(sbox): 'diff', '--properties-only', '-r', 'PREV', 'iota') +def diff_properties_no_newline(sbox): + "diff props; check no-newline-at-end messages" + + sbox.build() + old_cwd = os.getcwd() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + no_nl = "\\ No newline at end of property\n" + propchange_header = "Modified: p.*\n" + + subtests = [ + ('p1', 'val1', 'val2' ), + ('p2', 'val1', 'val2\n'), + ('p3', 'val1\n', 'val2' ), + ('p4', 'val1\n', 'val2\n'), + ] + + # The "before" state. + for pname, old_val, new_val in subtests: + sbox.simple_propset(pname, old_val, 'iota') + sbox.simple_commit() # r2 + + # Test one change at a time. (Because, with multiple changes, the order + # may not be predictable.) + for pname, old_val, new_val in subtests: + expected_output = \ + make_diff_header("iota", "revision 1", "working copy") + \ + make_diff_prop_header("iota") + \ + make_diff_prop_modified(pname, old_val, new_val) + + sbox.simple_propset(pname, new_val, 'iota') + svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff') + svntest.actions.run_and_verify_svn(None, None, [], 'revert', 'iota') + + os.chdir(old_cwd) + ######################################################################## #Run the tests @@ -4146,6 +4189,7 @@ test_list = [ None, diff_deleted_url, diff_arbitrary_files_and_dirs, diff_properties_only, + diff_properties_no_newline, ] if __name__ == '__main__':