#include <stdlib.h>
#define APR_WANT_STRFUNC
#include <apr_want.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"
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_repos_path_change_t *
svn_repos_path_change_create(apr_pool_t *result_pool)
{
svn_repos_path_change_t *change = apr_pcalloc(result_pool, sizeof(*change));
change->path.data = "";
change->change_kind = svn_fs_path_change_reset;
change->mergeinfo_mod = svn_tristate_unknown;
change->copyfrom_rev = SVN_INVALID_REVNUM;
return change;
}
svn_repos_path_change_t *
svn_repos_path_change_dup(svn_repos_path_change_t *change,
apr_pool_t *result_pool)
{
svn_repos_path_change_t *new_change = apr_pmemdup(result_pool, change,
sizeof(*new_change));
new_change->path.data = apr_pstrmemdup(result_pool, change->path.data,
change->path.len);
if (change->copyfrom_path)
new_change->copyfrom_path = apr_pstrdup(result_pool,
change->copyfrom_path);
return new_change;
}
svn_repos_log_entry_t *
svn_repos_log_entry_create(apr_pool_t *result_pool)
{
svn_repos_log_entry_t *log_entry = apr_pcalloc(result_pool,
sizeof(*log_entry));
return log_entry;
}
svn_repos_log_entry_t *
svn_repos_log_entry_dup(const svn_repos_log_entry_t *log_entry,
apr_pool_t *result_pool)
{
svn_repos_log_entry_t *new_entry = apr_pmemdup(result_pool, log_entry,
sizeof(*new_entry));
if (log_entry->revprops)
new_entry->revprops = svn_prop_hash_dup(log_entry->revprops, result_pool);
return new_entry;
}
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;
*access_level = svn_repos_revision_access_full;
if (! authz_read_func)
return SVN_NO_ERROR;
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));
if (!change)
return SVN_NO_ERROR;
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 (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 (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);
if (! found_readable)
*access_level = svn_repos_revision_access_none;
else if (found_unreadable)
*access_level = svn_repos_revision_access_partial;
return SVN_NO_ERROR;
}
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;
SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool));
SVN_ERR(svn_fs_path_change_get(&change, iterator));
if (!change)
{
*access_level = svn_repos_revision_access_full;
return SVN_NO_ERROR;
}
iterpool = svn_pool_create(scratch_pool);
while (change)
{
const char *path = change->path.data;
svn_pool_clear(iterpool);
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;
}
}
found_readable = TRUE;
if (change->node_kind == svn_node_unknown)
{
svn_fs_root_t *check_root = root;
const char *check_path = path;
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));
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;
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));
SVN_ERR(svn_fs_path_change_get(&change, iterator));
}
svn_pool_destroy(iterpool);
if (! found_readable)
{
*access_level = svn_repos_revision_access_none;
}
else if (found_unreadable)
{
*access_level = svn_repos_revision_access_partial;
}
else
{
*access_level = svn_repos_revision_access_full;
}
return SVN_NO_ERROR;
}
struct path_info
{
svn_stringbuf_t *path;
svn_revnum_t history_rev;
svn_boolean_t done;
svn_boolean_t first_time;
svn_fs_history_t *hist;
apr_pool_t *newpool;
apr_pool_t *oldpool;
};
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);
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;
}
SVN_ERR(svn_fs_history_location(&path, &info->history_rev,
hist, subpool));
svn_stringbuf_set(info->path, 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;
}
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;
}
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 (info->done)
return SVN_NO_ERROR;
if (info->history_rev < current)
return SVN_NO_ERROR;
*changed = TRUE;
return get_history(info, fs, strict, authz_read_func,
authz_read_baton, start, result_pool, scratch_pool);
}
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;
}
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;
*deleted_mergeinfo_catalog = svn_hash__make(result_pool);
*added_mergeinfo_catalog = svn_hash__make(result_pool);
if (rev == 0)
return SVN_NO_ERROR;
iterator_pool = svn_pool_create(scratch_pool);
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));
while (change && (!any_mergeinfo || !any_copy))
{
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));
}
if (! any_mergeinfo)
{
svn_pool_destroy(iterator_pool);
return SVN_NO_ERROR;
}
svn_pool_clear(iterator_pool);
SVN_ERR(svn_fs_paths_changed3(&iterator, root, iterator_pool,
iterator_pool));
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;
SVN_ERR(svn_fs_path_change_get(&change, iterator));
if (!change)
break;
if (change->mergeinfo_mod == svn_tristate_false)
continue;
if (! change->prop_mod)
continue;
changed_path = change->path.data;
svn_pool_clear(iterpool);
switch (change->change_kind)
{
case svn_fs_path_change_modify:
{
svn_revnum_t appeared_rev;
if (any_copy)
{
SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path,
&base_rev, fs, rev,
changed_path, iterpool));
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;
}
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;
}
case svn_fs_path_change_delete:
case svn_fs_path_change_reset:
default:
continue;
}
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));
}
SVN_ERR(svn_fs_node_prop(&mergeinfo_value, root, changed_path,
SVN_PROP_MERGEINFO, iterpool));
if (! (mergeinfo_value || prev_mergeinfo_value))
continue;
if ( mergeinfo_value && prev_mergeinfo_value
&& svn_string_compare(mergeinfo_value, prev_mergeinfo_value))
continue;
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));
}
{
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));
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;
}
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;
*added_mergeinfo = svn_hash__make(result_pool);
*deleted_mergeinfo = svn_hash__make(result_pool);
if (rev == 0)
return SVN_NO_ERROR;
if (! paths->nelts)
return SVN_NO_ERROR;
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)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
else
{
return svn_error_trace(err);
}
}
if ( apr_hash_count(deleted_mergeinfo_catalog) == 0
&& apr_hash_count(added_mergeinfo_catalog) == 0)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
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 (svn_hash_gets(deleted_mergeinfo_catalog, path))
continue;
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 (! (prev_path && SVN_IS_VALID_REVNUM(prev_rev)
&& (appeared_rev == rev)))
{
prev_path = path;
prev_rev = rev - 1;
}
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);
SVN_ERR(svn_fs__get_mergeinfo_for_path(&prev_inherited_mergeinfo,
prev_root, prev_path,
svn_mergeinfo_nearest_ancestor,
FALSE,
iterpool, iterpool));
SVN_ERR(svn_fs__get_mergeinfo_for_path(&mergeinfo,
root, path,
svn_mergeinfo_inherited, TRUE,
iterpool, iterpool));
SVN_ERR(svn_fs__get_mergeinfo_for_path(&inherited_mergeinfo,
root, path,
svn_mergeinfo_nearest_ancestor,
FALSE,
iterpool, iterpool));
if (!prev_mergeinfo && !mergeinfo)
continue;
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 (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;
}
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));
}
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;
}
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;
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)
{
get_revprops = FALSE;
}
else if (access_level == svn_repos_revision_access_partial)
{
censor_revprops = TRUE;
}
}
if (get_revprops && want_revprops)
{
SVN_ERR(svn_fs_revision_proplist2(&r_props, fs, rev, FALSE, pool,
pool));
if (revprops == NULL)
{
if (censor_revprops)
{
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
log_entry->revprops = r_props;
}
else
{
int i;
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);
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))
{
log_entry->revprops = NULL;
break;
}
}
}
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))
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;
}
typedef struct interesting_merge_baton_t
{
svn_revnum_t rev;
svn_mergeinfo_t log_target_history_as_mergeinfo;
svn_boolean_t found_rev_of_interest;
svn_repos_path_change_receiver_t inner;
void *inner_baton;
} interesting_merge_baton_t;
static svn_error_t *
interesting_merge(void *baton,
svn_repos_path_change_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;
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);
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;
}
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;
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;
if (baton.found_rev_of_interest)
{
apr_pool_t *scratch_pool;
if (nested_merges && handling_merged_revision)
{
if (svn_bit_array__get(nested_merges, rev))
{
return SVN_NO_ERROR;
}
else
{
svn_bit_array__set(nested_merges, rev, TRUE);
}
}
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;
}
#define MAX_OPEN_HISTORIES 32
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;
*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;
}
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;
}
struct path_list_range
{
apr_array_header_t *paths;
svn_merge_range_t range;
svn_boolean_t reverse_merge;
};
struct rangelist_path
{
svn_rangelist_t *rangelist;
const char *path;
};
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;
}
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);
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;
rp->rangelist = svn_rangelist_dup(rp->rangelist, subpool);
for (i = 0; i < rp->rangelist->nelts; i++)
APR_ARRAY_IDX(rp->rangelist, i, svn_merge_range_t *)->start += 1;
}
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;
svn_sort__array(rangelist_paths, compare_rangelist_paths);
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;
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;
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;
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 *);
range->start = tail + 1;
if (range->start > range->end)
{
if (rp->rangelist->nelts == 1)
{
array_pop_front(rangelist_paths);
i--;
num_revs--;
}
else
{
array_pop_front(rp->rangelist);
}
}
}
}
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;
}
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);
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;
}
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);
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);
empty_log_entry.revision = SVN_INVALID_REVNUM;
return (callbacks->revision_receiver)(callbacks->revision_receiver_baton,
&empty_log_entry, pool);
}
struct added_deleted_mergeinfo
{
svn_mergeinfo_t added_mergeinfo;
svn_mergeinfo_t deleted_mergeinfo;
};
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)
{
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;
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 (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;
}
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)
{
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;
}
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)
{
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));
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));
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);
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 (changed)
{
svn_mergeinfo_t added_mergeinfo = NULL;
svn_mergeinfo_t deleted_mergeinfo = NULL;
svn_boolean_t has_children = FALSE;
if (include_merged_revisions)
{
apr_array_header_t *cur_paths =
apr_array_make(iterpool, paths->nelts, sizeof(const char *));
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 (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)
{
if (!nested_merges)
{
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;
}
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));
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)
{
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 (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;
};
static svn_error_t *
location_segment_receiver(svn_location_segment_t *segment,
void *baton,
apr_pool_t *pool)
{
struct location_segment_baton *b = baton;
APR_ARRAY_PUSH(b->history_segments, svn_location_segment_t *) =
svn_location_segment_dup(segment, b->pool);
return SVN_NO_ERROR;
}
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));
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;
}
SVN_ERR(svn_fs_refresh_revision_props(fs, scratch_pool));
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;
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);
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 ((! include_merged_revisions)
&& ((! paths->nelts)
|| ((paths->nelts == 1)
&& (svn_path_is_empty(APR_ARRAY_IDX(paths, 0, const char *))
|| (strcmp(APR_ARRAY_IDX(paths, 0, const char *),
"/") == 0)))))
{
apr_uint64_t send_count = 0;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *rev_root;
SVN_ERR(svn_fs_revision_root(&rev_root, fs,
descending_order ? end : start,
scratch_pool));
SVN_ERR(authz_read_func(&readable, rev_root, "",
authz_read_baton, scratch_pool));
if (! readable)
return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL);
}
send_count = end - start + 1;
if (limit > 0 && send_count > limit)
send_count = limit;
for (i = 0; i < send_count; ++i)
{
svn_revnum_t rev;
svn_pool_clear(iterpool);
if (descending_order)
rev = end - i;
else
rev = start + i;
SVN_ERR(send_log(rev, fs, NULL, NULL,
FALSE, FALSE, revprops, FALSE,
&callbacks, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
if (include_merged_revisions)
{
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,
scratch_pool, subpool));
svn_pool_destroy(subpool);
}
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, &callbacks, scratch_pool);
}