1251881Speter/* 2251881Speter * load_editor.c: The svn_delta_editor_t editor used by svnrdump to 3251881Speter * load revisions. 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter */ 24251881Speter 25251881Speter#include "svn_cmdline.h" 26251881Speter#include "svn_pools.h" 27251881Speter#include "svn_delta.h" 28251881Speter#include "svn_repos.h" 29251881Speter#include "svn_props.h" 30251881Speter#include "svn_path.h" 31251881Speter#include "svn_ra.h" 32251881Speter#include "svn_subst.h" 33251881Speter#include "svn_io.h" 34251881Speter#include "svn_private_config.h" 35251881Speter#include "private/svn_repos_private.h" 36251881Speter#include "private/svn_ra_private.h" 37251881Speter#include "private/svn_mergeinfo_private.h" 38251881Speter#include "private/svn_fspath.h" 39251881Speter 40251881Speter#include "svnrdump.h" 41251881Speter 42251881Speter#define SVNRDUMP_PROP_LOCK SVN_PROP_PREFIX "rdump-lock" 43251881Speter 44289166Speter#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r)) 45289166Speter 46251881Speter 47251881Speter/** 48251881Speter * General baton used by the parser functions. 49251881Speter */ 50251881Speterstruct parse_baton 51251881Speter{ 52251881Speter /* Commit editor and baton used to transfer loaded revisions to 53251881Speter the target repository. */ 54251881Speter const svn_delta_editor_t *commit_editor; 55251881Speter void *commit_edit_baton; 56251881Speter 57251881Speter /* RA session(s) for committing to the target repository. */ 58251881Speter svn_ra_session_t *session; 59251881Speter svn_ra_session_t *aux_session; 60251881Speter 61251881Speter /* To bleep, or not to bleep? (What kind of question is that?) */ 62251881Speter svn_boolean_t quiet; 63251881Speter 64251881Speter /* Root URL of the target repository. */ 65251881Speter const char *root_url; 66251881Speter 67251881Speter /* The "parent directory" of the target repository in which to load. 68251881Speter (This is essentially the difference between ROOT_URL and 69251881Speter SESSION's url, and roughly equivalent to the 'svnadmin load 70251881Speter --parent-dir' option.) */ 71251881Speter const char *parent_dir; 72251881Speter 73251881Speter /* A mapping of svn_revnum_t * dump stream revisions to their 74251881Speter corresponding svn_revnum_t * target repository revisions. */ 75251881Speter /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903 76251881Speter ### for discussion about improving the memory costs of this mapping. */ 77251881Speter apr_hash_t *rev_map; 78251881Speter 79251881Speter /* The most recent (youngest) revision from the dump stream mapped in 80251881Speter REV_MAP, or SVN_INVALID_REVNUM if no revisions have been mapped. */ 81251881Speter svn_revnum_t last_rev_mapped; 82251881Speter 83251881Speter /* The oldest revision loaded from the dump stream, or 84251881Speter SVN_INVALID_REVNUM if none have been loaded. */ 85251881Speter svn_revnum_t oldest_dumpstream_rev; 86299742Sdim 87299742Sdim /* An hash containing specific revision properties to skip while 88299742Sdim loading. */ 89299742Sdim apr_hash_t *skip_revprops; 90251881Speter}; 91251881Speter 92251881Speter/** 93251881Speter * Use to wrap the dir_context_t in commit.c so we can keep track of 94299742Sdim * relpath and parent for open_directory and close_directory. 95251881Speter */ 96251881Speterstruct directory_baton 97251881Speter{ 98251881Speter void *baton; 99251881Speter const char *relpath; 100289166Speter 101289166Speter /* The copy-from source of this directory, no matter whether it is 102289166Speter copied explicitly (the root node of a copy) or implicitly (being an 103289166Speter existing child of a copied directory). For a node that is newly 104289166Speter added (without history), even inside a copied parent, these are 105289166Speter NULL and SVN_INVALID_REVNUM. */ 106289166Speter const char *copyfrom_path; 107289166Speter svn_revnum_t copyfrom_rev; 108289166Speter 109251881Speter struct directory_baton *parent; 110251881Speter}; 111251881Speter 112251881Speter/** 113251881Speter * Baton used to represent a node; to be used by the parser 114251881Speter * functions. Contains a link to the revision baton. 115251881Speter */ 116251881Speterstruct node_baton 117251881Speter{ 118251881Speter const char *path; 119251881Speter svn_node_kind_t kind; 120251881Speter enum svn_node_action action; 121251881Speter 122289166Speter /* Is this directory explicitly added? If not, then it already existed 123289166Speter or is a child of a copy. */ 124289166Speter svn_boolean_t is_added; 125289166Speter 126251881Speter svn_revnum_t copyfrom_rev; 127251881Speter const char *copyfrom_path; 128289166Speter const char *copyfrom_url; 129251881Speter 130251881Speter void *file_baton; 131251881Speter const char *base_checksum; 132251881Speter 133289166Speter /* (const char *name) -> (svn_prop_t *) */ 134289166Speter apr_hash_t *prop_changes; 135289166Speter 136251881Speter struct revision_baton *rb; 137251881Speter}; 138251881Speter 139251881Speter/** 140251881Speter * Baton used to represet a revision; used by the parser 141251881Speter * functions. Contains a link to the parser baton. 142251881Speter */ 143251881Speterstruct revision_baton 144251881Speter{ 145251881Speter svn_revnum_t rev; 146251881Speter apr_hash_t *revprop_table; 147251881Speter apr_int32_t rev_offset; 148251881Speter 149251881Speter const svn_string_t *datestamp; 150251881Speter const svn_string_t *author; 151251881Speter 152251881Speter struct parse_baton *pb; 153251881Speter struct directory_baton *db; 154251881Speter apr_pool_t *pool; 155251881Speter}; 156251881Speter 157251881Speter 158251881Speter 159251881Speter/* Record the mapping of FROM_REV to TO_REV in REV_MAP, ensuring that 160251881Speter anything added to the hash is allocated in the hash's pool. */ 161251881Speterstatic void 162251881Speterset_revision_mapping(apr_hash_t *rev_map, 163251881Speter svn_revnum_t from_rev, 164251881Speter svn_revnum_t to_rev) 165251881Speter{ 166251881Speter svn_revnum_t *mapped_revs = apr_palloc(apr_hash_pool_get(rev_map), 167251881Speter sizeof(svn_revnum_t) * 2); 168251881Speter mapped_revs[0] = from_rev; 169251881Speter mapped_revs[1] = to_rev; 170251881Speter apr_hash_set(rev_map, mapped_revs, 171251881Speter sizeof(svn_revnum_t), mapped_revs + 1); 172251881Speter} 173251881Speter 174251881Speter/* Return the revision to which FROM_REV maps in REV_MAP, or 175251881Speter SVN_INVALID_REVNUM if no such mapping exists. */ 176251881Speterstatic svn_revnum_t 177251881Speterget_revision_mapping(apr_hash_t *rev_map, 178251881Speter svn_revnum_t from_rev) 179251881Speter{ 180251881Speter svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev, 181251881Speter sizeof(from_rev)); 182251881Speter return to_rev ? *to_rev : SVN_INVALID_REVNUM; 183251881Speter} 184251881Speter 185251881Speter 186251881Speterstatic svn_error_t * 187251881Spetercommit_callback(const svn_commit_info_t *commit_info, 188251881Speter void *baton, 189251881Speter apr_pool_t *pool) 190251881Speter{ 191251881Speter struct revision_baton *rb = baton; 192251881Speter struct parse_baton *pb = rb->pb; 193251881Speter 194251881Speter /* ### Don't print directly; generate a notification. */ 195251881Speter if (! pb->quiet) 196251881Speter SVN_ERR(svn_cmdline_printf(pool, "* Loaded revision %ld.\n", 197251881Speter commit_info->revision)); 198251881Speter 199251881Speter /* Add the mapping of the dumpstream revision to the committed revision. */ 200251881Speter set_revision_mapping(pb->rev_map, rb->rev, commit_info->revision); 201251881Speter 202251881Speter /* If the incoming dump stream has non-contiguous revisions (e.g. from 203251881Speter using svndumpfilter --drop-empty-revs without --renumber-revs) then 204251881Speter we must account for the missing gaps in PB->REV_MAP. Otherwise we 205251881Speter might not be able to map all mergeinfo source revisions to the correct 206251881Speter revisions in the target repos. */ 207251881Speter if ((pb->last_rev_mapped != SVN_INVALID_REVNUM) 208251881Speter && (rb->rev != pb->last_rev_mapped + 1)) 209251881Speter { 210251881Speter svn_revnum_t i; 211251881Speter 212251881Speter for (i = pb->last_rev_mapped + 1; i < rb->rev; i++) 213251881Speter { 214251881Speter set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped); 215251881Speter } 216251881Speter } 217251881Speter 218251881Speter /* Update our "last revision mapped". */ 219251881Speter pb->last_rev_mapped = rb->rev; 220251881Speter 221251881Speter return SVN_NO_ERROR; 222251881Speter} 223251881Speter 224251881Speter/* Implements `svn_ra__lock_retry_func_t'. */ 225251881Speterstatic svn_error_t * 226251881Speterlock_retry_func(void *baton, 227251881Speter const svn_string_t *reposlocktoken, 228251881Speter apr_pool_t *pool) 229251881Speter{ 230251881Speter return svn_cmdline_printf(pool, 231251881Speter _("Failed to get lock on destination " 232251881Speter "repos, currently held by '%s'\n"), 233251881Speter reposlocktoken->data); 234251881Speter} 235251881Speter 236251881Speter 237251881Speterstatic svn_error_t * 238251881Speterfetch_base_func(const char **filename, 239251881Speter void *baton, 240251881Speter const char *path, 241251881Speter svn_revnum_t base_revision, 242251881Speter apr_pool_t *result_pool, 243251881Speter apr_pool_t *scratch_pool) 244251881Speter{ 245251881Speter struct revision_baton *rb = baton; 246251881Speter svn_stream_t *fstream; 247251881Speter svn_error_t *err; 248251881Speter 249251881Speter if (! SVN_IS_VALID_REVNUM(base_revision)) 250251881Speter base_revision = rb->rev - 1; 251251881Speter 252251881Speter SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL, 253251881Speter svn_io_file_del_on_pool_cleanup, 254251881Speter result_pool, scratch_pool)); 255251881Speter 256251881Speter err = svn_ra_get_file(rb->pb->aux_session, path, base_revision, 257251881Speter fstream, NULL, NULL, scratch_pool); 258251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 259251881Speter { 260251881Speter svn_error_clear(err); 261251881Speter SVN_ERR(svn_stream_close(fstream)); 262251881Speter 263251881Speter *filename = NULL; 264251881Speter return SVN_NO_ERROR; 265251881Speter } 266251881Speter else if (err) 267251881Speter return svn_error_trace(err); 268251881Speter 269251881Speter SVN_ERR(svn_stream_close(fstream)); 270251881Speter 271251881Speter return SVN_NO_ERROR; 272251881Speter} 273251881Speter 274251881Speterstatic svn_error_t * 275251881Speterfetch_props_func(apr_hash_t **props, 276251881Speter void *baton, 277251881Speter const char *path, 278251881Speter svn_revnum_t base_revision, 279251881Speter apr_pool_t *result_pool, 280251881Speter apr_pool_t *scratch_pool) 281251881Speter{ 282251881Speter struct revision_baton *rb = baton; 283251881Speter svn_node_kind_t node_kind; 284251881Speter 285251881Speter if (! SVN_IS_VALID_REVNUM(base_revision)) 286251881Speter base_revision = rb->rev - 1; 287251881Speter 288251881Speter SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision, 289251881Speter &node_kind, scratch_pool)); 290251881Speter 291251881Speter if (node_kind == svn_node_file) 292251881Speter { 293251881Speter SVN_ERR(svn_ra_get_file(rb->pb->aux_session, path, base_revision, 294251881Speter NULL, NULL, props, result_pool)); 295251881Speter } 296251881Speter else if (node_kind == svn_node_dir) 297251881Speter { 298251881Speter apr_array_header_t *tmp_props; 299251881Speter 300251881Speter SVN_ERR(svn_ra_get_dir2(rb->pb->aux_session, NULL, NULL, props, path, 301251881Speter base_revision, 0 /* Dirent fields */, 302251881Speter result_pool)); 303251881Speter tmp_props = svn_prop_hash_to_array(*props, result_pool); 304251881Speter SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props, 305251881Speter result_pool)); 306251881Speter *props = svn_prop_array_to_hash(tmp_props, result_pool); 307251881Speter } 308251881Speter else 309251881Speter { 310251881Speter *props = apr_hash_make(result_pool); 311251881Speter } 312251881Speter 313251881Speter return SVN_NO_ERROR; 314251881Speter} 315251881Speter 316251881Speterstatic svn_error_t * 317251881Speterfetch_kind_func(svn_node_kind_t *kind, 318251881Speter void *baton, 319251881Speter const char *path, 320251881Speter svn_revnum_t base_revision, 321251881Speter apr_pool_t *scratch_pool) 322251881Speter{ 323251881Speter struct revision_baton *rb = baton; 324251881Speter 325251881Speter if (! SVN_IS_VALID_REVNUM(base_revision)) 326251881Speter base_revision = rb->rev - 1; 327251881Speter 328251881Speter SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision, 329251881Speter kind, scratch_pool)); 330251881Speter 331251881Speter return SVN_NO_ERROR; 332251881Speter} 333251881Speter 334251881Speterstatic svn_delta_shim_callbacks_t * 335251881Speterget_shim_callbacks(struct revision_baton *rb, 336251881Speter apr_pool_t *pool) 337251881Speter{ 338251881Speter svn_delta_shim_callbacks_t *callbacks = 339251881Speter svn_delta_shim_callbacks_default(pool); 340251881Speter 341251881Speter callbacks->fetch_props_func = fetch_props_func; 342251881Speter callbacks->fetch_kind_func = fetch_kind_func; 343251881Speter callbacks->fetch_base_func = fetch_base_func; 344251881Speter callbacks->fetch_baton = rb; 345251881Speter 346251881Speter return callbacks; 347251881Speter} 348251881Speter 349251881Speter/* Acquire a lock (of sorts) on the repository associated with the 350251881Speter * given RA SESSION. This lock is just a revprop change attempt in a 351251881Speter * time-delay loop. This function is duplicated by svnsync in 352251881Speter * svnsync/svnsync.c 353251881Speter * 354251881Speter * ### TODO: Make this function more generic and 355251881Speter * expose it through a header for use by other Subversion 356251881Speter * applications to avoid duplication. 357251881Speter */ 358251881Speterstatic svn_error_t * 359251881Speterget_lock(const svn_string_t **lock_string_p, 360251881Speter svn_ra_session_t *session, 361251881Speter svn_cancel_func_t cancel_func, 362251881Speter void *cancel_baton, 363251881Speter apr_pool_t *pool) 364251881Speter{ 365251881Speter svn_boolean_t be_atomic; 366251881Speter 367251881Speter SVN_ERR(svn_ra_has_capability(session, &be_atomic, 368251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 369251881Speter pool)); 370251881Speter if (! be_atomic) 371251881Speter { 372251881Speter /* Pre-1.7 servers can't lock without a race condition. (Issue #3546) */ 373251881Speter svn_error_t *err = 374251881Speter svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 375251881Speter _("Target server does not support atomic revision " 376251881Speter "property edits; consider upgrading it to 1.7.")); 377251881Speter svn_handle_warning2(stderr, err, "svnrdump: "); 378251881Speter svn_error_clear(err); 379251881Speter } 380251881Speter 381251881Speter return svn_ra__get_operational_lock(lock_string_p, NULL, session, 382251881Speter SVNRDUMP_PROP_LOCK, FALSE, 383251881Speter 10 /* retries */, lock_retry_func, NULL, 384251881Speter cancel_func, cancel_baton, pool); 385251881Speter} 386251881Speter 387251881Speterstatic svn_error_t * 388251881Speternew_revision_record(void **revision_baton, 389251881Speter apr_hash_t *headers, 390251881Speter void *parse_baton, 391251881Speter apr_pool_t *pool) 392251881Speter{ 393251881Speter struct revision_baton *rb; 394251881Speter struct parse_baton *pb; 395251881Speter apr_hash_index_t *hi; 396251881Speter svn_revnum_t head_rev; 397251881Speter 398251881Speter rb = apr_pcalloc(pool, sizeof(*rb)); 399251881Speter pb = parse_baton; 400251881Speter rb->pool = svn_pool_create(pool); 401251881Speter rb->pb = pb; 402289166Speter rb->db = NULL; 403251881Speter 404251881Speter for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi)) 405251881Speter { 406299742Sdim const char *hname = apr_hash_this_key(hi); 407299742Sdim const char *hval = apr_hash_this_val(hi); 408251881Speter 409251881Speter if (strcmp(hname, SVN_REPOS_DUMPFILE_REVISION_NUMBER) == 0) 410251881Speter rb->rev = atoi(hval); 411251881Speter } 412251881Speter 413251881Speter SVN_ERR(svn_ra_get_latest_revnum(pb->session, &head_rev, pool)); 414251881Speter 415251881Speter /* FIXME: This is a lame fallback loading multiple segments of dump in 416251881Speter several separate operations. It is highly susceptible to race conditions. 417251881Speter Calculate the revision 'offset' for finding copyfrom sources. 418251881Speter It might be positive or negative. */ 419251881Speter rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1)); 420251881Speter 421251881Speter /* Stash the oldest (non-zero) dumpstream revision seen. */ 422251881Speter if ((rb->rev > 0) && (!SVN_IS_VALID_REVNUM(pb->oldest_dumpstream_rev))) 423251881Speter pb->oldest_dumpstream_rev = rb->rev; 424251881Speter 425251881Speter /* Set the commit_editor/ commit_edit_baton to NULL and wait for 426251881Speter them to be created in new_node_record */ 427251881Speter rb->pb->commit_editor = NULL; 428251881Speter rb->pb->commit_edit_baton = NULL; 429251881Speter rb->revprop_table = apr_hash_make(rb->pool); 430251881Speter 431251881Speter *revision_baton = rb; 432251881Speter return SVN_NO_ERROR; 433251881Speter} 434251881Speter 435251881Speterstatic svn_error_t * 436251881Spetermagic_header_record(int version, 437251881Speter void *parse_baton, 438251881Speter apr_pool_t *pool) 439251881Speter{ 440251881Speter return SVN_NO_ERROR; 441251881Speter} 442251881Speter 443251881Speterstatic svn_error_t * 444251881Speteruuid_record(const char *uuid, 445251881Speter void *parse_baton, 446251881Speter apr_pool_t *pool) 447251881Speter{ 448251881Speter return SVN_NO_ERROR; 449251881Speter} 450251881Speter 451289166Speter/* Push information about another directory onto the linked list RB->db. 452289166Speter * 453289166Speter * CHILD_BATON is the baton returned by the commit editor. RELPATH is the 454289166Speter * repository-relative path of this directory. IS_ADDED is true iff this 455289166Speter * directory is being added (with or without history). If added with 456289166Speter * history then COPYFROM_PATH/COPYFROM_REV are the copyfrom source, else 457289166Speter * are NULL/SVN_INVALID_REVNUM. 458289166Speter */ 459289166Speterstatic void 460289166Speterpush_directory(struct revision_baton *rb, 461289166Speter void *child_baton, 462289166Speter const char *relpath, 463289166Speter svn_boolean_t is_added, 464289166Speter const char *copyfrom_path, 465289166Speter svn_revnum_t copyfrom_rev) 466289166Speter{ 467289166Speter struct directory_baton *child_db = apr_pcalloc(rb->pool, sizeof (*child_db)); 468289166Speter 469289166Speter SVN_ERR_ASSERT_NO_RETURN( 470289166Speter is_added || (copyfrom_path == NULL && copyfrom_rev == SVN_INVALID_REVNUM)); 471289166Speter 472289166Speter /* If this node is an existing (not newly added) child of a copied node, 473289166Speter calculate where it was copied from. */ 474289166Speter if (!is_added 475289166Speter && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev)) 476289166Speter { 477289166Speter const char *name = svn_relpath_basename(relpath, NULL); 478289166Speter 479289166Speter copyfrom_path = svn_relpath_join(rb->db->copyfrom_path, name, 480289166Speter rb->pool); 481289166Speter copyfrom_rev = rb->db->copyfrom_rev; 482289166Speter } 483289166Speter 484289166Speter child_db->baton = child_baton; 485289166Speter child_db->relpath = relpath; 486289166Speter child_db->copyfrom_path = copyfrom_path; 487289166Speter child_db->copyfrom_rev = copyfrom_rev; 488289166Speter child_db->parent = rb->db; 489289166Speter rb->db = child_db; 490289166Speter} 491289166Speter 492251881Speterstatic svn_error_t * 493251881Speternew_node_record(void **node_baton, 494251881Speter apr_hash_t *headers, 495251881Speter void *revision_baton, 496251881Speter apr_pool_t *pool) 497251881Speter{ 498251881Speter struct revision_baton *rb = revision_baton; 499251881Speter const struct svn_delta_editor_t *commit_editor = rb->pb->commit_editor; 500251881Speter void *commit_edit_baton = rb->pb->commit_edit_baton; 501251881Speter struct node_baton *nb; 502251881Speter apr_hash_index_t *hi; 503251881Speter void *child_baton; 504251881Speter const char *nb_dirname; 505251881Speter 506251881Speter nb = apr_pcalloc(rb->pool, sizeof(*nb)); 507251881Speter nb->rb = rb; 508289166Speter nb->is_added = FALSE; 509251881Speter nb->copyfrom_path = NULL; 510289166Speter nb->copyfrom_url = NULL; 511251881Speter nb->copyfrom_rev = SVN_INVALID_REVNUM; 512289166Speter nb->prop_changes = apr_hash_make(rb->pool); 513251881Speter 514251881Speter /* If the creation of commit_editor is pending, create it now and 515251881Speter open_root on it; also create a top-level directory baton. */ 516251881Speter 517251881Speter if (!commit_editor) 518251881Speter { 519251881Speter /* The revprop_table should have been filled in with important 520251881Speter information like svn:log in set_revision_property. We can now 521251881Speter use it all this information to create our commit_editor. But 522251881Speter first, clear revprops that we aren't allowed to set with the 523251881Speter commit_editor. We'll set them separately using the RA API 524251881Speter after closing the editor (see close_revision). */ 525251881Speter 526251881Speter svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_AUTHOR, NULL); 527251881Speter svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_DATE, NULL); 528251881Speter 529251881Speter SVN_ERR(svn_ra__register_editor_shim_callbacks(rb->pb->session, 530251881Speter get_shim_callbacks(rb, rb->pool))); 531251881Speter SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor, 532251881Speter &commit_edit_baton, rb->revprop_table, 533251881Speter commit_callback, revision_baton, 534251881Speter NULL, FALSE, rb->pool)); 535251881Speter 536251881Speter rb->pb->commit_editor = commit_editor; 537251881Speter rb->pb->commit_edit_baton = commit_edit_baton; 538251881Speter 539251881Speter SVN_ERR(commit_editor->open_root(commit_edit_baton, 540251881Speter rb->rev - rb->rev_offset - 1, 541251881Speter rb->pool, &child_baton)); 542251881Speter 543289166Speter /* child_baton corresponds to the root directory baton here */ 544289166Speter push_directory(rb, child_baton, "", TRUE /*is_added*/, 545289166Speter NULL, SVN_INVALID_REVNUM); 546251881Speter } 547251881Speter 548251881Speter for (hi = apr_hash_first(rb->pool, headers); hi; hi = apr_hash_next(hi)) 549251881Speter { 550299742Sdim const char *hname = apr_hash_this_key(hi); 551299742Sdim const char *hval = apr_hash_this_val(hi); 552251881Speter 553251881Speter /* Parse the different kinds of headers we can encounter and 554251881Speter stuff them into the node_baton for writing later */ 555251881Speter if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_PATH) == 0) 556251881Speter nb->path = apr_pstrdup(rb->pool, hval); 557251881Speter if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_KIND) == 0) 558251881Speter nb->kind = strcmp(hval, "file") == 0 ? svn_node_file : svn_node_dir; 559251881Speter if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_ACTION) == 0) 560251881Speter { 561251881Speter if (strcmp(hval, "add") == 0) 562251881Speter nb->action = svn_node_action_add; 563251881Speter if (strcmp(hval, "change") == 0) 564251881Speter nb->action = svn_node_action_change; 565251881Speter if (strcmp(hval, "delete") == 0) 566251881Speter nb->action = svn_node_action_delete; 567251881Speter if (strcmp(hval, "replace") == 0) 568251881Speter nb->action = svn_node_action_replace; 569251881Speter } 570251881Speter if (strcmp(hname, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5) == 0) 571251881Speter nb->base_checksum = apr_pstrdup(rb->pool, hval); 572251881Speter if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV) == 0) 573251881Speter nb->copyfrom_rev = atoi(hval); 574251881Speter if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH) == 0) 575251881Speter nb->copyfrom_path = apr_pstrdup(rb->pool, hval); 576251881Speter } 577251881Speter 578299742Sdim /* Before handling the new node, ensure depth-first editing order by 579299742Sdim traversing the directory hierarchy from the old node's to the new 580299742Sdim node's parent directory. */ 581251881Speter nb_dirname = svn_relpath_dirname(nb->path, pool); 582251881Speter if (svn_path_compare_paths(nb_dirname, 583251881Speter rb->db->relpath) != 0) 584251881Speter { 585251881Speter char *ancestor_path; 586251881Speter apr_size_t residual_close_count; 587251881Speter apr_array_header_t *residual_open_path; 588251881Speter int i; 589251881Speter apr_size_t n; 590251881Speter 591251881Speter ancestor_path = 592251881Speter svn_relpath_get_longest_ancestor(nb_dirname, 593251881Speter rb->db->relpath, pool); 594251881Speter residual_close_count = 595251881Speter svn_path_component_count(svn_relpath_skip_ancestor(ancestor_path, 596251881Speter rb->db->relpath)); 597251881Speter residual_open_path = 598251881Speter svn_path_decompose(svn_relpath_skip_ancestor(ancestor_path, 599251881Speter nb_dirname), pool); 600251881Speter 601251881Speter /* First close all as many directories as there are after 602251881Speter skip_ancestor, and then open fresh directories */ 603251881Speter for (n = 0; n < residual_close_count; n ++) 604251881Speter { 605251881Speter /* Don't worry about destroying the actual rb->db object, 606251881Speter since the pool we're using has the lifetime of one 607251881Speter revision anyway */ 608251881Speter SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool)); 609251881Speter rb->db = rb->db->parent; 610251881Speter } 611251881Speter 612251881Speter for (i = 0; i < residual_open_path->nelts; i ++) 613251881Speter { 614289166Speter char *relpath_compose = 615251881Speter svn_relpath_join(rb->db->relpath, 616251881Speter APR_ARRAY_IDX(residual_open_path, i, const char *), 617251881Speter rb->pool); 618251881Speter SVN_ERR(commit_editor->open_directory(relpath_compose, 619251881Speter rb->db->baton, 620251881Speter rb->rev - rb->rev_offset - 1, 621251881Speter rb->pool, &child_baton)); 622289166Speter push_directory(rb, child_baton, relpath_compose, TRUE /*is_added*/, 623289166Speter NULL, SVN_INVALID_REVNUM); 624251881Speter } 625251881Speter } 626251881Speter 627251881Speter /* Fix up the copyfrom information in light of mapped revisions and 628251881Speter non-root load targets, and convert copyfrom path into a full 629251881Speter URL. */ 630251881Speter if (nb->copyfrom_path && SVN_IS_VALID_REVNUM(nb->copyfrom_rev)) 631251881Speter { 632251881Speter svn_revnum_t copyfrom_rev; 633251881Speter 634251881Speter /* Try to find the copyfrom revision in the revision map; 635251881Speter failing that, fall back to the revision offset approach. */ 636251881Speter copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev); 637251881Speter if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) 638251881Speter copyfrom_rev = nb->copyfrom_rev - rb->rev_offset; 639251881Speter 640251881Speter if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) 641251881Speter return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 642251881Speter _("Relative source revision %ld is not" 643251881Speter " available in current repository"), 644251881Speter copyfrom_rev); 645251881Speter 646251881Speter nb->copyfrom_rev = copyfrom_rev; 647251881Speter 648251881Speter if (rb->pb->parent_dir) 649251881Speter nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, 650251881Speter nb->copyfrom_path, rb->pool); 651299742Sdim /* Convert to a URL, as the commit editor requires. */ 652289166Speter nb->copyfrom_url = svn_path_url_add_component2(rb->pb->root_url, 653251881Speter nb->copyfrom_path, 654251881Speter rb->pool); 655251881Speter } 656251881Speter 657251881Speter 658251881Speter switch (nb->action) 659251881Speter { 660251881Speter case svn_node_action_delete: 661251881Speter case svn_node_action_replace: 662289166Speter SVN_ERR(commit_editor->delete_entry(nb->path, 663289166Speter rb->rev - rb->rev_offset - 1, 664251881Speter rb->db->baton, rb->pool)); 665251881Speter if (nb->action == svn_node_action_delete) 666251881Speter break; 667251881Speter else 668251881Speter /* FALL THROUGH */; 669251881Speter case svn_node_action_add: 670289166Speter nb->is_added = TRUE; 671251881Speter switch (nb->kind) 672251881Speter { 673251881Speter case svn_node_file: 674251881Speter SVN_ERR(commit_editor->add_file(nb->path, rb->db->baton, 675289166Speter nb->copyfrom_url, 676251881Speter nb->copyfrom_rev, 677251881Speter rb->pool, &(nb->file_baton))); 678251881Speter break; 679251881Speter case svn_node_dir: 680251881Speter SVN_ERR(commit_editor->add_directory(nb->path, rb->db->baton, 681289166Speter nb->copyfrom_url, 682251881Speter nb->copyfrom_rev, 683251881Speter rb->pool, &child_baton)); 684289166Speter push_directory(rb, child_baton, nb->path, TRUE /*is_added*/, 685289166Speter nb->copyfrom_path, nb->copyfrom_rev); 686251881Speter break; 687251881Speter default: 688251881Speter break; 689251881Speter } 690251881Speter break; 691251881Speter case svn_node_action_change: 692251881Speter switch (nb->kind) 693251881Speter { 694251881Speter case svn_node_file: 695251881Speter SVN_ERR(commit_editor->open_file(nb->path, rb->db->baton, 696251881Speter SVN_INVALID_REVNUM, rb->pool, 697251881Speter &(nb->file_baton))); 698251881Speter break; 699251881Speter default: 700251881Speter SVN_ERR(commit_editor->open_directory(nb->path, rb->db->baton, 701251881Speter rb->rev - rb->rev_offset - 1, 702251881Speter rb->pool, &child_baton)); 703289166Speter push_directory(rb, child_baton, nb->path, FALSE /*is_added*/, 704289166Speter NULL, SVN_INVALID_REVNUM); 705251881Speter break; 706251881Speter } 707251881Speter break; 708251881Speter } 709251881Speter 710251881Speter *node_baton = nb; 711251881Speter return SVN_NO_ERROR; 712251881Speter} 713251881Speter 714251881Speterstatic svn_error_t * 715251881Speterset_revision_property(void *baton, 716251881Speter const char *name, 717251881Speter const svn_string_t *value) 718251881Speter{ 719251881Speter struct revision_baton *rb = baton; 720251881Speter 721251881Speter SVN_ERR(svn_rdump__normalize_prop(name, &value, rb->pool)); 722251881Speter 723251881Speter SVN_ERR(svn_repos__validate_prop(name, value, rb->pool)); 724251881Speter 725251881Speter if (rb->rev > 0) 726251881Speter { 727299742Sdim if (! svn_hash_gets(rb->pb->skip_revprops, name)) 728299742Sdim svn_hash_sets(rb->revprop_table, 729299742Sdim apr_pstrdup(rb->pool, name), 730299742Sdim svn_string_dup(value, rb->pool)); 731251881Speter } 732299742Sdim else if (rb->rev_offset == -1 733299742Sdim && ! svn_hash_gets(rb->pb->skip_revprops, name)) 734251881Speter { 735251881Speter /* Special case: set revision 0 properties directly (which is 736251881Speter safe because the commit_editor hasn't been created yet), but 737251881Speter only when loading into an 'empty' filesystem. */ 738251881Speter SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, 0, 739251881Speter name, NULL, value, rb->pool)); 740251881Speter } 741251881Speter 742251881Speter /* Remember any datestamp/ author that passes through (see comment 743251881Speter in close_revision). */ 744251881Speter if (!strcmp(name, SVN_PROP_REVISION_DATE)) 745251881Speter rb->datestamp = svn_string_dup(value, rb->pool); 746251881Speter if (!strcmp(name, SVN_PROP_REVISION_AUTHOR)) 747251881Speter rb->author = svn_string_dup(value, rb->pool); 748251881Speter 749251881Speter return SVN_NO_ERROR; 750251881Speter} 751251881Speter 752251881Speterstatic svn_error_t * 753251881Speterset_node_property(void *baton, 754251881Speter const char *name, 755251881Speter const svn_string_t *value) 756251881Speter{ 757251881Speter struct node_baton *nb = baton; 758299742Sdim struct revision_baton *rb = nb->rb; 759299742Sdim struct parse_baton *pb = rb->pb; 760251881Speter apr_pool_t *pool = nb->rb->pool; 761289166Speter svn_prop_t *prop; 762251881Speter 763251881Speter if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) 764251881Speter { 765299742Sdim svn_string_t *new_value; 766299742Sdim svn_error_t *err; 767251881Speter 768299742Sdim err = svn_repos__adjust_mergeinfo_property(&new_value, value, 769299742Sdim pb->parent_dir, 770299742Sdim pb->rev_map, 771299742Sdim pb->oldest_dumpstream_rev, 772299742Sdim rb->rev_offset, 773299742Sdim NULL, NULL, /*notify*/ 774299742Sdim pool, pool); 775299742Sdim if (err) 776251881Speter { 777299742Sdim return svn_error_quick_wrap(err, 778299742Sdim _("Invalid svn:mergeinfo value")); 779251881Speter } 780251881Speter 781299742Sdim value = new_value; 782251881Speter } 783251881Speter 784251881Speter SVN_ERR(svn_rdump__normalize_prop(name, &value, pool)); 785251881Speter 786251881Speter SVN_ERR(svn_repos__validate_prop(name, value, pool)); 787251881Speter 788289166Speter prop = apr_palloc(nb->rb->pool, sizeof (*prop)); 789289166Speter prop->name = apr_pstrdup(pool, name); 790299742Sdim prop->value = svn_string_dup(value, pool); 791289166Speter svn_hash_sets(nb->prop_changes, prop->name, prop); 792289166Speter 793251881Speter return SVN_NO_ERROR; 794251881Speter} 795251881Speter 796251881Speterstatic svn_error_t * 797251881Speterdelete_node_property(void *baton, 798251881Speter const char *name) 799251881Speter{ 800251881Speter struct node_baton *nb = baton; 801251881Speter apr_pool_t *pool = nb->rb->pool; 802289166Speter svn_prop_t *prop; 803251881Speter 804251881Speter SVN_ERR(svn_repos__validate_prop(name, NULL, pool)); 805251881Speter 806289166Speter prop = apr_palloc(pool, sizeof (*prop)); 807289166Speter prop->name = apr_pstrdup(pool, name); 808289166Speter prop->value = NULL; 809289166Speter svn_hash_sets(nb->prop_changes, prop->name, prop); 810251881Speter 811251881Speter return SVN_NO_ERROR; 812251881Speter} 813251881Speter 814289166Speter/* Delete all the properties of the node, if any. 815289166Speter * 816289166Speter * The commit editor doesn't have a method to delete a node's properties 817289166Speter * without knowing what they are, so we have to first find out what 818289166Speter * properties the node would have had. If it's copied (explicitly or 819289166Speter * implicitly), we look at the copy source. If it's only being changed, 820289166Speter * we look at the node's current path in the head revision. 821289166Speter */ 822251881Speterstatic svn_error_t * 823251881Speterremove_node_props(void *baton) 824251881Speter{ 825251881Speter struct node_baton *nb = baton; 826289166Speter struct revision_baton *rb = nb->rb; 827251881Speter apr_pool_t *pool = nb->rb->pool; 828251881Speter apr_hash_index_t *hi; 829251881Speter apr_hash_t *props; 830289166Speter const char *orig_path; 831289166Speter svn_revnum_t orig_rev; 832251881Speter 833289166Speter /* Find the path and revision that has the node's original properties */ 834289166Speter if (ARE_VALID_COPY_ARGS(nb->copyfrom_path, nb->copyfrom_rev)) 835289166Speter { 836289166Speter orig_path = nb->copyfrom_path; 837289166Speter orig_rev = nb->copyfrom_rev; 838289166Speter } 839289166Speter else if (!nb->is_added 840289166Speter && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev)) 841289166Speter { 842289166Speter /* If this is a dir, then it's described by rb->db; 843289166Speter if this is a file, then it's a child of the dir in rb->db. */ 844289166Speter orig_path = (nb->kind == svn_node_dir) 845289166Speter ? rb->db->copyfrom_path 846289166Speter : svn_relpath_join(rb->db->copyfrom_path, 847289166Speter svn_relpath_basename(nb->path, NULL), 848289166Speter rb->pool); 849289166Speter orig_rev = rb->db->copyfrom_rev; 850289166Speter } 851289166Speter else 852289166Speter { 853289166Speter /* ### Should we query at a known, fixed, "head" revision number 854289166Speter instead of passing SVN_INVALID_REVNUM and getting a moving target? */ 855289166Speter orig_path = nb->path; 856289166Speter orig_rev = SVN_INVALID_REVNUM; 857289166Speter } 858289166Speter 859251881Speter if ((nb->action == svn_node_action_add 860251881Speter || nb->action == svn_node_action_replace) 861289166Speter && ! ARE_VALID_COPY_ARGS(orig_path, orig_rev)) 862251881Speter /* Add-without-history; no "old" properties to worry about. */ 863251881Speter return SVN_NO_ERROR; 864251881Speter 865251881Speter if (nb->kind == svn_node_file) 866251881Speter { 867289166Speter SVN_ERR(svn_ra_get_file(nb->rb->pb->aux_session, 868289166Speter orig_path, orig_rev, NULL, NULL, &props, pool)); 869251881Speter } 870251881Speter else /* nb->kind == svn_node_dir */ 871251881Speter { 872251881Speter SVN_ERR(svn_ra_get_dir2(nb->rb->pb->aux_session, NULL, NULL, &props, 873289166Speter orig_path, orig_rev, 0, pool)); 874251881Speter } 875251881Speter 876251881Speter for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) 877251881Speter { 878299742Sdim const char *name = apr_hash_this_key(hi); 879251881Speter svn_prop_kind_t kind = svn_property_kind2(name); 880251881Speter 881251881Speter if (kind == svn_prop_regular_kind) 882251881Speter SVN_ERR(set_node_property(nb, name, NULL)); 883251881Speter } 884251881Speter 885251881Speter return SVN_NO_ERROR; 886251881Speter} 887251881Speter 888251881Speterstatic svn_error_t * 889251881Speterset_fulltext(svn_stream_t **stream, 890251881Speter void *node_baton) 891251881Speter{ 892251881Speter struct node_baton *nb = node_baton; 893251881Speter const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor; 894251881Speter svn_txdelta_window_handler_t handler; 895251881Speter void *handler_baton; 896251881Speter apr_pool_t *pool = nb->rb->pool; 897251881Speter 898251881Speter SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum, 899251881Speter pool, &handler, &handler_baton)); 900251881Speter *stream = svn_txdelta_target_push(handler, handler_baton, 901251881Speter svn_stream_empty(pool), pool); 902251881Speter return SVN_NO_ERROR; 903251881Speter} 904251881Speter 905251881Speterstatic svn_error_t * 906251881Speterapply_textdelta(svn_txdelta_window_handler_t *handler, 907251881Speter void **handler_baton, 908251881Speter void *node_baton) 909251881Speter{ 910251881Speter struct node_baton *nb = node_baton; 911251881Speter const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor; 912251881Speter apr_pool_t *pool = nb->rb->pool; 913251881Speter 914251881Speter SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum, 915251881Speter pool, handler, handler_baton)); 916251881Speter 917251881Speter return SVN_NO_ERROR; 918251881Speter} 919251881Speter 920251881Speterstatic svn_error_t * 921251881Speterclose_node(void *baton) 922251881Speter{ 923251881Speter struct node_baton *nb = baton; 924251881Speter const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor; 925289166Speter apr_pool_t *pool = nb->rb->pool; 926289166Speter apr_hash_index_t *hi; 927251881Speter 928289166Speter for (hi = apr_hash_first(pool, nb->prop_changes); 929289166Speter hi; hi = apr_hash_next(hi)) 930289166Speter { 931299742Sdim const char *name = apr_hash_this_key(hi); 932299742Sdim svn_prop_t *prop = apr_hash_this_val(hi); 933289166Speter 934289166Speter switch (nb->kind) 935289166Speter { 936289166Speter case svn_node_file: 937289166Speter SVN_ERR(commit_editor->change_file_prop(nb->file_baton, 938289166Speter name, prop->value, pool)); 939289166Speter break; 940289166Speter case svn_node_dir: 941289166Speter SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton, 942289166Speter name, prop->value, pool)); 943289166Speter break; 944289166Speter default: 945289166Speter break; 946289166Speter } 947289166Speter } 948289166Speter 949251881Speter /* Pass a file node closure through to the editor *unless* we 950251881Speter deleted the file (which doesn't require us to open it). */ 951251881Speter if ((nb->kind == svn_node_file) && (nb->file_baton)) 952251881Speter { 953251881Speter SVN_ERR(commit_editor->close_file(nb->file_baton, NULL, nb->rb->pool)); 954251881Speter } 955251881Speter 956251881Speter /* The svn_node_dir case is handled in close_revision */ 957251881Speter 958251881Speter return SVN_NO_ERROR; 959251881Speter} 960251881Speter 961251881Speterstatic svn_error_t * 962251881Speterclose_revision(void *baton) 963251881Speter{ 964251881Speter struct revision_baton *rb = baton; 965251881Speter const svn_delta_editor_t *commit_editor = rb->pb->commit_editor; 966251881Speter void *commit_edit_baton = rb->pb->commit_edit_baton; 967251881Speter svn_revnum_t committed_rev = SVN_INVALID_REVNUM; 968251881Speter 969251881Speter /* Fake revision 0 */ 970251881Speter if (rb->rev == 0) 971251881Speter { 972251881Speter /* ### Don't print directly; generate a notification. */ 973251881Speter if (! rb->pb->quiet) 974251881Speter SVN_ERR(svn_cmdline_printf(rb->pool, "* Loaded revision 0.\n")); 975251881Speter } 976251881Speter else if (commit_editor) 977251881Speter { 978251881Speter /* Close all pending open directories, and then close the edit 979251881Speter session itself */ 980251881Speter while (rb->db && rb->db->parent) 981251881Speter { 982251881Speter SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool)); 983251881Speter rb->db = rb->db->parent; 984251881Speter } 985251881Speter /* root dir's baton */ 986251881Speter SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool)); 987251881Speter SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool)); 988251881Speter } 989251881Speter else 990251881Speter { 991251881Speter void *child_baton; 992251881Speter 993251881Speter /* Legitimate revision with no node information */ 994251881Speter SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor, 995251881Speter &commit_edit_baton, rb->revprop_table, 996251881Speter commit_callback, baton, 997251881Speter NULL, FALSE, rb->pool)); 998251881Speter 999251881Speter SVN_ERR(commit_editor->open_root(commit_edit_baton, 1000251881Speter rb->rev - rb->rev_offset - 1, 1001251881Speter rb->pool, &child_baton)); 1002251881Speter 1003251881Speter SVN_ERR(commit_editor->close_directory(child_baton, rb->pool)); 1004251881Speter SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool)); 1005251881Speter } 1006251881Speter 1007251881Speter /* svn_fs_commit_txn() rewrites the datestamp and author properties; 1008251881Speter we'll rewrite them again by hand after closing the commit_editor. 1009251881Speter The only time we don't do this is for revision 0 when loaded into 1010251881Speter a non-empty repository. */ 1011251881Speter if (rb->rev > 0) 1012251881Speter { 1013251881Speter committed_rev = get_revision_mapping(rb->pb->rev_map, rb->rev); 1014251881Speter } 1015251881Speter else if (rb->rev_offset == -1) 1016251881Speter { 1017251881Speter committed_rev = 0; 1018251881Speter } 1019251881Speter 1020251881Speter if (SVN_IS_VALID_REVNUM(committed_rev)) 1021251881Speter { 1022299742Sdim if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_DATE)) 1023299742Sdim { 1024299742Sdim SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_DATE, 1025299742Sdim rb->datestamp, rb->pool)); 1026299742Sdim SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev, 1027299742Sdim SVN_PROP_REVISION_DATE, 1028299742Sdim NULL, rb->datestamp, rb->pool)); 1029299742Sdim } 1030299742Sdim if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_AUTHOR)) 1031299742Sdim { 1032299742Sdim SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_AUTHOR, 1033299742Sdim rb->author, rb->pool)); 1034299742Sdim SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev, 1035299742Sdim SVN_PROP_REVISION_AUTHOR, 1036299742Sdim NULL, rb->author, rb->pool)); 1037299742Sdim } 1038251881Speter } 1039251881Speter 1040251881Speter svn_pool_destroy(rb->pool); 1041251881Speter 1042251881Speter return SVN_NO_ERROR; 1043251881Speter} 1044251881Speter 1045251881Spetersvn_error_t * 1046251881Spetersvn_rdump__load_dumpstream(svn_stream_t *stream, 1047251881Speter svn_ra_session_t *session, 1048251881Speter svn_ra_session_t *aux_session, 1049251881Speter svn_boolean_t quiet, 1050299742Sdim apr_hash_t *skip_revprops, 1051251881Speter svn_cancel_func_t cancel_func, 1052251881Speter void *cancel_baton, 1053251881Speter apr_pool_t *pool) 1054251881Speter{ 1055251881Speter svn_repos_parse_fns3_t *parser; 1056251881Speter struct parse_baton *parse_baton; 1057251881Speter const svn_string_t *lock_string; 1058251881Speter svn_boolean_t be_atomic; 1059251881Speter svn_error_t *err; 1060251881Speter const char *session_url, *root_url, *parent_dir; 1061251881Speter 1062251881Speter SVN_ERR(svn_ra_has_capability(session, &be_atomic, 1063251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 1064251881Speter pool)); 1065251881Speter SVN_ERR(get_lock(&lock_string, session, cancel_func, cancel_baton, pool)); 1066251881Speter SVN_ERR(svn_ra_get_repos_root2(session, &root_url, pool)); 1067251881Speter SVN_ERR(svn_ra_get_session_url(session, &session_url, pool)); 1068251881Speter SVN_ERR(svn_ra_get_path_relative_to_root(session, &parent_dir, 1069251881Speter session_url, pool)); 1070251881Speter 1071251881Speter parser = apr_pcalloc(pool, sizeof(*parser)); 1072251881Speter parser->magic_header_record = magic_header_record; 1073251881Speter parser->uuid_record = uuid_record; 1074251881Speter parser->new_revision_record = new_revision_record; 1075251881Speter parser->new_node_record = new_node_record; 1076251881Speter parser->set_revision_property = set_revision_property; 1077251881Speter parser->set_node_property = set_node_property; 1078251881Speter parser->delete_node_property = delete_node_property; 1079251881Speter parser->remove_node_props = remove_node_props; 1080251881Speter parser->set_fulltext = set_fulltext; 1081251881Speter parser->apply_textdelta = apply_textdelta; 1082251881Speter parser->close_node = close_node; 1083251881Speter parser->close_revision = close_revision; 1084251881Speter 1085251881Speter parse_baton = apr_pcalloc(pool, sizeof(*parse_baton)); 1086251881Speter parse_baton->session = session; 1087251881Speter parse_baton->aux_session = aux_session; 1088251881Speter parse_baton->quiet = quiet; 1089251881Speter parse_baton->root_url = root_url; 1090251881Speter parse_baton->parent_dir = parent_dir; 1091251881Speter parse_baton->rev_map = apr_hash_make(pool); 1092251881Speter parse_baton->last_rev_mapped = SVN_INVALID_REVNUM; 1093251881Speter parse_baton->oldest_dumpstream_rev = SVN_INVALID_REVNUM; 1094299742Sdim parse_baton->skip_revprops = skip_revprops; 1095251881Speter 1096251881Speter err = svn_repos_parse_dumpstream3(stream, parser, parse_baton, FALSE, 1097251881Speter cancel_func, cancel_baton, pool); 1098251881Speter 1099251881Speter /* If all goes well, or if we're cancelled cleanly, don't leave a 1100251881Speter stray lock behind. */ 1101251881Speter if ((! err) || (err && (err->apr_err == SVN_ERR_CANCELLED))) 1102251881Speter err = svn_error_compose_create( 1103251881Speter svn_ra__release_operational_lock(session, SVNRDUMP_PROP_LOCK, 1104251881Speter lock_string, pool), 1105251881Speter err); 1106251881Speter return err; 1107251881Speter} 1108