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 5F54D19AED for ; Fri, 29 Apr 2016 18:39:23 +0000 (UTC) Received: (qmail 11948 invoked by uid 500); 29 Apr 2016 18:39:23 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 11918 invoked by uid 500); 29 Apr 2016 18:39:23 -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 11908 invoked by uid 99); 29 Apr 2016 18:39:23 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 29 Apr 2016 18:39:23 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id BFD621A0120 for ; Fri, 29 Apr 2016 18:39:22 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.799 X-Spam-Level: * X-Spam-Status: No, score=1.799 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-0.001] autolearn=disabled Received: from mx2-lw-us.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id KbWko2XU0V9r for ; Fri, 29 Apr 2016 18:39:06 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx2-lw-us.apache.org (ASF Mail Server at mx2-lw-us.apache.org) with ESMTP id AF2D65FACD for ; Fri, 29 Apr 2016 18:39:05 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id E7C9CE1326 for ; Fri, 29 Apr 2016 18:39:02 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 3E2863A1B38 for ; Fri, 29 Apr 2016 18:39:02 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1741682 [15/26] - in /subversion/branches/authzperf: ./ build/ build/ac-macros/ build/generator/ contrib/server-side/svncutter/ notes/ notes/api-errata/1.9/ notes/move-tracking/ subversion/ subversion/bindings/ctypes-python/ subversion/bin... Date: Fri, 29 Apr 2016 18:38:56 -0000 To: commits@subversion.apache.org From: stefan2@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20160429183902.3E2863A1B38@svn01-us-west.apache.org> Modified: subversion/branches/authzperf/subversion/libsvn_repos/log.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_repos/log.c?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_repos/log.c (original) +++ subversion/branches/authzperf/subversion/libsvn_repos/log.c Fri Apr 29 18:38:53 2016 @@ -43,8 +43,21 @@ #include "private/svn_mergeinfo_private.h" #include "private/svn_subr_private.h" #include "private/svn_sorts_private.h" +#include "private/svn_string_private.h" +/* This is a mere convenience struct such that we don't need to pass that + many parameters around individually. */ +typedef struct log_callbacks_t +{ + svn_repos_path_change_receiver_t path_change_receiver; + void *path_change_receiver_baton; + svn_repos_log_entry_receiver_t revision_receiver; + void *revision_receiver_baton; + svn_repos_authz_func_t authz_read_func; + void *authz_read_baton; +} log_callbacks_t; + svn_error_t * svn_repos_check_revision_access(svn_repos_revision_access_level_t *access_level, @@ -56,11 +69,11 @@ svn_repos_check_revision_access(svn_repo { svn_fs_t *fs = svn_repos_fs(repos); svn_fs_root_t *rev_root; - apr_hash_t *changes; - apr_hash_index_t *hi; + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; svn_boolean_t found_readable = FALSE; svn_boolean_t found_unreadable = FALSE; - apr_pool_t *subpool; + apr_pool_t *iterpool; /* By default, we'll grant full read access to REVISION. */ *access_level = svn_repos_revision_access_full; @@ -71,25 +84,27 @@ svn_repos_check_revision_access(svn_repo /* Fetch the changes associated with REVISION. */ SVN_ERR(svn_fs_revision_root(&rev_root, fs, revision, pool)); - SVN_ERR(svn_fs_paths_changed2(&changes, rev_root, pool)); + SVN_ERR(svn_fs_paths_changed3(&iterator, rev_root, pool, pool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + + /* No changed paths? We're done. - /* No changed paths? We're done. */ - if (apr_hash_count(changes) == 0) + Note that the check at "decision:" assumes that at least one + path has been processed. So, this actually affects functionality. */ + if (!change) return SVN_NO_ERROR; /* Otherwise, we have to check the readability of each changed path, or at least enough to answer the question asked. */ - subpool = svn_pool_create(pool); - for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi)) + iterpool = svn_pool_create(pool); + while (change) { - const char *key = apr_hash_this_key(hi); - svn_fs_path_change2_t *change = apr_hash_this_val(hi); svn_boolean_t readable; - svn_pool_clear(subpool); + svn_pool_clear(iterpool); - SVN_ERR(authz_read_func(&readable, rev_root, key, - authz_read_baton, subpool)); + SVN_ERR(authz_read_func(&readable, rev_root, change->path.data, + authz_read_baton, iterpool)); if (! readable) found_unreadable = TRUE; else @@ -109,15 +124,16 @@ svn_repos_check_revision_access(svn_repo svn_revnum_t copyfrom_rev; SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path, - rev_root, key, subpool)); + rev_root, change->path.data, + iterpool)); if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev)) { svn_fs_root_t *copyfrom_root; SVN_ERR(svn_fs_revision_root(©from_root, fs, - copyfrom_rev, subpool)); + copyfrom_rev, iterpool)); SVN_ERR(authz_read_func(&readable, copyfrom_root, copyfrom_path, - authz_read_baton, subpool)); + authz_read_baton, iterpool)); if (! readable) found_unreadable = TRUE; @@ -134,10 +150,12 @@ svn_repos_check_revision_access(svn_repo default: break; } + + SVN_ERR(svn_fs_path_change_get(&change, iterator)); } decision: - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); /* Either every changed path was unreadable... */ if (! found_readable) @@ -152,181 +170,14 @@ svn_repos_check_revision_access(svn_repo } -/* Core filter logic of detect_changed(). Convert FS API-level path change - * CHANGE of PATH in FS at ROOT to higher level API *ITEM_P. +/* Find all significant changes under ROOT and, if not NULL, report them + * to the CALLBACKS->PATH_CHANGE_RECEIVER. "Significant" means that the + * text or properties of the node were changed, or that the node was added + * or deleted. * - * If optional AUTHZ_READ_FUNC is non-NULL, then use it (with - * AUTHZ_READ_BATON and FS) to check whether PATH and what parts of CHANGE - * (copyfrom_path) is readable. Update *FOUND_READABLE and - * *FOUND_UNREADABLE accordingly. - * - * NOTE: Much of this loop is going to look quite similar to - * svn_repos_check_revision_access(), but we have to do more things - * here, so we'll live with the duplication. - */ -static svn_error_t * -check_changed_path(svn_log_changed_path2_t **item_p, - svn_boolean_t *found_readable, - svn_boolean_t *found_unreadable, - svn_fs_root_t *root, - svn_fs_t *fs, - const char *path, - svn_fs_path_change2_t *change, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - char action; - svn_log_changed_path2_t *item; - - /* Skip path if unreadable. */ - if (authz_read_func) - { - svn_boolean_t readable; - SVN_ERR(authz_read_func(&readable, - root, path, - authz_read_baton, scratch_pool)); - if (! readable) - { - *found_unreadable = TRUE; - return SVN_NO_ERROR; - } - } - - /* At least one changed-path was readable. */ - *found_readable = TRUE; - - switch (change->change_kind) - { - case svn_fs_path_change_reset: - return SVN_NO_ERROR; - - case svn_fs_path_change_add: - action = 'A'; - break; - - case svn_fs_path_change_replace: - action = 'R'; - break; - - case svn_fs_path_change_delete: - action = 'D'; - break; - - case svn_fs_path_change_modify: - default: - action = 'M'; - break; - } - - item = svn_log_changed_path2_create(result_pool); - item->action = action; - item->node_kind = change->node_kind; - item->copyfrom_rev = SVN_INVALID_REVNUM; - item->text_modified = change->text_mod ? svn_tristate_true - : svn_tristate_false; - item->props_modified = change->prop_mod ? svn_tristate_true - : svn_tristate_false; - - /* Pre-1.6 revision files don't store the change path kind, so fetch - it manually. */ - if (item->node_kind == svn_node_unknown) - { - svn_fs_root_t *check_root = root; - const char *check_path = path; - - /* Deleted items don't exist so check earlier revision. We - know the parent must exist and could be a copy */ - if (change->change_kind == svn_fs_path_change_delete) - { - svn_fs_history_t *history; - svn_revnum_t prev_rev; - const char *parent_path, *name; - - svn_fspath__split(&parent_path, &name, path, scratch_pool); - - SVN_ERR(svn_fs_node_history2(&history, root, parent_path, - scratch_pool, scratch_pool)); - - /* Two calls because the first call returns the original - revision as the deleted child means it is 'interesting' */ - SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, scratch_pool, - scratch_pool)); - SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, scratch_pool, - scratch_pool)); - - SVN_ERR(svn_fs_history_location(&parent_path, &prev_rev, history, - scratch_pool)); - SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, - scratch_pool)); - check_path = svn_fspath__join(parent_path, name, scratch_pool); - } - - SVN_ERR(svn_fs_check_path(&item->node_kind, check_root, check_path, - scratch_pool)); - } - - - if ((action == 'A') || (action == 'R')) - { - const char *copyfrom_path = change->copyfrom_path; - svn_revnum_t copyfrom_rev = change->copyfrom_rev; - - /* the following is a potentially expensive operation since on FSFS - we will follow the DAG from ROOT to PATH and that requires - actually reading the directories along the way. */ - if (!change->copyfrom_known) - { - SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path, - root, path, scratch_pool)); - copyfrom_path = apr_pstrdup(result_pool, copyfrom_path); - } - - if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev)) - { - svn_boolean_t readable = TRUE; - - if (authz_read_func) - { - svn_fs_root_t *copyfrom_root; - - SVN_ERR(svn_fs_revision_root(©from_root, fs, - copyfrom_rev, scratch_pool)); - SVN_ERR(authz_read_func(&readable, - copyfrom_root, copyfrom_path, - authz_read_baton, scratch_pool)); - if (! readable) - *found_unreadable = TRUE; - } - - if (readable) - { - item->copyfrom_path = copyfrom_path; - item->copyfrom_rev = copyfrom_rev; - } - } - } - - *item_p = item; - return SVN_NO_ERROR; -} - -/* Store as keys in CHANGED the paths of all node in ROOT that show a - * significant change. "Significant" means that the text or - * properties of the node were changed, or that the node was added or - * deleted. - * - * The CHANGED hash set and its keys and values are allocated in POOL; - * keys are const char * paths and values are svn_log_changed_path_t. - * - * To prevent changes from being processed over and over again, the - * changed paths for ROOT may be passed in PREFETCHED_CHANGES. If the - * latter is NULL, we will request the list inside this function. - * - * If optional AUTHZ_READ_FUNC is non-NULL, then use it (with - * AUTHZ_READ_BATON and FS) to check whether each changed-path (and - * copyfrom_path) is readable: + * If optional CALLBACKS->AUTHZ_READ_FUNC is non-NULL, then use it (with + * CALLBACKS->AUTHZ_READ_BATON and FS) to check whether each changed-path + * (and copyfrom_path) is readable: * * - If absolutely every changed-path (and copyfrom_path) is * readable, then return the full CHANGED hash, and set @@ -344,41 +195,22 @@ check_changed_path(svn_log_changed_path2 */ static svn_error_t * detect_changed(svn_repos_revision_access_level_t *access_level, - apr_hash_t **changed, svn_fs_root_t *root, svn_fs_t *fs, - apr_hash_t *prefetched_changes, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, - apr_pool_t *result_pool) + const log_callbacks_t *callbacks, + apr_pool_t *scratch_pool) { - apr_hash_t *changes; - apr_hash_index_t *hi; + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; apr_pool_t *iterpool; svn_boolean_t found_readable = FALSE; svn_boolean_t found_unreadable = FALSE; - /* If we create the CHANGES hash ourselves, we can reuse it as the - * result hash as it contains the exact same keys - but with _all_ - * values being replaced by structs of a different type. */ - if (prefetched_changes == NULL) - { - SVN_ERR(svn_fs_paths_changed2(&changes, root, result_pool)); + /* Retrieve the first change in the list. */ + SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); - /* If we are going to filter the results, we won't use the exact - * same keys but put them into a new hash. */ - if (authz_read_func) - *changed = svn_hash__make(result_pool); - else - *changed = changes; - } - else - { - changes = prefetched_changes; - *changed = svn_hash__make(result_pool); - } - - if (apr_hash_count(changes) == 0) + if (!change) { /* No paths changed in this revision? Uh, sure, I guess the revision is readable, then. */ @@ -386,53 +218,122 @@ detect_changed(svn_repos_revision_access return SVN_NO_ERROR; } - iterpool = svn_pool_create(result_pool); - - /* Authz can be much faster when paths are being checked in tree order. */ - if (authz_read_func && apr_hash_count(changes) > 1) + iterpool = svn_pool_create(scratch_pool); + while (change) { - apr_array_header_t *sorted; - int i; - apr_pool_t *scratch_pool = svn_pool_create(result_pool); + /* NOTE: Much of this loop is going to look quite similar to + svn_repos_check_revision_access(), but we have to do more things + here, so we'll live with the duplication. */ + const char *path = change->path.data; + svn_pool_clear(iterpool); - sorted = svn_sort__hash(changes, svn_sort_compare_items_lexically, - scratch_pool); - for (i = 0; i < sorted->nelts; i++) + /* Skip path if unreadable. */ + if (callbacks->authz_read_func) { - svn_sort__item_t item = APR_ARRAY_IDX(sorted, i, svn_sort__item_t); - const char *path = item.key; - svn_fs_path_change2_t *change = item.value; + svn_boolean_t readable; + SVN_ERR(callbacks->authz_read_func(&readable, root, path, + callbacks->authz_read_baton, + iterpool)); + if (! readable) + { + found_unreadable = TRUE; + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + continue; + } + } - svn_log_changed_path2_t *changed_item = NULL; + /* At least one changed-path was readable. */ + found_readable = TRUE; - svn_pool_clear(iterpool); - SVN_ERR(check_changed_path(&changed_item, &found_readable, - &found_unreadable, root, fs, path, - change, authz_read_func, - authz_read_baton, result_pool, iterpool)); + /* Pre-1.6 revision files don't store the change path kind, so fetch + it manually. */ + if (change->node_kind == svn_node_unknown) + { + svn_fs_root_t *check_root = root; + const char *check_path = path; + + /* Deleted items don't exist so check earlier revision. We + know the parent must exist and could be a copy */ + if (change->change_kind == svn_fs_path_change_delete) + { + svn_fs_history_t *history; + svn_revnum_t prev_rev; + const char *parent_path, *name; + + svn_fspath__split(&parent_path, &name, path, iterpool); + + SVN_ERR(svn_fs_node_history2(&history, root, parent_path, + iterpool, iterpool)); + + /* Two calls because the first call returns the original + revision as the deleted child means it is 'interesting' */ + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); + + SVN_ERR(svn_fs_history_location(&parent_path, &prev_rev, + history, iterpool)); + SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, + iterpool)); + check_path = svn_fspath__join(parent_path, name, iterpool); + } - if (changed_item) - svn_hash_sets(*changed, path, changed_item); + SVN_ERR(svn_fs_check_path(&change->node_kind, check_root, check_path, + iterpool)); } - svn_pool_destroy(scratch_pool); - } - else - { - for (hi = apr_hash_first(result_pool, changes); hi; hi = apr_hash_next(hi)) + if ( (change->change_kind == svn_fs_path_change_add) + || (change->change_kind == svn_fs_path_change_replace)) { - const char *path = apr_hash_this_key(hi); - apr_ssize_t path_len = apr_hash_this_key_len(hi); - svn_fs_path_change2_t *change = apr_hash_this_val(hi); - svn_log_changed_path2_t *changed_item; + const char *copyfrom_path = change->copyfrom_path; + svn_revnum_t copyfrom_rev = change->copyfrom_rev; - svn_pool_clear(iterpool); - SVN_ERR(check_changed_path(&changed_item, &found_readable, - &found_unreadable, root, fs, path, - change, NULL, NULL, result_pool, iterpool)); + /* the following is a potentially expensive operation since on FSFS + we will follow the DAG from ROOT to PATH and that requires + actually reading the directories along the way. */ + if (!change->copyfrom_known) + { + SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path, + root, path, iterpool)); + change->copyfrom_known = TRUE; + } - apr_hash_set(*changed, path, path_len, changed_item); + if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev)) + { + svn_boolean_t readable = TRUE; + + if (callbacks->authz_read_func) + { + svn_fs_root_t *copyfrom_root; + + SVN_ERR(svn_fs_revision_root(©from_root, fs, + copyfrom_rev, iterpool)); + SVN_ERR(callbacks->authz_read_func(&readable, + copyfrom_root, + copyfrom_path, + callbacks->authz_read_baton, + iterpool)); + if (! readable) + found_unreadable = TRUE; + } + + if (readable) + { + change->copyfrom_path = copyfrom_path; + change->copyfrom_rev = copyfrom_rev; + } + } } + + if (callbacks->path_change_receiver) + SVN_ERR(callbacks->path_change_receiver( + callbacks->path_change_receiver_baton, + change, + iterpool)); + + /* Next changed path. */ + SVN_ERR(svn_fs_path_change_get(&change, iterator)); } svn_pool_destroy(iterpool); @@ -659,23 +560,21 @@ next_history_rev(const apr_array_header_ /* Set *DELETED_MERGEINFO_CATALOG and *ADDED_MERGEINFO_CATALOG to catalogs describing how mergeinfo values on paths (which are the - keys of those catalogs) were changed in REV. If *PREFETCHED_CHANGES - already contains the changed paths for REV, use that. Otherwise, - request that data and return it in *PREFETCHED_CHANGES. */ + keys of those catalogs) were changed in REV. */ /* ### TODO: This would make a *great*, useful public function, ### svn_repos_fs_mergeinfo_changed()! -- cmpilato */ static svn_error_t * fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, svn_mergeinfo_catalog_t *added_mergeinfo_catalog, - apr_hash_t **prefetched_changes, svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_fs_root_t *root; - apr_pool_t *iterpool; - apr_hash_index_t *hi; + apr_pool_t *iterpool, *iterator_pool; + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; svn_boolean_t any_mergeinfo = FALSE; svn_boolean_t any_copy = FALSE; @@ -687,56 +586,69 @@ fs_mergeinfo_changed(svn_mergeinfo_catal if (rev == 0) return SVN_NO_ERROR; + /* FS iterators are potentially heavy objects. + * Hold them in a separate pool to clean them up asap. */ + iterator_pool = svn_pool_create(scratch_pool); + /* We're going to use the changed-paths information for REV to narrow down our search. */ SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool)); - if (*prefetched_changes == NULL) - SVN_ERR(svn_fs_paths_changed2(prefetched_changes, root, scratch_pool)); + SVN_ERR(svn_fs_paths_changed3(&iterator, root, iterator_pool, + iterator_pool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); /* Look for copies and (potential) mergeinfo changes. - We will use both flags to take shortcuts further down the road. */ - for (hi = apr_hash_first(scratch_pool, *prefetched_changes); - hi; - hi = apr_hash_next(hi)) - { - svn_fs_path_change2_t *change = apr_hash_this_val(hi); + We will use both flags to take shortcuts further down the road. + The critical information here is whether there are any copies + because that greatly influences the costs for log processing. + So, it is faster to iterate over the changes twice - in the worst + case b/c most times there is no m/i at all and we exit out early + without any overhead. + */ + while (change && (!any_mergeinfo || !any_copy)) + { /* If there was a prop change and we are not positive that _no_ mergeinfo change happened, we must assume that it might have. */ if (change->mergeinfo_mod != svn_tristate_false && change->prop_mod) any_mergeinfo = TRUE; - switch (change->change_kind) - { - case svn_fs_path_change_add: - case svn_fs_path_change_replace: - any_copy = TRUE; - break; + if ( (change->change_kind == svn_fs_path_change_add) + || (change->change_kind == svn_fs_path_change_replace)) + any_copy = TRUE; - default: - break; - } + SVN_ERR(svn_fs_path_change_get(&change, iterator)); } /* No potential mergeinfo changes? We're done. */ if (! any_mergeinfo) - return SVN_NO_ERROR; + { + svn_pool_destroy(iterator_pool); + return SVN_NO_ERROR; + } + + /* There is or may be some m/i change. Look closely now. */ + svn_pool_clear(iterator_pool); + SVN_ERR(svn_fs_paths_changed3(&iterator, root, iterator_pool, + iterator_pool)); /* Loop over changes, looking for anything that might carry an svn:mergeinfo change and is one of our paths of interest, or a child or [grand]parent directory thereof. */ iterpool = svn_pool_create(scratch_pool); - for (hi = apr_hash_first(scratch_pool, *prefetched_changes); - hi; - hi = apr_hash_next(hi)) + while (TRUE) { const char *changed_path; - svn_fs_path_change2_t *change = apr_hash_this_val(hi); const char *base_path = NULL; svn_revnum_t base_rev = SVN_INVALID_REVNUM; svn_fs_root_t *base_root = NULL; svn_string_t *prev_mergeinfo_value = NULL, *mergeinfo_value; + /* Next change. */ + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + if (!change) + break; + /* Cheap pre-checks that don't require memory allocation etc. */ /* No mergeinfo change? -> nothing to do here. */ @@ -748,7 +660,7 @@ fs_mergeinfo_changed(svn_mergeinfo_catal continue; /* Begin actual processing */ - changed_path = apr_hash_this_key(hi); + changed_path = change->path.data; svn_pool_clear(iterpool); switch (change->change_kind) @@ -906,20 +818,18 @@ fs_mergeinfo_changed(svn_mergeinfo_catal } svn_pool_destroy(iterpool); + svn_pool_destroy(iterator_pool); + return SVN_NO_ERROR; } /* Determine what (if any) mergeinfo for PATHS was modified in revision REV, returning the differences for added mergeinfo in - *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO. - If *PREFETCHED_CHANGES already contains the changed paths for - REV, use that. Otherwise, request that data and return it in - *PREFETCHED_CHANGES. */ + *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO. */ static svn_error_t * get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, svn_mergeinfo_t *deleted_mergeinfo, - apr_hash_t **prefetched_changes, svn_fs_t *fs, const apr_array_header_t *paths, svn_revnum_t rev, @@ -948,7 +858,6 @@ get_combined_mergeinfo_changes(svn_merge /* Fetch the mergeinfo changes for REV. */ err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog, &added_mergeinfo_catalog, - prefetched_changes, fs, rev, scratch_pool, scratch_pool); if (err) @@ -1137,38 +1046,31 @@ get_combined_mergeinfo_changes(svn_merge /* Fill LOG_ENTRY with history information in FS at REV. */ static svn_error_t * -fill_log_entry(svn_log_entry_t *log_entry, +fill_log_entry(svn_repos_log_entry_t *log_entry, svn_revnum_t rev, svn_fs_t *fs, - apr_hash_t *prefetched_changes, - svn_boolean_t discover_changed_paths, const apr_array_header_t *revprops, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, + const log_callbacks_t *callbacks, apr_pool_t *pool) { - apr_hash_t *r_props, *changed_paths = NULL; + apr_hash_t *r_props; svn_boolean_t get_revprops = TRUE, censor_revprops = FALSE; svn_boolean_t want_revprops = !revprops || revprops->nelts; /* Discover changed paths if the user requested them or if we need to check that they are readable. */ if ((rev > 0) - && (authz_read_func || discover_changed_paths)) + && (callbacks->authz_read_func || callbacks->path_change_receiver)) { svn_fs_root_t *newroot; svn_repos_revision_access_level_t access_level; SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool)); - SVN_ERR(detect_changed(&access_level, &changed_paths, - newroot, fs, prefetched_changes, - authz_read_func, authz_read_baton, - pool)); + SVN_ERR(detect_changed(&access_level, newroot, fs, callbacks, pool)); if (access_level == svn_repos_revision_access_none) { /* All changed-paths are unreadable, so clear all fields. */ - changed_paths = NULL; get_revprops = FALSE; } else if (access_level == svn_repos_revision_access_partial) @@ -1178,17 +1080,13 @@ fill_log_entry(svn_log_entry_t *log_entr missing from the hash.) */ censor_revprops = TRUE; } - - /* It may be the case that an authz func was passed in, but - the user still doesn't want to see any changed-paths. */ - if (! discover_changed_paths) - changed_paths = NULL; } if (get_revprops && want_revprops) { /* User is allowed to see at least some revprops. */ - SVN_ERR(svn_fs_revision_proplist(&r_props, fs, rev, pool)); + SVN_ERR(svn_fs_revision_proplist2(&r_props, fs, rev, FALSE, pool, + pool)); if (revprops == NULL) { /* Requested all revprops... */ @@ -1216,9 +1114,9 @@ fill_log_entry(svn_log_entry_t *log_entr we want static initialization here and must therefore emulate strlen(x) by sizeof(x)-1. */ static const svn_string_t svn_prop_revision_author - = {SVN_PROP_REVISION_AUTHOR, sizeof(SVN_PROP_REVISION_AUTHOR)-1}; + = SVN__STATIC_STRING(SVN_PROP_REVISION_AUTHOR); static const svn_string_t svn_prop_revision_date - = {SVN_PROP_REVISION_DATE, sizeof(SVN_PROP_REVISION_DATE)-1}; + = SVN__STATIC_STRING(SVN_PROP_REVISION_DATE); /* often only the standard revprops got requested and delivered. In that case, we can simply pass the hash on. */ @@ -1258,20 +1156,83 @@ fill_log_entry(svn_log_entry_t *log_entr } } - log_entry->changed_paths = changed_paths; - log_entry->changed_paths2 = changed_paths; log_entry->revision = rev; return SVN_NO_ERROR; } -/* Send a log message for REV to RECEIVER with its RECEIVER_BATON. +/* Baton type to be used with the interesting_merge callback. */ +typedef struct interesting_merge_baton_t +{ + /* What we are looking for. */ + svn_revnum_t rev; + svn_mergeinfo_t log_target_history_as_mergeinfo; + + /* Set to TRUE if we found it. */ + svn_boolean_t found_rev_of_interest; + + /* We need to invoke this user-provided callback if not NULL. */ + svn_repos_path_change_receiver_t inner; + void *inner_baton; +} interesting_merge_baton_t; + +/* Implements svn_repos_path_change_receiver_t. + * *BATON is a interesting_merge_baton_t. + * + * If BATON->REV a merged revision that is not already part of + * BATON->LOG_TARGET_HISTORY_AS_MERGEINFO, set BATON->FOUND_REV_OF_INTEREST. + */ +static svn_error_t * +interesting_merge(void *baton, + svn_fs_path_change3_t *change, + apr_pool_t *scratch_pool) +{ + interesting_merge_baton_t *b = baton; + apr_hash_index_t *hi; + + if (b->inner) + SVN_ERR(b->inner(b->inner_baton, change, scratch_pool)); + + if (b->found_rev_of_interest) + return SVN_NO_ERROR; + + /* Look at each path on the log target's mergeinfo. */ + for (hi = apr_hash_first(scratch_pool, b->log_target_history_as_mergeinfo); + hi; + hi = apr_hash_next(hi)) + { + const char *mergeinfo_path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); + + /* Check whether CHANGED_PATH at revision REV is a child of + a (path, revision) tuple in LOG_TARGET_HISTORY_AS_MERGEINFO. */ + if (svn_fspath__skip_ancestor(mergeinfo_path, change->path.data)) + { + int i; + + for (i = 0; i < rangelist->nelts; i++) + { + svn_merge_range_t *range + = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); + if (b->rev > range->start && b->rev <= range->end) + return SVN_NO_ERROR; + } + } + } + + b->found_rev_of_interest = TRUE; + + return SVN_NO_ERROR; +} + +/* Send a log message for REV to the CALLBACKS. FS is used with REV to fetch the interesting history information, such as changed paths, revprops, etc. - The detect_changed function is used if either AUTHZ_READ_FUNC is - not NULL, or if DISCOVER_CHANGED_PATHS is TRUE. See it for details. + The detect_changed function is used if either CALLBACKS->AUTHZ_READ_FUNC + is not NULL, or if CALLBACKS->PATH_CHANGE_RECEIVER is not NULL. + See it for details. If DESCENDING_ORDER is true, send child messages in descending order. @@ -1293,107 +1254,51 @@ fill_log_entry(svn_log_entry_t *log_entr static svn_error_t * send_log(svn_revnum_t rev, svn_fs_t *fs, - apr_hash_t *prefetched_changes, svn_mergeinfo_t log_target_history_as_mergeinfo, svn_bit_array__t *nested_merges, - svn_boolean_t discover_changed_paths, svn_boolean_t subtractive_merge, svn_boolean_t handling_merged_revision, const apr_array_header_t *revprops, svn_boolean_t has_children, - svn_log_entry_receiver_t receiver, - void *receiver_baton, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, + const log_callbacks_t *callbacks, apr_pool_t *pool) { - svn_log_entry_t *log_entry; - /* Assume we want to send the log for REV. */ - svn_boolean_t found_rev_of_interest = TRUE; - - log_entry = svn_log_entry_create(pool); - SVN_ERR(fill_log_entry(log_entry, rev, fs, prefetched_changes, - discover_changed_paths || handling_merged_revision, - revprops, authz_read_func, authz_read_baton, pool)); - log_entry->has_children = has_children; - log_entry->subtractive_merge = subtractive_merge; + svn_repos_log_entry_t log_entry = { 0 }; + log_callbacks_t my_callbacks = *callbacks; + + interesting_merge_baton_t baton; /* Is REV a merged revision that is already part of LOG_TARGET_HISTORY_AS_MERGEINFO? If so then there is no - need to send it, since it already was (or will be) sent. */ + need to send it, since it already was (or will be) sent. + + Use our callback to snoop through the changes. */ if (handling_merged_revision - && log_entry->changed_paths2 && log_target_history_as_mergeinfo && apr_hash_count(log_target_history_as_mergeinfo)) { - apr_hash_index_t *hi; - apr_pool_t *iterpool = svn_pool_create(pool); - - /* REV was merged in, but it might already be part of the log target's - natural history, so change our starting assumption. */ - found_rev_of_interest = FALSE; - - /* Look at each changed path in REV. */ - for (hi = apr_hash_first(pool, log_entry->changed_paths2); - hi; - hi = apr_hash_next(hi)) - { - svn_boolean_t path_is_in_history = FALSE; - const char *changed_path = apr_hash_this_key(hi); - apr_hash_index_t *hi2; - - /* Look at each path on the log target's mergeinfo. */ - for (hi2 = apr_hash_first(iterpool, - log_target_history_as_mergeinfo); - hi2; - hi2 = apr_hash_next(hi2)) - { - const char *mergeinfo_path = apr_hash_this_key(hi2); - svn_rangelist_t *rangelist = apr_hash_this_val(hi2); - - /* Check whether CHANGED_PATH at revision REV is a child of - a (path, revision) tuple in LOG_TARGET_HISTORY_AS_MERGEINFO. */ - if (svn_fspath__skip_ancestor(mergeinfo_path, changed_path)) - { - int i; - - for (i = 0; i < rangelist->nelts; i++) - { - svn_merge_range_t *range = - APR_ARRAY_IDX(rangelist, i, - svn_merge_range_t *); - if (rev > range->start && rev <= range->end) - { - path_is_in_history = TRUE; - break; - } - } - } - if (path_is_in_history) - break; - } - svn_pool_clear(iterpool); - - if (!path_is_in_history) - { - /* If even one path in LOG_ENTRY->CHANGED_PATHS2 is not part of - LOG_TARGET_HISTORY_AS_MERGEINFO, then we want to send the - log for REV. */ - found_rev_of_interest = TRUE; - break; - } - } - svn_pool_destroy(iterpool); + baton.found_rev_of_interest = FALSE; + baton.rev = rev; + baton.log_target_history_as_mergeinfo = log_target_history_as_mergeinfo; + baton.inner = callbacks->path_change_receiver; + baton.inner_baton = callbacks->path_change_receiver_baton; + + my_callbacks.path_change_receiver = interesting_merge; + my_callbacks.path_change_receiver_baton = &baton; + callbacks = &my_callbacks; + } + else + { + baton.found_rev_of_interest = TRUE; } - /* If we only got changed paths the sake of detecting redundant merged - revisions, then be sure we don't send that info to the receiver. */ - if (!discover_changed_paths && handling_merged_revision) - log_entry->changed_paths = log_entry->changed_paths2 = NULL; + SVN_ERR(fill_log_entry(&log_entry, rev, fs, revprops, callbacks, pool)); + log_entry.has_children = has_children; + log_entry.subtractive_merge = subtractive_merge; /* Send the entry to the receiver, unless it is a redundant merged revision. */ - if (found_rev_of_interest) + if (baton.found_rev_of_interest) { apr_pool_t *scratch_pool; @@ -1417,7 +1322,8 @@ send_log(svn_revnum_t rev, /* Pass a scratch pool to ensure no temporary state stored by the receiver callback persists. */ scratch_pool = svn_pool_create(pool); - SVN_ERR(receiver(receiver_baton, log_entry, scratch_pool)); + SVN_ERR(callbacks->revision_receiver(callbacks->revision_receiver_baton, + &log_entry, scratch_pool)); svn_pool_destroy(scratch_pool); } @@ -1766,7 +1672,6 @@ do_logs(svn_fs_t *fs, svn_revnum_t hist_start, svn_revnum_t hist_end, int limit, - svn_boolean_t discover_changed_paths, svn_boolean_t strict_node_history, svn_boolean_t include_merged_revisions, svn_boolean_t handling_merged_revisions, @@ -1774,10 +1679,7 @@ do_logs(svn_fs_t *fs, svn_boolean_t ignore_missing_locations, const apr_array_header_t *revprops, svn_boolean_t descending_order, - svn_log_entry_receiver_t receiver, - void *receiver_baton, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, + log_callbacks_t *callbacks, apr_pool_t *pool); /* Comparator function for handle_merged_revisions(). Sorts path_list_range @@ -1819,17 +1721,13 @@ handle_merged_revisions(svn_revnum_t rev svn_mergeinfo_t processed, svn_mergeinfo_t added_mergeinfo, svn_mergeinfo_t deleted_mergeinfo, - svn_boolean_t discover_changed_paths, svn_boolean_t strict_node_history, const apr_array_header_t *revprops, - svn_log_entry_receiver_t receiver, - void *receiver_baton, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, + log_callbacks_t *callbacks, apr_pool_t *pool) { apr_array_header_t *combined_list = NULL; - svn_log_entry_t *empty_log_entry; + svn_repos_log_entry_t empty_log_entry = { 0 }; apr_pool_t *iterpool; int i; @@ -1860,17 +1758,16 @@ handle_merged_revisions(svn_revnum_t rev SVN_ERR(do_logs(fs, pl_range->paths, log_target_history_as_mergeinfo, processed, nested_merges, pl_range->range.start, pl_range->range.end, 0, - discover_changed_paths, strict_node_history, + strict_node_history, TRUE, pl_range->reverse_merge, TRUE, TRUE, - revprops, TRUE, receiver, receiver_baton, - authz_read_func, authz_read_baton, iterpool)); + revprops, TRUE, callbacks, iterpool)); } svn_pool_destroy(iterpool); /* Send the empty revision. */ - empty_log_entry = svn_log_entry_create(pool); - empty_log_entry->revision = SVN_INVALID_REVNUM; - return (*receiver)(receiver_baton, empty_log_entry, pool); + empty_log_entry.revision = SVN_INVALID_REVNUM; + return (callbacks->revision_receiver)(callbacks->revision_receiver_baton, + &empty_log_entry, pool); } /* This is used by do_logs to differentiate between forward and @@ -1987,10 +1884,10 @@ store_search(svn_mergeinfo_t processed, return SVN_NO_ERROR; } -/* Find logs for PATHS from HIST_START to HIST_END in FS, and invoke - RECEIVER with RECEIVER_BATON on them. If DESCENDING_ORDER is TRUE, send - the logs back as we find them, else buffer the logs and send them back - in youngest->oldest order. +/* Find logs for PATHS from HIST_START to HIST_END in FS, and invoke the + CALLBACKS on them. If DESCENDING_ORDER is TRUE, send the logs back as + we find them, else buffer the logs and send them back in youngest->oldest + order. If IGNORE_MISSING_LOCATIONS is set, don't treat requests for bogus repository locations as fatal -- just ignore them. @@ -2026,7 +1923,6 @@ do_logs(svn_fs_t *fs, svn_revnum_t hist_start, svn_revnum_t hist_end, int limit, - svn_boolean_t discover_changed_paths, svn_boolean_t strict_node_history, svn_boolean_t include_merged_revisions, svn_boolean_t subtractive_merge, @@ -2034,10 +1930,7 @@ do_logs(svn_fs_t *fs, svn_boolean_t ignore_missing_locations, const apr_array_header_t *revprops, svn_boolean_t descending_order, - svn_log_entry_receiver_t receiver, - void *receiver_baton, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, + log_callbacks_t *callbacks, apr_pool_t *pool) { apr_pool_t *iterpool, *iterpool2; @@ -2070,7 +1963,8 @@ do_logs(svn_fs_t *fs, revisions contain real changes to at least one of our paths. */ SVN_ERR(get_path_histories(&histories, fs, paths, hist_start, hist_end, strict_node_history, ignore_missing_locations, - authz_read_func, authz_read_baton, pool)); + callbacks->authz_read_func, + callbacks->authz_read_baton, pool)); /* Loop through all the revisions in the range and add any where a path was changed to the array, or if they wanted @@ -2094,9 +1988,10 @@ do_logs(svn_fs_t *fs, /* Check history for this path in current rev. */ SVN_ERR(check_history(&changed, info, fs, current, - strict_node_history, authz_read_func, - authz_read_baton, hist_start, pool, - iterpool2)); + strict_node_history, + callbacks->authz_read_func, + callbacks->authz_read_baton, + hist_start, pool, iterpool2)); if (! info->done) any_histories_left = TRUE; } @@ -2109,7 +2004,6 @@ do_logs(svn_fs_t *fs, svn_mergeinfo_t added_mergeinfo = NULL; svn_mergeinfo_t deleted_mergeinfo = NULL; svn_boolean_t has_children = FALSE; - apr_hash_t *changes = NULL; /* If we're including merged revisions, we need to calculate the mergeinfo deltas committed in this revision to our @@ -2130,7 +2024,6 @@ do_logs(svn_fs_t *fs, } SVN_ERR(get_combined_mergeinfo_changes(&added_mergeinfo, &deleted_mergeinfo, - &changes, fs, cur_paths, current, iterpool, iterpool)); @@ -2143,13 +2036,10 @@ do_logs(svn_fs_t *fs, in anyway). */ if (descending_order) { - SVN_ERR(send_log(current, fs, changes, + SVN_ERR(send_log(current, fs, log_target_history_as_mergeinfo, nested_merges, - discover_changed_paths, subtractive_merge, handling_merged_revisions, - revprops, has_children, - receiver, receiver_baton, - authz_read_func, authz_read_baton, iterpool)); + revprops, has_children, callbacks, iterpool)); if (has_children) /* Implies include_merged_revisions == TRUE */ { @@ -2168,12 +2058,9 @@ do_logs(svn_fs_t *fs, log_target_history_as_mergeinfo, nested_merges, processed, added_mergeinfo, deleted_mergeinfo, - discover_changed_paths, strict_node_history, revprops, - receiver, receiver_baton, - authz_read_func, - authz_read_baton, + callbacks, iterpool)); } if (limit && ++send_count >= limit) @@ -2192,7 +2079,7 @@ do_logs(svn_fs_t *fs, if (added_mergeinfo || deleted_mergeinfo) { svn_revnum_t *cur_rev = - apr_pmemdup(pool, ¤t, sizeof(cur_rev)); + apr_pmemdup(pool, ¤t, sizeof(*cur_rev)); struct added_deleted_mergeinfo *add_and_del_mergeinfo = apr_palloc(pool, sizeof(*add_and_del_mergeinfo)); @@ -2248,13 +2135,10 @@ do_logs(svn_fs_t *fs, || apr_hash_count(deleted_mergeinfo) > 0); } - SVN_ERR(send_log(current, fs, NULL, + SVN_ERR(send_log(current, fs, log_target_history_as_mergeinfo, nested_merges, - discover_changed_paths, subtractive_merge, - handling_merged_revisions, - revprops, has_children, - receiver, receiver_baton, authz_read_func, - authz_read_baton, iterpool)); + subtractive_merge, handling_merged_revisions, + revprops, has_children, callbacks, iterpool)); if (has_children) { if (!nested_merges) @@ -2269,12 +2153,8 @@ do_logs(svn_fs_t *fs, processed, added_mergeinfo, deleted_mergeinfo, - discover_changed_paths, strict_node_history, - revprops, - receiver, receiver_baton, - authz_read_func, - authz_read_baton, + revprops, callbacks, iterpool)); } if (limit && i + 1 >= limit) @@ -2373,41 +2253,56 @@ get_paths_history_as_mergeinfo(svn_merge } svn_error_t * -svn_repos_get_logs4(svn_repos_t *repos, +svn_repos_get_logs5(svn_repos_t *repos, const apr_array_header_t *paths, svn_revnum_t start, svn_revnum_t end, int limit, - svn_boolean_t discover_changed_paths, svn_boolean_t strict_node_history, svn_boolean_t include_merged_revisions, const apr_array_header_t *revprops, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, - svn_log_entry_receiver_t receiver, - void *receiver_baton, - apr_pool_t *pool) + svn_repos_path_change_receiver_t path_change_receiver, + void *path_change_receiver_baton, + svn_repos_log_entry_receiver_t revision_receiver, + void *revision_receiver_baton, + apr_pool_t *scratch_pool) { svn_revnum_t head = SVN_INVALID_REVNUM; svn_fs_t *fs = repos->fs; svn_boolean_t descending_order; svn_mergeinfo_t paths_history_mergeinfo = NULL; + log_callbacks_t callbacks; + + callbacks.path_change_receiver = path_change_receiver; + callbacks.path_change_receiver_baton = path_change_receiver_baton; + callbacks.revision_receiver = revision_receiver; + callbacks.revision_receiver_baton = revision_receiver_baton; + callbacks.authz_read_func = authz_read_func; + callbacks.authz_read_baton = authz_read_baton; if (revprops) { int i; apr_array_header_t *new_revprops - = apr_array_make(pool, revprops->nelts, sizeof(svn_string_t *)); + = apr_array_make(scratch_pool, revprops->nelts, + sizeof(svn_string_t *)); for (i = 0; i < revprops->nelts; ++i) APR_ARRAY_PUSH(new_revprops, svn_string_t *) - = svn_string_create(APR_ARRAY_IDX(revprops, i, const char *), pool); + = svn_string_create(APR_ARRAY_IDX(revprops, i, const char *), + scratch_pool); revprops = new_revprops; } + /* Make sure we catch up on the latest revprop changes. This is the only + * time we will refresh the revprop data in this query. */ + SVN_ERR(svn_fs_refresh_revision_props(fs, scratch_pool)); + /* Setup log range. */ - SVN_ERR(svn_fs_youngest_rev(&head, fs, pool)); + SVN_ERR(svn_fs_youngest_rev(&head, fs, scratch_pool)); if (! SVN_IS_VALID_REVNUM(start)) start = head; @@ -2436,7 +2331,7 @@ svn_repos_get_logs4(svn_repos_t *repos, } if (! paths) - paths = apr_array_make(pool, 0, sizeof(const char *)); + paths = apr_array_make(scratch_pool, 0, sizeof(const char *)); /* If we're not including merged revisions, and we were given no paths or a single empty (or "/") path, then we can bypass a bunch @@ -2451,7 +2346,7 @@ svn_repos_get_logs4(svn_repos_t *repos, { apr_uint64_t send_count = 0; int i; - apr_pool_t *iterpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); /* If we are provided an authz callback function, use it to verify that the user has read access to the root path in the @@ -2467,9 +2362,10 @@ svn_repos_get_logs4(svn_repos_t *repos, svn_fs_root_t *rev_root; SVN_ERR(svn_fs_revision_root(&rev_root, fs, - descending_order ? end : start, pool)); + descending_order ? end : start, + scratch_pool)); SVN_ERR(authz_read_func(&readable, rev_root, "", - authz_read_baton, pool)); + authz_read_baton, scratch_pool)); if (! readable) return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL); } @@ -2487,10 +2383,9 @@ svn_repos_get_logs4(svn_repos_t *repos, rev = end - i; else rev = start + i; - SVN_ERR(send_log(rev, fs, NULL, NULL, NULL, - discover_changed_paths, FALSE, - FALSE, revprops, FALSE, receiver, receiver_baton, - authz_read_func, authz_read_baton, iterpool)); + SVN_ERR(send_log(rev, fs, NULL, NULL, + FALSE, FALSE, revprops, FALSE, + &callbacks, iterpool)); } svn_pool_destroy(iterpool); @@ -2504,19 +2399,158 @@ svn_repos_get_logs4(svn_repos_t *repos, http://subversion.tigris.org/issues/show_bug.cgi?id=3650#desc5 */ if (include_merged_revisions) { - apr_pool_t *subpool = svn_pool_create(pool); + apr_pool_t *subpool = svn_pool_create(scratch_pool); SVN_ERR(get_paths_history_as_mergeinfo(&paths_history_mergeinfo, repos, paths, start, end, authz_read_func, authz_read_baton, - pool, subpool)); + scratch_pool, subpool)); svn_pool_destroy(subpool); } - return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end, - limit, discover_changed_paths, strict_node_history, + return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, + start, end, limit, strict_node_history, include_merged_revisions, FALSE, FALSE, FALSE, - revprops, descending_order, receiver, receiver_baton, - authz_read_func, authz_read_baton, pool); + revprops, descending_order, &callbacks, scratch_pool); +} + +/* Baton type to be used with both log4 compatibility callbacks. + * For each revision, we collect the CHANGES and then pass them + * on to INNER. */ +typedef struct log_entry_receiver_baton_t +{ + /* Pool to use to allocate CHANGES and its entries. + * Gets cleared after each revision. */ + apr_pool_t *changes_pool; + + /* Path changes reported so far for the current revision. + * Will be NULL before the first item gets added and will be reset + * to NULL after the INNER callback has returned. */ + apr_hash_t *changes; + + /* User-provided callback to send the log entry to. */ + svn_log_entry_receiver_t inner; + void *inner_baton; +} log_entry_receiver_baton_t; + +/* Return the action character (see svn_log_changed_path2_t) for KIND. + * Returns 0 for invalid KINDs. */ +static char +path_change_kind_to_char(svn_fs_path_change_kind_t kind) +{ + const char symbol[] = "MADR"; + + if (kind < svn_fs_path_change_modify || kind > svn_fs_path_change_replace) + return 0; + + return symbol[kind]; +} + +/* Implement svn_repos_path_change_receiver_t. + * Convert CHANGE and add it to the CHANGES list in *BATON. */ +static svn_error_t * +log4_path_change_receiver(void *baton, + svn_repos_path_change_t *change, + apr_pool_t *scratch_pool) +{ + log_entry_receiver_baton_t *b = baton; + svn_log_changed_path2_t *change_copy; + const char *path = apr_pstrmemdup(b->changes_pool, change->path.data, + change->path.len); + + /* Create a deep copy of the temporary CHANGE struct. */ + change_copy = svn_log_changed_path2_create(b->changes_pool); + change_copy->action = path_change_kind_to_char(change->change_kind); + + if (change->copyfrom_path) + change_copy->copyfrom_path = apr_pstrdup(b->changes_pool, + change->copyfrom_path); + + change_copy->copyfrom_rev = change->copyfrom_rev; + change_copy->node_kind = change->node_kind; + change_copy->text_modified = change->text_mod ? svn_tristate_true + : svn_tristate_false; + change_copy->props_modified = change->prop_mod ? svn_tristate_true + : svn_tristate_false; + + /* Auto-create the CHANGES container (happens for each first change + * in any revison. */ + if (b->changes == NULL) + b->changes = svn_hash__make(b->changes_pool); + + /* Add change to per-revision collection. */ + apr_hash_set(b->changes, path, change->path.len, change_copy); + + return SVN_NO_ERROR; +} + +/* Implement svn_log_entry_receiver_t. + * Combine the data gathered in BATON for this revision and send it + * to the user-provided log4-compatible callback. */ +static svn_error_t * +log4_entry_receiver(void *baton, + svn_repos_log_entry_t *log_entry, + apr_pool_t *scratch_pool) +{ + log_entry_receiver_baton_t *b = baton; + svn_log_entry_t *entry = svn_log_entry_create(scratch_pool); + + /* Complete the ENTRY. */ + entry->changed_paths = b->changes; + entry->revision = log_entry->revision; + entry->revprops = log_entry->revprops; + entry->has_children = log_entry->has_children; + entry->changed_paths2 = b->changes; + entry->non_inheritable = log_entry->non_inheritable; + entry->subtractive_merge = log_entry->subtractive_merge; + + /* Invoke the log4-compatible callback. */ + SVN_ERR(b->inner(b->inner_baton, entry, scratch_pool)); + + /* Release per-revision data. */ + svn_pool_clear(b->changes_pool); + b->changes = NULL; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_repos_get_logs4(svn_repos_t *repos, + const apr_array_header_t *paths, + svn_revnum_t start, + svn_revnum_t end, + int limit, + svn_boolean_t discover_changed_paths, + svn_boolean_t strict_node_history, + svn_boolean_t include_merged_revisions, + const apr_array_header_t *revprops, + svn_repos_authz_func_t authz_read_func, + void *authz_read_baton, + svn_log_entry_receiver_t receiver, + void *receiver_baton, + apr_pool_t *pool) +{ + apr_pool_t *changes_pool = svn_pool_create(pool); + + log_entry_receiver_baton_t baton; + baton.changes_pool = changes_pool; + baton.changes = NULL; + baton.inner = receiver; + baton.inner_baton = receiver_baton; + + SVN_ERR(svn_repos_get_logs5(repos, paths, start, end, limit, + strict_node_history, + include_merged_revisions, + revprops, + authz_read_func, authz_read_baton, + discover_changed_paths + ? log4_path_change_receiver + : NULL, + &baton, + log4_entry_receiver, &baton, + pool)); + + svn_pool_destroy(changes_pool); + return SVN_NO_ERROR; } Modified: subversion/branches/authzperf/subversion/libsvn_repos/replay.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_repos/replay.c?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_repos/replay.c (original) +++ subversion/branches/authzperf/subversion/libsvn_repos/replay.c Fri Apr 29 18:38:53 2016 @@ -198,7 +198,7 @@ add_subdir(svn_fs_root_t *source_root, for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) { - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t readable = TRUE; svn_fs_dirent_t *dent = apr_hash_this_val(hi); const char *copyfrom_path = NULL; @@ -412,7 +412,7 @@ fill_copyfrom(svn_fs_root_t **copyfrom_r svn_revnum_t *copyfrom_rev, svn_boolean_t *src_readable, svn_fs_root_t *root, - svn_fs_path_change2_t *change, + svn_fs_path_change3_t *change, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, const char *path, @@ -463,7 +463,7 @@ path_driver_cb_func(void **dir_baton, const svn_delta_editor_t *editor = cb->editor; void *edit_baton = cb->edit_baton; svn_fs_root_t *root = cb->root; - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t do_add = FALSE, do_delete = FALSE; void *file_baton = NULL; svn_revnum_t copyfrom_rev; @@ -843,6 +843,80 @@ fetch_props_func(apr_hash_t **props, +/* Retrieve the path changes under ROOT, filter them with AUTHZ_READ_FUNC + and AUTHZ_READ_BATON and return those that intersect with BASE_RELPATH. + + The svn_fs_path_change3_t* will be returned in *CHANGED_PATHS, keyed by + their path. The paths themselves are additionally returned in *PATHS. + + Allocate the returned data in RESULT_POOL and use SCRATCH_POOL for + temporary allocations. + */ +static svn_error_t * +get_relevant_changes(apr_hash_t **changed_paths, + apr_array_header_t **paths, + svn_fs_root_t *root, + const char *base_relpath, + svn_repos_authz_func_t authz_read_func, + void *authz_read_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Fetch the paths changed under ROOT. */ + SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + + /* Make an array from the keys of our CHANGED_PATHS hash, and copy + the values into a new hash whose keys have no leading slashes. */ + *paths = apr_array_make(result_pool, 16, sizeof(const char *)); + *changed_paths = apr_hash_make(result_pool); + while (change) + { + const char *path = change->path.data; + apr_ssize_t keylen = change->path.len; + svn_boolean_t allowed = TRUE; + + svn_pool_clear(iterpool); + if (authz_read_func) + SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, + iterpool)); + + if (allowed) + { + if (path[0] == '/') + { + path++; + keylen--; + } + + /* If the base_path doesn't match the top directory of this path + we don't want anything to do with it... + ...unless this was a change to one of the parent directories of + base_path. */ + if ( svn_relpath_skip_ancestor(base_relpath, path) + || svn_relpath_skip_ancestor(path, base_relpath)) + { + change = svn_fs_path_change3_dup(change, result_pool); + path = change->path.data; + if (path[0] == '/') + path++; + + APR_ARRAY_PUSH(*paths, const char *) = path; + apr_hash_set(*changed_paths, path, keylen, change); + } + } + + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + svn_error_t * svn_repos_replay2(svn_fs_root_t *root, const char *base_path, @@ -855,9 +929,7 @@ svn_repos_replay2(svn_fs_root_t *root, apr_pool_t *pool) { #ifndef USE_EV2_IMPL - apr_hash_t *fs_changes; apr_hash_t *changed_paths; - apr_hash_index_t *hi; apr_array_header_t *paths; struct path_driver_cb_baton cb_baton; @@ -869,54 +941,15 @@ svn_repos_replay2(svn_fs_root_t *root, return SVN_NO_ERROR; } - /* Fetch the paths changed under ROOT. */ - SVN_ERR(svn_fs_paths_changed2(&fs_changes, root, pool)); - if (! base_path) base_path = ""; else if (base_path[0] == '/') ++base_path; - /* Make an array from the keys of our CHANGED_PATHS hash, and copy - the values into a new hash whose keys have no leading slashes. */ - paths = apr_array_make(pool, apr_hash_count(fs_changes), - sizeof(const char *)); - changed_paths = apr_hash_make(pool); - for (hi = apr_hash_first(pool, fs_changes); hi; hi = apr_hash_next(hi)) - { - const char *path = apr_hash_this_key(hi); - apr_ssize_t keylen = apr_hash_this_key_len(hi); - svn_fs_path_change2_t *change = apr_hash_this_val(hi); - svn_boolean_t allowed = TRUE; - - if (authz_read_func) - SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, - pool)); - - if (allowed) - { - if (path[0] == '/') - { - path++; - keylen--; - } - - /* If the base_path doesn't match the top directory of this path - we don't want anything to do with it... */ - if (svn_relpath_skip_ancestor(base_path, path) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - /* ...unless this was a change to one of the parent directories of - base_path. */ - else if (svn_relpath_skip_ancestor(path, base_path) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - } - } + /* Fetch the paths changed under ROOT. */ + SVN_ERR(get_relevant_changes(&changed_paths, &paths, root, base_path, + authz_read_func, authz_read_baton, + pool, pool)); /* If we were not given a low water mark, assume that everything is there, all the way back to revision 0. */ @@ -1062,7 +1095,7 @@ add_subdir_ev2(svn_fs_root_t *source_roo for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t readable = TRUE; svn_fs_dirent_t *dent = apr_hash_this_val(hi); const char *copyfrom_path = NULL; @@ -1190,7 +1223,7 @@ replay_node(svn_fs_root_t *root, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_fs_path_change2_t *change; + svn_fs_path_change3_t *change; svn_boolean_t do_add = FALSE; svn_boolean_t do_delete = FALSE; svn_revnum_t copyfrom_rev; @@ -1485,9 +1518,7 @@ svn_repos__replay_ev2(svn_fs_root_t *roo void *authz_read_baton, apr_pool_t *scratch_pool) { - apr_hash_t *fs_changes; apr_hash_t *changed_paths; - apr_hash_index_t *hi; apr_array_header_t *paths; apr_array_header_t *copies; apr_pool_t *iterpool; @@ -1505,49 +1536,10 @@ svn_repos__replay_ev2(svn_fs_root_t *roo } /* Fetch the paths changed under ROOT. */ - SVN_ERR(svn_fs_paths_changed2(&fs_changes, root, scratch_pool)); - - /* Make an array from the keys of our CHANGED_PATHS hash, and copy - the values into a new hash whose keys have no leading slashes. */ - paths = apr_array_make(scratch_pool, apr_hash_count(fs_changes), - sizeof(const char *)); - changed_paths = apr_hash_make(scratch_pool); - for (hi = apr_hash_first(scratch_pool, fs_changes); hi; - hi = apr_hash_next(hi)) - { - const char *path = apr_hash_this_key(hi); - apr_ssize_t keylen = apr_hash_this_key_len(hi); - svn_fs_path_change2_t *change = apr_hash_this_val(hi); - svn_boolean_t allowed = TRUE; - - if (authz_read_func) - SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, - scratch_pool)); - - if (allowed) - { - if (path[0] == '/') - { - path++; - keylen--; - } - - /* If the base_path doesn't match the top directory of this path - we don't want anything to do with it... */ - if (svn_relpath_skip_ancestor(base_repos_relpath, path) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - /* ...unless this was a change to one of the parent directories of - base_path. */ - else if (svn_relpath_skip_ancestor(path, base_repos_relpath) != NULL) - { - APR_ARRAY_PUSH(paths, const char *) = path; - apr_hash_set(changed_paths, path, keylen, change); - } - } - } + SVN_ERR(get_relevant_changes(&changed_paths, &paths, root, + base_repos_relpath, + authz_read_func, authz_read_baton, + scratch_pool, scratch_pool)); /* If we were not given a low water mark, assume that everything is there, all the way back to revision 0. */ Modified: subversion/branches/authzperf/subversion/libsvn_repos/reporter.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_repos/reporter.c?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_repos/reporter.c (original) +++ subversion/branches/authzperf/subversion/libsvn_repos/reporter.c Fri Apr 29 18:38:53 2016 @@ -481,10 +481,12 @@ get_revision_info(report_baton_t *b, { /* Info is not available, yet. Get all revprops. */ - SVN_ERR(svn_fs_revision_proplist(&r_props, - b->repos->fs, - rev, - scratch_pool)); + SVN_ERR(svn_fs_revision_proplist2(&r_props, + b->repos->fs, + rev, + FALSE, + scratch_pool, + scratch_pool)); /* Extract the committed-date. */ cdate = svn_hash_gets(r_props, SVN_PROP_REVISION_DATE); @@ -520,19 +522,13 @@ delta_proplists(report_baton_t *b, svn_r { svn_fs_root_t *s_root; apr_hash_t *s_props = NULL, *t_props; - apr_array_header_t *prop_diffs; - int i; svn_revnum_t crev; - revision_info_t *revision_info; - svn_boolean_t changed; - const svn_prop_t *pc; - svn_lock_t *lock; - apr_hash_index_t *hi; /* Fetch the created-rev and send entry props. */ SVN_ERR(svn_fs_node_created_rev(&crev, b->t_root, t_path, pool)); if (SVN_IS_VALID_REVNUM(crev)) { + revision_info_t *revision_info; /* convert committed-rev to string */ char buf[SVN_INT64_BUFFER_SIZE]; svn_string_t cr_str; @@ -563,6 +559,7 @@ delta_proplists(report_baton_t *b, svn_r /* Update lock properties. */ if (lock_token) { + svn_lock_t *lock; SVN_ERR(svn_fs_get_lock(&lock, b->repos->fs, t_path, pool)); /* Delete a defunct lock. */ @@ -573,6 +570,7 @@ delta_proplists(report_baton_t *b, svn_r if (s_path) { + svn_boolean_t changed; SVN_ERR(get_source_root(b, &s_root, s_rev)); /* Is this deltification worth our time? */ @@ -590,16 +588,20 @@ delta_proplists(report_baton_t *b, svn_r if (s_props && apr_hash_count(s_props)) { + apr_array_header_t *prop_diffs; + int i; + /* Now transmit the differences. */ SVN_ERR(svn_prop_diffs(&prop_diffs, t_props, s_props, pool)); for (i = 0; i < prop_diffs->nelts; i++) { - pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t); + const svn_prop_t *pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t); SVN_ERR(change_fn(b, object, pc->name, pc->value, pool)); } } else if (apr_hash_count(t_props)) { + apr_hash_index_t *hi; /* So source, i.e. all new. Transmit all target props. */ for (hi = apr_hash_first(pool, t_props); hi; hi = apr_hash_next(hi)) { @@ -669,7 +671,6 @@ delta_files(report_baton_t *b, void *fil const char *s_path, const char *t_path, const char *lock_token, apr_pool_t *pool) { - svn_boolean_t changed; svn_fs_root_t *s_root = NULL; svn_txdelta_stream_t *dstream = NULL; svn_checksum_t *s_checksum; @@ -683,14 +684,15 @@ delta_files(report_baton_t *b, void *fil if (s_path) { + svn_boolean_t changed; SVN_ERR(get_source_root(b, &s_root, s_rev)); /* We're not interested in the theoretical difference between "has contents which have not changed with respect to" and "has the same actual contents as" when sending text-deltas. If we know the delta is an empty one, we avoiding sending it in either case. */ - SVN_ERR(svn_repos__compare_files(&changed, b->t_root, t_path, - s_root, s_path, pool)); + SVN_ERR(svn_fs_contents_different(&changed, b->t_root, t_path, + s_root, s_path, pool)); if (!changed) return SVN_NO_ERROR; @@ -1548,6 +1550,7 @@ svn_repos_finish_report(void *baton, apr { report_baton_t *b = baton; + SVN_ERR(svn_fs_refresh_revision_props(svn_repos_fs(b->repos), pool)); return svn_error_trace(finish_report(b, pool)); } Modified: subversion/branches/authzperf/subversion/libsvn_repos/repos.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_repos/repos.c?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_repos/repos.c (original) +++ subversion/branches/authzperf/subversion/libsvn_repos/repos.c Fri Apr 29 18:38:53 2016 @@ -848,11 +848,16 @@ create_conf(svn_repos_t *repos, apr_pool "### no path-based access control is done." NL "### Uncomment the line below to use the default authorization file." NL "# authz-db = " SVN_REPOS__CONF_AUTHZ NL -"### The groups-db option controls the location of the groups file." NL -"### Unless you specify a path starting with a /, the file's location is" NL -"### relative to the directory containing this file. The specified path" NL -"### may be a repository relative URL (^/) or an absolute file:// URL to a" NL -"### text file in a Subversion repository." NL +"### The groups-db option controls the location of the file with the" NL +"### group definitions and allows maintaining groups separately from the" NL +"### authorization rules. The groups-db file is of the same format as the" NL +"### authz-db file and should contain a single [groups] section with the" NL +"### group definitions. If the option is enabled, the authz-db file cannot" NL +"### contain a [groups] section. Unless you specify a path starting with" NL +"### a /, the file's location is relative to the directory containing this" NL +"### file. The specified path may be a repository relative URL (^/) or an" NL +"### absolute file:// URL to a text file in a Subversion repository." NL +"### This option is not being used by default." NL "# groups-db = " SVN_REPOS__CONF_GROUPS NL "### This option specifies the authentication realm of the repository." NL "### If two repositories have the same authentication realm, they should" NL @@ -1175,8 +1180,8 @@ svn_repos_create(svn_repos_t **repos_p, SVN_ERR(lock_repos(repos, FALSE, FALSE, scratch_pool)); /* Create an environment for the filesystem. */ - if ((err = svn_fs_create(&repos->fs, repos->db_path, fs_config, - result_pool))) + if ((err = svn_fs_create2(&repos->fs, repos->db_path, fs_config, + result_pool, scratch_pool))) { /* If there was an error making the filesytem, e.g. unknown/supported * filesystem type. Clean up after ourselves. Yes this is safe because Modified: subversion/branches/authzperf/subversion/libsvn_repos/repos.h URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_repos/repos.h?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_repos/repos.h (original) +++ subversion/branches/authzperf/subversion/libsvn_repos/repos.h Fri Apr 29 18:38:53 2016 @@ -388,17 +388,6 @@ svn_repos__authz_read(svn_authz_t **auth /*** Utility Functions ***/ -/* Set *CHANGED_P to TRUE if ROOT1/PATH1 and ROOT2/PATH2 have - different contents, FALSE if they have the same contents. - Use POOL for temporary allocation. */ -svn_error_t * -svn_repos__compare_files(svn_boolean_t *changed_p, - svn_fs_root_t *root1, - const char *path1, - svn_fs_root_t *root2, - const char *path2, - apr_pool_t *pool); - /* Set *PREV_PATH and *PREV_REV to the path and revision which represent the location at which PATH in FS was located immediately prior to REVISION iff there was a copy operation (to PATH or one of Modified: subversion/branches/authzperf/subversion/libsvn_repos/rev_hunt.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_repos/rev_hunt.c?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_repos/rev_hunt.c (original) +++ subversion/branches/authzperf/subversion/libsvn_repos/rev_hunt.c Fri Apr 29 18:38:53 2016 @@ -65,8 +65,8 @@ get_time(apr_time_t *tm, { svn_string_t *date_str; - SVN_ERR(svn_fs_revision_prop(&date_str, fs, rev, SVN_PROP_REVISION_DATE, - pool)); + SVN_ERR(svn_fs_revision_prop2(&date_str, fs, rev, SVN_PROP_REVISION_DATE, + FALSE, pool, pool)); if (! date_str) return svn_error_createf (SVN_ERR_FS_GENERAL, NULL, @@ -88,6 +88,7 @@ svn_repos_dated_revision(svn_revnum_t *r /* Initialize top and bottom values of binary search. */ SVN_ERR(svn_fs_youngest_rev(&rev_latest, fs, pool)); + SVN_ERR(svn_fs_refresh_revision_props(fs, pool)); rev_bot = 0; rev_top = rev_latest; @@ -170,7 +171,8 @@ svn_repos_get_committed_info(svn_revnum_ SVN_ERR(svn_fs_node_created_rev(committed_rev, root, path, pool)); /* Get the revision properties of this revision. */ - SVN_ERR(svn_fs_revision_proplist(&revprops, fs, *committed_rev, pool)); + SVN_ERR(svn_fs_revision_proplist2(&revprops, fs, *committed_rev, TRUE, + pool, pool)); /* Extract date and author from these revprops. */ committed_date_s = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE); @@ -1010,26 +1012,39 @@ get_merged_mergeinfo(apr_hash_t **merged apr_hash_t *curr_mergeinfo, *prev_mergeinfo, *deleted, *changed; svn_error_t *err; svn_fs_root_t *root, *prev_root; - apr_hash_t *changed_paths; - const char *path = old_path_rev->path; + const char *start_path = old_path_rev->path; + const char *path = NULL; + + svn_fs_path_change_iterator_t *iterator; + svn_fs_path_change3_t *change; /* Getting/parsing/diffing svn:mergeinfo is expensive, so only do it if there is a property change. */ SVN_ERR(svn_fs_revision_root(&root, repos->fs, old_path_rev->revnum, scratch_pool)); - SVN_ERR(svn_fs_paths_changed2(&changed_paths, root, scratch_pool)); - while (1) - { - svn_fs_path_change2_t *changed_path = svn_hash_gets(changed_paths, path); - if (changed_path && changed_path->prop_mod - && changed_path->mergeinfo_mod != svn_tristate_false) - break; - if (svn_fspath__is_root(path, strlen(path))) + SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + + /* Find the changed PATH closest to START_PATH which may have a mergeinfo + * change. */ + while (change) + { + if ( change->prop_mod + && change->mergeinfo_mod != svn_tristate_false + && svn_fspath__skip_ancestor(change->path.data, start_path)) { - *merged_mergeinfo = NULL; - return SVN_NO_ERROR; + if (!path || svn_fspath__skip_ancestor(path, change->path.data)) + path = apr_pstrmemdup(scratch_pool, change->path.data, + change->path.len); } - path = svn_fspath__dirname(path, scratch_pool); + + SVN_ERR(svn_fs_path_change_get(&change, iterator)); + } + + if (path == NULL) + { + *merged_mergeinfo = NULL; + return SVN_NO_ERROR; } /* First, find the mergeinfo difference for old_path_rev->revnum, and @@ -1354,22 +1369,48 @@ send_path_revision(struct path_revision void *delta_baton = NULL; apr_pool_t *tmp_pool; /* For swapping */ svn_boolean_t contents_changed; + svn_boolean_t props_changed; svn_pool_clear(sb->iterpool); /* Get the revision properties. */ - SVN_ERR(svn_fs_revision_proplist(&rev_props, repos->fs, - path_rev->revnum, sb->iterpool)); + SVN_ERR(svn_fs_revision_proplist2(&rev_props, repos->fs, + path_rev->revnum, FALSE, + sb->iterpool, sb->iterpool)); /* Open the revision root. */ SVN_ERR(svn_fs_revision_root(&root, repos->fs, path_rev->revnum, sb->iterpool)); - /* Get the file's properties for this revision and compute the diffs. */ - SVN_ERR(svn_fs_node_proplist(&props, root, path_rev->path, + /* Check if the props *may* have changed. */ + if (sb->last_root) + { + /* We don't use svn_fs_props_different() because it's more + * expensive. */ + SVN_ERR(svn_fs_props_changed(&props_changed, + sb->last_root, sb->last_path, + root, path_rev->path, sb->iterpool)); + } + else + { + props_changed = TRUE; + } + + /* Calculate actual difference between last and current properties. */ + if (props_changed) + { + /* Get the file's properties for this revision and compute the diffs. */ + SVN_ERR(svn_fs_node_proplist(&props, root, path_rev->path, sb->iterpool)); - SVN_ERR(svn_prop_diffs(&prop_diffs, props, sb->last_props, - sb->iterpool)); + SVN_ERR(svn_prop_diffs(&prop_diffs, props, sb->last_props, + sb->iterpool)); + } + else + { + /* Properties didn't change: copy LAST_PROPS to current POOL. */ + props = svn_prop_hash_dup(sb->last_props, sb->iterpool); + prop_diffs = apr_array_make(sb->iterpool, 0, sizeof(svn_prop_t)); + } /* Check if the contents *may* have changed. */ if (! sb->last_root) @@ -1594,6 +1635,10 @@ svn_repos_get_file_revs2(svn_repos_t *re end = youngest_rev; } + /* Make sure we catch up on the latest revprop changes. This is the only + * time we will refresh the revprop data in this query. */ + SVN_ERR(svn_fs_refresh_revision_props(repos->fs, scratch_pool)); + if (end < start) { if (include_merged_revisions) Modified: subversion/branches/authzperf/subversion/libsvn_subr/atomic.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_subr/atomic.c?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_subr/atomic.c (original) +++ subversion/branches/authzperf/subversion/libsvn_subr/atomic.c Fri Apr 29 18:38:53 2016 @@ -22,8 +22,11 @@ #include #include -#include "private/svn_atomic.h" +#include "svn_pools.h" + +#include "private/svn_atomic.h" +#include "private/svn_mutex.h" /* Magic values for atomic initialization */ #define SVN_ATOMIC_UNINITIALIZED 0 @@ -174,3 +177,42 @@ svn_atomic__init_once_no_error(volatile else return init_baton.errstr; } + +/* The process-global counter that we use to produce process-wide unique + * values. Since APR has no 64 bit atomics, all access to this will be + * serialized through COUNTER_MUTEX. */ +static apr_uint64_t uniqiue_counter = 0; + +/* The corresponding mutex and initialization state. */ +static volatile svn_atomic_t counter_status = SVN_ATOMIC_UNINITIALIZED; +static svn_mutex__t *counter_mutex = NULL; + +/* svn_atomic__err_init_func_t implementation that initializes COUNTER_MUTEX. + * Note that neither argument will be used and should be NULL. */ +static svn_error_t * +init_unique_counter(void *null_baton, + apr_pool_t *null_pool) +{ + /* COUNTER_MUTEX is global, so it needs to live in a global pool. + * APR also makes those thread-safe by default. */ + SVN_ERR(svn_mutex__init(&counter_mutex, TRUE, svn_pool_create(NULL))); + return SVN_NO_ERROR; +} + +/* Read and increment UNIQIUE_COUNTER. Return the new value in *VALUE. + * Call this function only while having acquired the COUNTER_MUTEX. */ +static svn_error_t * +read_unique_counter(apr_uint64_t *value) +{ + *value = ++uniqiue_counter; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_atomic__unique_counter(apr_uint64_t *value) +{ + SVN_ERR(svn_atomic__init_once(&counter_status, init_unique_counter, NULL, + NULL)); + SVN_MUTEX__WITH_LOCK(counter_mutex, read_unique_counter(value)); + return SVN_NO_ERROR; +} Modified: subversion/branches/authzperf/subversion/libsvn_subr/base64.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_subr/base64.c?rev=1741682&r1=1741681&r2=1741682&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_subr/base64.c (original) +++ subversion/branches/authzperf/subversion/libsvn_subr/base64.c Fri Apr 29 18:38:53 2016 @@ -58,6 +58,7 @@ struct encode_baton { unsigned char buf[3]; /* Bytes waiting to be encoded */ size_t buflen; /* Number of bytes waiting */ size_t linelen; /* Bytes output so far on this line */ + svn_boolean_t break_lines; apr_pool_t *scratch_pool; }; @@ -140,7 +141,7 @@ encode_bytes(svn_stringbuf_t *str, const svn_stringbuf_ensure(str, str->len + buflen); /* Keep encoding three-byte groups until we run out. */ - while (*inbuflen + (end - p) >= 3) + while ((end - p) >= (3 - *inbuflen)) { /* May we encode BYTES_PER_LINE bytes without caring about line breaks, data in the temporary INBUF or running out @@ -214,7 +215,8 @@ encode_data(void *baton, const char *dat svn_error_t *err = SVN_NO_ERROR; /* Encode this block of data and write it out. */ - encode_bytes(encoded, data, *len, eb->buf, &eb->buflen, &eb->linelen, TRUE); + encode_bytes(encoded, data, *len, eb->buf, &eb->buflen, &eb->linelen, + eb->break_lines); enclen = encoded->len; if (enclen != 0) err = svn_stream_write(eb->output, encoded->data, &enclen); @@ -233,7 +235,8 @@ finish_encoding_data(void *baton) svn_error_t *err = SVN_NO_ERROR; /* Encode a partial group at the end if necessary, and write it out. */ - encode_partial_group(encoded, eb->buf, eb->buflen, eb->linelen, TRUE); + encode_partial_group(encoded, eb->buf, eb->buflen, eb->linelen, + eb->break_lines); enclen = encoded->len; if (enclen != 0) err = svn_stream_write(eb->output, encoded->data, &enclen); @@ -247,7 +250,9 @@ finish_encoding_data(void *baton) svn_stream_t * -svn_base64_encode(svn_stream_t *output, apr_pool_t *pool) +svn_base64_encode2(svn_stream_t *output, + svn_boolean_t break_lines, + apr_pool_t *pool) { struct encode_baton *eb = apr_palloc(pool, sizeof(*eb)); svn_stream_t *stream; @@ -255,6 +260,7 @@ svn_base64_encode(svn_stream_t *output, eb->output = output; eb->buflen = 0; eb->linelen = 0; + eb->break_lines = break_lines; eb->scratch_pool = svn_pool_create(pool); stream = svn_stream_create(eb, pool); svn_stream_set_write(stream, encode_data); @@ -424,7 +430,7 @@ decode_bytes(svn_stringbuf_t *str, const /* If no data is left in temporary INBUF and there is at least one line-sized chunk left to decode, we may use the optimized code path. */ - if ((*inbuflen == 0) && (p + BASE64_LINELEN <= end)) + if ((*inbuflen == 0) && (end - p >= BASE64_LINELEN)) if (decode_line(str, &p)) continue;