/* ### FIXME: Consider somehow sharing code with ### libsvn_repos/load-fs-vtable.c:renumber_mergeinfo_revs() */ static svn_error_t * renumber_mergeinfo_revs(svn_string_t **final_val, const svn_string_t *initial_val, struct revision_baton *rb, apr_pool_t *pool) { apr_pool_t *subpool = svn_pool_create(pool); svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo; svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool); apr_hash_index_t *hi; SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool)); /* Issue #3020 http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16 Remove mergeinfo older than the oldest revision in the dump stream and adjust its revisions by the difference between the head rev of the target repository and the current dump stream rev. */ if (rb->pb->oldest_dumpstream_rev > 1) { SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &predates_stream_mergeinfo, mergeinfo, rb->pb->oldest_dumpstream_rev - 1, 0, TRUE, subpool, subpool)); SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &mergeinfo, mergeinfo, rb->pb->oldest_dumpstream_rev - 1, 0, FALSE, subpool, subpool)); SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists( &predates_stream_mergeinfo, predates_stream_mergeinfo, -rb->rev_offset, subpool, subpool)); } else { predates_stream_mergeinfo = NULL; } for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi)) { svn_rangelist_t *rangelist; struct parse_baton *pb = rb->pb; int i; const void *path; apr_ssize_t pathlen; void *val; apr_hash_this(hi, &path, &pathlen, &val); rangelist = val; /* Possibly renumber revisions in merge source's rangelist. */ for (i = 0; i < rangelist->nelts; i++) { svn_revnum_t rev_from_map; svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); rev_from_map = get_revision_mapping(pb->rev_map, range->start); if (SVN_IS_VALID_REVNUM(rev_from_map)) { range->start = rev_from_map; } else if (range->start == pb->oldest_dumpstream_rev - 1) { /* Since the start revision of svn_merge_range_t are not inclusive there is one possible valid start revision that won't be found in the PB->REV_MAP mapping of load stream revsions to loaded revisions: The revision immediately preceeding the oldest revision from the load stream. This is a valid revision for mergeinfo, but not a valid copy from revision (which PB->REV_MAP also maps for) so it will never be in the mapping. If that is what we have here, then find the mapping for the oldest rev from the load stream and subtract 1 to get the renumbered, non-inclusive, start revision. */ rev_from_map = get_revision_mapping(pb->rev_map, pb->oldest_dumpstream_rev); if (SVN_IS_VALID_REVNUM(rev_from_map)) range->start = rev_from_map - 1; } else { /* If we can't remap the start revision then don't even bother trying to remap the end revision. It's possible we might actually succeed at the latter, which can result in invalid mergeinfo with a start rev > end rev. If that gets into the repository then a world of bustage breaks loose anytime that bogus mergeinfo is parsed. See http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16. */ continue; } rev_from_map = get_revision_mapping(pb->rev_map, range->end); if (SVN_IS_VALID_REVNUM(rev_from_map)) range->end = rev_from_map; } apr_hash_set(final_mergeinfo, path, pathlen, rangelist); } if (predates_stream_mergeinfo) { SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo, subpool, subpool)); } SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool)); SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool)); svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* Perform a copy or a plain add. * * For a copy, also adjust the copy-from rev, check any copy-source checksum, * and send a notification. */ static svn_error_t * maybe_add_with_history(struct node_baton *nb, struct revision_baton *rb, apr_pool_t *pool) { struct parse_baton *pb = rb->pb; if ((nb->copyfrom_path == NULL) || (! pb->use_history)) { /* Add empty file or dir, without history. */ if (nb->kind == svn_node_file) SVN_ERR(svn_fs_make_file(rb->txn_root, nb->path, pool)); else if (nb->kind == svn_node_dir) SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool)); } else { /* Hunt down the source revision in this fs. */ svn_fs_root_t *copy_root; svn_revnum_t copyfrom_rev; /* Try to find the copyfrom revision in the revision map; failing that, fall back to the revision offset approach. */ copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev); if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) copyfrom_rev = nb->copyfrom_rev - rb->rev_offset; if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, _("Relative source revision %ld is not" " available in current repository"), copyfrom_rev); SVN_ERR(svn_fs_revision_root(©_root, pb->fs, copyfrom_rev, pool)); if (nb->copy_source_checksum) { svn_checksum_t *checksum; SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root, nb->copyfrom_path, TRUE, pool)); if (!svn_checksum_match(nb->copy_source_checksum, checksum)) return svn_checksum_mismatch_err(nb->copy_source_checksum, checksum, pool, _("Copy source checksum mismatch on copy from '%s'@%ld\n" "to '%s' in rev based on r%ld"), nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev); } SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path, rb->txn_root, nb->path, pool)); if (pb->notify_func) { /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ svn_repos_notify_t *notify = svn_repos_notify_create( svn_repos_notify_load_copied_node, pb->notify_pool); pb->notify_func(pb->notify_baton, notify, pb->notify_pool); svn_pool_clear(pb->notify_pool); } } return SVN_NO_ERROR; }