merge.c revision 251881
1/* 2 * merge.c: merging changes into a working file 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24#include "svn_wc.h" 25#include "svn_diff.h" 26#include "svn_dirent_uri.h" 27#include "svn_path.h" 28#include "svn_pools.h" 29 30#include "wc.h" 31#include "adm_files.h" 32#include "conflicts.h" 33#include "translate.h" 34#include "workqueue.h" 35 36#include "private/svn_skel.h" 37 38#include "svn_private_config.h" 39 40/* Contains some information on the merge target before merge, and some 41 information needed for the diff processing. */ 42typedef struct merge_target_t 43{ 44 svn_wc__db_t *db; /* The DB used to access target */ 45 const char *local_abspath; /* The absolute path to target */ 46 const char *wri_abspath; /* The working copy of target */ 47 48 apr_hash_t *old_actual_props; /* The set of actual properties 49 before merging */ 50 const apr_array_header_t *prop_diff; /* The property changes */ 51 52 const char *diff3_cmd; /* The diff3 command and options */ 53 const apr_array_header_t *merge_options; 54 55} merge_target_t; 56 57 58/* Return a pointer to the svn_prop_t structure from PROP_DIFF 59 belonging to PROP_NAME, if any. NULL otherwise.*/ 60static const svn_prop_t * 61get_prop(const apr_array_header_t *prop_diff, 62 const char *prop_name) 63{ 64 if (prop_diff) 65 { 66 int i; 67 for (i = 0; i < prop_diff->nelts; i++) 68 { 69 const svn_prop_t *elt = &APR_ARRAY_IDX(prop_diff, i, 70 svn_prop_t); 71 72 if (strcmp(elt->name, prop_name) == 0) 73 return elt; 74 } 75 } 76 77 return NULL; 78} 79 80 81/* Detranslate a working copy file MERGE_TARGET to achieve the effect of: 82 83 1. Detranslate 84 2. Install new props 85 3. Retranslate 86 4. Detranslate 87 88 in one pass, to get a file which can be compared with the left and right 89 files which are in repository normal form. 90 91 Property changes make this a little complex though. Changes in 92 93 - svn:mime-type 94 - svn:eol-style 95 - svn:keywords 96 - svn:special 97 98 may change the way a file is translated. 99 100 Effect for svn:mime-type: 101 102 If svn:mime-type is considered 'binary', we ignore svn:eol-style (but 103 still translate keywords). 104 105 I) both old and new mime-types are texty 106 -> just do the translation dance (as lined out below) 107 ### actually we do a shortcut with just one translation: 108 detranslate with the old keywords and ... eol-style 109 (the new re+detranslation is a no-op w.r.t. keywords [1]) 110 111 II) the old one is texty, the new one is binary 112 -> detranslate with the old eol-style and keywords 113 (the new re+detranslation is a no-op [1]) 114 115 III) the old one is binary, the new one texty 116 -> detranslate with the old keywords and new eol-style 117 (the old detranslation is a no-op w.r.t. eol, and 118 the new re+detranslation is a no-op w.r.t. keywords [1]) 119 120 IV) the old and new ones are binary 121 -> detranslate with the old keywords 122 (the new re+detranslation is a no-op [1]) 123 124 Effect for svn:eol-style 125 126 I) On add or change of svn:eol-style, use the new value 127 128 II) otherwise: use the old value (absent means 'no translation') 129 130 Effect for svn:keywords 131 132 Always use the old settings (re+detranslation are no-op [1]). 133 134 [1] Translation of keywords from repository normal form to WC form and 135 back is normally a no-op, but is not a no-op if text contains a kw 136 that is only enabled by the new props and is present in non- 137 contracted form (such as "$Rev: 1234 $"). If we want to catch this 138 case we should detranslate with both the old & the new keywords 139 together. 140 141 Effect for svn:special 142 143 Always use the old settings (re+detranslation are no-op). 144 145 Sets *DETRANSLATED_ABSPATH to the path to the detranslated file, 146 this may be the same as SOURCE_ABSPATH if FORCE_COPY is FALSE and no 147 translation is required. 148 149 If FORCE_COPY is FALSE and *DETRANSLATED_ABSPATH is a file distinct 150 from SOURCE_ABSPATH then the file will be deleted on RESULT_POOL 151 cleanup. 152 153 If FORCE_COPY is TRUE then *DETRANSLATED_ABSPATH will always be a 154 new file distinct from SOURCE_ABSPATH and it will be the callers 155 responsibility to delete the file. 156 157*/ 158static svn_error_t * 159detranslate_wc_file(const char **detranslated_abspath, 160 const merge_target_t *mt, 161 svn_boolean_t force_copy, 162 const char *source_abspath, 163 svn_cancel_func_t cancel_func, 164 void *cancel_baton, 165 apr_pool_t *result_pool, 166 apr_pool_t *scratch_pool) 167{ 168 svn_boolean_t old_is_binary, new_is_binary; 169 svn_subst_eol_style_t style; 170 const char *eol; 171 apr_hash_t *keywords; 172 svn_boolean_t special; 173 174 { 175 const char *old_mime_value 176 = svn_prop_get_value(mt->old_actual_props, SVN_PROP_MIME_TYPE); 177 const svn_prop_t *prop = get_prop(mt->prop_diff, SVN_PROP_MIME_TYPE); 178 const char *new_mime_value 179 = prop ? (prop->value ? prop->value->data : NULL) : old_mime_value; 180 181 old_is_binary = old_mime_value && svn_mime_type_is_binary(old_mime_value); 182 new_is_binary = new_mime_value && svn_mime_type_is_binary(new_mime_value);; 183 } 184 185 /* See what translations we want to do */ 186 if (old_is_binary && new_is_binary) 187 { 188 /* Case IV. Old and new props 'binary': detranslate keywords only */ 189 SVN_ERR(svn_wc__get_translate_info(NULL, NULL, &keywords, NULL, 190 mt->db, mt->local_abspath, 191 mt->old_actual_props, TRUE, 192 scratch_pool, scratch_pool)); 193 /* ### Why override 'special'? Elsewhere it has precedence. */ 194 special = FALSE; 195 eol = NULL; 196 style = svn_subst_eol_style_none; 197 } 198 else if (!old_is_binary && new_is_binary) 199 { 200 /* Case II. Old props indicate texty, new props indicate binary: 201 detranslate keywords and old eol-style */ 202 SVN_ERR(svn_wc__get_translate_info(&style, &eol, 203 &keywords, 204 &special, 205 mt->db, mt->local_abspath, 206 mt->old_actual_props, TRUE, 207 scratch_pool, scratch_pool)); 208 } 209 else 210 { 211 /* Case I & III. New props indicate texty, regardless of old props */ 212 213 /* In case the file used to be special, detranslate specially */ 214 SVN_ERR(svn_wc__get_translate_info(&style, &eol, 215 &keywords, 216 &special, 217 mt->db, mt->local_abspath, 218 mt->old_actual_props, TRUE, 219 scratch_pool, scratch_pool)); 220 221 if (special) 222 { 223 keywords = NULL; 224 eol = NULL; 225 style = svn_subst_eol_style_none; 226 } 227 else 228 { 229 const svn_prop_t *prop; 230 231 /* In case a new eol style was set, use that for detranslation */ 232 if ((prop = get_prop(mt->prop_diff, SVN_PROP_EOL_STYLE)) && prop->value) 233 { 234 /* Value added or changed */ 235 svn_subst_eol_style_from_value(&style, &eol, prop->value->data); 236 } 237 else if (!old_is_binary) 238 { 239 /* Already fetched */ 240 } 241 else 242 { 243 eol = NULL; 244 style = svn_subst_eol_style_none; 245 } 246 } 247 } 248 249 /* Now, detranslate with the settings we created above */ 250 251 if (force_copy || keywords || eol || special) 252 { 253 const char *temp_dir_abspath; 254 const char *detranslated; 255 256 /* Force a copy into the temporary wc area to avoid having 257 temporary files created below to appear in the actual wc. */ 258 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db, 259 mt->wri_abspath, 260 scratch_pool, scratch_pool)); 261 262 /* ### svn_subst_copy_and_translate4() also creates a tempfile 263 ### internally. Anyway to piggyback on that? */ 264 SVN_ERR(svn_io_open_unique_file3(NULL, &detranslated, temp_dir_abspath, 265 (force_copy 266 ? svn_io_file_del_none 267 : svn_io_file_del_on_pool_cleanup), 268 result_pool, scratch_pool)); 269 270 /* Always 'repair' EOLs here, so that we can apply a diff that 271 changes from inconsistent newlines and no 'svn:eol-style' to 272 consistent newlines and 'svn:eol-style' set. */ 273 274 if (style == svn_subst_eol_style_native) 275 eol = SVN_SUBST_NATIVE_EOL_STR; 276 else if (style != svn_subst_eol_style_fixed 277 && style != svn_subst_eol_style_none) 278 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL); 279 280 SVN_ERR(svn_subst_copy_and_translate4(source_abspath, 281 detranslated, 282 eol, 283 TRUE /* repair */, 284 keywords, 285 FALSE /* contract keywords */, 286 special, 287 cancel_func, cancel_baton, 288 scratch_pool)); 289 290 SVN_ERR(svn_dirent_get_absolute(detranslated_abspath, detranslated, 291 result_pool)); 292 } 293 else 294 *detranslated_abspath = apr_pstrdup(result_pool, source_abspath); 295 296 return SVN_NO_ERROR; 297} 298 299/* Updates (by copying and translating) the eol style in 300 OLD_TARGET_ABSPATH returning the filename containing the 301 correct eol style in NEW_TARGET_ABSPATH, if an eol style 302 change is contained in PROP_DIFF. */ 303static svn_error_t * 304maybe_update_target_eols(const char **new_target_abspath, 305 const apr_array_header_t *prop_diff, 306 const char *old_target_abspath, 307 svn_cancel_func_t cancel_func, 308 void *cancel_baton, 309 apr_pool_t *result_pool, 310 apr_pool_t *scratch_pool) 311{ 312 const svn_prop_t *prop = get_prop(prop_diff, SVN_PROP_EOL_STYLE); 313 314 if (prop && prop->value) 315 { 316 const char *eol; 317 const char *tmp_new; 318 319 svn_subst_eol_style_from_value(NULL, &eol, prop->value->data); 320 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_new, NULL, 321 svn_io_file_del_on_pool_cleanup, 322 result_pool, scratch_pool)); 323 324 /* Always 'repair' EOLs here, so that we can apply a diff that 325 changes from inconsistent newlines and no 'svn:eol-style' to 326 consistent newlines and 'svn:eol-style' set. */ 327 SVN_ERR(svn_subst_copy_and_translate4(old_target_abspath, 328 tmp_new, 329 eol, 330 TRUE /* repair */, 331 NULL /* keywords */, 332 FALSE /* expand */, 333 FALSE /* special */, 334 cancel_func, cancel_baton, 335 scratch_pool)); 336 *new_target_abspath = apr_pstrdup(result_pool, tmp_new); 337 } 338 else 339 *new_target_abspath = apr_pstrdup(result_pool, old_target_abspath); 340 341 return SVN_NO_ERROR; 342} 343 344 345/* Set *TARGET_MARKER, *LEFT_MARKER and *RIGHT_MARKER to strings suitable 346 for delimiting the alternative texts in a text conflict. Include in each 347 marker a string that may be given by TARGET_LABEL, LEFT_LABEL and 348 RIGHT_LABEL respectively or a default value where any of those are NULL. 349 350 Allocate the results in POOL or statically. */ 351static void 352init_conflict_markers(const char **target_marker, 353 const char **left_marker, 354 const char **right_marker, 355 const char *target_label, 356 const char *left_label, 357 const char *right_label, 358 apr_pool_t *pool) 359{ 360 /* Labels fall back to sensible defaults if not specified. */ 361 if (target_label) 362 *target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label); 363 else 364 *target_marker = "<<<<<<< .working"; 365 366 if (left_label) 367 *left_marker = apr_psprintf(pool, "||||||| %s", left_label); 368 else 369 *left_marker = "||||||| .old"; 370 371 if (right_label) 372 *right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label); 373 else 374 *right_marker = ">>>>>>> .new"; 375} 376 377/* Do a 3-way merge of the files at paths LEFT, DETRANSLATED_TARGET, 378 * and RIGHT, using diff options provided in MERGE_OPTIONS. Store the merge 379 * result in the file RESULT_F. 380 * If there are conflicts, set *CONTAINS_CONFLICTS to true, and use 381 * TARGET_LABEL, LEFT_LABEL, and RIGHT_LABEL as labels for conflict 382 * markers. Else, set *CONTAINS_CONFLICTS to false. 383 * Do all allocations in POOL. */ 384static svn_error_t * 385do_text_merge(svn_boolean_t *contains_conflicts, 386 apr_file_t *result_f, 387 const apr_array_header_t *merge_options, 388 const char *detranslated_target, 389 const char *left, 390 const char *right, 391 const char *target_label, 392 const char *left_label, 393 const char *right_label, 394 apr_pool_t *pool) 395{ 396 svn_diff_t *diff; 397 svn_stream_t *ostream; 398 const char *target_marker; 399 const char *left_marker; 400 const char *right_marker; 401 svn_diff_file_options_t *diff3_options; 402 403 diff3_options = svn_diff_file_options_create(pool); 404 405 if (merge_options) 406 SVN_ERR(svn_diff_file_options_parse(diff3_options, 407 merge_options, pool)); 408 409 410 init_conflict_markers(&target_marker, &left_marker, &right_marker, 411 target_label, left_label, right_label, pool); 412 413 SVN_ERR(svn_diff_file_diff3_2(&diff, left, detranslated_target, right, 414 diff3_options, pool)); 415 416 ostream = svn_stream_from_aprfile2(result_f, TRUE, pool); 417 418 SVN_ERR(svn_diff_file_output_merge2(ostream, diff, 419 left, detranslated_target, right, 420 left_marker, 421 target_marker, 422 right_marker, 423 "=======", /* separator */ 424 svn_diff_conflict_display_modified_latest, 425 pool)); 426 SVN_ERR(svn_stream_close(ostream)); 427 428 *contains_conflicts = svn_diff_contains_conflicts(diff); 429 430 return SVN_NO_ERROR; 431} 432 433/* Same as do_text_merge() above, but use the external diff3 434 * command DIFF3_CMD to perform the merge. Pass MERGE_OPTIONS 435 * to the diff3 command. Do all allocations in POOL. */ 436static svn_error_t * 437do_text_merge_external(svn_boolean_t *contains_conflicts, 438 apr_file_t *result_f, 439 const char *diff3_cmd, 440 const apr_array_header_t *merge_options, 441 const char *detranslated_target, 442 const char *left_abspath, 443 const char *right_abspath, 444 const char *target_label, 445 const char *left_label, 446 const char *right_label, 447 apr_pool_t *scratch_pool) 448{ 449 int exit_code; 450 451 SVN_ERR(svn_io_run_diff3_3(&exit_code, ".", 452 detranslated_target, left_abspath, right_abspath, 453 target_label, left_label, right_label, 454 result_f, diff3_cmd, 455 merge_options, scratch_pool)); 456 457 *contains_conflicts = exit_code == 1; 458 459 return SVN_NO_ERROR; 460} 461 462/* Preserve the three pre-merge files. 463 464 Create three empty files, with unique names that each include the 465 basename of TARGET_ABSPATH and one of LEFT_LABEL, RIGHT_LABEL and 466 TARGET_LABEL, in the directory that contains TARGET_ABSPATH. Typical 467 names are "foo.c.r37" or "foo.c.2.mine". Set *LEFT_COPY, *RIGHT_COPY and 468 *TARGET_COPY to their absolute paths. 469 470 Set *WORK_ITEMS to a list of new work items that will write copies of 471 LEFT_ABSPATH, RIGHT_ABSPATH and TARGET_ABSPATH into the three files, 472 translated to working-copy form. 473 474 The translation to working-copy form will be done according to the 475 versioned properties of TARGET_ABSPATH that are current when the work 476 queue items are executed. 477 478 If target_abspath is not versioned use detranslated_target_abspath 479 as the target file. 480 ### NOT IMPLEMENTED -- 'detranslated_target_abspath' is not used. 481*/ 482static svn_error_t * 483preserve_pre_merge_files(svn_skel_t **work_items, 484 const char **left_copy, 485 const char **right_copy, 486 const char **target_copy, 487 const merge_target_t *mt, 488 const char *left_abspath, 489 const char *right_abspath, 490 const char *left_label, 491 const char *right_label, 492 const char *target_label, 493 const char *detranslated_target_abspath, 494 svn_cancel_func_t cancel_func, 495 void *cancel_baton, 496 apr_pool_t *result_pool, 497 apr_pool_t *scratch_pool) 498{ 499 const char *tmp_left, *tmp_right, *detranslated_target_copy; 500 const char *dir_abspath, *target_name; 501 const char *wcroot_abspath, *temp_dir_abspath; 502 svn_skel_t *work_item, *last_items = NULL; 503 504 *work_items = NULL; 505 506 svn_dirent_split(&dir_abspath, &target_name, mt->local_abspath, 507 scratch_pool); 508 509 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, mt->db, mt->wri_abspath, 510 scratch_pool, scratch_pool)); 511 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db, 512 mt->wri_abspath, 513 scratch_pool, scratch_pool)); 514 515 /* Create three empty files in DIR_ABSPATH, naming them with unique names 516 that each include TARGET_NAME and one of {LEFT,RIGHT,TARGET}_LABEL, 517 and set *{LEFT,RIGHT,TARGET}_COPY to those names. */ 518 SVN_ERR(svn_io_open_uniquely_named( 519 NULL, left_copy, dir_abspath, target_name, left_label, 520 svn_io_file_del_none, result_pool, scratch_pool)); 521 SVN_ERR(svn_io_open_uniquely_named( 522 NULL, right_copy, dir_abspath, target_name, right_label, 523 svn_io_file_del_none, result_pool, scratch_pool)); 524 SVN_ERR(svn_io_open_uniquely_named( 525 NULL, target_copy, dir_abspath, target_name, target_label, 526 svn_io_file_del_none, result_pool, scratch_pool)); 527 528 /* We preserve all the files with keywords expanded and line 529 endings in local (working) form. */ 530 531 /* The workingqueue requires its paths to be in the subtree 532 relative to the wcroot path they are executed in. 533 534 Make our LEFT and RIGHT files 'local' if they aren't... */ 535 if (! svn_dirent_is_ancestor(wcroot_abspath, left_abspath)) 536 { 537 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_left, temp_dir_abspath, 538 svn_io_file_del_none, 539 scratch_pool, scratch_pool)); 540 SVN_ERR(svn_io_copy_file(left_abspath, tmp_left, TRUE, scratch_pool)); 541 542 /* And create a wq item to remove the file later */ 543 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath, 544 tmp_left, 545 result_pool, scratch_pool)); 546 547 last_items = svn_wc__wq_merge(last_items, work_item, result_pool); 548 } 549 else 550 tmp_left = left_abspath; 551 552 if (! svn_dirent_is_ancestor(wcroot_abspath, right_abspath)) 553 { 554 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_right, temp_dir_abspath, 555 svn_io_file_del_none, 556 scratch_pool, scratch_pool)); 557 SVN_ERR(svn_io_copy_file(right_abspath, tmp_right, TRUE, scratch_pool)); 558 559 /* And create a wq item to remove the file later */ 560 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath, 561 tmp_right, 562 result_pool, scratch_pool)); 563 564 last_items = svn_wc__wq_merge(last_items, work_item, result_pool); 565 } 566 else 567 tmp_right = right_abspath; 568 569 /* NOTE: Callers must ensure that the svn:eol-style and 570 svn:keywords property values are correct in the currently 571 installed props. With 'svn merge', it's no big deal. But 572 when 'svn up' calls this routine, it needs to make sure that 573 this routine is using the newest property values that may 574 have been received *during* the update. Since this routine 575 will be run from within a log-command, merge_file() 576 needs to make sure that a previous log-command to 'install 577 latest props' has already executed first. Ben and I just 578 checked, and that is indeed the order in which the log items 579 are written, so everything should be fine. Really. */ 580 581 /* Create LEFT and RIGHT backup files, in expanded form. 582 We use TARGET_ABSPATH's current properties to do the translation. */ 583 /* Derive the basenames of the 3 backup files. */ 584 SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item, 585 mt->db, mt->local_abspath, 586 tmp_left, *left_copy, 587 result_pool, scratch_pool)); 588 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 589 590 SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item, 591 mt->db, mt->local_abspath, 592 tmp_right, *right_copy, 593 result_pool, scratch_pool)); 594 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 595 596 /* Back up TARGET_ABSPATH through detranslation/retranslation: 597 the new translation properties may not match the current ones */ 598 SVN_ERR(detranslate_wc_file(&detranslated_target_copy, mt, TRUE, 599 mt->local_abspath, 600 cancel_func, cancel_baton, 601 scratch_pool, scratch_pool)); 602 603 SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item, 604 mt->db, mt->local_abspath, 605 detranslated_target_copy, 606 *target_copy, 607 result_pool, scratch_pool)); 608 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 609 610 /* And maybe delete some tempfiles */ 611 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath, 612 detranslated_target_copy, 613 result_pool, scratch_pool)); 614 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 615 616 *work_items = svn_wc__wq_merge(*work_items, last_items, result_pool); 617 618 return SVN_NO_ERROR; 619} 620 621/* Attempt a trivial merge of LEFT_ABSPATH and RIGHT_ABSPATH to 622 * the target file at TARGET_ABSPATH. 623 * 624 * These are the inherently trivial cases: 625 * 626 * left == right == target => no-op 627 * left != right, left == target => target := right 628 * 629 * This case is also treated as trivial: 630 * 631 * left != right, right == target => no-op 632 * 633 * ### Strictly, this case is a conflict, and the no-op outcome is only 634 * one of the possible resolutions. 635 * 636 * TODO: Raise a conflict at this level and implement the 'no-op' 637 * resolution of that conflict at a higher level, in preparation for 638 * being able to support stricter conflict detection. 639 * 640 * This case is inherently trivial but not currently handled here: 641 * 642 * left == right != target => no-op 643 * 644 * The files at LEFT_ABSPATH and RIGHT_ABSPATH are in repository normal 645 * form. The file at DETRANSLATED_TARGET_ABSPATH is a copy of the target, 646 * 'detranslated' to repository normal form, or may be the target file 647 * itself if no translation is necessary. 648 * 649 * When this function updates the target file, it translates to working copy 650 * form. 651 * 652 * On success, set *MERGE_OUTCOME to SVN_WC_MERGE_MERGED in case the 653 * target was changed, or to SVN_WC_MERGE_UNCHANGED if the target was not 654 * changed. Install work queue items allocated in RESULT_POOL in *WORK_ITEMS. 655 * On failure, set *MERGE_OUTCOME to SVN_WC_MERGE_NO_MERGE. 656 */ 657static svn_error_t * 658merge_file_trivial(svn_skel_t **work_items, 659 enum svn_wc_merge_outcome_t *merge_outcome, 660 const char *left_abspath, 661 const char *right_abspath, 662 const char *target_abspath, 663 const char *detranslated_target_abspath, 664 svn_boolean_t dry_run, 665 svn_wc__db_t *db, 666 svn_cancel_func_t cancel_func, 667 void *cancel_baton, 668 apr_pool_t *result_pool, 669 apr_pool_t *scratch_pool) 670{ 671 svn_skel_t *work_item; 672 svn_boolean_t same_left_right; 673 svn_boolean_t same_right_target; 674 svn_boolean_t same_left_target; 675 svn_node_kind_t kind; 676 svn_boolean_t is_special; 677 678 /* If the target is not a normal file, do not attempt a trivial merge. */ 679 SVN_ERR(svn_io_check_special_path(target_abspath, &kind, &is_special, 680 scratch_pool)); 681 if (kind != svn_node_file || is_special) 682 { 683 *merge_outcome = svn_wc_merge_no_merge; 684 return SVN_NO_ERROR; 685 } 686 687 /* Check the files */ 688 SVN_ERR(svn_io_files_contents_three_same_p(&same_left_right, 689 &same_right_target, 690 &same_left_target, 691 left_abspath, 692 right_abspath, 693 detranslated_target_abspath, 694 scratch_pool)); 695 696 /* If the LEFT side of the merge is equal to WORKING, then we can 697 * copy RIGHT directly. */ 698 if (same_left_target) 699 { 700 /* If the left side equals the right side, there is no change to merge 701 * so we leave the target unchanged. */ 702 if (same_left_right) 703 { 704 *merge_outcome = svn_wc_merge_unchanged; 705 } 706 else 707 { 708 *merge_outcome = svn_wc_merge_merged; 709 if (!dry_run) 710 { 711 const char *wcroot_abspath; 712 svn_boolean_t delete_src = FALSE; 713 714 /* The right_abspath might be outside our working copy. In that 715 case we should copy the file to a safe location before 716 installing to avoid breaking the workqueue. 717 718 This matches the behavior in preserve_pre_merge_files */ 719 720 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, 721 db, target_abspath, 722 scratch_pool, scratch_pool)); 723 724 if (!svn_dirent_is_child(wcroot_abspath, right_abspath, NULL)) 725 { 726 svn_stream_t *tmp_src; 727 svn_stream_t *tmp_dst; 728 729 SVN_ERR(svn_stream_open_readonly(&tmp_src, right_abspath, 730 scratch_pool, 731 scratch_pool)); 732 733 SVN_ERR(svn_wc__open_writable_base(&tmp_dst, &right_abspath, 734 NULL, NULL, 735 db, target_abspath, 736 scratch_pool, 737 scratch_pool)); 738 739 SVN_ERR(svn_stream_copy3(tmp_src, tmp_dst, 740 cancel_func, cancel_baton, 741 scratch_pool)); 742 743 delete_src = TRUE; 744 } 745 746 SVN_ERR(svn_wc__wq_build_file_install( 747 &work_item, db, target_abspath, right_abspath, 748 FALSE /* use_commit_times */, 749 FALSE /* record_fileinfo */, 750 result_pool, scratch_pool)); 751 *work_items = svn_wc__wq_merge(*work_items, work_item, 752 result_pool); 753 754 if (delete_src) 755 { 756 SVN_ERR(svn_wc__wq_build_file_remove( 757 &work_item, db, wcroot_abspath, 758 right_abspath, 759 result_pool, scratch_pool)); 760 *work_items = svn_wc__wq_merge(*work_items, work_item, 761 result_pool); 762 } 763 } 764 } 765 766 return SVN_NO_ERROR; 767 } 768 else 769 { 770 /* If the locally existing, changed file equals the incoming 'right' 771 * file, there is no conflict. For binary files, we historically 772 * conflicted them needlessly, while merge_text_file figured it out 773 * eventually and returned svn_wc_merge_unchanged for them, which 774 * is what we do here. */ 775 if (same_right_target) 776 { 777 *merge_outcome = svn_wc_merge_unchanged; 778 return SVN_NO_ERROR; 779 } 780 } 781 782 *merge_outcome = svn_wc_merge_no_merge; 783 return SVN_NO_ERROR; 784} 785 786 787/* Handle a non-trivial merge of 'text' files. (Assume that a trivial 788 * merge was not possible.) 789 * 790 * Set *WORK_ITEMS, *CONFLICT_SKEL and *MERGE_OUTCOME according to the 791 * result -- to install the merged file, or to indicate a conflict. 792 * 793 * On successful merge, leave the result in a temporary file and set 794 * *WORK_ITEMS to hold work items that will translate and install that 795 * file into its proper form and place (unless DRY_RUN) and delete the 796 * temporary file (in any case). Set *MERGE_OUTCOME to 'merged' or 797 * 'unchanged'. 798 * 799 * If a conflict occurs, set *MERGE_OUTCOME to 'conflicted', and (unless 800 * DRY_RUN) set *WORK_ITEMS and *CONFLICT_SKEL to record the conflict 801 * and copies of the pre-merge files. See preserve_pre_merge_files() 802 * for details. 803 * 804 * On entry, all of the output pointers must be non-null and *CONFLICT_SKEL 805 * must either point to an existing conflict skel or be NULL. 806 */ 807static svn_error_t* 808merge_text_file(svn_skel_t **work_items, 809 svn_skel_t **conflict_skel, 810 enum svn_wc_merge_outcome_t *merge_outcome, 811 const merge_target_t *mt, 812 const char *left_abspath, 813 const char *right_abspath, 814 const char *left_label, 815 const char *right_label, 816 const char *target_label, 817 svn_boolean_t dry_run, 818 const char *detranslated_target_abspath, 819 svn_cancel_func_t cancel_func, 820 void *cancel_baton, 821 apr_pool_t *result_pool, 822 apr_pool_t *scratch_pool) 823{ 824 apr_pool_t *pool = scratch_pool; /* ### temporary rename */ 825 svn_boolean_t contains_conflicts; 826 apr_file_t *result_f; 827 const char *result_target; 828 const char *base_name; 829 const char *temp_dir; 830 svn_skel_t *work_item; 831 832 *work_items = NULL; 833 834 base_name = svn_dirent_basename(mt->local_abspath, scratch_pool); 835 836 /* Open a second temporary file for writing; this is where diff3 837 will write the merged results. We want to use a tempfile 838 with a name that reflects the original, in case this 839 ultimately winds up in a conflict resolution editor. */ 840 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, mt->db, mt->wri_abspath, 841 pool, pool)); 842 SVN_ERR(svn_io_open_uniquely_named(&result_f, &result_target, 843 temp_dir, base_name, ".tmp", 844 svn_io_file_del_none, pool, pool)); 845 846 /* Run the external or internal merge, as requested. */ 847 if (mt->diff3_cmd) 848 SVN_ERR(do_text_merge_external(&contains_conflicts, 849 result_f, 850 mt->diff3_cmd, 851 mt->merge_options, 852 detranslated_target_abspath, 853 left_abspath, 854 right_abspath, 855 target_label, 856 left_label, 857 right_label, 858 pool)); 859 else /* Use internal merge. */ 860 SVN_ERR(do_text_merge(&contains_conflicts, 861 result_f, 862 mt->merge_options, 863 detranslated_target_abspath, 864 left_abspath, 865 right_abspath, 866 target_label, 867 left_label, 868 right_label, 869 pool)); 870 871 SVN_ERR(svn_io_file_close(result_f, pool)); 872 873 /* Determine the MERGE_OUTCOME, and record any conflict. */ 874 if (contains_conflicts && ! dry_run) 875 { 876 *merge_outcome = svn_wc_merge_conflict; 877 if (*merge_outcome == svn_wc_merge_conflict) 878 { 879 const char *left_copy, *right_copy, *target_copy; 880 881 /* Preserve the three conflict files */ 882 SVN_ERR(preserve_pre_merge_files( 883 &work_item, 884 &left_copy, &right_copy, &target_copy, 885 mt, left_abspath, right_abspath, 886 left_label, right_label, target_label, 887 detranslated_target_abspath, 888 cancel_func, cancel_baton, 889 result_pool, scratch_pool)); 890 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 891 892 /* Track the conflict marker files in the metadata. */ 893 894 if (!*conflict_skel) 895 *conflict_skel = svn_wc__conflict_skel_create(result_pool); 896 897 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(*conflict_skel, 898 mt->db, mt->local_abspath, 899 target_copy, 900 left_copy, 901 right_copy, 902 result_pool, 903 scratch_pool)); 904 } 905 906 if (*merge_outcome == svn_wc_merge_merged) 907 goto done; 908 } 909 else if (contains_conflicts && dry_run) 910 *merge_outcome = svn_wc_merge_conflict; 911 else 912 { 913 svn_boolean_t same, special; 914 915 /* If 'special', then use the detranslated form of the 916 target file. This is so we don't try to follow symlinks, 917 but the same treatment is probably also appropriate for 918 whatever special file types we may invent in the future. */ 919 SVN_ERR(svn_wc__get_translate_info(NULL, NULL, NULL, 920 &special, mt->db, mt->local_abspath, 921 mt->old_actual_props, TRUE, 922 pool, pool)); 923 SVN_ERR(svn_io_files_contents_same_p(&same, result_target, 924 (special ? 925 detranslated_target_abspath : 926 mt->local_abspath), 927 pool)); 928 929 *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged; 930 } 931 932 if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run) 933 { 934 /* replace TARGET_ABSPATH with the new merged file, expanding. */ 935 SVN_ERR(svn_wc__wq_build_file_install(&work_item, 936 mt->db, mt->local_abspath, 937 result_target, 938 FALSE /* use_commit_times */, 939 FALSE /* record_fileinfo */, 940 result_pool, scratch_pool)); 941 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 942 } 943 944done: 945 /* Remove the tempfile after use */ 946 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, mt->local_abspath, 947 result_target, 948 result_pool, scratch_pool)); 949 950 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 951 952 return SVN_NO_ERROR; 953} 954 955/* Handle a non-trivial merge of 'binary' files: don't actually merge, just 956 * flag a conflict. (Assume that a trivial merge was not possible.) 957 * 958 * Copy* the files at LEFT_ABSPATH and RIGHT_ABSPATH into the same directory 959 * as the target file, giving them unique names that start with the target 960 * file's name and end with LEFT_LABEL and RIGHT_LABEL respectively. 961 * If the merge target has been 'detranslated' to repository normal form, 962 * move the detranslated file similarly to a unique name ending with 963 * TARGET_LABEL. 964 * 965 * ### * Why do we copy the left and right temp files when we could (maybe 966 * not always?) move them? 967 * 968 * On entry, all of the output pointers must be non-null and *CONFLICT_SKEL 969 * must either point to an existing conflict skel or be NULL. 970 * 971 * Set *WORK_ITEMS, *CONFLICT_SKEL and *MERGE_OUTCOME to indicate the 972 * conflict. 973 * 974 * ### Why do we not use preserve_pre_merge_files() in here? The 975 * behaviour would be slightly different, more consistent: the 976 * preserved 'left' and 'right' files would be translated to working 977 * copy form, which may make a difference when a binary file 978 * contains keyword expansions or when some versions of the file are 979 * not 'binary' even though we're merging in 'binary files' mode. 980 */ 981static svn_error_t * 982merge_binary_file(svn_skel_t **work_items, 983 svn_skel_t **conflict_skel, 984 enum svn_wc_merge_outcome_t *merge_outcome, 985 const merge_target_t *mt, 986 const char *left_abspath, 987 const char *right_abspath, 988 const char *left_label, 989 const char *right_label, 990 const char *target_label, 991 svn_boolean_t dry_run, 992 const char *detranslated_target_abspath, 993 apr_pool_t *result_pool, 994 apr_pool_t *scratch_pool) 995{ 996 apr_pool_t *pool = scratch_pool; /* ### temporary rename */ 997 /* ### when making the binary-file backups, should we be honoring 998 keywords and eol stuff? */ 999 const char *left_copy, *right_copy; 1000 const char *merge_dirpath, *merge_filename; 1001 const char *conflict_wrk; 1002 1003 *work_items = NULL; 1004 1005 svn_dirent_split(&merge_dirpath, &merge_filename, mt->local_abspath, pool); 1006 1007 if (dry_run) 1008 { 1009 *merge_outcome = svn_wc_merge_conflict; 1010 return SVN_NO_ERROR; 1011 } 1012 1013 /* reserve names for backups of left and right fulltexts */ 1014 SVN_ERR(svn_io_open_uniquely_named(NULL, 1015 &left_copy, 1016 merge_dirpath, 1017 merge_filename, 1018 left_label, 1019 svn_io_file_del_none, 1020 pool, pool)); 1021 1022 SVN_ERR(svn_io_open_uniquely_named(NULL, 1023 &right_copy, 1024 merge_dirpath, 1025 merge_filename, 1026 right_label, 1027 svn_io_file_del_none, 1028 pool, pool)); 1029 1030 /* create the backup files */ 1031 SVN_ERR(svn_io_copy_file(left_abspath, left_copy, TRUE, pool)); 1032 SVN_ERR(svn_io_copy_file(right_abspath, right_copy, TRUE, pool)); 1033 1034 /* Was the merge target detranslated? */ 1035 if (strcmp(mt->local_abspath, detranslated_target_abspath) != 0) 1036 { 1037 /* Create a .mine file too */ 1038 SVN_ERR(svn_io_open_uniquely_named(NULL, 1039 &conflict_wrk, 1040 merge_dirpath, 1041 merge_filename, 1042 target_label, 1043 svn_io_file_del_none, 1044 pool, pool)); 1045 SVN_ERR(svn_wc__wq_build_file_move(work_items, mt->db, 1046 mt->local_abspath, 1047 detranslated_target_abspath, 1048 conflict_wrk, 1049 pool, result_pool)); 1050 } 1051 else 1052 { 1053 conflict_wrk = NULL; 1054 } 1055 1056 /* Mark target_abspath's entry as "Conflicted", and start tracking 1057 the backup files in the entry as well. */ 1058 if (!*conflict_skel) 1059 *conflict_skel = svn_wc__conflict_skel_create(result_pool); 1060 1061 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(*conflict_skel, 1062 mt->db, mt->local_abspath, 1063 conflict_wrk, 1064 left_copy, 1065 right_copy, 1066 result_pool, scratch_pool)); 1067 1068 *merge_outcome = svn_wc_merge_conflict; /* a conflict happened */ 1069 1070 return SVN_NO_ERROR; 1071} 1072 1073svn_error_t * 1074svn_wc__internal_merge(svn_skel_t **work_items, 1075 svn_skel_t **conflict_skel, 1076 enum svn_wc_merge_outcome_t *merge_outcome, 1077 svn_wc__db_t *db, 1078 const char *left_abspath, 1079 const char *right_abspath, 1080 const char *target_abspath, 1081 const char *wri_abspath, 1082 const char *left_label, 1083 const char *right_label, 1084 const char *target_label, 1085 apr_hash_t *old_actual_props, 1086 svn_boolean_t dry_run, 1087 const char *diff3_cmd, 1088 const apr_array_header_t *merge_options, 1089 const apr_array_header_t *prop_diff, 1090 svn_cancel_func_t cancel_func, 1091 void *cancel_baton, 1092 apr_pool_t *result_pool, 1093 apr_pool_t *scratch_pool) 1094{ 1095 const char *detranslated_target_abspath; 1096 svn_boolean_t is_binary = FALSE; 1097 const svn_prop_t *mimeprop; 1098 svn_skel_t *work_item; 1099 merge_target_t mt; 1100 1101 SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath)); 1102 SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath)); 1103 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath)); 1104 1105 *work_items = NULL; 1106 1107 /* Fill the merge target baton */ 1108 mt.db = db; 1109 mt.local_abspath = target_abspath; 1110 mt.wri_abspath = wri_abspath; 1111 mt.old_actual_props = old_actual_props; 1112 mt.prop_diff = prop_diff; 1113 mt.diff3_cmd = diff3_cmd; 1114 mt.merge_options = merge_options; 1115 1116 /* Decide if the merge target is a text or binary file. */ 1117 if ((mimeprop = get_prop(prop_diff, SVN_PROP_MIME_TYPE)) 1118 && mimeprop->value) 1119 is_binary = svn_mime_type_is_binary(mimeprop->value->data); 1120 else 1121 { 1122 const char *value = svn_prop_get_value(mt.old_actual_props, 1123 SVN_PROP_MIME_TYPE); 1124 1125 is_binary = value && svn_mime_type_is_binary(value); 1126 } 1127 1128 SVN_ERR(detranslate_wc_file(&detranslated_target_abspath, &mt, 1129 (! is_binary) && diff3_cmd != NULL, 1130 target_abspath, 1131 cancel_func, cancel_baton, 1132 scratch_pool, scratch_pool)); 1133 1134 /* We cannot depend on the left file to contain the same eols as the 1135 right file. If the merge target has mods, this will mark the entire 1136 file as conflicted, so we need to compensate. */ 1137 SVN_ERR(maybe_update_target_eols(&left_abspath, prop_diff, left_abspath, 1138 cancel_func, cancel_baton, 1139 scratch_pool, scratch_pool)); 1140 1141 SVN_ERR(merge_file_trivial(work_items, merge_outcome, 1142 left_abspath, right_abspath, 1143 target_abspath, detranslated_target_abspath, 1144 dry_run, db, cancel_func, cancel_baton, 1145 result_pool, scratch_pool)); 1146 if (*merge_outcome == svn_wc_merge_no_merge) 1147 { 1148 /* We have a non-trivial merge. If we classify it as a merge of 1149 * 'binary' files we'll just raise a conflict, otherwise we'll do 1150 * the actual merge of 'text' file contents. */ 1151 if (is_binary) 1152 { 1153 /* Raise a text conflict */ 1154 SVN_ERR(merge_binary_file(work_items, 1155 conflict_skel, 1156 merge_outcome, 1157 &mt, 1158 left_abspath, 1159 right_abspath, 1160 left_label, 1161 right_label, 1162 target_label, 1163 dry_run, 1164 detranslated_target_abspath, 1165 result_pool, scratch_pool)); 1166 } 1167 else 1168 { 1169 SVN_ERR(merge_text_file(work_items, 1170 conflict_skel, 1171 merge_outcome, 1172 &mt, 1173 left_abspath, 1174 right_abspath, 1175 left_label, 1176 right_label, 1177 target_label, 1178 dry_run, 1179 detranslated_target_abspath, 1180 cancel_func, cancel_baton, 1181 result_pool, scratch_pool)); 1182 } 1183 } 1184 1185 /* Merging is complete. Regardless of text or binariness, we might 1186 need to tweak the executable bit on the new working file, and 1187 possibly make it read-only. */ 1188 if (! dry_run) 1189 { 1190 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, 1191 target_abspath, 1192 result_pool, scratch_pool)); 1193 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1194 } 1195 1196 return SVN_NO_ERROR; 1197} 1198 1199 1200svn_error_t * 1201svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, 1202 enum svn_wc_notify_state_t *merge_props_outcome, 1203 svn_wc_context_t *wc_ctx, 1204 const char *left_abspath, 1205 const char *right_abspath, 1206 const char *target_abspath, 1207 const char *left_label, 1208 const char *right_label, 1209 const char *target_label, 1210 const svn_wc_conflict_version_t *left_version, 1211 const svn_wc_conflict_version_t *right_version, 1212 svn_boolean_t dry_run, 1213 const char *diff3_cmd, 1214 const apr_array_header_t *merge_options, 1215 apr_hash_t *original_props, 1216 const apr_array_header_t *prop_diff, 1217 svn_wc_conflict_resolver_func2_t conflict_func, 1218 void *conflict_baton, 1219 svn_cancel_func_t cancel_func, 1220 void *cancel_baton, 1221 apr_pool_t *scratch_pool) 1222{ 1223 const char *dir_abspath = svn_dirent_dirname(target_abspath, scratch_pool); 1224 svn_skel_t *work_items; 1225 svn_skel_t *conflict_skel = NULL; 1226 apr_hash_t *pristine_props = NULL; 1227 apr_hash_t *old_actual_props; 1228 apr_hash_t *new_actual_props = NULL; 1229 1230 SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath)); 1231 SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath)); 1232 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath)); 1233 1234 /* Before we do any work, make sure we hold a write lock. */ 1235 if (!dry_run) 1236 SVN_ERR(svn_wc__write_check(wc_ctx->db, dir_abspath, scratch_pool)); 1237 1238 /* Sanity check: the merge target must be a file under revision control */ 1239 { 1240 svn_wc__db_status_t status; 1241 svn_node_kind_t kind; 1242 svn_boolean_t had_props; 1243 svn_boolean_t props_mod; 1244 svn_boolean_t conflicted; 1245 1246 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 1247 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1248 NULL, NULL, NULL, NULL, NULL, NULL, 1249 &conflicted, NULL, &had_props, &props_mod, 1250 NULL, NULL, NULL, 1251 wc_ctx->db, target_abspath, 1252 scratch_pool, scratch_pool)); 1253 1254 if (kind != svn_node_file || (status != svn_wc__db_status_normal 1255 && status != svn_wc__db_status_added)) 1256 { 1257 *merge_content_outcome = svn_wc_merge_no_merge; 1258 if (merge_props_outcome) 1259 *merge_props_outcome = svn_wc_notify_state_unchanged; 1260 return SVN_NO_ERROR; 1261 } 1262 1263 if (conflicted) 1264 { 1265 svn_boolean_t text_conflicted; 1266 svn_boolean_t prop_conflicted; 1267 svn_boolean_t tree_conflicted; 1268 1269 SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted, 1270 &prop_conflicted, 1271 &tree_conflicted, 1272 wc_ctx->db, target_abspath, 1273 scratch_pool)); 1274 1275 /* We can't install two prop conflicts on a single node, so 1276 avoid even checking that we have to merge it */ 1277 if (text_conflicted || prop_conflicted || tree_conflicted) 1278 { 1279 return svn_error_createf( 1280 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 1281 _("Can't merge into conflicted node '%s'"), 1282 svn_dirent_local_style(target_abspath, 1283 scratch_pool)); 1284 } 1285 /* else: Conflict was resolved by removing markers */ 1286 } 1287 1288 if (merge_props_outcome && had_props) 1289 { 1290 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, 1291 wc_ctx->db, target_abspath, 1292 scratch_pool, scratch_pool)); 1293 } 1294 else if (merge_props_outcome) 1295 pristine_props = apr_hash_make(scratch_pool); 1296 1297 if (props_mod) 1298 { 1299 SVN_ERR(svn_wc__db_read_props(&old_actual_props, 1300 wc_ctx->db, target_abspath, 1301 scratch_pool, scratch_pool)); 1302 } 1303 else if (pristine_props) 1304 old_actual_props = pristine_props; 1305 else 1306 old_actual_props = apr_hash_make(scratch_pool); 1307 } 1308 1309 /* Merge the properties, if requested. We merge the properties first 1310 * because the properties can affect the text (EOL style, keywords). */ 1311 if (merge_props_outcome) 1312 { 1313 int i; 1314 1315 /* The PROPCHANGES may not have non-"normal" properties in it. If entry 1316 or wc props were allowed, then the following code would install them 1317 into the BASE and/or WORKING properties(!). */ 1318 for (i = prop_diff->nelts; i--; ) 1319 { 1320 const svn_prop_t *change = &APR_ARRAY_IDX(prop_diff, i, svn_prop_t); 1321 1322 if (!svn_wc_is_normal_prop(change->name)) 1323 return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL, 1324 _("The property '%s' may not be merged " 1325 "into '%s'."), 1326 change->name, 1327 svn_dirent_local_style(target_abspath, 1328 scratch_pool)); 1329 } 1330 1331 SVN_ERR(svn_wc__merge_props(&conflict_skel, 1332 merge_props_outcome, 1333 &new_actual_props, 1334 wc_ctx->db, target_abspath, 1335 original_props, pristine_props, old_actual_props, 1336 prop_diff, 1337 scratch_pool, scratch_pool)); 1338 } 1339 1340 /* Merge the text. */ 1341 SVN_ERR(svn_wc__internal_merge(&work_items, 1342 &conflict_skel, 1343 merge_content_outcome, 1344 wc_ctx->db, 1345 left_abspath, 1346 right_abspath, 1347 target_abspath, 1348 target_abspath, 1349 left_label, right_label, target_label, 1350 old_actual_props, 1351 dry_run, 1352 diff3_cmd, 1353 merge_options, 1354 prop_diff, 1355 cancel_func, cancel_baton, 1356 scratch_pool, scratch_pool)); 1357 1358 /* If this isn't a dry run, then update the DB, run the work, and 1359 * call the conflict resolver callback. */ 1360 if (!dry_run) 1361 { 1362 if (conflict_skel) 1363 { 1364 svn_skel_t *work_item; 1365 1366 SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_skel, 1367 left_version, 1368 right_version, 1369 scratch_pool, 1370 scratch_pool)); 1371 1372 SVN_ERR(svn_wc__conflict_create_markers(&work_item, 1373 wc_ctx->db, target_abspath, 1374 conflict_skel, 1375 scratch_pool, scratch_pool)); 1376 1377 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 1378 } 1379 1380 if (new_actual_props) 1381 SVN_ERR(svn_wc__db_op_set_props(wc_ctx->db, target_abspath, 1382 new_actual_props, 1383 svn_wc__has_magic_property(prop_diff), 1384 conflict_skel, work_items, 1385 scratch_pool)); 1386 else if (conflict_skel) 1387 SVN_ERR(svn_wc__db_op_mark_conflict(wc_ctx->db, target_abspath, 1388 conflict_skel, work_items, 1389 scratch_pool)); 1390 else if (work_items) 1391 SVN_ERR(svn_wc__db_wq_add(wc_ctx->db, target_abspath, work_items, 1392 scratch_pool)); 1393 1394 if (work_items) 1395 SVN_ERR(svn_wc__wq_run(wc_ctx->db, target_abspath, 1396 cancel_func, cancel_baton, 1397 scratch_pool)); 1398 1399 if (conflict_skel && conflict_func) 1400 { 1401 svn_boolean_t text_conflicted, prop_conflicted; 1402 1403 SVN_ERR(svn_wc__conflict_invoke_resolver( 1404 wc_ctx->db, target_abspath, 1405 conflict_skel, merge_options, 1406 conflict_func, conflict_baton, 1407 cancel_func, cancel_baton, 1408 scratch_pool)); 1409 1410 /* Reset *MERGE_CONTENT_OUTCOME etc. if a conflict was resolved. */ 1411 SVN_ERR(svn_wc__internal_conflicted_p( 1412 &text_conflicted, &prop_conflicted, NULL, 1413 wc_ctx->db, target_abspath, scratch_pool)); 1414 if (*merge_props_outcome == svn_wc_notify_state_conflicted 1415 && ! prop_conflicted) 1416 *merge_props_outcome = svn_wc_notify_state_merged; 1417 if (*merge_content_outcome == svn_wc_merge_conflict 1418 && ! text_conflicted) 1419 *merge_content_outcome = svn_wc_merge_merged; 1420 } 1421 } 1422 1423 return SVN_NO_ERROR; 1424} 1425