1251881Speter/* 2251881Speter * ==================================================================== 3251881Speter * Licensed to the Apache Software Foundation (ASF) under one 4251881Speter * or more contributor license agreements. See the NOTICE file 5251881Speter * distributed with this work for additional information 6251881Speter * regarding copyright ownership. The ASF licenses this file 7251881Speter * to you under the Apache License, Version 2.0 (the 8251881Speter * "License"); you may not use this file except in compliance 9251881Speter * with the License. You may obtain a copy of the License at 10251881Speter * 11251881Speter * http://www.apache.org/licenses/LICENSE-2.0 12251881Speter * 13251881Speter * Unless required by applicable law or agreed to in writing, 14251881Speter * software distributed under the License is distributed on an 15251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16251881Speter * KIND, either express or implied. See the License for the 17251881Speter * specific language governing permissions and limitations 18251881Speter * under the License. 19251881Speter * ==================================================================== 20251881Speter */ 21251881Speter 22251881Speter#include "svn_hash.h" 23251881Speter#include "svn_cmdline.h" 24251881Speter#include "svn_config.h" 25251881Speter#include "svn_pools.h" 26251881Speter#include "svn_delta.h" 27251881Speter#include "svn_dirent_uri.h" 28251881Speter#include "svn_path.h" 29251881Speter#include "svn_props.h" 30251881Speter#include "svn_auth.h" 31251881Speter#include "svn_opt.h" 32251881Speter#include "svn_ra.h" 33251881Speter#include "svn_utf.h" 34251881Speter#include "svn_subst.h" 35251881Speter#include "svn_string.h" 36251881Speter 37251881Speter#include "sync.h" 38251881Speter 39251881Speter#include "svn_private_config.h" 40251881Speter 41251881Speter#include <apr_network_io.h> 42251881Speter#include <apr_signal.h> 43251881Speter#include <apr_uuid.h> 44251881Speter 45251881Speter 46251881Speter/* Normalize the encoding and line ending style of *STR, so that it contains 47251881Speter * only LF (\n) line endings and is encoded in UTF-8. After return, *STR may 48251881Speter * point at a new svn_string_t* allocated in RESULT_POOL. 49251881Speter * 50251881Speter * If SOURCE_PROP_ENCODING is NULL, then *STR is presumed to be encoded in 51251881Speter * UTF-8. 52251881Speter * 53251881Speter * *WAS_NORMALIZED is set to TRUE when *STR needed line ending normalization. 54251881Speter * Otherwise it is set to FALSE. 55251881Speter * 56251881Speter * SCRATCH_POOL is used for temporary allocations. 57251881Speter */ 58251881Speterstatic svn_error_t * 59251881Speternormalize_string(const svn_string_t **str, 60251881Speter svn_boolean_t *was_normalized, 61251881Speter const char *source_prop_encoding, 62251881Speter apr_pool_t *result_pool, 63251881Speter apr_pool_t *scratch_pool) 64251881Speter{ 65251881Speter svn_string_t *new_str; 66251881Speter 67251881Speter *was_normalized = FALSE; 68251881Speter 69251881Speter if (*str == NULL) 70251881Speter return SVN_NO_ERROR; 71251881Speter 72251881Speter SVN_ERR_ASSERT((*str)->data != NULL); 73251881Speter 74251881Speter if (source_prop_encoding == NULL) 75251881Speter source_prop_encoding = "UTF-8"; 76251881Speter 77251881Speter new_str = NULL; 78251881Speter SVN_ERR(svn_subst_translate_string2(&new_str, NULL, was_normalized, 79251881Speter *str, source_prop_encoding, TRUE, 80251881Speter result_pool, scratch_pool)); 81251881Speter *str = new_str; 82251881Speter 83251881Speter return SVN_NO_ERROR; 84251881Speter} 85251881Speter 86251881Speter 87251881Speter/* Normalize the encoding and line ending style of the values of properties 88251881Speter * in REV_PROPS that "need translation" (according to 89251881Speter * svn_prop_needs_translation(), which is currently all svn:* props) so that 90251881Speter * they are encoded in UTF-8 and contain only LF (\n) line endings. 91251881Speter * 92251881Speter * The number of properties that needed line ending normalization is returned in 93251881Speter * *NORMALIZED_COUNT. 94251881Speter * 95251881Speter * No re-encoding is performed if SOURCE_PROP_ENCODING is NULL. 96251881Speter */ 97251881Spetersvn_error_t * 98251881Spetersvnsync_normalize_revprops(apr_hash_t *rev_props, 99251881Speter int *normalized_count, 100251881Speter const char *source_prop_encoding, 101251881Speter apr_pool_t *pool) 102251881Speter{ 103251881Speter apr_hash_index_t *hi; 104251881Speter *normalized_count = 0; 105251881Speter 106251881Speter for (hi = apr_hash_first(pool, rev_props); 107251881Speter hi; 108251881Speter hi = apr_hash_next(hi)) 109251881Speter { 110251881Speter const char *propname = svn__apr_hash_index_key(hi); 111251881Speter const svn_string_t *propval = svn__apr_hash_index_val(hi); 112251881Speter 113251881Speter if (svn_prop_needs_translation(propname)) 114251881Speter { 115251881Speter svn_boolean_t was_normalized; 116251881Speter SVN_ERR(normalize_string(&propval, &was_normalized, 117251881Speter source_prop_encoding, pool, pool)); 118251881Speter 119251881Speter /* Replace the existing prop value. */ 120251881Speter svn_hash_sets(rev_props, propname, propval); 121251881Speter 122251881Speter if (was_normalized) 123251881Speter (*normalized_count)++; /* Count it. */ 124251881Speter } 125251881Speter } 126251881Speter return SVN_NO_ERROR; 127251881Speter} 128251881Speter 129251881Speter 130251881Speter/*** Synchronization Editor ***/ 131251881Speter 132251881Speter/* This editor has a couple of jobs. 133251881Speter * 134251881Speter * First, it needs to filter out the propchanges that can't be passed over 135251881Speter * libsvn_ra. 136251881Speter * 137251881Speter * Second, it needs to adjust for the fact that we might not actually have 138251881Speter * permission to see all of the data from the remote repository, which means 139251881Speter * we could get revisions that are totally empty from our point of view. 140251881Speter * 141251881Speter * Third, it needs to adjust copyfrom paths, adding the root url for the 142251881Speter * destination repository to the beginning of them. 143251881Speter */ 144251881Speter 145251881Speter 146251881Speter/* Edit baton */ 147251881Spetertypedef struct edit_baton_t { 148251881Speter const svn_delta_editor_t *wrapped_editor; 149251881Speter void *wrapped_edit_baton; 150251881Speter const char *to_url; /* URL we're copying into, for correct copyfrom URLs */ 151251881Speter const char *source_prop_encoding; 152251881Speter svn_boolean_t called_open_root; 153251881Speter svn_boolean_t got_textdeltas; 154251881Speter svn_revnum_t base_revision; 155251881Speter svn_boolean_t quiet; 156251881Speter svn_boolean_t strip_mergeinfo; /* Are we stripping svn:mergeinfo? */ 157251881Speter svn_boolean_t migrate_svnmerge; /* Are we converting svnmerge.py data? */ 158251881Speter svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */ 159251881Speter svn_boolean_t svnmerge_migrated; /* Did we convert svnmerge.py data? */ 160251881Speter svn_boolean_t svnmerge_blocked; /* Was there any blocked svnmerge data? */ 161251881Speter int *normalized_node_props_counter; /* Where to count normalizations? */ 162251881Speter} edit_baton_t; 163251881Speter 164251881Speter 165251881Speter/* A dual-purpose baton for files and directories. */ 166251881Spetertypedef struct node_baton_t { 167251881Speter void *edit_baton; 168251881Speter void *wrapped_node_baton; 169251881Speter} node_baton_t; 170251881Speter 171251881Speter 172251881Speter/*** Editor vtable functions ***/ 173251881Speter 174251881Speterstatic svn_error_t * 175251881Speterset_target_revision(void *edit_baton, 176251881Speter svn_revnum_t target_revision, 177251881Speter apr_pool_t *pool) 178251881Speter{ 179251881Speter edit_baton_t *eb = edit_baton; 180251881Speter return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton, 181251881Speter target_revision, pool); 182251881Speter} 183251881Speter 184251881Speterstatic svn_error_t * 185251881Speteropen_root(void *edit_baton, 186251881Speter svn_revnum_t base_revision, 187251881Speter apr_pool_t *pool, 188251881Speter void **root_baton) 189251881Speter{ 190251881Speter edit_baton_t *eb = edit_baton; 191251881Speter node_baton_t *dir_baton = apr_palloc(pool, sizeof(*dir_baton)); 192251881Speter 193251881Speter SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, 194251881Speter base_revision, pool, 195251881Speter &dir_baton->wrapped_node_baton)); 196251881Speter 197251881Speter eb->called_open_root = TRUE; 198251881Speter dir_baton->edit_baton = edit_baton; 199251881Speter *root_baton = dir_baton; 200251881Speter 201251881Speter return SVN_NO_ERROR; 202251881Speter} 203251881Speter 204251881Speterstatic svn_error_t * 205251881Speterdelete_entry(const char *path, 206251881Speter svn_revnum_t base_revision, 207251881Speter void *parent_baton, 208251881Speter apr_pool_t *pool) 209251881Speter{ 210251881Speter node_baton_t *pb = parent_baton; 211251881Speter edit_baton_t *eb = pb->edit_baton; 212251881Speter 213251881Speter return eb->wrapped_editor->delete_entry(path, base_revision, 214251881Speter pb->wrapped_node_baton, pool); 215251881Speter} 216251881Speter 217251881Speterstatic svn_error_t * 218251881Speteradd_directory(const char *path, 219251881Speter void *parent_baton, 220251881Speter const char *copyfrom_path, 221251881Speter svn_revnum_t copyfrom_rev, 222251881Speter apr_pool_t *pool, 223251881Speter void **child_baton) 224251881Speter{ 225251881Speter node_baton_t *pb = parent_baton; 226251881Speter edit_baton_t *eb = pb->edit_baton; 227251881Speter node_baton_t *b = apr_palloc(pool, sizeof(*b)); 228251881Speter 229251881Speter /* if copyfrom_path is an fspath create a proper uri */ 230251881Speter if (copyfrom_path && copyfrom_path[0] == '/') 231251881Speter copyfrom_path = svn_path_url_add_component2(eb->to_url, 232251881Speter copyfrom_path + 1, pool); 233251881Speter 234251881Speter SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_node_baton, 235251881Speter copyfrom_path, 236251881Speter copyfrom_rev, pool, 237251881Speter &b->wrapped_node_baton)); 238251881Speter 239251881Speter b->edit_baton = eb; 240251881Speter *child_baton = b; 241251881Speter 242251881Speter return SVN_NO_ERROR; 243251881Speter} 244251881Speter 245251881Speterstatic svn_error_t * 246251881Speteropen_directory(const char *path, 247251881Speter void *parent_baton, 248251881Speter svn_revnum_t base_revision, 249251881Speter apr_pool_t *pool, 250251881Speter void **child_baton) 251251881Speter{ 252251881Speter node_baton_t *pb = parent_baton; 253251881Speter edit_baton_t *eb = pb->edit_baton; 254251881Speter node_baton_t *db = apr_palloc(pool, sizeof(*db)); 255251881Speter 256251881Speter SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_node_baton, 257251881Speter base_revision, pool, 258251881Speter &db->wrapped_node_baton)); 259251881Speter 260251881Speter db->edit_baton = eb; 261251881Speter *child_baton = db; 262251881Speter 263251881Speter return SVN_NO_ERROR; 264251881Speter} 265251881Speter 266251881Speterstatic svn_error_t * 267251881Speteradd_file(const char *path, 268251881Speter void *parent_baton, 269251881Speter const char *copyfrom_path, 270251881Speter svn_revnum_t copyfrom_rev, 271251881Speter apr_pool_t *pool, 272251881Speter void **file_baton) 273251881Speter{ 274251881Speter node_baton_t *pb = parent_baton; 275251881Speter edit_baton_t *eb = pb->edit_baton; 276251881Speter node_baton_t *fb = apr_palloc(pool, sizeof(*fb)); 277251881Speter 278251881Speter /* if copyfrom_path is an fspath create a proper uri */ 279251881Speter if (copyfrom_path && copyfrom_path[0] == '/') 280251881Speter copyfrom_path = svn_path_url_add_component2(eb->to_url, 281251881Speter copyfrom_path + 1, pool); 282251881Speter 283251881Speter SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_node_baton, 284251881Speter copyfrom_path, copyfrom_rev, 285251881Speter pool, &fb->wrapped_node_baton)); 286251881Speter 287251881Speter fb->edit_baton = eb; 288251881Speter *file_baton = fb; 289251881Speter 290251881Speter return SVN_NO_ERROR; 291251881Speter} 292251881Speter 293251881Speterstatic svn_error_t * 294251881Speteropen_file(const char *path, 295251881Speter void *parent_baton, 296251881Speter svn_revnum_t base_revision, 297251881Speter apr_pool_t *pool, 298251881Speter void **file_baton) 299251881Speter{ 300251881Speter node_baton_t *pb = parent_baton; 301251881Speter edit_baton_t *eb = pb->edit_baton; 302251881Speter node_baton_t *fb = apr_palloc(pool, sizeof(*fb)); 303251881Speter 304251881Speter SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_node_baton, 305251881Speter base_revision, pool, 306251881Speter &fb->wrapped_node_baton)); 307251881Speter 308251881Speter fb->edit_baton = eb; 309251881Speter *file_baton = fb; 310251881Speter 311251881Speter return SVN_NO_ERROR; 312251881Speter} 313251881Speter 314251881Speterstatic svn_error_t * 315251881Speterapply_textdelta(void *file_baton, 316251881Speter const char *base_checksum, 317251881Speter apr_pool_t *pool, 318251881Speter svn_txdelta_window_handler_t *handler, 319251881Speter void **handler_baton) 320251881Speter{ 321251881Speter node_baton_t *fb = file_baton; 322251881Speter edit_baton_t *eb = fb->edit_baton; 323251881Speter 324251881Speter if (! eb->quiet) 325251881Speter { 326251881Speter if (! eb->got_textdeltas) 327251881Speter SVN_ERR(svn_cmdline_printf(pool, _("Transmitting file data "))); 328251881Speter SVN_ERR(svn_cmdline_printf(pool, ".")); 329251881Speter SVN_ERR(svn_cmdline_fflush(stdout)); 330251881Speter } 331251881Speter 332251881Speter eb->got_textdeltas = TRUE; 333251881Speter return eb->wrapped_editor->apply_textdelta(fb->wrapped_node_baton, 334251881Speter base_checksum, pool, 335251881Speter handler, handler_baton); 336251881Speter} 337251881Speter 338251881Speterstatic svn_error_t * 339251881Speterclose_file(void *file_baton, 340251881Speter const char *text_checksum, 341251881Speter apr_pool_t *pool) 342251881Speter{ 343251881Speter node_baton_t *fb = file_baton; 344251881Speter edit_baton_t *eb = fb->edit_baton; 345251881Speter return eb->wrapped_editor->close_file(fb->wrapped_node_baton, 346251881Speter text_checksum, pool); 347251881Speter} 348251881Speter 349251881Speterstatic svn_error_t * 350251881Speterabsent_file(const char *path, 351251881Speter void *file_baton, 352251881Speter apr_pool_t *pool) 353251881Speter{ 354251881Speter node_baton_t *fb = file_baton; 355251881Speter edit_baton_t *eb = fb->edit_baton; 356251881Speter return eb->wrapped_editor->absent_file(path, fb->wrapped_node_baton, pool); 357251881Speter} 358251881Speter 359251881Speterstatic svn_error_t * 360251881Speterclose_directory(void *dir_baton, 361251881Speter apr_pool_t *pool) 362251881Speter{ 363251881Speter node_baton_t *db = dir_baton; 364251881Speter edit_baton_t *eb = db->edit_baton; 365251881Speter return eb->wrapped_editor->close_directory(db->wrapped_node_baton, pool); 366251881Speter} 367251881Speter 368251881Speterstatic svn_error_t * 369251881Speterabsent_directory(const char *path, 370251881Speter void *dir_baton, 371251881Speter apr_pool_t *pool) 372251881Speter{ 373251881Speter node_baton_t *db = dir_baton; 374251881Speter edit_baton_t *eb = db->edit_baton; 375251881Speter return eb->wrapped_editor->absent_directory(path, db->wrapped_node_baton, 376251881Speter pool); 377251881Speter} 378251881Speter 379251881Speterstatic svn_error_t * 380251881Speterchange_file_prop(void *file_baton, 381251881Speter const char *name, 382251881Speter const svn_string_t *value, 383251881Speter apr_pool_t *pool) 384251881Speter{ 385251881Speter node_baton_t *fb = file_baton; 386251881Speter edit_baton_t *eb = fb->edit_baton; 387251881Speter 388251881Speter /* only regular properties can pass over libsvn_ra */ 389251881Speter if (svn_property_kind2(name) != svn_prop_regular_kind) 390251881Speter return SVN_NO_ERROR; 391251881Speter 392251881Speter /* Maybe drop svn:mergeinfo. */ 393251881Speter if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0)) 394251881Speter { 395251881Speter eb->mergeinfo_stripped = TRUE; 396251881Speter return SVN_NO_ERROR; 397251881Speter } 398251881Speter 399251881Speter /* Maybe drop (errantly set, as this is a file) svnmerge.py properties. */ 400251881Speter if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0)) 401251881Speter { 402251881Speter eb->svnmerge_migrated = TRUE; 403251881Speter return SVN_NO_ERROR; 404251881Speter } 405251881Speter 406251881Speter /* Remember if we see any svnmerge-blocked properties. (They really 407251881Speter shouldn't be here, as this is a file, but whatever...) */ 408251881Speter if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0)) 409251881Speter { 410251881Speter eb->svnmerge_blocked = TRUE; 411251881Speter } 412251881Speter 413251881Speter /* Normalize svn:* properties as necessary. */ 414251881Speter if (svn_prop_needs_translation(name)) 415251881Speter { 416251881Speter svn_boolean_t was_normalized; 417251881Speter SVN_ERR(normalize_string(&value, &was_normalized, 418251881Speter eb->source_prop_encoding, pool, pool)); 419251881Speter if (was_normalized) 420251881Speter (*(eb->normalized_node_props_counter))++; 421251881Speter } 422251881Speter 423251881Speter return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton, 424251881Speter name, value, pool); 425251881Speter} 426251881Speter 427251881Speterstatic svn_error_t * 428251881Speterchange_dir_prop(void *dir_baton, 429251881Speter const char *name, 430251881Speter const svn_string_t *value, 431251881Speter apr_pool_t *pool) 432251881Speter{ 433251881Speter node_baton_t *db = dir_baton; 434251881Speter edit_baton_t *eb = db->edit_baton; 435251881Speter 436251881Speter /* Only regular properties can pass over libsvn_ra */ 437251881Speter if (svn_property_kind2(name) != svn_prop_regular_kind) 438251881Speter return SVN_NO_ERROR; 439251881Speter 440251881Speter /* Maybe drop svn:mergeinfo. */ 441251881Speter if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0)) 442251881Speter { 443251881Speter eb->mergeinfo_stripped = TRUE; 444251881Speter return SVN_NO_ERROR; 445251881Speter } 446251881Speter 447251881Speter /* Maybe convert svnmerge-integrated data into svn:mergeinfo. (We 448251881Speter ignore svnmerge-blocked for now.) */ 449251881Speter /* ### FIXME: Consult the mirror repository's HEAD prop values and 450251881Speter ### merge svn:mergeinfo, svnmerge-integrated, and svnmerge-blocked. */ 451251881Speter if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0)) 452251881Speter { 453251881Speter if (value) 454251881Speter { 455251881Speter /* svnmerge-integrated differs from svn:mergeinfo in a pair 456251881Speter of ways. First, it can use tabs, newlines, or spaces to 457251881Speter delimit source information. Secondly, the source paths 458251881Speter are relative URLs, whereas svn:mergeinfo uses relative 459251881Speter paths (not URI-encoded). */ 460251881Speter svn_error_t *err; 461251881Speter svn_stringbuf_t *mergeinfo_buf = svn_stringbuf_create_empty(pool); 462251881Speter svn_mergeinfo_t mergeinfo; 463251881Speter int i; 464251881Speter apr_array_header_t *sources = 465251881Speter svn_cstring_split(value->data, " \t\n", TRUE, pool); 466251881Speter svn_string_t *new_value; 467251881Speter 468251881Speter for (i = 0; i < sources->nelts; i++) 469251881Speter { 470251881Speter const char *rel_path; 471251881Speter apr_array_header_t *path_revs = 472251881Speter svn_cstring_split(APR_ARRAY_IDX(sources, i, const char *), 473251881Speter ":", TRUE, pool); 474251881Speter 475251881Speter /* ### TODO: Warn? */ 476251881Speter if (path_revs->nelts != 2) 477251881Speter continue; 478251881Speter 479251881Speter /* Append this source's mergeinfo data. */ 480251881Speter rel_path = APR_ARRAY_IDX(path_revs, 0, const char *); 481251881Speter rel_path = svn_path_uri_decode(rel_path, pool); 482251881Speter svn_stringbuf_appendcstr(mergeinfo_buf, rel_path); 483251881Speter svn_stringbuf_appendcstr(mergeinfo_buf, ":"); 484251881Speter svn_stringbuf_appendcstr(mergeinfo_buf, 485251881Speter APR_ARRAY_IDX(path_revs, 1, 486251881Speter const char *)); 487251881Speter svn_stringbuf_appendcstr(mergeinfo_buf, "\n"); 488251881Speter } 489251881Speter 490251881Speter /* Try to parse the mergeinfo string we've created, just to 491251881Speter check for bogosity. If all goes well, we'll unparse it 492251881Speter again and use that as our property value. */ 493251881Speter err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_buf->data, pool); 494251881Speter if (err) 495251881Speter { 496251881Speter svn_error_clear(err); 497251881Speter return SVN_NO_ERROR; 498251881Speter } 499251881Speter SVN_ERR(svn_mergeinfo_to_string(&new_value, mergeinfo, pool)); 500251881Speter value = new_value; 501251881Speter } 502251881Speter name = SVN_PROP_MERGEINFO; 503251881Speter eb->svnmerge_migrated = TRUE; 504251881Speter } 505251881Speter 506251881Speter /* Remember if we see any svnmerge-blocked properties. */ 507251881Speter if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0)) 508251881Speter { 509251881Speter eb->svnmerge_blocked = TRUE; 510251881Speter } 511251881Speter 512251881Speter /* Normalize svn:* properties as necessary. */ 513251881Speter if (svn_prop_needs_translation(name)) 514251881Speter { 515251881Speter svn_boolean_t was_normalized; 516251881Speter SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding, 517251881Speter pool, pool)); 518251881Speter if (was_normalized) 519251881Speter (*(eb->normalized_node_props_counter))++; 520251881Speter } 521251881Speter 522251881Speter return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton, 523251881Speter name, value, pool); 524251881Speter} 525251881Speter 526251881Speterstatic svn_error_t * 527251881Speterclose_edit(void *edit_baton, 528251881Speter apr_pool_t *pool) 529251881Speter{ 530251881Speter edit_baton_t *eb = edit_baton; 531251881Speter 532251881Speter /* If we haven't opened the root yet, that means we're transfering 533251881Speter an empty revision, probably because we aren't allowed to see the 534251881Speter contents for some reason. In any event, we need to open the root 535251881Speter and close it again, before we can close out the edit, or the 536251881Speter commit will fail. */ 537251881Speter 538251881Speter if (! eb->called_open_root) 539251881Speter { 540251881Speter void *baton; 541251881Speter SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, 542251881Speter eb->base_revision, pool, 543251881Speter &baton)); 544251881Speter SVN_ERR(eb->wrapped_editor->close_directory(baton, pool)); 545251881Speter } 546251881Speter 547251881Speter if (! eb->quiet) 548251881Speter { 549251881Speter if (eb->got_textdeltas) 550251881Speter SVN_ERR(svn_cmdline_printf(pool, "\n")); 551251881Speter if (eb->mergeinfo_stripped) 552251881Speter SVN_ERR(svn_cmdline_printf(pool, 553251881Speter "NOTE: Dropped Subversion mergeinfo " 554251881Speter "from this revision.\n")); 555251881Speter if (eb->svnmerge_migrated) 556251881Speter SVN_ERR(svn_cmdline_printf(pool, 557251881Speter "NOTE: Migrated 'svnmerge-integrated' in " 558251881Speter "this revision.\n")); 559251881Speter if (eb->svnmerge_blocked) 560251881Speter SVN_ERR(svn_cmdline_printf(pool, 561251881Speter "NOTE: Saw 'svnmerge-blocked' in this " 562251881Speter "revision (but didn't migrate it).\n")); 563251881Speter } 564251881Speter 565251881Speter return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool); 566251881Speter} 567251881Speter 568251881Speterstatic svn_error_t * 569251881Speterabort_edit(void *edit_baton, 570251881Speter apr_pool_t *pool) 571251881Speter{ 572251881Speter edit_baton_t *eb = edit_baton; 573251881Speter return eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool); 574251881Speter} 575251881Speter 576251881Speter 577251881Speter/*** Editor factory function ***/ 578251881Speter 579251881Spetersvn_error_t * 580251881Spetersvnsync_get_sync_editor(const svn_delta_editor_t *wrapped_editor, 581251881Speter void *wrapped_edit_baton, 582251881Speter svn_revnum_t base_revision, 583251881Speter const char *to_url, 584251881Speter const char *source_prop_encoding, 585251881Speter svn_boolean_t quiet, 586251881Speter const svn_delta_editor_t **editor, 587251881Speter void **edit_baton, 588251881Speter int *normalized_node_props_counter, 589251881Speter apr_pool_t *pool) 590251881Speter{ 591251881Speter svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool); 592251881Speter edit_baton_t *eb = apr_pcalloc(pool, sizeof(*eb)); 593251881Speter 594251881Speter tree_editor->set_target_revision = set_target_revision; 595251881Speter tree_editor->open_root = open_root; 596251881Speter tree_editor->delete_entry = delete_entry; 597251881Speter tree_editor->add_directory = add_directory; 598251881Speter tree_editor->open_directory = open_directory; 599251881Speter tree_editor->change_dir_prop = change_dir_prop; 600251881Speter tree_editor->close_directory = close_directory; 601251881Speter tree_editor->absent_directory = absent_directory; 602251881Speter tree_editor->add_file = add_file; 603251881Speter tree_editor->open_file = open_file; 604251881Speter tree_editor->apply_textdelta = apply_textdelta; 605251881Speter tree_editor->change_file_prop = change_file_prop; 606251881Speter tree_editor->close_file = close_file; 607251881Speter tree_editor->absent_file = absent_file; 608251881Speter tree_editor->close_edit = close_edit; 609251881Speter tree_editor->abort_edit = abort_edit; 610251881Speter 611251881Speter eb->wrapped_editor = wrapped_editor; 612251881Speter eb->wrapped_edit_baton = wrapped_edit_baton; 613251881Speter eb->base_revision = base_revision; 614251881Speter eb->to_url = to_url; 615251881Speter eb->source_prop_encoding = source_prop_encoding; 616251881Speter eb->quiet = quiet; 617251881Speter eb->normalized_node_props_counter = normalized_node_props_counter; 618251881Speter 619251881Speter if (getenv("SVNSYNC_UNSUPPORTED_STRIP_MERGEINFO")) 620251881Speter { 621251881Speter eb->strip_mergeinfo = TRUE; 622251881Speter } 623251881Speter if (getenv("SVNSYNC_UNSUPPORTED_MIGRATE_SVNMERGE")) 624251881Speter { 625251881Speter /* Current we can't merge property values. That's only possible 626251881Speter if all the properties to be merged were always modified in 627251881Speter exactly the same revisions, or if we allow ourselves to 628251881Speter lookup the current state of properties in the sync 629251881Speter destination. So for now, migrating svnmerge.py data implies 630251881Speter stripping pre-existing svn:mergeinfo. */ 631251881Speter /* ### FIXME: Do a real migration by consulting the mirror 632251881Speter ### repository's HEAD propvalues and merging svn:mergeinfo, 633251881Speter ### svnmerge-integrated, and svnmerge-blocked together. */ 634251881Speter eb->migrate_svnmerge = TRUE; 635251881Speter eb->strip_mergeinfo = TRUE; 636251881Speter } 637251881Speter 638251881Speter *editor = tree_editor; 639251881Speter *edit_baton = eb; 640251881Speter 641251881Speter return SVN_NO_ERROR; 642251881Speter} 643251881Speter 644