sync.c revision 299742
1/* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 */ 21 22#include "svn_hash.h" 23#include "svn_cmdline.h" 24#include "svn_config.h" 25#include "svn_pools.h" 26#include "svn_delta.h" 27#include "svn_dirent_uri.h" 28#include "svn_path.h" 29#include "svn_props.h" 30#include "svn_auth.h" 31#include "svn_opt.h" 32#include "svn_ra.h" 33#include "svn_utf.h" 34#include "svn_subst.h" 35#include "svn_string.h" 36 37#include "private/svn_string_private.h" 38 39#include "sync.h" 40 41#include "svn_private_config.h" 42 43#include <apr_network_io.h> 44#include <apr_signal.h> 45#include <apr_uuid.h> 46 47 48/* Normalize the encoding and line ending style of *STR, so that it contains 49 * only LF (\n) line endings and is encoded in UTF-8. After return, *STR may 50 * point at a new svn_string_t* allocated in RESULT_POOL. 51 * 52 * If SOURCE_PROP_ENCODING is NULL, then *STR is presumed to be encoded in 53 * UTF-8. 54 * 55 * *WAS_NORMALIZED is set to TRUE when *STR needed line ending normalization. 56 * Otherwise it is set to FALSE. 57 * 58 * SCRATCH_POOL is used for temporary allocations. 59 */ 60static svn_error_t * 61normalize_string(const svn_string_t **str, 62 svn_boolean_t *was_normalized, 63 const char *source_prop_encoding, 64 apr_pool_t *result_pool, 65 apr_pool_t *scratch_pool) 66{ 67 svn_string_t *new_str; 68 69 *was_normalized = FALSE; 70 71 if (*str == NULL) 72 return SVN_NO_ERROR; 73 74 SVN_ERR_ASSERT((*str)->data != NULL); 75 76 if (source_prop_encoding == NULL) 77 source_prop_encoding = "UTF-8"; 78 79 new_str = NULL; 80 SVN_ERR(svn_subst_translate_string2(&new_str, NULL, was_normalized, 81 *str, source_prop_encoding, TRUE, 82 result_pool, scratch_pool)); 83 *str = new_str; 84 85 return SVN_NO_ERROR; 86} 87 88/* Remove r0 references from the mergeinfo string *STR. 89 * 90 * r0 was never a valid mergeinfo reference and cannot be committed with 91 * recent servers, but can be committed through a server older than 1.6.18 92 * for HTTP or older than 1.6.17 for the other protocols. See issue #4476 93 * "Mergeinfo containing r0 makes svnsync and dump and load fail". 94 * 95 * Set *WAS_CHANGED to TRUE if *STR was changed, otherwise to FALSE. 96 */ 97static svn_error_t * 98remove_r0_mergeinfo(const svn_string_t **str, 99 svn_boolean_t *was_changed, 100 apr_pool_t *result_pool, 101 apr_pool_t *scratch_pool) 102{ 103 svn_stringbuf_t *new_str = svn_stringbuf_create_empty(result_pool); 104 apr_array_header_t *lines; 105 int i; 106 107 SVN_ERR_ASSERT(*str && (*str)->data); 108 109 *was_changed = FALSE; 110 111 /* for each line */ 112 lines = svn_cstring_split((*str)->data, "\n", FALSE, scratch_pool); 113 114 for (i = 0; i < lines->nelts; i++) 115 { 116 char *line = APR_ARRAY_IDX(lines, i, char *); 117 char *colon; 118 char *rangelist; 119 120 /* split at the last colon */ 121 colon = strrchr(line, ':'); 122 123 if (! colon) 124 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 125 _("Missing colon in svn:mergeinfo " 126 "property")); 127 128 rangelist = colon + 1; 129 130 /* remove r0 */ 131 if (colon[1] == '0') 132 { 133 if (strncmp(rangelist, "0*,", 3) == 0) 134 { 135 rangelist += 3; 136 } 137 else if (strcmp(rangelist, "0*") == 0 138 || strncmp(rangelist, "0,", 2) == 0 139 || strncmp(rangelist, "0-1*", 4) == 0 140 || strncmp(rangelist, "0-1,", 4) == 0 141 || strcmp(rangelist, "0-1") == 0) 142 { 143 rangelist += 2; 144 } 145 else if (strcmp(rangelist, "0") == 0) 146 { 147 rangelist += 1; 148 } 149 else if (strncmp(rangelist, "0-", 2) == 0) 150 { 151 rangelist[0] = '1'; 152 } 153 } 154 155 /* reassemble */ 156 if (rangelist[0]) 157 { 158 if (new_str->len) 159 svn_stringbuf_appendbyte(new_str, '\n'); 160 svn_stringbuf_appendbytes(new_str, line, colon + 1 - line); 161 svn_stringbuf_appendcstr(new_str, rangelist); 162 } 163 } 164 165 if (strcmp((*str)->data, new_str->data) != 0) 166 { 167 *was_changed = TRUE; 168 } 169 170 *str = svn_stringbuf__morph_into_string(new_str); 171 return SVN_NO_ERROR; 172} 173 174 175/* Normalize the encoding and line ending style of the values of properties 176 * in REV_PROPS that "need translation" (according to 177 * svn_prop_needs_translation(), which is currently all svn:* props) so that 178 * they are encoded in UTF-8 and contain only LF (\n) line endings. 179 * 180 * The number of properties that needed line ending normalization is returned in 181 * *NORMALIZED_COUNT. 182 * 183 * No re-encoding is performed if SOURCE_PROP_ENCODING is NULL. 184 */ 185svn_error_t * 186svnsync_normalize_revprops(apr_hash_t *rev_props, 187 int *normalized_count, 188 const char *source_prop_encoding, 189 apr_pool_t *pool) 190{ 191 apr_hash_index_t *hi; 192 *normalized_count = 0; 193 194 for (hi = apr_hash_first(pool, rev_props); 195 hi; 196 hi = apr_hash_next(hi)) 197 { 198 const char *propname = apr_hash_this_key(hi); 199 const svn_string_t *propval = apr_hash_this_val(hi); 200 201 if (svn_prop_needs_translation(propname)) 202 { 203 svn_boolean_t was_normalized; 204 SVN_ERR(normalize_string(&propval, &was_normalized, 205 source_prop_encoding, pool, pool)); 206 207 /* Replace the existing prop value. */ 208 svn_hash_sets(rev_props, propname, propval); 209 210 if (was_normalized) 211 (*normalized_count)++; /* Count it. */ 212 } 213 } 214 return SVN_NO_ERROR; 215} 216 217 218/*** Synchronization Editor ***/ 219 220/* This editor has a couple of jobs. 221 * 222 * First, it needs to filter out the propchanges that can't be passed over 223 * libsvn_ra. 224 * 225 * Second, it needs to adjust for the fact that we might not actually have 226 * permission to see all of the data from the remote repository, which means 227 * we could get revisions that are totally empty from our point of view. 228 * 229 * Third, it needs to adjust copyfrom paths, adding the root url for the 230 * destination repository to the beginning of them. 231 */ 232 233 234/* Edit baton */ 235typedef struct edit_baton_t { 236 const svn_delta_editor_t *wrapped_editor; 237 void *wrapped_edit_baton; 238 const char *to_url; /* URL we're copying into, for correct copyfrom URLs */ 239 const char *source_prop_encoding; 240 svn_boolean_t called_open_root; 241 svn_boolean_t got_textdeltas; 242 svn_revnum_t base_revision; 243 svn_boolean_t quiet; 244 svn_boolean_t mergeinfo_tweaked; /* Did we tweak svn:mergeinfo? */ 245 svn_boolean_t strip_mergeinfo; /* Are we stripping svn:mergeinfo? */ 246 svn_boolean_t migrate_svnmerge; /* Are we converting svnmerge.py data? */ 247 svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */ 248 svn_boolean_t svnmerge_migrated; /* Did we convert svnmerge.py data? */ 249 svn_boolean_t svnmerge_blocked; /* Was there any blocked svnmerge data? */ 250 int *normalized_node_props_counter; /* Where to count normalizations? */ 251} edit_baton_t; 252 253 254/* A dual-purpose baton for files and directories. */ 255typedef struct node_baton_t { 256 void *edit_baton; 257 void *wrapped_node_baton; 258} node_baton_t; 259 260 261/*** Editor vtable functions ***/ 262 263static svn_error_t * 264set_target_revision(void *edit_baton, 265 svn_revnum_t target_revision, 266 apr_pool_t *pool) 267{ 268 edit_baton_t *eb = edit_baton; 269 return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton, 270 target_revision, pool); 271} 272 273static svn_error_t * 274open_root(void *edit_baton, 275 svn_revnum_t base_revision, 276 apr_pool_t *pool, 277 void **root_baton) 278{ 279 edit_baton_t *eb = edit_baton; 280 node_baton_t *dir_baton = apr_palloc(pool, sizeof(*dir_baton)); 281 282 SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, 283 base_revision, pool, 284 &dir_baton->wrapped_node_baton)); 285 286 eb->called_open_root = TRUE; 287 dir_baton->edit_baton = edit_baton; 288 *root_baton = dir_baton; 289 290 return SVN_NO_ERROR; 291} 292 293static svn_error_t * 294delete_entry(const char *path, 295 svn_revnum_t base_revision, 296 void *parent_baton, 297 apr_pool_t *pool) 298{ 299 node_baton_t *pb = parent_baton; 300 edit_baton_t *eb = pb->edit_baton; 301 302 return eb->wrapped_editor->delete_entry(path, base_revision, 303 pb->wrapped_node_baton, pool); 304} 305 306static svn_error_t * 307add_directory(const char *path, 308 void *parent_baton, 309 const char *copyfrom_path, 310 svn_revnum_t copyfrom_rev, 311 apr_pool_t *pool, 312 void **child_baton) 313{ 314 node_baton_t *pb = parent_baton; 315 edit_baton_t *eb = pb->edit_baton; 316 node_baton_t *b = apr_palloc(pool, sizeof(*b)); 317 318 /* if copyfrom_path is an fspath create a proper uri */ 319 if (copyfrom_path && copyfrom_path[0] == '/') 320 copyfrom_path = svn_path_url_add_component2(eb->to_url, 321 copyfrom_path + 1, pool); 322 323 SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_node_baton, 324 copyfrom_path, 325 copyfrom_rev, pool, 326 &b->wrapped_node_baton)); 327 328 b->edit_baton = eb; 329 *child_baton = b; 330 331 return SVN_NO_ERROR; 332} 333 334static svn_error_t * 335open_directory(const char *path, 336 void *parent_baton, 337 svn_revnum_t base_revision, 338 apr_pool_t *pool, 339 void **child_baton) 340{ 341 node_baton_t *pb = parent_baton; 342 edit_baton_t *eb = pb->edit_baton; 343 node_baton_t *db = apr_palloc(pool, sizeof(*db)); 344 345 SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_node_baton, 346 base_revision, pool, 347 &db->wrapped_node_baton)); 348 349 db->edit_baton = eb; 350 *child_baton = db; 351 352 return SVN_NO_ERROR; 353} 354 355static svn_error_t * 356add_file(const char *path, 357 void *parent_baton, 358 const char *copyfrom_path, 359 svn_revnum_t copyfrom_rev, 360 apr_pool_t *pool, 361 void **file_baton) 362{ 363 node_baton_t *pb = parent_baton; 364 edit_baton_t *eb = pb->edit_baton; 365 node_baton_t *fb = apr_palloc(pool, sizeof(*fb)); 366 367 /* if copyfrom_path is an fspath create a proper uri */ 368 if (copyfrom_path && copyfrom_path[0] == '/') 369 copyfrom_path = svn_path_url_add_component2(eb->to_url, 370 copyfrom_path + 1, pool); 371 372 SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_node_baton, 373 copyfrom_path, copyfrom_rev, 374 pool, &fb->wrapped_node_baton)); 375 376 fb->edit_baton = eb; 377 *file_baton = fb; 378 379 return SVN_NO_ERROR; 380} 381 382static svn_error_t * 383open_file(const char *path, 384 void *parent_baton, 385 svn_revnum_t base_revision, 386 apr_pool_t *pool, 387 void **file_baton) 388{ 389 node_baton_t *pb = parent_baton; 390 edit_baton_t *eb = pb->edit_baton; 391 node_baton_t *fb = apr_palloc(pool, sizeof(*fb)); 392 393 SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_node_baton, 394 base_revision, pool, 395 &fb->wrapped_node_baton)); 396 397 fb->edit_baton = eb; 398 *file_baton = fb; 399 400 return SVN_NO_ERROR; 401} 402 403static svn_error_t * 404apply_textdelta(void *file_baton, 405 const char *base_checksum, 406 apr_pool_t *pool, 407 svn_txdelta_window_handler_t *handler, 408 void **handler_baton) 409{ 410 node_baton_t *fb = file_baton; 411 edit_baton_t *eb = fb->edit_baton; 412 413 if (! eb->quiet) 414 { 415 if (! eb->got_textdeltas) 416 SVN_ERR(svn_cmdline_printf(pool, _("Transmitting file data "))); 417 SVN_ERR(svn_cmdline_printf(pool, ".")); 418 SVN_ERR(svn_cmdline_fflush(stdout)); 419 } 420 421 eb->got_textdeltas = TRUE; 422 return eb->wrapped_editor->apply_textdelta(fb->wrapped_node_baton, 423 base_checksum, pool, 424 handler, handler_baton); 425} 426 427static svn_error_t * 428close_file(void *file_baton, 429 const char *text_checksum, 430 apr_pool_t *pool) 431{ 432 node_baton_t *fb = file_baton; 433 edit_baton_t *eb = fb->edit_baton; 434 return eb->wrapped_editor->close_file(fb->wrapped_node_baton, 435 text_checksum, pool); 436} 437 438static svn_error_t * 439absent_file(const char *path, 440 void *file_baton, 441 apr_pool_t *pool) 442{ 443 node_baton_t *fb = file_baton; 444 edit_baton_t *eb = fb->edit_baton; 445 return eb->wrapped_editor->absent_file(path, fb->wrapped_node_baton, pool); 446} 447 448static svn_error_t * 449close_directory(void *dir_baton, 450 apr_pool_t *pool) 451{ 452 node_baton_t *db = dir_baton; 453 edit_baton_t *eb = db->edit_baton; 454 return eb->wrapped_editor->close_directory(db->wrapped_node_baton, pool); 455} 456 457static svn_error_t * 458absent_directory(const char *path, 459 void *dir_baton, 460 apr_pool_t *pool) 461{ 462 node_baton_t *db = dir_baton; 463 edit_baton_t *eb = db->edit_baton; 464 return eb->wrapped_editor->absent_directory(path, db->wrapped_node_baton, 465 pool); 466} 467 468static svn_error_t * 469change_file_prop(void *file_baton, 470 const char *name, 471 const svn_string_t *value, 472 apr_pool_t *pool) 473{ 474 node_baton_t *fb = file_baton; 475 edit_baton_t *eb = fb->edit_baton; 476 477 /* only regular properties can pass over libsvn_ra */ 478 if (svn_property_kind2(name) != svn_prop_regular_kind) 479 return SVN_NO_ERROR; 480 481 /* Maybe drop svn:mergeinfo. */ 482 if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0)) 483 { 484 eb->mergeinfo_stripped = TRUE; 485 return SVN_NO_ERROR; 486 } 487 488 /* Maybe drop (errantly set, as this is a file) svnmerge.py properties. */ 489 if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0)) 490 { 491 eb->svnmerge_migrated = TRUE; 492 return SVN_NO_ERROR; 493 } 494 495 /* Remember if we see any svnmerge-blocked properties. (They really 496 shouldn't be here, as this is a file, but whatever...) */ 497 if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0)) 498 { 499 eb->svnmerge_blocked = TRUE; 500 } 501 502 /* Normalize svn:* properties as necessary. */ 503 if (svn_prop_needs_translation(name)) 504 { 505 svn_boolean_t was_normalized; 506 svn_boolean_t mergeinfo_tweaked = FALSE; 507 508 /* Normalize encoding to UTF-8, and EOL style to LF. */ 509 SVN_ERR(normalize_string(&value, &was_normalized, 510 eb->source_prop_encoding, pool, pool)); 511 /* Correct malformed mergeinfo. */ 512 if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) 513 { 514 SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked, 515 pool, pool)); 516 if (mergeinfo_tweaked) 517 eb->mergeinfo_tweaked = TRUE; 518 } 519 if (was_normalized) 520 (*(eb->normalized_node_props_counter))++; 521 } 522 523 return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton, 524 name, value, pool); 525} 526 527static svn_error_t * 528change_dir_prop(void *dir_baton, 529 const char *name, 530 const svn_string_t *value, 531 apr_pool_t *pool) 532{ 533 node_baton_t *db = dir_baton; 534 edit_baton_t *eb = db->edit_baton; 535 536 /* Only regular properties can pass over libsvn_ra */ 537 if (svn_property_kind2(name) != svn_prop_regular_kind) 538 return SVN_NO_ERROR; 539 540 /* Maybe drop svn:mergeinfo. */ 541 if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0)) 542 { 543 eb->mergeinfo_stripped = TRUE; 544 return SVN_NO_ERROR; 545 } 546 547 /* Maybe convert svnmerge-integrated data into svn:mergeinfo. (We 548 ignore svnmerge-blocked for now.) */ 549 /* ### FIXME: Consult the mirror repository's HEAD prop values and 550 ### merge svn:mergeinfo, svnmerge-integrated, and svnmerge-blocked. */ 551 if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0)) 552 { 553 if (value) 554 { 555 /* svnmerge-integrated differs from svn:mergeinfo in a pair 556 of ways. First, it can use tabs, newlines, or spaces to 557 delimit source information. Secondly, the source paths 558 are relative URLs, whereas svn:mergeinfo uses relative 559 paths (not URI-encoded). */ 560 svn_error_t *err; 561 svn_stringbuf_t *mergeinfo_buf = svn_stringbuf_create_empty(pool); 562 svn_mergeinfo_t mergeinfo; 563 int i; 564 apr_array_header_t *sources = 565 svn_cstring_split(value->data, " \t\n", TRUE, pool); 566 svn_string_t *new_value; 567 568 for (i = 0; i < sources->nelts; i++) 569 { 570 const char *rel_path; 571 apr_array_header_t *path_revs = 572 svn_cstring_split(APR_ARRAY_IDX(sources, i, const char *), 573 ":", TRUE, pool); 574 575 /* ### TODO: Warn? */ 576 if (path_revs->nelts != 2) 577 continue; 578 579 /* Append this source's mergeinfo data. */ 580 rel_path = APR_ARRAY_IDX(path_revs, 0, const char *); 581 rel_path = svn_path_uri_decode(rel_path, pool); 582 svn_stringbuf_appendcstr(mergeinfo_buf, rel_path); 583 svn_stringbuf_appendcstr(mergeinfo_buf, ":"); 584 svn_stringbuf_appendcstr(mergeinfo_buf, 585 APR_ARRAY_IDX(path_revs, 1, 586 const char *)); 587 svn_stringbuf_appendcstr(mergeinfo_buf, "\n"); 588 } 589 590 /* Try to parse the mergeinfo string we've created, just to 591 check for bogosity. If all goes well, we'll unparse it 592 again and use that as our property value. */ 593 err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_buf->data, pool); 594 if (err) 595 { 596 svn_error_clear(err); 597 return SVN_NO_ERROR; 598 } 599 SVN_ERR(svn_mergeinfo_to_string(&new_value, mergeinfo, pool)); 600 value = new_value; 601 } 602 name = SVN_PROP_MERGEINFO; 603 eb->svnmerge_migrated = TRUE; 604 } 605 606 /* Remember if we see any svnmerge-blocked properties. */ 607 if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0)) 608 { 609 eb->svnmerge_blocked = TRUE; 610 } 611 612 /* Normalize svn:* properties as necessary. */ 613 if (svn_prop_needs_translation(name)) 614 { 615 svn_boolean_t was_normalized; 616 svn_boolean_t mergeinfo_tweaked = FALSE; 617 618 /* Normalize encoding to UTF-8, and EOL style to LF. */ 619 SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding, 620 pool, pool)); 621 /* Maybe adjust svn:mergeinfo. */ 622 if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) 623 { 624 SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked, 625 pool, pool)); 626 if (mergeinfo_tweaked) 627 eb->mergeinfo_tweaked = TRUE; 628 } 629 if (was_normalized) 630 (*(eb->normalized_node_props_counter))++; 631 } 632 633 return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton, 634 name, value, pool); 635} 636 637static svn_error_t * 638close_edit(void *edit_baton, 639 apr_pool_t *pool) 640{ 641 edit_baton_t *eb = edit_baton; 642 643 /* If we haven't opened the root yet, that means we're transfering 644 an empty revision, probably because we aren't allowed to see the 645 contents for some reason. In any event, we need to open the root 646 and close it again, before we can close out the edit, or the 647 commit will fail. */ 648 649 if (! eb->called_open_root) 650 { 651 void *baton; 652 SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, 653 eb->base_revision, pool, 654 &baton)); 655 SVN_ERR(eb->wrapped_editor->close_directory(baton, pool)); 656 } 657 658 if (! eb->quiet) 659 { 660 if (eb->got_textdeltas) 661 SVN_ERR(svn_cmdline_printf(pool, "\n")); 662 if (eb->mergeinfo_tweaked) 663 SVN_ERR(svn_cmdline_printf(pool, 664 "NOTE: Adjusted Subversion mergeinfo in " 665 "this revision.\n")); 666 if (eb->mergeinfo_stripped) 667 SVN_ERR(svn_cmdline_printf(pool, 668 "NOTE: Dropped Subversion mergeinfo " 669 "from this revision.\n")); 670 if (eb->svnmerge_migrated) 671 SVN_ERR(svn_cmdline_printf(pool, 672 "NOTE: Migrated 'svnmerge-integrated' in " 673 "this revision.\n")); 674 if (eb->svnmerge_blocked) 675 SVN_ERR(svn_cmdline_printf(pool, 676 "NOTE: Saw 'svnmerge-blocked' in this " 677 "revision (but didn't migrate it).\n")); 678 } 679 680 return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool); 681} 682 683static svn_error_t * 684abort_edit(void *edit_baton, 685 apr_pool_t *pool) 686{ 687 edit_baton_t *eb = edit_baton; 688 return eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool); 689} 690 691 692/*** Editor factory function ***/ 693 694svn_error_t * 695svnsync_get_sync_editor(const svn_delta_editor_t *wrapped_editor, 696 void *wrapped_edit_baton, 697 svn_revnum_t base_revision, 698 const char *to_url, 699 const char *source_prop_encoding, 700 svn_boolean_t quiet, 701 const svn_delta_editor_t **editor, 702 void **edit_baton, 703 int *normalized_node_props_counter, 704 apr_pool_t *pool) 705{ 706 svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool); 707 edit_baton_t *eb = apr_pcalloc(pool, sizeof(*eb)); 708 709 tree_editor->set_target_revision = set_target_revision; 710 tree_editor->open_root = open_root; 711 tree_editor->delete_entry = delete_entry; 712 tree_editor->add_directory = add_directory; 713 tree_editor->open_directory = open_directory; 714 tree_editor->change_dir_prop = change_dir_prop; 715 tree_editor->close_directory = close_directory; 716 tree_editor->absent_directory = absent_directory; 717 tree_editor->add_file = add_file; 718 tree_editor->open_file = open_file; 719 tree_editor->apply_textdelta = apply_textdelta; 720 tree_editor->change_file_prop = change_file_prop; 721 tree_editor->close_file = close_file; 722 tree_editor->absent_file = absent_file; 723 tree_editor->close_edit = close_edit; 724 tree_editor->abort_edit = abort_edit; 725 726 eb->wrapped_editor = wrapped_editor; 727 eb->wrapped_edit_baton = wrapped_edit_baton; 728 eb->base_revision = base_revision; 729 eb->to_url = to_url; 730 eb->source_prop_encoding = source_prop_encoding; 731 eb->quiet = quiet; 732 eb->normalized_node_props_counter = normalized_node_props_counter; 733 734 if (getenv("SVNSYNC_UNSUPPORTED_STRIP_MERGEINFO")) 735 { 736 eb->strip_mergeinfo = TRUE; 737 } 738 if (getenv("SVNSYNC_UNSUPPORTED_MIGRATE_SVNMERGE")) 739 { 740 /* Current we can't merge property values. That's only possible 741 if all the properties to be merged were always modified in 742 exactly the same revisions, or if we allow ourselves to 743 lookup the current state of properties in the sync 744 destination. So for now, migrating svnmerge.py data implies 745 stripping pre-existing svn:mergeinfo. */ 746 /* ### FIXME: Do a real migration by consulting the mirror 747 ### repository's HEAD propvalues and merging svn:mergeinfo, 748 ### svnmerge-integrated, and svnmerge-blocked together. */ 749 eb->migrate_svnmerge = TRUE; 750 eb->strip_mergeinfo = TRUE; 751 } 752 753 *editor = tree_editor; 754 *edit_baton = eb; 755 756 return SVN_NO_ERROR; 757} 758 759