sync.c revision 299742
1246145Shselasky/* 2246145Shselasky * ==================================================================== 3246145Shselasky * Licensed to the Apache Software Foundation (ASF) under one 4246145Shselasky * or more contributor license agreements. See the NOTICE file 5246145Shselasky * distributed with this work for additional information 6246145Shselasky * regarding copyright ownership. The ASF licenses this file 7246145Shselasky * to you under the Apache License, Version 2.0 (the 8246145Shselasky * "License"); you may not use this file except in compliance 9246145Shselasky * with the License. You may obtain a copy of the License at 10246145Shselasky * 11246145Shselasky * http://www.apache.org/licenses/LICENSE-2.0 12246145Shselasky * 13246145Shselasky * Unless required by applicable law or agreed to in writing, 14246145Shselasky * software distributed under the License is distributed on an 15246145Shselasky * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16246145Shselasky * KIND, either express or implied. See the License for the 17246145Shselasky * specific language governing permissions and limitations 18246145Shselasky * under the License. 19246145Shselasky * ==================================================================== 20246145Shselasky */ 21246145Shselasky 22246145Shselasky#include "svn_hash.h" 23246145Shselasky#include "svn_cmdline.h" 24246145Shselasky#include "svn_config.h" 25246145Shselasky#include "svn_pools.h" 26246145Shselasky#include "svn_delta.h" 27246145Shselasky#include "svn_dirent_uri.h" 28246145Shselasky#include "svn_path.h" 29246363Shselasky#include "svn_props.h" 30246363Shselasky#include "svn_auth.h" 31246145Shselasky#include "svn_opt.h" 32246145Shselasky#include "svn_ra.h" 33246145Shselasky#include "svn_utf.h" 34246145Shselasky#include "svn_subst.h" 35246145Shselasky#include "svn_string.h" 36246145Shselasky 37246145Shselasky#include "private/svn_string_private.h" 38246145Shselasky 39246145Shselasky#include "sync.h" 40246145Shselasky 41246145Shselasky#include "svn_private_config.h" 42246145Shselasky 43246145Shselasky#include <apr_network_io.h> 44246145Shselasky#include <apr_signal.h> 45246145Shselasky#include <apr_uuid.h> 46246145Shselasky 47246145Shselasky 48246145Shselasky/* Normalize the encoding and line ending style of *STR, so that it contains 49246145Shselasky * only LF (\n) line endings and is encoded in UTF-8. After return, *STR may 50246145Shselasky * point at a new svn_string_t* allocated in RESULT_POOL. 51246145Shselasky * 52246145Shselasky * If SOURCE_PROP_ENCODING is NULL, then *STR is presumed to be encoded in 53246145Shselasky * UTF-8. 54246145Shselasky * 55246145Shselasky * *WAS_NORMALIZED is set to TRUE when *STR needed line ending normalization. 56246145Shselasky * Otherwise it is set to FALSE. 57246145Shselasky * 58246145Shselasky * SCRATCH_POOL is used for temporary allocations. 59246145Shselasky */ 60246145Shselaskystatic svn_error_t * 61246145Shselaskynormalize_string(const svn_string_t **str, 62246145Shselasky svn_boolean_t *was_normalized, 63246145Shselasky const char *source_prop_encoding, 64246145Shselasky apr_pool_t *result_pool, 65246145Shselasky apr_pool_t *scratch_pool) 66246145Shselasky{ 67246145Shselasky svn_string_t *new_str; 68246145Shselasky 69246145Shselasky *was_normalized = FALSE; 70246145Shselasky 71246145Shselasky if (*str == NULL) 72246145Shselasky return SVN_NO_ERROR; 73246145Shselasky 74246145Shselasky SVN_ERR_ASSERT((*str)->data != NULL); 75246145Shselasky 76246145Shselasky if (source_prop_encoding == NULL) 77246145Shselasky source_prop_encoding = "UTF-8"; 78246145Shselasky 79246145Shselasky new_str = NULL; 80246145Shselasky SVN_ERR(svn_subst_translate_string2(&new_str, NULL, was_normalized, 81246145Shselasky *str, source_prop_encoding, TRUE, 82246145Shselasky result_pool, scratch_pool)); 83246145Shselasky *str = new_str; 84246145Shselasky 85246145Shselasky return SVN_NO_ERROR; 86246145Shselasky} 87246145Shselasky 88246145Shselasky/* Remove r0 references from the mergeinfo string *STR. 89246145Shselasky * 90246145Shselasky * r0 was never a valid mergeinfo reference and cannot be committed with 91246145Shselasky * recent servers, but can be committed through a server older than 1.6.18 92246145Shselasky * for HTTP or older than 1.6.17 for the other protocols. See issue #4476 93246145Shselasky * "Mergeinfo containing r0 makes svnsync and dump and load fail". 94246145Shselasky * 95246145Shselasky * Set *WAS_CHANGED to TRUE if *STR was changed, otherwise to FALSE. 96246145Shselasky */ 97246145Shselaskystatic svn_error_t * 98246145Shselaskyremove_r0_mergeinfo(const svn_string_t **str, 99246145Shselasky svn_boolean_t *was_changed, 100246145Shselasky apr_pool_t *result_pool, 101246145Shselasky apr_pool_t *scratch_pool) 102246145Shselasky{ 103246145Shselasky svn_stringbuf_t *new_str = svn_stringbuf_create_empty(result_pool); 104246145Shselasky apr_array_header_t *lines; 105246145Shselasky int i; 106246145Shselasky 107246145Shselasky SVN_ERR_ASSERT(*str && (*str)->data); 108246145Shselasky 109246145Shselasky *was_changed = FALSE; 110246145Shselasky 111246145Shselasky /* for each line */ 112246145Shselasky lines = svn_cstring_split((*str)->data, "\n", FALSE, scratch_pool); 113246145Shselasky 114246145Shselasky for (i = 0; i < lines->nelts; i++) 115246145Shselasky { 116246145Shselasky char *line = APR_ARRAY_IDX(lines, i, char *); 117246145Shselasky char *colon; 118246145Shselasky char *rangelist; 119246145Shselasky 120246145Shselasky /* split at the last colon */ 121246145Shselasky colon = strrchr(line, ':'); 122246145Shselasky 123246145Shselasky if (! colon) 124246145Shselasky return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 125246145Shselasky _("Missing colon in svn:mergeinfo " 126246145Shselasky "property")); 127246145Shselasky 128246145Shselasky rangelist = colon + 1; 129246145Shselasky 130246145Shselasky /* remove r0 */ 131246145Shselasky if (colon[1] == '0') 132246145Shselasky { 133246145Shselasky if (strncmp(rangelist, "0*,", 3) == 0) 134246145Shselasky { 135246145Shselasky rangelist += 3; 136246145Shselasky } 137246145Shselasky else if (strcmp(rangelist, "0*") == 0 138246145Shselasky || strncmp(rangelist, "0,", 2) == 0 139246145Shselasky || strncmp(rangelist, "0-1*", 4) == 0 140246145Shselasky || strncmp(rangelist, "0-1,", 4) == 0 141246145Shselasky || strcmp(rangelist, "0-1") == 0) 142246145Shselasky { 143246145Shselasky rangelist += 2; 144246145Shselasky } 145246145Shselasky else if (strcmp(rangelist, "0") == 0) 146246145Shselasky { 147246145Shselasky rangelist += 1; 148246145Shselasky } 149246145Shselasky else if (strncmp(rangelist, "0-", 2) == 0) 150246145Shselasky { 151246145Shselasky rangelist[0] = '1'; 152246145Shselasky } 153246145Shselasky } 154246145Shselasky 155246145Shselasky /* reassemble */ 156246145Shselasky if (rangelist[0]) 157246145Shselasky { 158246145Shselasky if (new_str->len) 159246145Shselasky svn_stringbuf_appendbyte(new_str, '\n'); 160246145Shselasky svn_stringbuf_appendbytes(new_str, line, colon + 1 - line); 161246145Shselasky svn_stringbuf_appendcstr(new_str, rangelist); 162246145Shselasky } 163246145Shselasky } 164246145Shselasky 165246145Shselasky if (strcmp((*str)->data, new_str->data) != 0) 166246145Shselasky { 167246145Shselasky *was_changed = TRUE; 168246145Shselasky } 169246145Shselasky 170246145Shselasky *str = svn_stringbuf__morph_into_string(new_str); 171246145Shselasky return SVN_NO_ERROR; 172246145Shselasky} 173246145Shselasky 174246145Shselasky 175246145Shselasky/* Normalize the encoding and line ending style of the values of properties 176246145Shselasky * in REV_PROPS that "need translation" (according to 177246145Shselasky * svn_prop_needs_translation(), which is currently all svn:* props) so that 178246145Shselasky * they are encoded in UTF-8 and contain only LF (\n) line endings. 179246145Shselasky * 180246145Shselasky * The number of properties that needed line ending normalization is returned in 181246145Shselasky * *NORMALIZED_COUNT. 182246145Shselasky * 183246145Shselasky * No re-encoding is performed if SOURCE_PROP_ENCODING is NULL. 184246145Shselasky */ 185246145Shselaskysvn_error_t * 186246145Shselaskysvnsync_normalize_revprops(apr_hash_t *rev_props, 187246145Shselasky int *normalized_count, 188246145Shselasky const char *source_prop_encoding, 189246145Shselasky apr_pool_t *pool) 190246145Shselasky{ 191246145Shselasky apr_hash_index_t *hi; 192246145Shselasky *normalized_count = 0; 193246145Shselasky 194246145Shselasky for (hi = apr_hash_first(pool, rev_props); 195246145Shselasky hi; 196246145Shselasky hi = apr_hash_next(hi)) 197246145Shselasky { 198246145Shselasky const char *propname = apr_hash_this_key(hi); 199246145Shselasky const svn_string_t *propval = apr_hash_this_val(hi); 200246145Shselasky 201246145Shselasky if (svn_prop_needs_translation(propname)) 202246145Shselasky { 203246145Shselasky svn_boolean_t was_normalized; 204246145Shselasky SVN_ERR(normalize_string(&propval, &was_normalized, 205246145Shselasky source_prop_encoding, pool, pool)); 206246145Shselasky 207246145Shselasky /* Replace the existing prop value. */ 208246145Shselasky svn_hash_sets(rev_props, propname, propval); 209246145Shselasky 210246145Shselasky if (was_normalized) 211246145Shselasky (*normalized_count)++; /* Count it. */ 212246145Shselasky } 213246145Shselasky } 214246145Shselasky return SVN_NO_ERROR; 215246145Shselasky} 216246145Shselasky 217246145Shselasky 218246145Shselasky/*** Synchronization Editor ***/ 219246145Shselasky 220246145Shselasky/* This editor has a couple of jobs. 221246145Shselasky * 222246145Shselasky * First, it needs to filter out the propchanges that can't be passed over 223246145Shselasky * libsvn_ra. 224246145Shselasky * 225246145Shselasky * Second, it needs to adjust for the fact that we might not actually have 226246145Shselasky * permission to see all of the data from the remote repository, which means 227246145Shselasky * we could get revisions that are totally empty from our point of view. 228246145Shselasky * 229246145Shselasky * Third, it needs to adjust copyfrom paths, adding the root url for the 230246145Shselasky * destination repository to the beginning of them. 231246145Shselasky */ 232246145Shselasky 233246145Shselasky 234246145Shselasky/* Edit baton */ 235246145Shselaskytypedef struct edit_baton_t { 236246145Shselasky const svn_delta_editor_t *wrapped_editor; 237246145Shselasky void *wrapped_edit_baton; 238246145Shselasky const char *to_url; /* URL we're copying into, for correct copyfrom URLs */ 239246145Shselasky const char *source_prop_encoding; 240246145Shselasky svn_boolean_t called_open_root; 241246145Shselasky svn_boolean_t got_textdeltas; 242246145Shselasky svn_revnum_t base_revision; 243246145Shselasky svn_boolean_t quiet; 244246145Shselasky svn_boolean_t mergeinfo_tweaked; /* Did we tweak svn:mergeinfo? */ 245246145Shselasky svn_boolean_t strip_mergeinfo; /* Are we stripping svn:mergeinfo? */ 246246145Shselasky svn_boolean_t migrate_svnmerge; /* Are we converting svnmerge.py data? */ 247246145Shselasky svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */ 248246145Shselasky svn_boolean_t svnmerge_migrated; /* Did we convert svnmerge.py data? */ 249246145Shselasky svn_boolean_t svnmerge_blocked; /* Was there any blocked svnmerge data? */ 250246145Shselasky int *normalized_node_props_counter; /* Where to count normalizations? */ 251246145Shselasky} edit_baton_t; 252246145Shselasky 253246145Shselasky 254246145Shselasky/* A dual-purpose baton for files and directories. */ 255246145Shselaskytypedef struct node_baton_t { 256246145Shselasky void *edit_baton; 257246145Shselasky void *wrapped_node_baton; 258246145Shselasky} node_baton_t; 259246145Shselasky 260246145Shselasky 261246145Shselasky/*** Editor vtable functions ***/ 262246145Shselasky 263246145Shselaskystatic svn_error_t * 264246145Shselaskyset_target_revision(void *edit_baton, 265246145Shselasky svn_revnum_t target_revision, 266246145Shselasky apr_pool_t *pool) 267246145Shselasky{ 268246145Shselasky edit_baton_t *eb = edit_baton; 269246145Shselasky return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton, 270246145Shselasky target_revision, pool); 271246145Shselasky} 272246145Shselasky 273246145Shselaskystatic svn_error_t * 274246145Shselaskyopen_root(void *edit_baton, 275246145Shselasky svn_revnum_t base_revision, 276246145Shselasky apr_pool_t *pool, 277246145Shselasky void **root_baton) 278246145Shselasky{ 279246145Shselasky edit_baton_t *eb = edit_baton; 280246145Shselasky node_baton_t *dir_baton = apr_palloc(pool, sizeof(*dir_baton)); 281246145Shselasky 282246145Shselasky SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, 283246145Shselasky base_revision, pool, 284246145Shselasky &dir_baton->wrapped_node_baton)); 285246145Shselasky 286246145Shselasky eb->called_open_root = TRUE; 287246145Shselasky dir_baton->edit_baton = edit_baton; 288246145Shselasky *root_baton = dir_baton; 289246145Shselasky 290246145Shselasky return SVN_NO_ERROR; 291246145Shselasky} 292246145Shselasky 293246145Shselaskystatic svn_error_t * 294246145Shselaskydelete_entry(const char *path, 295246145Shselasky svn_revnum_t base_revision, 296246145Shselasky void *parent_baton, 297246145Shselasky apr_pool_t *pool) 298246145Shselasky{ 299246145Shselasky node_baton_t *pb = parent_baton; 300246145Shselasky edit_baton_t *eb = pb->edit_baton; 301246145Shselasky 302246145Shselasky return eb->wrapped_editor->delete_entry(path, base_revision, 303246145Shselasky pb->wrapped_node_baton, pool); 304246145Shselasky} 305246145Shselasky 306246145Shselaskystatic svn_error_t * 307246145Shselaskyadd_directory(const char *path, 308246145Shselasky void *parent_baton, 309246145Shselasky const char *copyfrom_path, 310246145Shselasky svn_revnum_t copyfrom_rev, 311246145Shselasky apr_pool_t *pool, 312246145Shselasky void **child_baton) 313246145Shselasky{ 314246145Shselasky node_baton_t *pb = parent_baton; 315246145Shselasky edit_baton_t *eb = pb->edit_baton; 316246145Shselasky node_baton_t *b = apr_palloc(pool, sizeof(*b)); 317246145Shselasky 318246145Shselasky /* if copyfrom_path is an fspath create a proper uri */ 319246145Shselasky if (copyfrom_path && copyfrom_path[0] == '/') 320246145Shselasky copyfrom_path = svn_path_url_add_component2(eb->to_url, 321246145Shselasky copyfrom_path + 1, pool); 322246145Shselasky 323246145Shselasky SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_node_baton, 324246145Shselasky copyfrom_path, 325246145Shselasky copyfrom_rev, pool, 326246145Shselasky &b->wrapped_node_baton)); 327246145Shselasky 328246145Shselasky b->edit_baton = eb; 329246145Shselasky *child_baton = b; 330246145Shselasky 331246145Shselasky return SVN_NO_ERROR; 332246145Shselasky} 333246145Shselasky 334246145Shselaskystatic svn_error_t * 335246145Shselaskyopen_directory(const char *path, 336246145Shselasky void *parent_baton, 337246145Shselasky svn_revnum_t base_revision, 338246145Shselasky apr_pool_t *pool, 339246145Shselasky void **child_baton) 340246145Shselasky{ 341246145Shselasky node_baton_t *pb = parent_baton; 342246145Shselasky edit_baton_t *eb = pb->edit_baton; 343246145Shselasky node_baton_t *db = apr_palloc(pool, sizeof(*db)); 344246145Shselasky 345246145Shselasky SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_node_baton, 346246145Shselasky base_revision, pool, 347246145Shselasky &db->wrapped_node_baton)); 348246145Shselasky 349246145Shselasky db->edit_baton = eb; 350246145Shselasky *child_baton = db; 351246145Shselasky 352246145Shselasky return SVN_NO_ERROR; 353246145Shselasky} 354246145Shselasky 355246145Shselaskystatic svn_error_t * 356246145Shselaskyadd_file(const char *path, 357246145Shselasky void *parent_baton, 358246145Shselasky const char *copyfrom_path, 359246145Shselasky svn_revnum_t copyfrom_rev, 360246145Shselasky apr_pool_t *pool, 361246145Shselasky void **file_baton) 362246145Shselasky{ 363246145Shselasky node_baton_t *pb = parent_baton; 364246145Shselasky edit_baton_t *eb = pb->edit_baton; 365246145Shselasky node_baton_t *fb = apr_palloc(pool, sizeof(*fb)); 366246145Shselasky 367246145Shselasky /* if copyfrom_path is an fspath create a proper uri */ 368246145Shselasky if (copyfrom_path && copyfrom_path[0] == '/') 369246145Shselasky copyfrom_path = svn_path_url_add_component2(eb->to_url, 370246145Shselasky copyfrom_path + 1, pool); 371246145Shselasky 372246145Shselasky SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_node_baton, 373246145Shselasky copyfrom_path, copyfrom_rev, 374246145Shselasky pool, &fb->wrapped_node_baton)); 375246145Shselasky 376246145Shselasky fb->edit_baton = eb; 377246145Shselasky *file_baton = fb; 378246145Shselasky 379246145Shselasky return SVN_NO_ERROR; 380246145Shselasky} 381246145Shselasky 382246145Shselaskystatic svn_error_t * 383269921Shselaskyopen_file(const char *path, 384269921Shselasky void *parent_baton, 385246145Shselasky svn_revnum_t base_revision, 386269921Shselasky apr_pool_t *pool, 387246145Shselasky void **file_baton) 388246145Shselasky{ 389246145Shselasky node_baton_t *pb = parent_baton; 390246145Shselasky edit_baton_t *eb = pb->edit_baton; 391246145Shselasky node_baton_t *fb = apr_palloc(pool, sizeof(*fb)); 392246145Shselasky 393246145Shselasky SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_node_baton, 394246145Shselasky base_revision, pool, 395246145Shselasky &fb->wrapped_node_baton)); 396246145Shselasky 397246145Shselasky fb->edit_baton = eb; 398246145Shselasky *file_baton = fb; 399246145Shselasky 400269921Shselasky return SVN_NO_ERROR; 401269921Shselasky} 402269921Shselasky 403269921Shselaskystatic svn_error_t * 404269921Shselaskyapply_textdelta(void *file_baton, 405269921Shselasky const char *base_checksum, 406269921Shselasky apr_pool_t *pool, 407269921Shselasky svn_txdelta_window_handler_t *handler, 408269921Shselasky void **handler_baton) 409269921Shselasky{ 410246145Shselasky node_baton_t *fb = file_baton; 411246145Shselasky edit_baton_t *eb = fb->edit_baton; 412246145Shselasky 413246145Shselasky if (! eb->quiet) 414246145Shselasky { 415246145Shselasky if (! eb->got_textdeltas) 416246145Shselasky SVN_ERR(svn_cmdline_printf(pool, _("Transmitting file data "))); 417246145Shselasky SVN_ERR(svn_cmdline_printf(pool, ".")); 418246145Shselasky SVN_ERR(svn_cmdline_fflush(stdout)); 419246145Shselasky } 420246145Shselasky 421246145Shselasky eb->got_textdeltas = TRUE; 422246145Shselasky return eb->wrapped_editor->apply_textdelta(fb->wrapped_node_baton, 423246145Shselasky base_checksum, pool, 424246145Shselasky handler, handler_baton); 425246145Shselasky} 426246145Shselasky 427246145Shselaskystatic svn_error_t * 428246145Shselaskyclose_file(void *file_baton, 429246145Shselasky const char *text_checksum, 430246145Shselasky apr_pool_t *pool) 431246145Shselasky{ 432246145Shselasky node_baton_t *fb = file_baton; 433246145Shselasky edit_baton_t *eb = fb->edit_baton; 434246145Shselasky return eb->wrapped_editor->close_file(fb->wrapped_node_baton, 435246145Shselasky text_checksum, pool); 436246145Shselasky} 437246145Shselasky 438246145Shselaskystatic svn_error_t * 439246145Shselaskyabsent_file(const char *path, 440246145Shselasky void *file_baton, 441246145Shselasky apr_pool_t *pool) 442246145Shselasky{ 443246145Shselasky node_baton_t *fb = file_baton; 444246145Shselasky edit_baton_t *eb = fb->edit_baton; 445246145Shselasky return eb->wrapped_editor->absent_file(path, fb->wrapped_node_baton, pool); 446246145Shselasky} 447246145Shselasky 448246145Shselaskystatic svn_error_t * 449246145Shselaskyclose_directory(void *dir_baton, 450246145Shselasky apr_pool_t *pool) 451246145Shselasky{ 452246145Shselasky node_baton_t *db = dir_baton; 453246145Shselasky edit_baton_t *eb = db->edit_baton; 454246145Shselasky return eb->wrapped_editor->close_directory(db->wrapped_node_baton, pool); 455246145Shselasky} 456246145Shselasky 457246145Shselaskystatic svn_error_t * 458246145Shselaskyabsent_directory(const char *path, 459246145Shselasky void *dir_baton, 460246145Shselasky apr_pool_t *pool) 461246145Shselasky{ 462246145Shselasky node_baton_t *db = dir_baton; 463246145Shselasky edit_baton_t *eb = db->edit_baton; 464246145Shselasky return eb->wrapped_editor->absent_directory(path, db->wrapped_node_baton, 465246145Shselasky pool); 466246145Shselasky} 467246145Shselasky 468246145Shselaskystatic svn_error_t * 469246145Shselaskychange_file_prop(void *file_baton, 470246145Shselasky const char *name, 471246145Shselasky const svn_string_t *value, 472246145Shselasky apr_pool_t *pool) 473246145Shselasky{ 474246145Shselasky node_baton_t *fb = file_baton; 475246145Shselasky edit_baton_t *eb = fb->edit_baton; 476246145Shselasky 477246145Shselasky /* only regular properties can pass over libsvn_ra */ 478246145Shselasky if (svn_property_kind2(name) != svn_prop_regular_kind) 479246145Shselasky return SVN_NO_ERROR; 480246145Shselasky 481246145Shselasky /* Maybe drop svn:mergeinfo. */ 482246145Shselasky if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0)) 483246145Shselasky { 484246145Shselasky eb->mergeinfo_stripped = TRUE; 485246145Shselasky return SVN_NO_ERROR; 486246145Shselasky } 487246145Shselasky 488246145Shselasky /* Maybe drop (errantly set, as this is a file) svnmerge.py properties. */ 489246145Shselasky if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0)) 490246145Shselasky { 491246145Shselasky eb->svnmerge_migrated = TRUE; 492246145Shselasky return SVN_NO_ERROR; 493246145Shselasky } 494246145Shselasky 495246145Shselasky /* Remember if we see any svnmerge-blocked properties. (They really 496246145Shselasky shouldn't be here, as this is a file, but whatever...) */ 497246145Shselasky if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0)) 498246145Shselasky { 499246145Shselasky eb->svnmerge_blocked = TRUE; 500246145Shselasky } 501246145Shselasky 502246145Shselasky /* Normalize svn:* properties as necessary. */ 503246145Shselasky if (svn_prop_needs_translation(name)) 504246145Shselasky { 505246145Shselasky svn_boolean_t was_normalized; 506246145Shselasky svn_boolean_t mergeinfo_tweaked = FALSE; 507246145Shselasky 508246145Shselasky /* Normalize encoding to UTF-8, and EOL style to LF. */ 509246145Shselasky SVN_ERR(normalize_string(&value, &was_normalized, 510246145Shselasky eb->source_prop_encoding, pool, pool)); 511246145Shselasky /* Correct malformed mergeinfo. */ 512246145Shselasky if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) 513246145Shselasky { 514246145Shselasky SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked, 515246145Shselasky pool, pool)); 516246145Shselasky if (mergeinfo_tweaked) 517246145Shselasky eb->mergeinfo_tweaked = TRUE; 518246145Shselasky } 519246145Shselasky if (was_normalized) 520246145Shselasky (*(eb->normalized_node_props_counter))++; 521246145Shselasky } 522246145Shselasky 523246145Shselasky return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton, 524246145Shselasky name, value, pool); 525246145Shselasky} 526246145Shselasky 527246145Shselaskystatic svn_error_t * 528246145Shselaskychange_dir_prop(void *dir_baton, 529246145Shselasky const char *name, 530246145Shselasky const svn_string_t *value, 531246145Shselasky apr_pool_t *pool) 532246145Shselasky{ 533246145Shselasky node_baton_t *db = dir_baton; 534246145Shselasky edit_baton_t *eb = db->edit_baton; 535246145Shselasky 536246145Shselasky /* Only regular properties can pass over libsvn_ra */ 537246145Shselasky if (svn_property_kind2(name) != svn_prop_regular_kind) 538246145Shselasky return SVN_NO_ERROR; 539246145Shselasky 540246145Shselasky /* Maybe drop svn:mergeinfo. */ 541246145Shselasky if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0)) 542246145Shselasky { 543246145Shselasky eb->mergeinfo_stripped = TRUE; 544246145Shselasky return SVN_NO_ERROR; 545246145Shselasky } 546246145Shselasky 547246145Shselasky /* Maybe convert svnmerge-integrated data into svn:mergeinfo. (We 548246145Shselasky ignore svnmerge-blocked for now.) */ 549246145Shselasky /* ### FIXME: Consult the mirror repository's HEAD prop values and 550246145Shselasky ### merge svn:mergeinfo, svnmerge-integrated, and svnmerge-blocked. */ 551246145Shselasky if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0)) 552246145Shselasky { 553246145Shselasky if (value) 554246145Shselasky { 555246145Shselasky /* svnmerge-integrated differs from svn:mergeinfo in a pair 556246145Shselasky of ways. First, it can use tabs, newlines, or spaces to 557246145Shselasky delimit source information. Secondly, the source paths 558246145Shselasky are relative URLs, whereas svn:mergeinfo uses relative 559246145Shselasky paths (not URI-encoded). */ 560246145Shselasky svn_error_t *err; 561246145Shselasky svn_stringbuf_t *mergeinfo_buf = svn_stringbuf_create_empty(pool); 562246145Shselasky svn_mergeinfo_t mergeinfo; 563246145Shselasky int i; 564246145Shselasky apr_array_header_t *sources = 565246145Shselasky svn_cstring_split(value->data, " \t\n", TRUE, pool); 566246145Shselasky svn_string_t *new_value; 567246145Shselasky 568246145Shselasky for (i = 0; i < sources->nelts; i++) 569246145Shselasky { 570246145Shselasky const char *rel_path; 571246145Shselasky apr_array_header_t *path_revs = 572246145Shselasky svn_cstring_split(APR_ARRAY_IDX(sources, i, const char *), 573246145Shselasky ":", TRUE, pool); 574246145Shselasky 575246145Shselasky /* ### TODO: Warn? */ 576246145Shselasky if (path_revs->nelts != 2) 577246145Shselasky continue; 578246145Shselasky 579246145Shselasky /* Append this source's mergeinfo data. */ 580246145Shselasky rel_path = APR_ARRAY_IDX(path_revs, 0, const char *); 581246145Shselasky rel_path = svn_path_uri_decode(rel_path, pool); 582246145Shselasky svn_stringbuf_appendcstr(mergeinfo_buf, rel_path); 583246145Shselasky svn_stringbuf_appendcstr(mergeinfo_buf, ":"); 584246145Shselasky svn_stringbuf_appendcstr(mergeinfo_buf, 585246145Shselasky APR_ARRAY_IDX(path_revs, 1, 586246145Shselasky const char *)); 587246145Shselasky svn_stringbuf_appendcstr(mergeinfo_buf, "\n"); 588246145Shselasky } 589246145Shselasky 590246145Shselasky /* Try to parse the mergeinfo string we've created, just to 591246145Shselasky check for bogosity. If all goes well, we'll unparse it 592246145Shselasky again and use that as our property value. */ 593246145Shselasky err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_buf->data, pool); 594246145Shselasky if (err) 595246145Shselasky { 596246145Shselasky svn_error_clear(err); 597246145Shselasky return SVN_NO_ERROR; 598246145Shselasky } 599246145Shselasky SVN_ERR(svn_mergeinfo_to_string(&new_value, mergeinfo, pool)); 600246145Shselasky value = new_value; 601246145Shselasky } 602246145Shselasky name = SVN_PROP_MERGEINFO; 603246145Shselasky eb->svnmerge_migrated = TRUE; 604246145Shselasky } 605246145Shselasky 606246145Shselasky /* Remember if we see any svnmerge-blocked properties. */ 607246145Shselasky if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0)) 608246145Shselasky { 609246145Shselasky eb->svnmerge_blocked = TRUE; 610246145Shselasky } 611246145Shselasky 612246145Shselasky /* Normalize svn:* properties as necessary. */ 613246145Shselasky if (svn_prop_needs_translation(name)) 614246145Shselasky { 615246145Shselasky svn_boolean_t was_normalized; 616246145Shselasky svn_boolean_t mergeinfo_tweaked = FALSE; 617246145Shselasky 618246145Shselasky /* Normalize encoding to UTF-8, and EOL style to LF. */ 619246145Shselasky SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding, 620246145Shselasky pool, pool)); 621246145Shselasky /* Maybe adjust svn:mergeinfo. */ 622246145Shselasky if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) 623246145Shselasky { 624246145Shselasky SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked, 625246145Shselasky pool, pool)); 626246145Shselasky if (mergeinfo_tweaked) 627246145Shselasky eb->mergeinfo_tweaked = TRUE; 628246145Shselasky } 629246145Shselasky if (was_normalized) 630246145Shselasky (*(eb->normalized_node_props_counter))++; 631246145Shselasky } 632246145Shselasky 633246145Shselasky return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton, 634246145Shselasky name, value, pool); 635246145Shselasky} 636246145Shselasky 637246145Shselaskystatic svn_error_t * 638246145Shselaskyclose_edit(void *edit_baton, 639246145Shselasky apr_pool_t *pool) 640246145Shselasky{ 641246145Shselasky edit_baton_t *eb = edit_baton; 642246145Shselasky 643246145Shselasky /* If we haven't opened the root yet, that means we're transfering 644246145Shselasky an empty revision, probably because we aren't allowed to see the 645246145Shselasky contents for some reason. In any event, we need to open the root 646246145Shselasky and close it again, before we can close out the edit, or the 647246145Shselasky commit will fail. */ 648246145Shselasky 649246145Shselasky if (! eb->called_open_root) 650246145Shselasky { 651246145Shselasky void *baton; 652246145Shselasky SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, 653246145Shselasky eb->base_revision, pool, 654246145Shselasky &baton)); 655246145Shselasky SVN_ERR(eb->wrapped_editor->close_directory(baton, pool)); 656246145Shselasky } 657246145Shselasky 658246145Shselasky if (! eb->quiet) 659246145Shselasky { 660246145Shselasky if (eb->got_textdeltas) 661246145Shselasky SVN_ERR(svn_cmdline_printf(pool, "\n")); 662246145Shselasky if (eb->mergeinfo_tweaked) 663246145Shselasky SVN_ERR(svn_cmdline_printf(pool, 664246145Shselasky "NOTE: Adjusted Subversion mergeinfo in " 665246145Shselasky "this revision.\n")); 666246145Shselasky if (eb->mergeinfo_stripped) 667246145Shselasky SVN_ERR(svn_cmdline_printf(pool, 668246145Shselasky "NOTE: Dropped Subversion mergeinfo " 669246145Shselasky "from this revision.\n")); 670246145Shselasky if (eb->svnmerge_migrated) 671246145Shselasky SVN_ERR(svn_cmdline_printf(pool, 672246145Shselasky "NOTE: Migrated 'svnmerge-integrated' in " 673246145Shselasky "this revision.\n")); 674246145Shselasky if (eb->svnmerge_blocked) 675246145Shselasky SVN_ERR(svn_cmdline_printf(pool, 676246145Shselasky "NOTE: Saw 'svnmerge-blocked' in this " 677246145Shselasky "revision (but didn't migrate it).\n")); 678246145Shselasky } 679246145Shselasky 680246145Shselasky return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool); 681246145Shselasky} 682246145Shselasky 683246145Shselaskystatic svn_error_t * 684246145Shselaskyabort_edit(void *edit_baton, 685246145Shselasky apr_pool_t *pool) 686246145Shselasky{ 687246145Shselasky edit_baton_t *eb = edit_baton; 688246145Shselasky return eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool); 689246145Shselasky} 690246145Shselasky 691246145Shselasky 692246145Shselasky/*** Editor factory function ***/ 693246145Shselasky 694246145Shselaskysvn_error_t * 695246145Shselaskysvnsync_get_sync_editor(const svn_delta_editor_t *wrapped_editor, 696246145Shselasky void *wrapped_edit_baton, 697246145Shselasky svn_revnum_t base_revision, 698246145Shselasky const char *to_url, 699246145Shselasky const char *source_prop_encoding, 700246145Shselasky svn_boolean_t quiet, 701246145Shselasky const svn_delta_editor_t **editor, 702246145Shselasky void **edit_baton, 703246145Shselasky int *normalized_node_props_counter, 704246145Shselasky apr_pool_t *pool) 705246145Shselasky{ 706246145Shselasky svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool); 707246145Shselasky edit_baton_t *eb = apr_pcalloc(pool, sizeof(*eb)); 708246145Shselasky 709246145Shselasky tree_editor->set_target_revision = set_target_revision; 710246145Shselasky tree_editor->open_root = open_root; 711246145Shselasky tree_editor->delete_entry = delete_entry; 712246145Shselasky tree_editor->add_directory = add_directory; 713246145Shselasky tree_editor->open_directory = open_directory; 714246145Shselasky tree_editor->change_dir_prop = change_dir_prop; 715246145Shselasky tree_editor->close_directory = close_directory; 716246145Shselasky tree_editor->absent_directory = absent_directory; 717246145Shselasky tree_editor->add_file = add_file; 718246145Shselasky tree_editor->open_file = open_file; 719246145Shselasky tree_editor->apply_textdelta = apply_textdelta; 720246145Shselasky tree_editor->change_file_prop = change_file_prop; 721246145Shselasky tree_editor->close_file = close_file; 722246145Shselasky tree_editor->absent_file = absent_file; 723246145Shselasky tree_editor->close_edit = close_edit; 724246145Shselasky tree_editor->abort_edit = abort_edit; 725246145Shselasky 726246145Shselasky eb->wrapped_editor = wrapped_editor; 727246145Shselasky eb->wrapped_edit_baton = wrapped_edit_baton; 728246145Shselasky eb->base_revision = base_revision; 729246145Shselasky eb->to_url = to_url; 730246145Shselasky eb->source_prop_encoding = source_prop_encoding; 731246145Shselasky eb->quiet = quiet; 732246145Shselasky eb->normalized_node_props_counter = normalized_node_props_counter; 733246145Shselasky 734246145Shselasky if (getenv("SVNSYNC_UNSUPPORTED_STRIP_MERGEINFO")) 735246145Shselasky { 736246145Shselasky eb->strip_mergeinfo = TRUE; 737246145Shselasky } 738246145Shselasky if (getenv("SVNSYNC_UNSUPPORTED_MIGRATE_SVNMERGE")) 739246145Shselasky { 740246145Shselasky /* Current we can't merge property values. That's only possible 741246145Shselasky if all the properties to be merged were always modified in 742246145Shselasky exactly the same revisions, or if we allow ourselves to 743246145Shselasky lookup the current state of properties in the sync 744246145Shselasky destination. So for now, migrating svnmerge.py data implies 745246145Shselasky stripping pre-existing svn:mergeinfo. */ 746246145Shselasky /* ### FIXME: Do a real migration by consulting the mirror 747246145Shselasky ### repository's HEAD propvalues and merging svn:mergeinfo, 748246145Shselasky ### svnmerge-integrated, and svnmerge-blocked together. */ 749246145Shselasky eb->migrate_svnmerge = TRUE; 750246145Shselasky eb->strip_mergeinfo = TRUE; 751246145Shselasky } 752246145Shselasky 753246145Shselasky *editor = tree_editor; 754246145Shselasky *edit_baton = eb; 755246145Shselasky 756246145Shselasky return SVN_NO_ERROR; 757246145Shselasky} 758246145Shselasky 759246145Shselasky