#include <stdlib.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include "svn_compat.h"
#include "svn_private_config.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_mergeinfo_private.h"
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;
apr_hash_t *changes;
apr_hash_index_t *hi;
svn_boolean_t found_readable = FALSE;
svn_boolean_t found_unreadable = FALSE;
apr_pool_t *subpool;
*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_changed2(&changes, rev_root, pool));
if (apr_hash_count(changes) == 0)
return SVN_NO_ERROR;
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
svn_fs_path_change2_t *change;
svn_boolean_t readable;
svn_pool_clear(subpool);
apr_hash_this(hi, &key, NULL, &val);
change = val;
SVN_ERR(authz_read_func(&readable, rev_root, key,
authz_read_baton, subpool));
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, key, subpool));
if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
{
svn_fs_root_t *copyfrom_root;
SVN_ERR(svn_fs_revision_root(©from_root, fs,
copyfrom_rev, subpool));
SVN_ERR(authz_read_func(&readable,
copyfrom_root, copyfrom_path,
authz_read_baton, subpool));
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;
}
}
decision:
svn_pool_destroy(subpool);
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(apr_hash_t **changed,
svn_fs_root_t *root,
svn_fs_t *fs,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
apr_hash_t *changes;
apr_hash_index_t *hi;
apr_pool_t *subpool;
svn_boolean_t found_readable = FALSE;
svn_boolean_t found_unreadable = FALSE;
*changed = apr_hash_make(pool);
SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
if (apr_hash_count(changes) == 0)
return SVN_NO_ERROR;
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
svn_fs_path_change2_t *change;
const char *path;
char action;
svn_log_changed_path2_t *item;
svn_pool_clear(subpool);
apr_hash_this(hi, &key, NULL, &val);
path = (const char *) key;
change = val;
if (authz_read_func)
{
svn_boolean_t readable;
SVN_ERR(authz_read_func(&readable,
root, path,
authz_read_baton, subpool));
if (! readable)
{
found_unreadable = TRUE;
continue;
}
}
found_readable = TRUE;
switch (change->change_kind)
{
case svn_fs_path_change_reset:
continue;
case svn_fs_path_change_add:
action = 'A';
break;
case svn_fs_path_change_replace:
action = 'R';
break;
case svn_fs_path_change_delete:
action = 'D';
break;
case svn_fs_path_change_modify:
default:
action = 'M';
break;
}
item = svn_log_changed_path2_create(pool);
item->action = action;
item->node_kind = change->node_kind;
item->copyfrom_rev = SVN_INVALID_REVNUM;
item->text_modified = change->text_mod ? svn_tristate_true
: svn_tristate_false;
item->props_modified = change->prop_mod ? svn_tristate_true
: svn_tristate_false;
if ((action == 'A') || (action == 'R'))
{
const char *copyfrom_path;
svn_revnum_t copyfrom_rev;
SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path,
root, path, subpool));
if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
{
svn_boolean_t readable = TRUE;
if (authz_read_func)
{
svn_fs_root_t *copyfrom_root;
SVN_ERR(svn_fs_revision_root(©from_root, fs,
copyfrom_rev, subpool));
SVN_ERR(authz_read_func(&readable,
copyfrom_root, copyfrom_path,
authz_read_baton, subpool));
if (! readable)
found_unreadable = TRUE;
}
if (readable)
{
item->copyfrom_path = apr_pstrdup(pool, copyfrom_path);
item->copyfrom_rev = copyfrom_rev;
}
}
}
apr_hash_set(*changed, apr_pstrdup(pool, path),
APR_HASH_KEY_STRING, item);
}
svn_pool_destroy(subpool);
if (! found_readable)
return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE,
NULL, NULL);
if (found_unreadable)
return svn_error_create(SVN_ERR_AUTHZ_PARTIALLY_READABLE,
NULL, NULL);
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 *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_prev(&info->hist, info->hist, ! strict, subpool));
hist = info->hist;
}
else
{
subpool = svn_pool_create(pool);
SVN_ERR(svn_fs_revision_root(&history_root, fs, info->history_rev,
subpool));
SVN_ERR(svn_fs_node_history(&hist, history_root, info->path->data,
subpool));
SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool));
if (info->first_time)
info->first_time = FALSE;
else
SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool));
}
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,
subpool));
SVN_ERR(authz_read_func(&readable, history_root,
info->path->data,
authz_read_baton,
subpool));
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 *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, 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)
{
apr_hash_t *changes;
svn_fs_root_t *root;
apr_pool_t *iterpool;
apr_hash_index_t *hi;
*deleted_mergeinfo_catalog = apr_hash_make(result_pool);
*added_mergeinfo_catalog = apr_hash_make(result_pool);
if (rev == 0)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
SVN_ERR(svn_fs_paths_changed2(&changes, root, scratch_pool));
if (apr_hash_count(changes) == 0)
return SVN_NO_ERROR;
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
svn_fs_path_change2_t *change;
const char *changed_path, *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_pool_clear(iterpool);
apr_hash_this(hi, &key, NULL, &val);
changed_path = key;
change = val;
if (! change->prop_mod)
continue;
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,
root, changed_path, iterpool));
if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
{
base_path = apr_pstrdup(scratch_pool, copyfrom_path);
base_rev = copyfrom_rev;
}
break;
}
case svn_fs_path_change_modify:
{
svn_revnum_t appeared_rev;
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;
}
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 (prev_mergeinfo_value && (! mergeinfo_value))
{
apr_array_header_t *query_paths =
apr_array_make(iterpool, 1, sizeof(const char *));
svn_mergeinfo_t tmp_mergeinfo;
svn_mergeinfo_catalog_t tmp_catalog;
APR_ARRAY_PUSH(query_paths, const char *) = changed_path;
SVN_ERR(svn_fs_get_mergeinfo(&tmp_catalog, root,
query_paths, svn_mergeinfo_inherited,
FALSE, iterpool));
tmp_mergeinfo = apr_hash_get(tmp_catalog, changed_path,
APR_HASH_KEY_STRING);
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))
{
apr_array_header_t *query_paths =
apr_array_make(iterpool, 1, sizeof(const char *));
svn_mergeinfo_t tmp_mergeinfo;
svn_mergeinfo_catalog_t tmp_catalog;
APR_ARRAY_PUSH(query_paths, const char *) = base_path;
SVN_ERR(svn_fs_get_mergeinfo(&tmp_catalog, base_root,
query_paths, svn_mergeinfo_inherited,
FALSE, iterpool));
tmp_mergeinfo = apr_hash_get(tmp_catalog, base_path,
APR_HASH_KEY_STRING);
if (tmp_mergeinfo)
SVN_ERR(svn_mergeinfo_to_string(&prev_mergeinfo_value,
tmp_mergeinfo,
iterpool));
}
if ((prev_mergeinfo_value && (! mergeinfo_value))
|| ((! prev_mergeinfo_value) && mergeinfo_value)
|| (prev_mergeinfo_value && mergeinfo_value
&& (! svn_string_compare(mergeinfo_value,
prev_mergeinfo_value))))
{
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_diff(&deleted, &added, prev_mergeinfo,
mergeinfo, FALSE, iterpool));
hash_path = apr_pstrdup(result_pool, changed_path);
apr_hash_set(*deleted_mergeinfo_catalog, hash_path,
APR_HASH_KEY_STRING, svn_mergeinfo_dup(deleted,
result_pool));
apr_hash_set(*added_mergeinfo_catalog, hash_path,
APR_HASH_KEY_STRING, svn_mergeinfo_dup(added,
result_pool));
}
}
svn_pool_destroy(iterpool);
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 = apr_hash_make(result_pool);
*deleted_mergeinfo = apr_hash_make(result_pool);
if (rev == 0)
return SVN_NO_ERROR;
if (! paths->nelts)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
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);
}
}
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_catalog_t catalog;
svn_mergeinfo_t prev_mergeinfo, mergeinfo, deleted, added;
apr_array_header_t *query_paths;
svn_pool_clear(iterpool);
if (apr_hash_get(deleted_mergeinfo_catalog, path, APR_HASH_KEY_STRING))
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));
query_paths = apr_array_make(iterpool, 1, sizeof(const char *));
APR_ARRAY_PUSH(query_paths, const char *) = prev_path;
err = svn_fs_get_mergeinfo(&catalog, prev_root, query_paths,
svn_mergeinfo_inherited, FALSE, 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);
prev_mergeinfo = apr_hash_get(catalog, prev_path, APR_HASH_KEY_STRING);
APR_ARRAY_IDX(query_paths, 0, const char *) = path;
SVN_ERR(svn_fs_get_mergeinfo(&catalog, root, query_paths,
svn_mergeinfo_inherited, FALSE, iterpool));
mergeinfo = apr_hash_get(catalog, path, APR_HASH_KEY_STRING);
if (!prev_mergeinfo && !mergeinfo)
continue;
SVN_ERR(svn_mergeinfo_diff(&deleted, &added, prev_mergeinfo,
mergeinfo, FALSE, iterpool));
SVN_ERR(svn_mergeinfo_merge(*deleted_mergeinfo,
svn_mergeinfo_dup(deleted, result_pool),
result_pool));
SVN_ERR(svn_mergeinfo_merge(*added_mergeinfo,
svn_mergeinfo_dup(added, result_pool),
result_pool));
}
svn_pool_destroy(iterpool);
for (hi = apr_hash_first(scratch_pool, added_mergeinfo_catalog);
hi; hi = apr_hash_next(hi))
{
const void *key;
apr_ssize_t klen;
void *val;
const char *changed_path;
svn_mergeinfo_t added, deleted;
apr_hash_this(hi, &key, &klen, &val);
changed_path = key;
added = val;
for (i = 0; i < paths->nelts; i++)
{
const char *path = APR_ARRAY_IDX(paths, i, const char *);
if (! svn_dirent_is_ancestor(path, changed_path))
continue;
deleted = apr_hash_get(deleted_mergeinfo_catalog, key, klen);
SVN_ERR(svn_mergeinfo_merge(*deleted_mergeinfo,
svn_mergeinfo_dup(deleted, result_pool),
result_pool));
SVN_ERR(svn_mergeinfo_merge(*added_mergeinfo,
svn_mergeinfo_dup(added, result_pool),
result_pool));
break;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
fill_log_entry(svn_log_entry_t *log_entry,
svn_revnum_t rev,
svn_fs_t *fs,
svn_boolean_t discover_changed_paths,
const apr_array_header_t *revprops,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
apr_hash_t *r_props, *changed_paths = NULL;
svn_boolean_t get_revprops = TRUE, censor_revprops = FALSE;
if ((rev > 0)
&& (authz_read_func || discover_changed_paths))
{
svn_fs_root_t *newroot;
svn_error_t *patherr;
SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool));
patherr = detect_changed(&changed_paths,
newroot, fs,
authz_read_func, authz_read_baton,
pool);
if (patherr
&& patherr->apr_err == SVN_ERR_AUTHZ_UNREADABLE)
{
svn_error_clear(patherr);
changed_paths = NULL;
get_revprops = FALSE;
}
else if (patherr
&& patherr->apr_err == SVN_ERR_AUTHZ_PARTIALLY_READABLE)
{
svn_error_clear(patherr);
censor_revprops = TRUE;
}
else if (patherr)
return patherr;
if (! discover_changed_paths)
changed_paths = NULL;
}
if (get_revprops)
{
SVN_ERR(svn_fs_revision_proplist(&r_props, fs, rev, pool));
if (revprops == NULL)
{
if (censor_revprops)
{
log_entry->revprops = apr_hash_make(pool);
apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_AUTHOR,
APR_HASH_KEY_STRING,
apr_hash_get(r_props, SVN_PROP_REVISION_AUTHOR,
APR_HASH_KEY_STRING));
apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_DATE,
APR_HASH_KEY_STRING,
apr_hash_get(r_props, SVN_PROP_REVISION_DATE,
APR_HASH_KEY_STRING));
}
else
log_entry->revprops = r_props;
}
else
{
int i;
for (i = 0; i < revprops->nelts; i++)
{
char *name = APR_ARRAY_IDX(revprops, i, char *);
svn_string_t *value = apr_hash_get(r_props, name,
APR_HASH_KEY_STRING);
if (censor_revprops
&& !(strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0
|| strcmp(name, SVN_PROP_REVISION_DATE) == 0))
continue;
if (log_entry->revprops == NULL)
log_entry->revprops = apr_hash_make(pool);
apr_hash_set(log_entry->revprops, name,
APR_HASH_KEY_STRING, value);
}
}
}
log_entry->changed_paths = changed_paths;
log_entry->changed_paths2 = changed_paths;
log_entry->revision = rev;
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,
apr_hash_t *nested_merges,
svn_boolean_t discover_changed_paths,
svn_boolean_t subtractive_merge,
svn_boolean_t handling_merged_revision,
const apr_array_header_t *revprops,
svn_boolean_t has_children,
svn_log_entry_receiver_t receiver,
void *receiver_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
svn_log_entry_t *log_entry;
svn_boolean_t found_rev_of_interest = TRUE;
log_entry = svn_log_entry_create(pool);
SVN_ERR(fill_log_entry(log_entry, rev, fs,
discover_changed_paths || handling_merged_revision,
revprops, authz_read_func, authz_read_baton,
pool));
log_entry->has_children = has_children;
log_entry->subtractive_merge = subtractive_merge;
if (handling_merged_revision
&& log_entry->changed_paths2
&& log_target_history_as_mergeinfo
&& apr_hash_count(log_target_history_as_mergeinfo))
{
apr_hash_index_t *hi;
apr_pool_t *subpool = svn_pool_create(pool);
found_rev_of_interest = FALSE;
for (hi = apr_hash_first(subpool, log_entry->changed_paths2);
hi;
hi = apr_hash_next(hi))
{
svn_boolean_t path_is_in_history = FALSE;
const char *changed_path = svn__apr_hash_index_key(hi);
apr_hash_index_t *hi2;
apr_pool_t *inner_subpool = svn_pool_create(subpool);
for (hi2 = apr_hash_first(inner_subpool,
log_target_history_as_mergeinfo);
hi2;
hi2 = apr_hash_next(hi2))
{
const char *mergeinfo_path =
svn__apr_hash_index_key(hi2);
apr_array_header_t *rangelist =
svn__apr_hash_index_val(hi2);
if (svn_fspath__is_ancestor(mergeinfo_path,
changed_path))
{
int i;
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(rangelist, i,
svn_merge_range_t *);
if (rev > range->start && rev <= range->end)
{
path_is_in_history = TRUE;
break;
}
}
}
if (path_is_in_history)
break;
}
svn_pool_destroy(inner_subpool);
if (!path_is_in_history)
{
found_rev_of_interest = TRUE;
break;
}
}
svn_pool_destroy(subpool);
}
if (!discover_changed_paths && handling_merged_revision)
log_entry->changed_paths = log_entry->changed_paths2 = NULL;
if (found_rev_of_interest)
{
if (nested_merges && handling_merged_revision)
{
svn_revnum_t *merged_rev = apr_hash_get(nested_merges, &rev,
sizeof(svn_revnum_t *));
if (merged_rev)
{
return SVN_NO_ERROR;
}
else
{
apr_pool_t *hash_pool = apr_hash_pool_get(nested_merges);
svn_revnum_t *long_lived_rev = apr_palloc(hash_pool,
sizeof(svn_revnum_t));
*long_lived_rev = rev;
apr_hash_set(nested_merges, long_lived_rev,
sizeof(svn_revnum_t *), long_lived_rev);
}
}
return (*receiver)(receiver_baton, log_entry, pool);
}
else
{
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));
if (authz_read_func)
{
svn_boolean_t readable;
svn_pool_clear(iterpool);
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_history(&info->hist, root, this_path, pool);
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);
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
{
apr_array_header_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));
apr_hash_this(hi, (void *) &rp->path, NULL,
(void *) &rp->rangelist);
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;
qsort(rangelist_paths->elts, rangelist_paths->nelts,
rangelist_paths->elt_size, 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,
apr_hash_t *nested_merges,
svn_revnum_t hist_start,
svn_revnum_t hist_end,
int limit,
svn_boolean_t discover_changed_paths,
svn_boolean_t strict_node_history,
svn_boolean_t include_merged_revisions,
svn_boolean_t handling_merged_revisions,
svn_boolean_t subtractive_merge,
svn_boolean_t ignore_missing_locations,
const apr_array_header_t *revprops,
svn_boolean_t descending_order,
svn_log_entry_receiver_t receiver,
void *receiver_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
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,
apr_hash_t *nested_merges,
svn_mergeinfo_t processed,
svn_mergeinfo_t added_mergeinfo,
svn_mergeinfo_t deleted_mergeinfo,
svn_boolean_t discover_changed_paths,
svn_boolean_t strict_node_history,
const apr_array_header_t *revprops,
svn_log_entry_receiver_t receiver,
void *receiver_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
apr_array_header_t *combined_list = NULL;
svn_log_entry_t *empty_log_entry;
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);
qsort(combined_list->elts, combined_list->nelts,
combined_list->elt_size, 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,
discover_changed_paths, strict_node_history,
TRUE, pl_range->reverse_merge, TRUE, TRUE,
revprops, TRUE, receiver, receiver_baton,
authz_read_func, authz_read_baton, iterpool));
}
svn_pool_destroy(iterpool);
empty_log_entry = svn_log_entry_create(pool);
empty_log_entry->revision = SVN_INVALID_REVNUM;
return (*receiver)(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 *);
apr_array_header_t *ranges = apr_hash_get(processed, path,
APR_HASH_KEY_STRING);
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 = apr_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 *);
apr_array_header_t *ranges = apr_array_make(processed_pool, 1,
sizeof(svn_merge_range_t*));
svn_merge_range_t *range = apr_palloc(processed_pool,
sizeof(svn_merge_range_t));
range->start = start;
range->end = end;
range->inheritable = TRUE;
APR_ARRAY_PUSH(ranges, svn_merge_range_t *) = range;
apr_hash_set(mergeinfo, apr_pstrdup(processed_pool, path),
APR_HASH_KEY_STRING, ranges);
}
SVN_ERR(svn_mergeinfo_merge(processed, mergeinfo,
apr_hash_pool_get(processed)));
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,
apr_hash_t *nested_merges,
svn_revnum_t hist_start,
svn_revnum_t hist_end,
int limit,
svn_boolean_t discover_changed_paths,
svn_boolean_t strict_node_history,
svn_boolean_t include_merged_revisions,
svn_boolean_t subtractive_merge,
svn_boolean_t handling_merged_revisions,
svn_boolean_t ignore_missing_locations,
const apr_array_header_t *revprops,
svn_boolean_t descending_order,
svn_log_entry_receiver_t receiver,
void *receiver_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
apr_pool_t *iterpool;
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,
authz_read_func, authz_read_baton, pool));
iterpool = 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_ERR(check_history(&changed, info, fs, current,
strict_node_history, authz_read_func,
authz_read_baton, hist_start, pool));
if (! info->done)
any_histories_left = TRUE;
}
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,
discover_changed_paths,
subtractive_merge, handling_merged_revisions,
revprops, has_children,
receiver, receiver_baton,
authz_read_func, authz_read_baton, iterpool));
if (has_children)
{
if (!nested_merges)
{
subpool = svn_pool_create(pool);
nested_merges = apr_hash_make(subpool);
processed = apr_hash_make(subpool);
}
SVN_ERR(handle_merged_revisions(
current, fs,
log_target_history_as_mergeinfo, nested_merges,
processed,
added_mergeinfo, deleted_mergeinfo,
discover_changed_paths,
strict_node_history,
revprops,
receiver, receiver_baton,
authz_read_func,
authz_read_baton,
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_pcalloc(pool, sizeof(*cur_rev));
struct added_deleted_mergeinfo *add_and_del_mergeinfo =
apr_palloc(pool, sizeof(*add_and_del_mergeinfo));
if (added_mergeinfo)
add_and_del_mergeinfo->added_mergeinfo =
svn_mergeinfo_dup(added_mergeinfo, pool);
if (deleted_mergeinfo)
add_and_del_mergeinfo->deleted_mergeinfo =
svn_mergeinfo_dup(deleted_mergeinfo, pool);
*cur_rev = current;
if (! rev_mergeinfo)
rev_mergeinfo = apr_hash_make(pool);
apr_hash_set(rev_mergeinfo, cur_rev, sizeof(*cur_rev),
add_and_del_mergeinfo);
}
}
}
}
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(svn_revnum_t));
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,
discover_changed_paths, subtractive_merge,
handling_merged_revisions, revprops, has_children,
receiver, receiver_baton, authz_read_func,
authz_read_baton, iterpool));
if (has_children)
{
if (!nested_merges)
{
subpool = svn_pool_create(pool);
nested_merges = apr_hash_make(subpool);
}
SVN_ERR(handle_merged_revisions(current, fs,
log_target_history_as_mergeinfo,
nested_merges,
processed,
added_mergeinfo,
deleted_mergeinfo,
discover_changed_paths,
strict_node_history, revprops,
receiver, receiver_baton,
authz_read_func,
authz_read_baton,
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 = apr_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_merge(*paths_history_mergeinfo,
svn_mergeinfo_dup(path_history_mergeinfo,
result_pool),
result_pool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_get_logs4(svn_repos_t *repos,
const apr_array_header_t *paths,
svn_revnum_t start,
svn_revnum_t end,
int limit,
svn_boolean_t discover_changed_paths,
svn_boolean_t strict_node_history,
svn_boolean_t include_merged_revisions,
const apr_array_header_t *revprops,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
svn_log_entry_receiver_t receiver,
void *receiver_baton,
apr_pool_t *pool)
{
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;
SVN_ERR(svn_fs_youngest_rev(&head, fs, 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(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(pool);
send_count = end - start + 1;
if (limit && send_count > limit)
send_count = limit;
for (i = 0; i < send_count; ++i)
{
svn_revnum_t rev = start + i;
svn_pool_clear(iterpool);
if (descending_order)
rev = end - i;
SVN_ERR(send_log(rev, fs, NULL, NULL, discover_changed_paths, FALSE,
FALSE, revprops, FALSE, receiver,
receiver_baton, authz_read_func,
authz_read_baton, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
if (include_merged_revisions)
{
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(get_paths_history_as_mergeinfo(&paths_history_mergeinfo,
repos, paths, start, end,
authz_read_func,
authz_read_baton,
pool, subpool));
svn_pool_destroy(subpool);
}
return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end,
limit, discover_changed_paths, strict_node_history,
include_merged_revisions, FALSE, FALSE, FALSE, revprops,
descending_order, receiver, receiver_baton,
authz_read_func, authz_read_baton, pool);
}