You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2016/06/05 14:58:17 UTC
svn commit: r1746926 [2/3] - in /subversion/trunk/subversion:
include/private/svn_repos_private.h include/svn_repos.h
libsvn_ra_local/ra_plugin.c libsvn_repos/compat.c libsvn_repos/deprecated.c
libsvn_repos/log.c
Copied: subversion/trunk/subversion/libsvn_repos/compat.c (from r1746922, subversion/trunk/subversion/libsvn_repos/log.c)
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/compat.c?p2=subversion/trunk/subversion/libsvn_repos/compat.c&p1=subversion/trunk/subversion/libsvn_repos/log.c&r1=1746922&r2=1746926&rev=1746926&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/log.c (original)
+++ subversion/trunk/subversion/libsvn_repos/compat.c Sun Jun 5 14:58:17 2016
@@ -1,4 +1,5 @@
-/* log.c --- retrieving log messages
+/*
+ * compat.c: compatibility shims to adapt between different API versions.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
@@ -20,2400 +21,22 @@
* ====================================================================
*/
-
-#include <stdlib.h>
-#define APR_WANT_STRFUNC
-#include <apr_want.h>
-
+#include "svn_repos.h"
#include "svn_compat.h"
-#include "svn_private_config.h"
#include "svn_hash.h"
-#include "svn_pools.h"
-#include "svn_error.h"
-#include "svn_path.h"
-#include "svn_fs.h"
-#include "svn_repos.h"
-#include "svn_string.h"
-#include "svn_sorts.h"
#include "svn_props.h"
-#include "svn_mergeinfo.h"
-#include "repos.h"
-#include "private/svn_fspath.h"
-#include "private/svn_fs_private.h"
-#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,
- svn_repos_t *repos,
- svn_revnum_t revision,
- svn_repos_authz_func_t authz_read_func,
- void *authz_read_baton,
- apr_pool_t *pool)
-{
- svn_fs_t *fs = svn_repos_fs(repos);
- svn_fs_root_t *rev_root;
- 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 *iterpool;
-
- /* By default, we'll grant full read access to REVISION. */
- *access_level = svn_repos_revision_access_full;
-
- /* No auth-checking function? We're done. */
- if (! authz_read_func)
- return SVN_NO_ERROR;
-
- /* Fetch the changes associated with REVISION. */
- SVN_ERR(svn_fs_revision_root(&rev_root, fs, revision, 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.
-
- 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. */
- iterpool = svn_pool_create(pool);
- while (change)
- {
- svn_boolean_t readable;
-
- svn_pool_clear(iterpool);
-
- SVN_ERR(authz_read_func(&readable, rev_root, change->path.data,
- authz_read_baton, iterpool));
- if (! readable)
- found_unreadable = TRUE;
- else
- found_readable = TRUE;
-
- /* If we have at least one of each (readable/unreadable), we
- have our answer. */
- if (found_readable && found_unreadable)
- goto decision;
-
- switch (change->change_kind)
- {
- case svn_fs_path_change_add:
- case svn_fs_path_change_replace:
- {
- const char *copyfrom_path;
- svn_revnum_t copyfrom_rev;
-
- SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path,
- 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, iterpool));
- SVN_ERR(authz_read_func(&readable,
- copyfrom_root, copyfrom_path,
- authz_read_baton, iterpool));
- if (! readable)
- found_unreadable = TRUE;
-
- /* If we have at least one of each (readable/unreadable), we
- have our answer. */
- if (found_readable && found_unreadable)
- goto decision;
- }
- }
- break;
-
- case svn_fs_path_change_delete:
- case svn_fs_path_change_modify:
- default:
- break;
- }
-
- SVN_ERR(svn_fs_path_change_get(&change, iterator));
- }
-
- decision:
- svn_pool_destroy(iterpool);
-
- /* Either every changed path was unreadable... */
- if (! found_readable)
- *access_level = svn_repos_revision_access_none;
-
- /* ... or some changed path was unreadable... */
- else if (found_unreadable)
- *access_level = svn_repos_revision_access_partial;
-
- /* ... or every changed path was readable (the default). */
- return SVN_NO_ERROR;
-}
-
-
-/* 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 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
- * *ACCESS_LEVEL to svn_repos_revision_access_full.
- *
- * - If some paths are readable and some are not, then silently
- * omit the unreadable paths from the CHANGED hash, and set
- * *ACCESS_LEVEL to svn_repos_revision_access_partial.
- *
- * - If absolutely every changed-path (and copyfrom_path) is
- * unreadable, then return an empty CHANGED hash, and set
- * *ACCESS_LEVEL to svn_repos_revision_access_none. (This is
- * to distinguish a revision which truly has no changed paths
- * from a revision in which all paths are unreadable.)
- */
-static svn_error_t *
-detect_changed(svn_repos_revision_access_level_t *access_level,
- svn_fs_root_t *root,
- svn_fs_t *fs,
- const log_callbacks_t *callbacks,
- apr_pool_t *scratch_pool)
-{
- 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;
-
- /* 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 (!change)
- {
- /* No paths changed in this revision? Uh, sure, I guess the
- revision is readable, then. */
- *access_level = svn_repos_revision_access_full;
- return SVN_NO_ERROR;
- }
-
- iterpool = svn_pool_create(scratch_pool);
- while (change)
- {
- /* 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);
-
- /* Skip path if unreadable. */
- if (callbacks->authz_read_func)
- {
- 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;
- }
- }
-
- /* At least one changed-path was readable. */
- found_readable = TRUE;
-
- /* 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);
- }
-
- SVN_ERR(svn_fs_check_path(&change->node_kind, check_root, check_path,
- iterpool));
- }
-
- if ( (change->change_kind == svn_fs_path_change_add)
- || (change->change_kind == svn_fs_path_change_replace))
- {
- 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, iterpool));
- change->copyfrom_known = TRUE;
- }
-
- 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);
-
- if (! found_readable)
- {
- /* Every changed-path was unreadable. */
- *access_level = svn_repos_revision_access_none;
- }
- else if (found_unreadable)
- {
- /* At least one changed-path was unreadable. */
- *access_level = svn_repos_revision_access_partial;
- }
- else
- {
- /* Every changed-path was readable. */
- *access_level = svn_repos_revision_access_full;
- }
-
- return SVN_NO_ERROR;
-}
-
-/* This is used by svn_repos_get_logs to keep track of multiple
- * path history information while working through history.
- *
- * The two pools are swapped after each iteration through history because
- * to get the next history requires the previous one.
- */
-struct path_info
-{
- svn_stringbuf_t *path;
- svn_revnum_t history_rev;
- svn_boolean_t done;
- svn_boolean_t first_time;
-
- /* If possible, we like to keep open the history object for each path,
- since it avoids needed to open and close it many times as we walk
- backwards in time. To do so we need two pools, so that we can clear
- one each time through. If we're not holding the history open for
- this path then these three pointers will be NULL. */
- svn_fs_history_t *hist;
- apr_pool_t *newpool;
- apr_pool_t *oldpool;
-};
-
-/* Advance to the next history for the path.
- *
- * If INFO->HIST is not NULL we do this using that existing history object,
- * otherwise we open a new one.
- *
- * If no more history is available or the history revision is less
- * (earlier) than START, or the history is not available due
- * to authorization, then INFO->DONE is set to TRUE.
- *
- * A STRICT value of FALSE will indicate to follow history across copied
- * paths.
- *
- * If optional AUTHZ_READ_FUNC is non-NULL, then use it (with
- * AUTHZ_READ_BATON and FS) to check whether INFO->PATH is still readable if
- * we do indeed find more history for the path.
- */
-static svn_error_t *
-get_history(struct path_info *info,
- svn_fs_t *fs,
- svn_boolean_t strict,
- svn_repos_authz_func_t authz_read_func,
- void *authz_read_baton,
- svn_revnum_t start,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_fs_root_t *history_root = NULL;
- svn_fs_history_t *hist;
- apr_pool_t *subpool;
- const char *path;
-
- if (info->hist)
- {
- subpool = info->newpool;
-
- SVN_ERR(svn_fs_history_prev2(&info->hist, info->hist, ! strict,
- subpool, scratch_pool));
-
- hist = info->hist;
- }
- else
- {
- subpool = svn_pool_create(result_pool);
-
- /* Open the history located at the last rev we were at. */
- SVN_ERR(svn_fs_revision_root(&history_root, fs, info->history_rev,
- subpool));
-
- SVN_ERR(svn_fs_node_history2(&hist, history_root, info->path->data,
- subpool, scratch_pool));
-
- SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool,
- scratch_pool));
-
- if (info->first_time)
- info->first_time = FALSE;
- else
- SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool,
- scratch_pool));
- }
-
- if (! hist)
- {
- svn_pool_destroy(subpool);
- if (info->oldpool)
- svn_pool_destroy(info->oldpool);
- info->done = TRUE;
- return SVN_NO_ERROR;
- }
-
- /* Fetch the location information for this history step. */
- SVN_ERR(svn_fs_history_location(&path, &info->history_rev,
- hist, subpool));
-
- svn_stringbuf_set(info->path, path);
-
- /* If this history item predates our START revision then
- don't fetch any more for this path. */
- if (info->history_rev < start)
- {
- svn_pool_destroy(subpool);
- if (info->oldpool)
- svn_pool_destroy(info->oldpool);
- info->done = TRUE;
- return SVN_NO_ERROR;
- }
-
- /* Is the history item readable? If not, done with path. */
- if (authz_read_func)
- {
- svn_boolean_t readable;
- SVN_ERR(svn_fs_revision_root(&history_root, fs,
- info->history_rev,
- scratch_pool));
- SVN_ERR(authz_read_func(&readable, history_root,
- info->path->data,
- authz_read_baton,
- scratch_pool));
- if (! readable)
- info->done = TRUE;
- }
-
- if (! info->hist)
- {
- svn_pool_destroy(subpool);
- }
- else
- {
- apr_pool_t *temppool = info->oldpool;
- info->oldpool = info->newpool;
- svn_pool_clear(temppool);
- info->newpool = temppool;
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Set INFO->HIST to the next history for the path *if* there is history
- * available and INFO->HISTORY_REV is equal to or greater than CURRENT.
- *
- * *CHANGED is set to TRUE if the path has history in the CURRENT revision,
- * otherwise it is not touched.
- *
- * If we do need to get the next history revision for the path, call
- * get_history to do it -- see it for details.
- */
-static svn_error_t *
-check_history(svn_boolean_t *changed,
- struct path_info *info,
- svn_fs_t *fs,
- svn_revnum_t current,
- svn_boolean_t strict,
- svn_repos_authz_func_t authz_read_func,
- void *authz_read_baton,
- svn_revnum_t start,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- /* If we're already done with histories for this path,
- don't try to fetch any more. */
- if (info->done)
- return SVN_NO_ERROR;
-
- /* If the last rev we got for this path is less than CURRENT,
- then just return and don't fetch history for this path.
- The caller will get to this rev eventually or else reach
- the limit. */
- if (info->history_rev < current)
- return SVN_NO_ERROR;
-
- /* If the last rev we got for this path is equal to CURRENT
- then set *CHANGED to true and get the next history
- rev where this path was changed. */
- *changed = TRUE;
- return get_history(info, fs, strict, authz_read_func,
- authz_read_baton, start, result_pool, scratch_pool);
-}
-
-/* Return the next interesting revision in our list of HISTORIES. */
-static svn_revnum_t
-next_history_rev(const apr_array_header_t *histories)
-{
- svn_revnum_t next_rev = SVN_INVALID_REVNUM;
- int i;
-
- for (i = 0; i < histories->nelts; ++i)
- {
- struct path_info *info = APR_ARRAY_IDX(histories, i,
- struct path_info *);
- if (info->done)
- continue;
- if (info->history_rev > next_rev)
- next_rev = info->history_rev;
- }
-
- return next_rev;
-}
-
-/* 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. */
-/* ### 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,
- 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, *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;
-
- /* Initialize return variables. */
- *deleted_mergeinfo_catalog = svn_hash__make(result_pool);
- *added_mergeinfo_catalog = svn_hash__make(result_pool);
-
- /* Revision 0 has no mergeinfo and no mergeinfo changes. */
- 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));
- 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.
-
- 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;
-
- if ( (change->change_kind == svn_fs_path_change_add)
- || (change->change_kind == svn_fs_path_change_replace))
- any_copy = TRUE;
-
- SVN_ERR(svn_fs_path_change_get(&change, iterator));
- }
-
- /* No potential mergeinfo changes? We're done. */
- if (! any_mergeinfo)
- {
- 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);
- while (TRUE)
- {
- const char *changed_path;
- 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. */
- if (change->mergeinfo_mod == svn_tristate_false)
- continue;
-
- /* If there was no property change on this item, ignore it. */
- if (! change->prop_mod)
- continue;
-
- /* Begin actual processing */
- changed_path = change->path.data;
- svn_pool_clear(iterpool);
-
- switch (change->change_kind)
- {
-
- /* ### TODO: Can the add, replace, and modify cases be joined
- ### together to all use svn_repos__prev_location()? The
- ### difference would be the fallback case (path/rev-1 for
- ### modifies, NULL otherwise). -- cmpilato */
-
- /* If the path was merely modified, see if its previous
- location was affected by a copy which happened in this
- revision before assuming it holds the same path it did the
- previous revision. */
- case svn_fs_path_change_modify:
- {
- svn_revnum_t appeared_rev;
-
- /* If there were no copies in this revision, the path will have
- existed in the previous rev. Otherwise, we might just got
- copied here and need to check for that eventuality. */
- if (any_copy)
- {
- SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path,
- &base_rev, fs, rev,
- changed_path, iterpool));
-
- /* If this path isn't the result of a copy that occurred
- in this revision, we can find the previous version of
- it in REV - 1 at the same path. */
- if (! (base_path && SVN_IS_VALID_REVNUM(base_rev)
- && (appeared_rev == rev)))
- {
- base_path = changed_path;
- base_rev = rev - 1;
- }
- }
- else
- {
- base_path = changed_path;
- base_rev = rev - 1;
- }
- break;
- }
-
- /* If the path was added or replaced, see if it was created via
- copy. If so, set BASE_REV/BASE_PATH to its previous location.
- If not, there's no previous location to examine -- leave
- BASE_REV/BASE_PATH = -1/NULL. */
- case svn_fs_path_change_add:
- case svn_fs_path_change_replace:
- {
- if (change->copyfrom_known)
- {
- base_rev = change->copyfrom_rev;
- base_path = change->copyfrom_path;
- }
- else
- {
- SVN_ERR(svn_fs_copied_from(&base_rev, &base_path,
- root, changed_path, iterpool));
- }
- break;
- }
-
- /* We don't care about any of the other cases. */
- case svn_fs_path_change_delete:
- case svn_fs_path_change_reset:
- default:
- continue;
- }
-
- /* If there was a base location, fetch its mergeinfo property value. */
- if (base_path && SVN_IS_VALID_REVNUM(base_rev))
- {
- SVN_ERR(svn_fs_revision_root(&base_root, fs, base_rev, iterpool));
- SVN_ERR(svn_fs_node_prop(&prev_mergeinfo_value, base_root, base_path,
- SVN_PROP_MERGEINFO, iterpool));
- }
-
- /* Now fetch the current (as of REV) mergeinfo property value. */
- SVN_ERR(svn_fs_node_prop(&mergeinfo_value, root, changed_path,
- SVN_PROP_MERGEINFO, iterpool));
-
- /* No mergeinfo on either the new or previous location? Just
- skip it. (If there *was* a change, it would have been in
- inherited mergeinfo only, which should be picked up by the
- iteration of this loop that finds the parent paths that
- really got changed.) */
- if (! (mergeinfo_value || prev_mergeinfo_value))
- continue;
-
- /* Mergeinfo on both sides but it did not change? Skip that too. */
- if ( mergeinfo_value && prev_mergeinfo_value
- && svn_string_compare(mergeinfo_value, prev_mergeinfo_value))
- continue;
-
- /* If mergeinfo was explicitly added or removed on this path, we
- need to check to see if that was a real semantic change of
- meaning. So, fill in the "missing" mergeinfo value with the
- inherited mergeinfo for that path/revision. */
- if (prev_mergeinfo_value && (! mergeinfo_value))
- {
- svn_mergeinfo_t tmp_mergeinfo;
-
- SVN_ERR(svn_fs__get_mergeinfo_for_path(&tmp_mergeinfo,
- root, changed_path,
- svn_mergeinfo_inherited, TRUE,
- iterpool, iterpool));
- if (tmp_mergeinfo)
- SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_value,
- tmp_mergeinfo,
- iterpool));
- }
- else if (mergeinfo_value && (! prev_mergeinfo_value)
- && base_path && SVN_IS_VALID_REVNUM(base_rev))
- {
- svn_mergeinfo_t tmp_mergeinfo;
-
- SVN_ERR(svn_fs__get_mergeinfo_for_path(&tmp_mergeinfo,
- base_root, base_path,
- svn_mergeinfo_inherited, TRUE,
- iterpool, iterpool));
- if (tmp_mergeinfo)
- SVN_ERR(svn_mergeinfo_to_string(&prev_mergeinfo_value,
- tmp_mergeinfo,
- iterpool));
- }
-
- /* Old and new mergeinfo probably differ in some way (we already
- checked for textual equality further up). Store the before and
- after mergeinfo values in our return hashes. They may still be
- equal as manual intervention may have only changed the formatting
- but not the relevant contents. */
- {
- svn_mergeinfo_t prev_mergeinfo = NULL, mergeinfo = NULL;
- svn_mergeinfo_t deleted, added;
- const char *hash_path;
-
- if (mergeinfo_value)
- SVN_ERR(svn_mergeinfo_parse(&mergeinfo,
- mergeinfo_value->data, iterpool));
- if (prev_mergeinfo_value)
- SVN_ERR(svn_mergeinfo_parse(&prev_mergeinfo,
- prev_mergeinfo_value->data, iterpool));
- SVN_ERR(svn_mergeinfo_diff2(&deleted, &added, prev_mergeinfo,
- mergeinfo, FALSE, result_pool,
- iterpool));
-
- /* Toss interesting stuff into our return catalogs. */
- hash_path = apr_pstrdup(result_pool, changed_path);
- svn_hash_sets(*deleted_mergeinfo_catalog, hash_path, deleted);
- svn_hash_sets(*added_mergeinfo_catalog, hash_path, added);
- }
- }
-
- 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. */
-static svn_error_t *
-get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo,
- svn_mergeinfo_t *deleted_mergeinfo,
- svn_fs_t *fs,
- const apr_array_header_t *paths,
- svn_revnum_t rev,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_mergeinfo_catalog_t added_mergeinfo_catalog, deleted_mergeinfo_catalog;
- apr_hash_index_t *hi;
- svn_fs_root_t *root;
- apr_pool_t *iterpool;
- int i;
- svn_error_t *err;
-
- /* Initialize return value. */
- *added_mergeinfo = svn_hash__make(result_pool);
- *deleted_mergeinfo = svn_hash__make(result_pool);
-
- /* If we're asking about revision 0, there's no mergeinfo to be found. */
- if (rev == 0)
- return SVN_NO_ERROR;
-
- /* No paths? No mergeinfo. */
- if (! paths->nelts)
- return SVN_NO_ERROR;
-
- /* Fetch the mergeinfo changes for REV. */
- err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog,
- &added_mergeinfo_catalog,
- fs, rev,
- scratch_pool, scratch_pool);
- if (err)
- {
- if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
- {
- /* Issue #3896: If invalid mergeinfo is encountered the
- best we can do is ignore it and act as if there were
- no mergeinfo modifications. */
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
- else
- {
- return svn_error_trace(err);
- }
- }
-
- /* In most revisions, there will be no mergeinfo change at all. */
- if ( apr_hash_count(deleted_mergeinfo_catalog) == 0
- && apr_hash_count(added_mergeinfo_catalog) == 0)
- return SVN_NO_ERROR;
-
- /* Create a work subpool and get a root for REV. */
- SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
-
- /* Check our PATHS for any changes to their inherited mergeinfo.
- (We deal with changes to mergeinfo directly *on* the paths in the
- following loop.) */
- iterpool = svn_pool_create(scratch_pool);
- for (i = 0; i < paths->nelts; i++)
- {
- const char *path = APR_ARRAY_IDX(paths, i, const char *);
- const char *prev_path;
- svn_revnum_t appeared_rev, prev_rev;
- svn_fs_root_t *prev_root;
- svn_mergeinfo_t prev_mergeinfo, mergeinfo, deleted, added,
- prev_inherited_mergeinfo, inherited_mergeinfo;
-
- svn_pool_clear(iterpool);
-
- /* If this path is represented in the changed-mergeinfo hashes,
- we'll deal with it in the loop below. */
- if (svn_hash_gets(deleted_mergeinfo_catalog, path))
- continue;
-
- /* Figure out what path/rev to compare against. Ignore
- not-found errors returned by the filesystem. */
- err = svn_repos__prev_location(&appeared_rev, &prev_path, &prev_rev,
- fs, rev, path, iterpool);
- if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
- err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
- {
- svn_error_clear(err);
- err = SVN_NO_ERROR;
- continue;
- }
- SVN_ERR(err);
-
- /* If this path isn't the result of a copy that occurred in this
- revision, we can find the previous version of it in REV - 1
- at the same path. */
- if (! (prev_path && SVN_IS_VALID_REVNUM(prev_rev)
- && (appeared_rev == rev)))
- {
- prev_path = path;
- prev_rev = rev - 1;
- }
-
- /* Fetch the previous mergeinfo (including inherited stuff) for
- this path. Ignore not-found errors returned by the
- filesystem or invalid mergeinfo (Issue #3896).*/
- SVN_ERR(svn_fs_revision_root(&prev_root, fs, prev_rev, iterpool));
- err = svn_fs__get_mergeinfo_for_path(&prev_mergeinfo,
- prev_root, prev_path,
- svn_mergeinfo_inherited, TRUE,
- iterpool, iterpool);
- if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
- err->apr_err == SVN_ERR_FS_NOT_DIRECTORY ||
- err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR))
- {
- svn_error_clear(err);
- err = SVN_NO_ERROR;
- continue;
- }
- SVN_ERR(err);
-
- /* Issue #4022 'svn log -g interprets change in inherited mergeinfo due
- to move as a merge': A copy where the source and destination inherit
- mergeinfo from the same parent means the inherited mergeinfo of the
- source and destination will differ, but this diffrence is not
- indicative of a merge unless the mergeinfo on the inherited parent
- has actually changed.
-
- To check for this we must fetch the "raw" previous inherited
- mergeinfo and the "raw" mergeinfo @REV then compare these. */
- SVN_ERR(svn_fs__get_mergeinfo_for_path(&prev_inherited_mergeinfo,
- prev_root, prev_path,
- svn_mergeinfo_nearest_ancestor,
- FALSE, /* adjust_inherited_mergeinfo */
- iterpool, iterpool));
-
- /* Fetch the current mergeinfo (as of REV, and including
- inherited stuff) for this path. */
- SVN_ERR(svn_fs__get_mergeinfo_for_path(&mergeinfo,
- root, path,
- svn_mergeinfo_inherited, TRUE,
- iterpool, iterpool));
-
- /* Issue #4022 again, fetch the raw inherited mergeinfo. */
- SVN_ERR(svn_fs__get_mergeinfo_for_path(&inherited_mergeinfo,
- root, path,
- svn_mergeinfo_nearest_ancestor,
- FALSE, /* adjust_inherited_mergeinfo */
- iterpool, iterpool));
-
- if (!prev_mergeinfo && !mergeinfo)
- continue;
-
- /* Last bit of issue #4022 checking. */
- if (prev_inherited_mergeinfo && inherited_mergeinfo)
- {
- svn_boolean_t inherits_same_mergeinfo;
-
- SVN_ERR(svn_mergeinfo__equals(&inherits_same_mergeinfo,
- prev_inherited_mergeinfo,
- inherited_mergeinfo,
- TRUE, iterpool));
- /* If a copy rather than an actual merge brought about an
- inherited mergeinfo change then we are finished. */
- if (inherits_same_mergeinfo)
- continue;
- }
- else
- {
- svn_boolean_t same_mergeinfo;
- SVN_ERR(svn_mergeinfo__equals(&same_mergeinfo,
- prev_inherited_mergeinfo,
- NULL,
- TRUE, iterpool));
- if (same_mergeinfo)
- continue;
- }
-
- /* Compare, constrast, and combine the results. */
- SVN_ERR(svn_mergeinfo_diff2(&deleted, &added, prev_mergeinfo,
- mergeinfo, FALSE, result_pool, iterpool));
- SVN_ERR(svn_mergeinfo_merge2(*deleted_mergeinfo, deleted,
- result_pool, iterpool));
- SVN_ERR(svn_mergeinfo_merge2(*added_mergeinfo, added,
- result_pool, iterpool));
- }
-
- /* Merge all the mergeinfos which are, or are children of, one of
- our paths of interest into one giant delta mergeinfo. */
- for (hi = apr_hash_first(scratch_pool, added_mergeinfo_catalog);
- hi; hi = apr_hash_next(hi))
- {
- const char *changed_path = apr_hash_this_key(hi);
- apr_ssize_t klen = apr_hash_this_key_len(hi);
- svn_mergeinfo_t added = apr_hash_this_val(hi);
- svn_mergeinfo_t deleted;
-
- for (i = 0; i < paths->nelts; i++)
- {
- const char *path = APR_ARRAY_IDX(paths, i, const char *);
- if (! svn_fspath__skip_ancestor(path, changed_path))
- continue;
- svn_pool_clear(iterpool);
- deleted = apr_hash_get(deleted_mergeinfo_catalog, changed_path, klen);
- SVN_ERR(svn_mergeinfo_merge2(*deleted_mergeinfo,
- svn_mergeinfo_dup(deleted, result_pool),
- result_pool, iterpool));
- SVN_ERR(svn_mergeinfo_merge2(*added_mergeinfo,
- svn_mergeinfo_dup(added, result_pool),
- result_pool, iterpool));
-
- break;
- }
- }
-
- svn_pool_destroy(iterpool);
- return SVN_NO_ERROR;
-}
-
-
-/* Fill LOG_ENTRY with history information in FS at REV. */
-static svn_error_t *
-fill_log_entry(svn_repos_log_entry_t *log_entry,
- svn_revnum_t rev,
- svn_fs_t *fs,
- const apr_array_header_t *revprops,
- const log_callbacks_t *callbacks,
- apr_pool_t *pool)
-{
- 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)
- && (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, newroot, fs, callbacks, pool));
-
- if (access_level == svn_repos_revision_access_none)
- {
- /* All changed-paths are unreadable, so clear all fields. */
- get_revprops = FALSE;
- }
- else if (access_level == svn_repos_revision_access_partial)
- {
- /* At least one changed-path was unreadable, so censor all
- but author and date. (The unreadable paths are already
- missing from the hash.) */
- censor_revprops = TRUE;
- }
- }
-
- if (get_revprops && want_revprops)
- {
- /* User is allowed to see at least some revprops. */
- SVN_ERR(svn_fs_revision_proplist2(&r_props, fs, rev, FALSE, pool,
- pool));
- if (revprops == NULL)
- {
- /* Requested all revprops... */
- if (censor_revprops)
- {
- /* ... but we can only return author/date. */
- log_entry->revprops = svn_hash__make(pool);
- svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR,
- svn_hash_gets(r_props, SVN_PROP_REVISION_AUTHOR));
- svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_DATE,
- svn_hash_gets(r_props, SVN_PROP_REVISION_DATE));
- }
- else
- /* ... so return all we got. */
- log_entry->revprops = r_props;
- }
- else
- {
- int i;
-
- /* Requested only some revprops... */
-
- /* Make "svn:author" and "svn:date" available as svn_string_t
- for efficient comparison via svn_string_compare(). Note that
- 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__STATIC_STRING(SVN_PROP_REVISION_AUTHOR);
- static const svn_string_t svn_prop_revision_date
- = 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. */
- if (revprops->nelts == apr_hash_count(r_props) && !censor_revprops)
- {
- log_entry->revprops = r_props;
- for (i = 0; i < revprops->nelts; i++)
- {
- const svn_string_t *name
- = APR_ARRAY_IDX(revprops, i, const svn_string_t *);
- if (!apr_hash_get(r_props, name->data, name->len))
- {
- /* hash does not match list of revprops we want */
- log_entry->revprops = NULL;
- break;
- }
- }
- }
-
- /* slow, revprop-by-revprop filtering */
- if (log_entry->revprops == NULL)
- for (i = 0; i < revprops->nelts; i++)
- {
- const svn_string_t *name
- = APR_ARRAY_IDX(revprops, i, const svn_string_t *);
- svn_string_t *value
- = apr_hash_get(r_props, name->data, name->len);
- if (censor_revprops
- && !svn_string_compare(name, &svn_prop_revision_author)
- && !svn_string_compare(name, &svn_prop_revision_date))
- /* ... but we can only return author/date. */
- continue;
- if (log_entry->revprops == NULL)
- log_entry->revprops = svn_hash__make(pool);
- apr_hash_set(log_entry->revprops, name->data, name->len, value);
- }
- }
- }
-
- log_entry->revision = rev;
-
- return SVN_NO_ERROR;
-}
-
-/* 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 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.
-
- If REVPROPS is NULL, retrieve all revision properties; else, retrieve
- only the revision properties named by the (const char *) array elements
- (i.e. retrieve none if the array is empty).
-
- LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, and
- NESTED_MERGES are as per the arguments of the same name to DO_LOGS.
- If HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
- already represented in LOG_TARGET_HISTORY_AS_MERGEINFO, then don't send
- the log message for REV. If SUBTRACTIVE_MERGE is true, then REV was
- reverse merged.
-
- If HANDLING_MERGED_REVISIONS is FALSE then ignore NESTED_MERGES. Otherwise
- if NESTED_MERGES is not NULL and REV is contained in it, then don't send
- the log for REV, otherwise send it normally and add REV to
- NESTED_MERGES. */
-static svn_error_t *
-send_log(svn_revnum_t rev,
- svn_fs_t *fs,
- svn_mergeinfo_t log_target_history_as_mergeinfo,
- svn_bit_array__t *nested_merges,
- svn_boolean_t subtractive_merge,
- svn_boolean_t handling_merged_revision,
- const apr_array_header_t *revprops,
- svn_boolean_t has_children,
- const log_callbacks_t *callbacks,
- apr_pool_t *pool)
-{
- 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.
-
- Use our callback to snoop through the changes. */
- if (handling_merged_revision
- && log_target_history_as_mergeinfo
- && apr_hash_count(log_target_history_as_mergeinfo))
- {
- 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;
- }
-
- 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 (baton.found_rev_of_interest)
- {
- apr_pool_t *scratch_pool;
-
- /* Is REV a merged revision we've already sent? */
- if (nested_merges && handling_merged_revision)
- {
- if (svn_bit_array__get(nested_merges, rev))
- {
- /* We already sent REV. */
- return SVN_NO_ERROR;
- }
- else
- {
- /* NESTED_REVS needs to last across all the send_log, do_logs,
- handle_merged_revisions() recursions, so use the pool it
- was created in at the top of the recursion. */
- svn_bit_array__set(nested_merges, rev, TRUE);
- }
- }
-
- /* Pass a scratch pool to ensure no temporary state stored
- by the receiver callback persists. */
- scratch_pool = svn_pool_create(pool);
- SVN_ERR(callbacks->revision_receiver(callbacks->revision_receiver_baton,
- &log_entry, scratch_pool));
- svn_pool_destroy(scratch_pool);
- }
-
- return SVN_NO_ERROR;
-}
-
-/* This controls how many history objects we keep open. For any targets
- over this number we have to open and close their histories as needed,
- which is CPU intensive, but keeps us from using an unbounded amount of
- memory. */
-#define MAX_OPEN_HISTORIES 32
-
-/* Get the histories for PATHS, and store them in *HISTORIES.
-
- If IGNORE_MISSING_LOCATIONS is set, don't treat requests for bogus
- repository locations as fatal -- just ignore them. */
-static svn_error_t *
-get_path_histories(apr_array_header_t **histories,
- svn_fs_t *fs,
- const apr_array_header_t *paths,
- svn_revnum_t hist_start,
- svn_revnum_t hist_end,
- svn_boolean_t strict_node_history,
- svn_boolean_t ignore_missing_locations,
- svn_repos_authz_func_t authz_read_func,
- void *authz_read_baton,
- apr_pool_t *pool)
-{
- svn_fs_root_t *root;
- apr_pool_t *iterpool;
- svn_error_t *err;
- int i;
-
- /* Create a history object for each path so we can walk through
- them all at the same time until we have all changes or LIMIT
- is reached.
-
- There is some pool fun going on due to the fact that we have
- to hold on to the old pool with the history before we can
- get the next history.
- */
- *histories = apr_array_make(pool, paths->nelts,
- sizeof(struct path_info *));
-
- SVN_ERR(svn_fs_revision_root(&root, fs, hist_end, pool));
-
- iterpool = svn_pool_create(pool);
- for (i = 0; i < paths->nelts; i++)
- {
- const char *this_path = APR_ARRAY_IDX(paths, i, const char *);
- struct path_info *info = apr_palloc(pool,
- sizeof(struct path_info));
- svn_pool_clear(iterpool);
-
- if (authz_read_func)
- {
- svn_boolean_t readable;
- SVN_ERR(authz_read_func(&readable, root, this_path,
- authz_read_baton, iterpool));
- if (! readable)
- return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL);
- }
-
- info->path = svn_stringbuf_create(this_path, pool);
- info->done = FALSE;
- info->history_rev = hist_end;
- info->first_time = TRUE;
-
- if (i < MAX_OPEN_HISTORIES)
- {
- err = svn_fs_node_history2(&info->hist, root, this_path, pool,
- iterpool);
- if (err
- && ignore_missing_locations
- && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
- err->apr_err == SVN_ERR_FS_NOT_DIRECTORY ||
- err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION))
- {
- svn_error_clear(err);
- continue;
- }
- SVN_ERR(err);
- info->newpool = svn_pool_create(pool);
- info->oldpool = svn_pool_create(pool);
- }
- else
- {
- info->hist = NULL;
- info->oldpool = NULL;
- info->newpool = NULL;
- }
-
- err = get_history(info, fs,
- strict_node_history,
- authz_read_func, authz_read_baton,
- hist_start, pool, iterpool);
- if (err
- && ignore_missing_locations
- && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
- err->apr_err == SVN_ERR_FS_NOT_DIRECTORY ||
- err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION))
- {
- svn_error_clear(err);
- continue;
- }
- SVN_ERR(err);
- APR_ARRAY_PUSH(*histories, struct path_info *) = info;
- }
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
-/* Remove and return the first item from ARR. */
-static void *
-array_pop_front(apr_array_header_t *arr)
-{
- void *item = arr->elts;
-
- if (apr_is_empty_array(arr))
- return NULL;
-
- arr->elts += arr->elt_size;
- arr->nelts -= 1;
- arr->nalloc -= 1;
- return item;
-}
-
-/* A struct which represents a single revision range, and the paths which
- have mergeinfo in that range. */
-struct path_list_range
-{
- apr_array_header_t *paths;
- svn_merge_range_t range;
-
- /* Is RANGE the result of a reverse merge? */
- svn_boolean_t reverse_merge;
-};
-
-/* A struct which represents "inverse mergeinfo", that is, instead of having
- a path->revision_range_list mapping, which is the way mergeinfo is commonly
- represented, this struct enables a revision_range_list,path tuple, where
- the paths can be accessed by revision. */
-struct rangelist_path
-{
- svn_rangelist_t *rangelist;
- const char *path;
-};
-
-/* Comparator function for combine_mergeinfo_path_lists(). Sorts
- rangelist_path structs in increasing order based upon starting revision,
- then ending revision of the first element in the rangelist.
-
- This does not sort rangelists based upon subsequent elements, only the
- first range. We'll sort any subsequent ranges in the correct order
- when they get bumped up to the front by removal of earlier ones, so we
- don't really have to sort them here. See combine_mergeinfo_path_lists()
- for details. */
-static int
-compare_rangelist_paths(const void *a, const void *b)
-{
- struct rangelist_path *rpa = *((struct rangelist_path *const *) a);
- struct rangelist_path *rpb = *((struct rangelist_path *const *) b);
- svn_merge_range_t *mra = APR_ARRAY_IDX(rpa->rangelist, 0,
- svn_merge_range_t *);
- svn_merge_range_t *mrb = APR_ARRAY_IDX(rpb->rangelist, 0,
- svn_merge_range_t *);
-
- if (mra->start < mrb->start)
- return -1;
- if (mra->start > mrb->start)
- return 1;
- if (mra->end < mrb->end)
- return -1;
- if (mra->end > mrb->end)
- return 1;
-
- return 0;
-}
-
-/* From MERGEINFO, return in *COMBINED_LIST, allocated in POOL, a list of
- 'struct path_list_range's. This list represents the rangelists in
- MERGEINFO and each path which has mergeinfo in that range.
- If REVERSE_MERGE is true, then MERGEINFO represents mergeinfo removed
- as the result of a reverse merge. */
-static svn_error_t *
-combine_mergeinfo_path_lists(apr_array_header_t **combined_list,
- svn_mergeinfo_t mergeinfo,
- svn_boolean_t reverse_merge,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
- apr_array_header_t *rangelist_paths;
- apr_pool_t *subpool = svn_pool_create(pool);
-
- /* Create a list of (revision range, path) tuples from MERGEINFO. */
- rangelist_paths = apr_array_make(subpool, apr_hash_count(mergeinfo),
- sizeof(struct rangelist_path *));
- for (hi = apr_hash_first(subpool, mergeinfo); hi;
- hi = apr_hash_next(hi))
- {
- int i;
- struct rangelist_path *rp = apr_palloc(subpool, sizeof(*rp));
-
- rp->path = apr_hash_this_key(hi);
- rp->rangelist = apr_hash_this_val(hi);
- APR_ARRAY_PUSH(rangelist_paths, struct rangelist_path *) = rp;
-
- /* We need to make local copies of the rangelist, since we will be
- modifying it, below. */
- rp->rangelist = svn_rangelist_dup(rp->rangelist, subpool);
-
- /* Make all of the rangelists inclusive, both start and end. */
- for (i = 0; i < rp->rangelist->nelts; i++)
- APR_ARRAY_IDX(rp->rangelist, i, svn_merge_range_t *)->start += 1;
- }
-
- /* Loop over the (revision range, path) tuples, chopping them into
- (revision range, paths) tuples, and appending those to the output
- list. */
- if (! *combined_list)
- *combined_list = apr_array_make(pool, 0, sizeof(struct path_list_range *));
-
- while (rangelist_paths->nelts > 1)
- {
- svn_revnum_t youngest, next_youngest, tail, youngest_end;
- struct path_list_range *plr;
- struct rangelist_path *rp;
- int num_revs;
- int i;
-
- /* First, sort the list such that the start revision of the first
- revision arrays are sorted. */
- svn_sort__array(rangelist_paths, compare_rangelist_paths);
-
- /* Next, find the number of revision ranges which start with the same
- revision. */
- rp = APR_ARRAY_IDX(rangelist_paths, 0, struct rangelist_path *);
- youngest =
- APR_ARRAY_IDX(rp->rangelist, 0, struct svn_merge_range_t *)->start;
- next_youngest = youngest;
- for (num_revs = 1; next_youngest == youngest; num_revs++)
- {
- if (num_revs == rangelist_paths->nelts)
- {
- num_revs += 1;
- break;
- }
- rp = APR_ARRAY_IDX(rangelist_paths, num_revs,
- struct rangelist_path *);
- next_youngest = APR_ARRAY_IDX(rp->rangelist, 0,
- struct svn_merge_range_t *)->start;
- }
- num_revs -= 1;
-
- /* The start of the new range will be YOUNGEST, and we now find the end
- of the new range, which should be either one less than the next
- earliest start of a rangelist, or the end of the first rangelist. */
- youngest_end =
- APR_ARRAY_IDX(APR_ARRAY_IDX(rangelist_paths, 0,
- struct rangelist_path *)->rangelist,
- 0, svn_merge_range_t *)->end;
- if ( (next_youngest == youngest) || (youngest_end < next_youngest) )
- tail = youngest_end;
- else
- tail = next_youngest - 1;
-
- /* Insert the (earliest, tail) tuple into the output list, along with
- a list of paths which match it. */
- plr = apr_palloc(pool, sizeof(*plr));
- plr->reverse_merge = reverse_merge;
- plr->range.start = youngest;
- plr->range.end = tail;
- plr->paths = apr_array_make(pool, num_revs, sizeof(const char *));
- for (i = 0; i < num_revs; i++)
- APR_ARRAY_PUSH(plr->paths, const char *) =
- APR_ARRAY_IDX(rangelist_paths, i, struct rangelist_path *)->path;
- APR_ARRAY_PUSH(*combined_list, struct path_list_range *) = plr;
-
- /* Now, check to see which (rangelist path) combinations we can remove,
- and do so. */
- for (i = 0; i < num_revs; i++)
- {
- svn_merge_range_t *range;
- rp = APR_ARRAY_IDX(rangelist_paths, i, struct rangelist_path *);
- range = APR_ARRAY_IDX(rp->rangelist, 0, svn_merge_range_t *);
-
- /* Set the start of the range to beyond the end of the range we
- just built. If the range is now "inverted", we can get pop it
- off the list. */
- range->start = tail + 1;
- if (range->start > range->end)
- {
- if (rp->rangelist->nelts == 1)
- {
- /* The range is the only on its list, so we should remove
- the entire rangelist_path, adjusting our loop control
- variables appropriately. */
- array_pop_front(rangelist_paths);
- i--;
- num_revs--;
- }
- else
- {
- /* We have more than one range on the list, so just remove
- the first one. */
- array_pop_front(rp->rangelist);
- }
- }
- }
- }
-
- /* Finally, add the last remaining (revision range, path) to the output
- list. */
- if (rangelist_paths->nelts > 0)
- {
- struct rangelist_path *first_rp =
- APR_ARRAY_IDX(rangelist_paths, 0, struct rangelist_path *);
- while (first_rp->rangelist->nelts > 0)
- {
- struct path_list_range *plr = apr_palloc(pool, sizeof(*plr));
-
- plr->reverse_merge = reverse_merge;
- plr->paths = apr_array_make(pool, 1, sizeof(const char *));
- APR_ARRAY_PUSH(plr->paths, const char *) = first_rp->path;
- plr->range = *APR_ARRAY_IDX(first_rp->rangelist, 0,
- svn_merge_range_t *);
- array_pop_front(first_rp->rangelist);
- APR_ARRAY_PUSH(*combined_list, struct path_list_range *) = plr;
- }
- }
-
- svn_pool_destroy(subpool);
-
- return SVN_NO_ERROR;
-}
-
-
-/* Pity that C is so ... linear. */
-static svn_error_t *
-do_logs(svn_fs_t *fs,
- const apr_array_header_t *paths,
- svn_mergeinfo_t log_target_history_as_mergeinfo,
- svn_mergeinfo_t processed,
- svn_bit_array__t *nested_merges,
- svn_revnum_t hist_start,
- svn_revnum_t hist_end,
- int limit,
- svn_boolean_t strict_node_history,
- svn_boolean_t include_merged_revisions,
- svn_boolean_t handling_merged_revisions,
- svn_boolean_t subtractive_merge,
- svn_boolean_t ignore_missing_locations,
- const apr_array_header_t *revprops,
- svn_boolean_t descending_order,
- log_callbacks_t *callbacks,
- apr_pool_t *pool);
-
-/* Comparator function for handle_merged_revisions(). Sorts path_list_range
- structs in increasing order based on the struct's RANGE.START revision,
- then RANGE.END revision. */
-static int
-compare_path_list_range(const void *a, const void *b)
-{
- struct path_list_range *plr_a = *((struct path_list_range *const *) a);
- struct path_list_range *plr_b = *((struct path_list_range *const *) b);
-
- if (plr_a->range.start < plr_b->range.start)
- return -1;
- if (plr_a->range.start > plr_b->range.start)
- return 1;
- if (plr_a->range.end < plr_b->range.end)
- return -1;
- if (plr_a->range.end > plr_b->range.end)
- return 1;
-
- return 0;
-}
-
-/* Examine the ADDED_MERGEINFO and DELETED_MERGEINFO for revision REV in FS
- (as collected by examining paths of interest to a log operation), and
- determine which revisions to report as having been merged or reverse-merged
- via the commit resulting in REV.
-
- Silently ignore some failures to find the revisions mentioned in the
- added/deleted mergeinfos, as might happen if there is invalid mergeinfo.
-
- Other parameters are as described by do_logs(), around which this
- is a recursion wrapper. */
-static svn_error_t *
-handle_merged_revisions(svn_revnum_t rev,
- svn_fs_t *fs,
- svn_mergeinfo_t log_target_history_as_mergeinfo,
- svn_bit_array__t *nested_merges,
- svn_mergeinfo_t processed,
- svn_mergeinfo_t added_mergeinfo,
- svn_mergeinfo_t deleted_mergeinfo,
- svn_boolean_t strict_node_history,
- const apr_array_header_t *revprops,
- log_callbacks_t *callbacks,
- apr_pool_t *pool)
-{
- apr_array_header_t *combined_list = NULL;
- svn_repos_log_entry_t empty_log_entry = { 0 };
- apr_pool_t *iterpool;
- int i;
-
- if (apr_hash_count(added_mergeinfo) == 0
- && apr_hash_count(deleted_mergeinfo) == 0)
- return SVN_NO_ERROR;
-
- if (apr_hash_count(added_mergeinfo))
- SVN_ERR(combine_mergeinfo_path_lists(&combined_list, added_mergeinfo,
- FALSE, pool));
-
- if (apr_hash_count(deleted_mergeinfo))
- SVN_ERR(combine_mergeinfo_path_lists(&combined_list, deleted_mergeinfo,
- TRUE, pool));
-
- SVN_ERR_ASSERT(combined_list != NULL);
- svn_sort__array(combined_list, compare_path_list_range);
-
- /* Because the combined_lists are ordered youngest to oldest,
- iterate over them in reverse. */
- iterpool = svn_pool_create(pool);
- for (i = combined_list->nelts - 1; i >= 0; i--)
- {
- struct path_list_range *pl_range
- = APR_ARRAY_IDX(combined_list, i, struct path_list_range *);
-
- svn_pool_clear(iterpool);
- 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,
- strict_node_history,
- TRUE, pl_range->reverse_merge, TRUE, TRUE,
- revprops, TRUE, callbacks, iterpool));
- }
- svn_pool_destroy(iterpool);
-
- /* Send the empty revision. */
- 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
- reverse merges. */
-struct added_deleted_mergeinfo
-{
- svn_mergeinfo_t added_mergeinfo;
- svn_mergeinfo_t deleted_mergeinfo;
-};
-
-/* Reduce the search range PATHS, HIST_START, HIST_END by removing
- parts already covered by PROCESSED. If reduction is possible
- elements may be removed from PATHS and *START_REDUCED and
- *END_REDUCED may be set to a narrower range. */
-static svn_error_t *
-reduce_search(apr_array_header_t *paths,
- svn_revnum_t *hist_start,
- svn_revnum_t *hist_end,
- svn_mergeinfo_t processed,
- apr_pool_t *scratch_pool)
-{
- /* We add 1 to end to compensate for store_search */
- svn_revnum_t start = *hist_start <= *hist_end ? *hist_start : *hist_end;
- svn_revnum_t end = *hist_start <= *hist_end ? *hist_end + 1 : *hist_start + 1;
- int i;
-
- for (i = 0; i < paths->nelts; ++i)
- {
- const char *path = APR_ARRAY_IDX(paths, i, const char *);
- svn_rangelist_t *ranges = svn_hash_gets(processed, path);
- int j;
-
- if (!ranges)
- continue;
-
- /* ranges is ordered, could we use some sort of binary search
- rather than iterating? */
- for (j = 0; j < ranges->nelts; ++j)
- {
- svn_merge_range_t *range = APR_ARRAY_IDX(ranges, j,
- svn_merge_range_t *);
- if (range->start <= start && range->end >= end)
- {
- for (j = i; j < paths->nelts - 1; ++j)
- APR_ARRAY_IDX(paths, j, const char *)
- = APR_ARRAY_IDX(paths, j + 1, const char *);
-
- --paths->nelts;
- --i;
- break;
- }
-
- /* If there is only one path then we also check for a
- partial overlap rather than the full overlap above, and
- reduce the [hist_start, hist_end] range rather than
- dropping the path. */
- if (paths->nelts == 1)
- {
- if (range->start <= start && range->end > start)
- {
- if (start == *hist_start)
- *hist_start = range->end - 1;
- else
- *hist_end = range->end - 1;
- break;
- }
- if (range->start < end && range->end >= end)
- {
- if (start == *hist_start)
- *hist_end = range->start;
- else
- *hist_start = range->start;
- break;
- }
- }
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Extend PROCESSED to cover PATHS from HIST_START to HIST_END */
-static svn_error_t *
-store_search(svn_mergeinfo_t processed,
- const apr_array_header_t *paths,
- svn_revnum_t hist_start,
- svn_revnum_t hist_end,
- apr_pool_t *scratch_pool)
-{
- /* We add 1 to end so that we can use the mergeinfo API to handle
- singe revisions where HIST_START is equal to HIST_END. */
- svn_revnum_t start = hist_start <= hist_end ? hist_start : hist_end;
- svn_revnum_t end = hist_start <= hist_end ? hist_end + 1 : hist_start + 1;
- svn_mergeinfo_t mergeinfo = svn_hash__make(scratch_pool);
- apr_pool_t *processed_pool = apr_hash_pool_get(processed);
- int i;
-
- for (i = 0; i < paths->nelts; ++i)
- {
- const char *path = APR_ARRAY_IDX(paths, i, const char *);
- svn_rangelist_t *ranges = apr_array_make(processed_pool, 1,
- sizeof(svn_merge_range_t*));
- svn_merge_range_t *range = apr_palloc(processed_pool, sizeof(*range));
-
- range->start = start;
- range->end = end;
- range->inheritable = TRUE;
- APR_ARRAY_PUSH(ranges, svn_merge_range_t *) = range;
- svn_hash_sets(mergeinfo, apr_pstrdup(processed_pool, path), ranges);
- }
- SVN_ERR(svn_mergeinfo_merge2(processed, mergeinfo,
- apr_hash_pool_get(processed), scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-/* 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.
-
- If LOG_TARGET_HISTORY_AS_MERGEINFO is not NULL then it contains mergeinfo
- representing the history of PATHS between HIST_START and HIST_END.
-
- If HANDLING_MERGED_REVISIONS is TRUE then this is a recursive call for
- merged revisions, see INCLUDE_MERGED_REVISIONS argument to
- svn_repos_get_logs4(). If SUBTRACTIVE_MERGE is true, then this is a
- recursive call for reverse merged revisions.
-
- If NESTED_MERGES is not NULL then it is a hash of revisions (svn_revnum_t *
- mapped to svn_revnum_t *) for logs that were previously sent. On the first
- call to do_logs it should always be NULL. If INCLUDE_MERGED_REVISIONS is
- TRUE, then NESTED_MERGES will be created on the first call to do_logs,
- allocated in POOL. It is then shared across
- do_logs()/send_logs()/handle_merge_revisions() recursions, see also the
- argument of the same name in send_logs().
-
- PROCESSED is a mergeinfo hash that represents the paths and
- revisions that have already been searched. Allocated like
- NESTED_MERGES above.
-
- All other parameters are the same as svn_repos_get_logs4().
- */
-static svn_error_t *
-do_logs(svn_fs_t *fs,
- const apr_array_header_t *paths,
- svn_mergeinfo_t log_target_history_as_mergeinfo,
- svn_mergeinfo_t processed,
- svn_bit_array__t *nested_merges,
- svn_revnum_t hist_start,
- svn_revnum_t hist_end,
- int limit,
- svn_boolean_t strict_node_history,
- svn_boolean_t include_merged_revisions,
- svn_boolean_t subtractive_merge,
- svn_boolean_t handling_merged_revisions,
- svn_boolean_t ignore_missing_locations,
- const apr_array_header_t *revprops,
- svn_boolean_t descending_order,
- log_callbacks_t *callbacks,
- apr_pool_t *pool)
-{
- apr_pool_t *iterpool, *iterpool2;
- apr_pool_t *subpool = NULL;
- apr_array_header_t *revs = NULL;
- apr_hash_t *rev_mergeinfo = NULL;
- svn_revnum_t current;
- apr_array_header_t *histories;
- svn_boolean_t any_histories_left = TRUE;
- int send_count = 0;
- int i;
-
- if (processed)
- {
- /* Casting away const. This only happens on recursive calls when
- it is known to be safe because we allocated paths. */
- SVN_ERR(reduce_search((apr_array_header_t *)paths, &hist_start, &hist_end,
- processed, pool));
- }
-
- if (!paths->nelts)
- return SVN_NO_ERROR;
-
- if (processed)
- SVN_ERR(store_search(processed, paths, hist_start, hist_end, pool));
-
- /* We have a list of paths and a revision range. But we don't care
- about all the revisions in the range -- only the ones in which
- one of our paths was changed. So let's go figure out which
- 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,
- 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
- history in reverse order just send it to them right away. */
- iterpool = svn_pool_create(pool);
- iterpool2 = svn_pool_create(pool);
- for (current = hist_end;
- any_histories_left;
- current = next_history_rev(histories))
- {
- svn_boolean_t changed = FALSE;
- any_histories_left = FALSE;
- svn_pool_clear(iterpool);
-
- for (i = 0; i < histories->nelts; i++)
- {
- struct path_info *info = APR_ARRAY_IDX(histories, i,
- struct path_info *);
-
- svn_pool_clear(iterpool2);
-
- /* Check history for this path in current rev. */
- SVN_ERR(check_history(&changed, info, fs, current,
- strict_node_history,
- callbacks->authz_read_func,
- callbacks->authz_read_baton,
- hist_start, pool, iterpool2));
- if (! info->done)
- any_histories_left = TRUE;
- }
-
- svn_pool_clear(iterpool2);
-
- /* If any of the paths changed in this rev then add or send it. */
- if (changed)
- {
- svn_mergeinfo_t added_mergeinfo = NULL;
- svn_mergeinfo_t deleted_mergeinfo = NULL;
- svn_boolean_t has_children = FALSE;
-
- /* If we're including merged revisions, we need to calculate
- the mergeinfo deltas committed in this revision to our
- various paths. */
- if (include_merged_revisions)
- {
- apr_array_header_t *cur_paths =
- apr_array_make(iterpool, paths->nelts, sizeof(const char *));
-
- /* Get the current paths of our history objects so we can
- query mergeinfo. */
- /* ### TODO: Should this be ignoring depleted history items? */
- for (i = 0; i < histories->nelts; i++)
- {
- struct path_info *info = APR_ARRAY_IDX(histories, i,
- struct path_info *);
- APR_ARRAY_PUSH(cur_paths, const char *) = info->path->data;
- }
- SVN_ERR(get_combined_mergeinfo_changes(&added_mergeinfo,
- &deleted_mergeinfo,
- fs, cur_paths,
- current,
- iterpool, iterpool));
- has_children = (apr_hash_count(added_mergeinfo) > 0
- || apr_hash_count(deleted_mergeinfo) > 0);
- }
-
- /* If our caller wants logs in descending order, we can send
- 'em now (because that's the order we're crawling history
- in anyway). */
- if (descending_order)
- {
- SVN_ERR(send_log(current, fs,
- log_target_history_as_mergeinfo, nested_merges,
- subtractive_merge, handling_merged_revisions,
- revprops, has_children, callbacks, iterpool));
-
- if (has_children) /* Implies include_merged_revisions == TRUE */
- {
- if (!nested_merges)
- {
- /* We're at the start of the recursion stack, create a
- single hash to be shared across all of the merged
- recursions so we can track and squelch duplicates. */
- subpool = svn_pool_create(pool);
- nested_merges = svn_bit_array__create(hist_end, subpool);
- processed = svn_hash__make(subpool);
- }
-
- SVN_ERR(handle_merged_revisions(
- current, fs,
- log_target_history_as_mergeinfo, nested_merges,
- processed,
- added_mergeinfo, deleted_mergeinfo,
- strict_node_history,
- revprops,
- callbacks,
- iterpool));
- }
- if (limit && ++send_count >= limit)
- break;
- }
- /* Otherwise, the caller wanted logs in ascending order, so
- we have to buffer up a list of revs and (if doing
- mergeinfo) a hash of related mergeinfo deltas, and
- process them later. */
- else
- {
- if (! revs)
- revs = apr_array_make(pool, 64, sizeof(svn_revnum_t));
- APR_ARRAY_PUSH(revs, svn_revnum_t) = current;
-
- if (added_mergeinfo || deleted_mergeinfo)
- {
- svn_revnum_t *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));
-
- /* If we have added or deleted mergeinfo, both are non-null */
- SVN_ERR_ASSERT(added_mergeinfo && deleted_mergeinfo);
- add_and_del_mergeinfo->added_mergeinfo =
- svn_mergeinfo_dup(added_mergeinfo, pool);
- add_and_del_mergeinfo->deleted_mergeinfo =
- svn_mergeinfo_dup(deleted_mergeinfo, pool);
-
- if (! rev_mergeinfo)
- rev_mergeinfo = svn_hash__make(pool);
- apr_hash_set(rev_mergeinfo, cur_rev, sizeof(*cur_rev),
- add_and_del_mergeinfo);
- }
- }
- }
- }
- svn_pool_destroy(iterpool2);
- svn_pool_destroy(iterpool);
-
- if (subpool)
- {
- nested_merges = NULL;
- svn_pool_destroy(subpool);
- }
-
- if (revs)
- {
- /* Work loop for processing the revisions we found since they wanted
- history in forward order. */
- iterpool = svn_pool_create(pool);
- for (i = 0; i < revs->nelts; ++i)
- {
- svn_mergeinfo_t added_mergeinfo;
- svn_mergeinfo_t deleted_mergeinfo;
- svn_boolean_t has_children = FALSE;
-
- svn_pool_clear(iterpool);
- current = APR_ARRAY_IDX(revs, revs->nelts - i - 1, svn_revnum_t);
-
- /* If we've got a hash of revision mergeinfo (which can only
- happen if INCLUDE_MERGED_REVISIONS was set), we check to
- see if this revision is one which merged in other
- revisions we need to handle recursively. */
- if (rev_mergeinfo)
- {
- struct added_deleted_mergeinfo *add_and_del_mergeinfo =
- apr_hash_get(rev_mergeinfo, ¤t, sizeof(current));
- added_mergeinfo = add_and_del_mergeinfo->added_mergeinfo;
- deleted_mergeinfo = add_and_del_mergeinfo->deleted_mergeinfo;
- has_children = (apr_hash_count(added_mergeinfo) > 0
- || apr_hash_count(deleted_mergeinfo) > 0);
- }
-
- SVN_ERR(send_log(current, fs,
- log_target_history_as_mergeinfo, nested_merges,
- subtractive_merge, handling_merged_revisions,
- revprops, has_children, callbacks, iterpool));
- if (has_children)
- {
- if (!nested_merges)
- {
- subpool = svn_pool_create(pool);
- nested_merges = svn_bit_array__create(current, subpool);
- }
-
- SVN_ERR(handle_merged_revisions(current, fs,
- log_target_history_as_mergeinfo,
- nested_merges,
- processed,
- added_mergeinfo,
- deleted_mergeinfo,
- strict_node_history,
- revprops, callbacks,
- iterpool));
- }
- if (limit && i + 1 >= limit)
- break;
- }
- svn_pool_destroy(iterpool);
- }
-
- return SVN_NO_ERROR;
-}
-
-struct location_segment_baton
-{
- apr_array_header_t *history_segments;
- apr_pool_t *pool;
-};
-
-/* svn_location_segment_receiver_t implementation for svn_repos_get_logs4. */
-static svn_error_t *
-location_segment_receiver(svn_location_segment_t *segment,
- void *baton,
- apr_pool_t *pool)
-{
- struct location_segment_baton *b = baton;
+#include "svn_pools.h"
- APR_ARRAY_PUSH(b->history_segments, svn_location_segment_t *) =
- svn_location_segment_dup(segment, b->pool);
+#include "svn_private_config.h"
- return SVN_NO_ERROR;
-}
+#include "repos.h"
+#include "private/svn_repos_private.h"
+#include "private/svn_subr_private.h"
-/* Populate *PATHS_HISTORY_MERGEINFO with mergeinfo representing the combined
- history of each path in PATHS between START_REV and END_REV in REPOS's
- filesystem. START_REV and END_REV must be valid revisions. RESULT_POOL
- is used to allocate *PATHS_HISTORY_MERGEINFO, SCRATCH_POOL is used for all
- other (temporary) allocations. Other parameters are the same as
- svn_repos_get_logs4(). */
-static svn_error_t *
-get_paths_history_as_mergeinfo(svn_mergeinfo_t *paths_history_mergeinfo,
- svn_repos_t *repos,
- const apr_array_header_t *paths,
- svn_revnum_t start_rev,
- svn_revnum_t end_rev,
- svn_repos_authz_func_t authz_read_func,
- void *authz_read_baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- int i;
- svn_mergeinfo_t path_history_mergeinfo;
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-
- SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_rev));
- SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(end_rev));
-
- /* Ensure START_REV is the youngest revision, as required by
- svn_repos_node_location_segments, for which this is an iterative
- wrapper. */
- if (start_rev < end_rev)
- {
- svn_revnum_t tmp_rev = start_rev;
- start_rev = end_rev;
- end_rev = tmp_rev;
- }
-
- *paths_history_mergeinfo = svn_hash__make(result_pool);
-
- for (i = 0; i < paths->nelts; i++)
- {
- const char *this_path = APR_ARRAY_IDX(paths, i, const char *);
- struct location_segment_baton loc_seg_baton;
-
- svn_pool_clear(iterpool);
- loc_seg_baton.pool = scratch_pool;
- loc_seg_baton.history_segments =
- apr_array_make(iterpool, 4, sizeof(svn_location_segment_t *));
-
- SVN_ERR(svn_repos_node_location_segments(repos, this_path, start_rev,
- start_rev, end_rev,
- location_segment_receiver,
- &loc_seg_baton,
- authz_read_func,
- authz_read_baton,
- iterpool));
-
- SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
- &path_history_mergeinfo, loc_seg_baton.history_segments, iterpool));
- SVN_ERR(svn_mergeinfo_merge2(*paths_history_mergeinfo,
- svn_mergeinfo_dup(path_history_mergeinfo,
- result_pool),
- result_pool, iterpool));
- }
- svn_pool_destroy(iterpool);
- return SVN_NO_ERROR;
-}
+
-svn_error_t *
-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 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_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(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 *),
- 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, scratch_pool));
-
- if (! SVN_IS_VALID_REVNUM(start))
- start = head;
-
- if (! SVN_IS_VALID_REVNUM(end))
- end = head;
-
- /* Check that revisions are sane before ever invoking receiver. */
- if (start > head)
- return svn_error_createf
- (SVN_ERR_FS_NO_SUCH_REVISION, 0,
- _("No such revision %ld"), start);
- if (end > head)
- return svn_error_createf
- (SVN_ERR_FS_NO_SUCH_REVISION, 0,
- _("No such revision %ld"), end);
-
- /* Ensure a youngest-to-oldest revision crawl ordering using our
- (possibly sanitized) range values. */
- descending_order = start >= end;
- if (descending_order)
- {
- svn_revnum_t tmp_rev = start;
- start = end;
- end = tmp_rev;
- }
-
- if (! paths)
- 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
[... 119 lines stripped ...]