svn_error_t * svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo, svn_repos_t *repos, const apr_array_header_t *paths, svn_revnum_t rev, svn_mergeinfo_inheritance_t inherit, svn_boolean_t include_descendants, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool) { /* Here we cast away 'const', but won't try to write through this pointer * without first allocating a new array. */ apr_array_header_t *readable_paths = (apr_array_header_t *) paths; svn_fs_root_t *root; apr_pool_t *iterpool = svn_pool_create(pool); if (!SVN_IS_VALID_REVNUM(rev)) SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool)); SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool)); /* Filter out unreadable paths before divining merge tracking info. */ if (authz_read_func) { int i; for (i = 0; i < paths->nelts; i++) { svn_boolean_t readable; const char *path = APR_ARRAY_IDX(paths, i, char *); svn_pool_clear(iterpool); SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton, iterpool)); if (readable && readable_paths != paths) APR_ARRAY_PUSH(readable_paths, const char *) = path; else if (!readable && readable_paths == paths) { /* Requested paths differ from readable paths. Fork list of readable paths from requested paths. */ int j; readable_paths = apr_array_make(pool, paths->nelts - 1, sizeof(char *)); for (j = 0; j < i; j++) { path = APR_ARRAY_IDX(paths, j, char *); APR_ARRAY_PUSH(readable_paths, const char *) = path; } } } }
/* Recursively traverse EDIT_PATH (as it exists under SOURCE_ROOT) emitting the appropriate editor calls to add it and its children without any history. This is meant to be used when either a subset of the tree has been ignored and we need to copy something from that subset to the part of the tree we do care about, or if a subset of the tree is unavailable because of authz and we need to use it as the source of a copy. */ static svn_error_t * add_subdir(svn_fs_root_t *source_root, svn_fs_root_t *target_root, const svn_delta_editor_t *editor, void *edit_baton, const char *edit_path, void *parent_baton, const char *source_fspath, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_hash_t *changed_paths, apr_pool_t *pool, void **dir_baton) { apr_pool_t *subpool = svn_pool_create(pool); apr_hash_index_t *hi, *phi; apr_hash_t *dirents; apr_hash_t *props; SVN_ERR(editor->add_directory(edit_path, parent_baton, NULL, SVN_INVALID_REVNUM, pool, dir_baton)); SVN_ERR(svn_fs_node_proplist(&props, target_root, edit_path, pool)); for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { const void *key; void *val; svn_pool_clear(subpool); apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_dir_prop(*dir_baton, key, val, subpool)); } /* We have to get the dirents from the source path, not the target, because we want nested copies from *readable* paths to be handled by path_driver_cb_func, not add_subdir (in order to preserve history). */ SVN_ERR(svn_fs_dir_entries(&dirents, source_root, source_fspath, pool)); for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) { svn_fs_path_change2_t *change; svn_boolean_t readable = TRUE; svn_fs_dirent_t *dent; const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; const char *new_edit_path; void *val; svn_pool_clear(subpool); apr_hash_this(hi, NULL, NULL, &val); dent = val; new_edit_path = svn_relpath_join(edit_path, dent->name, subpool); /* If a file or subdirectory of the copied directory is listed as a changed path (because it was modified after the copy but before the commit), we remove it from the changed_paths hash so that future calls to path_driver_cb_func will ignore it. */ change = apr_hash_get(changed_paths, new_edit_path, APR_HASH_KEY_STRING); if (change) { apr_hash_set(changed_paths, new_edit_path, APR_HASH_KEY_STRING, NULL); /* If it's a delete, skip this entry. */ if (change->change_kind == svn_fs_path_change_delete) continue; /* If it's a replacement, check for copyfrom info (if we don't have it already. */ if (change->change_kind == svn_fs_path_change_replace) { if (! change->copyfrom_known) { SVN_ERR(svn_fs_copied_from(&change->copyfrom_rev, &change->copyfrom_path, target_root, new_edit_path, pool)); change->copyfrom_known = TRUE; } copyfrom_path = change->copyfrom_path; copyfrom_rev = change->copyfrom_rev; } } if (authz_read_func) SVN_ERR(authz_read_func(&readable, target_root, new_edit_path, authz_read_baton, pool)); if (! readable) continue; if (dent->kind == svn_node_dir) { svn_fs_root_t *new_source_root; const char *new_source_fspath; void *new_dir_baton; if (copyfrom_path) { svn_fs_t *fs = svn_fs_root_fs(source_root); SVN_ERR(svn_fs_revision_root(&new_source_root, fs, copyfrom_rev, pool)); new_source_fspath = copyfrom_path; } else { new_source_root = source_root; new_source_fspath = svn_fspath__join(source_fspath, dent->name, subpool); } /* ### authz considerations? * * I think not; when path_driver_cb_func() calls add_subdir(), it * passes SOURCE_ROOT and SOURCE_FSPATH that are unreadable. */ if (change && change->change_kind == svn_fs_path_change_replace && copyfrom_path == NULL) { SVN_ERR(editor->add_directory(new_edit_path, *dir_baton, NULL, SVN_INVALID_REVNUM, subpool, &new_dir_baton)); } else { SVN_ERR(add_subdir(new_source_root, target_root, editor, edit_baton, new_edit_path, *dir_baton, new_source_fspath, authz_read_func, authz_read_baton, changed_paths, subpool, &new_dir_baton)); } SVN_ERR(editor->close_directory(new_dir_baton, subpool)); } else if (dent->kind == svn_node_file) { svn_txdelta_window_handler_t delta_handler; void *delta_handler_baton, *file_baton; svn_txdelta_stream_t *delta_stream; svn_checksum_t *checksum; SVN_ERR(editor->add_file(new_edit_path, *dir_baton, NULL, SVN_INVALID_REVNUM, pool, &file_baton)); SVN_ERR(svn_fs_node_proplist(&props, target_root, new_edit_path, subpool)); for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { const void *key; apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_file_prop(file_baton, key, val, subpool)); } SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool, &delta_handler, &delta_handler_baton)); SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream, NULL, NULL, target_root, new_edit_path, pool)); SVN_ERR(svn_txdelta_send_txstream(delta_stream, delta_handler, delta_handler_baton, pool)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, target_root, new_edit_path, TRUE, pool)); SVN_ERR(editor->close_file(file_baton, svn_checksum_to_cstring(checksum, pool), pool)); } else SVN_ERR_MALFUNCTION(); } svn_pool_destroy(subpool); return SVN_NO_ERROR; }