conflicts.c revision 362181
1/* 2 * conflicts.c: routines for managing conflict data. 3 * NOTE: this code doesn't know where the conflict is 4 * actually stored. 5 * 6 * ==================================================================== 7 * Licensed to the Apache Software Foundation (ASF) under one 8 * or more contributor license agreements. See the NOTICE file 9 * distributed with this work for additional information 10 * regarding copyright ownership. The ASF licenses this file 11 * to you under the Apache License, Version 2.0 (the 12 * "License"); you may not use this file except in compliance 13 * with the License. You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, 18 * software distributed under the License is distributed on an 19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 * KIND, either express or implied. See the License for the 21 * specific language governing permissions and limitations 22 * under the License. 23 * ==================================================================== 24 */ 25 26 27 28#include <string.h> 29 30#include <apr_pools.h> 31#include <apr_tables.h> 32#include <apr_hash.h> 33#include <apr_errno.h> 34 35#include "svn_hash.h" 36#include "svn_types.h" 37#include "svn_pools.h" 38#include "svn_string.h" 39#include "svn_error.h" 40#include "svn_dirent_uri.h" 41#include "svn_wc.h" 42#include "svn_io.h" 43#include "svn_diff.h" 44 45#include "wc.h" 46#include "wc_db.h" 47#include "conflicts.h" 48#include "workqueue.h" 49#include "props.h" 50 51#include "private/svn_wc_private.h" 52#include "private/svn_skel.h" 53#include "private/svn_sorts_private.h" 54#include "private/svn_string_private.h" 55 56#include "svn_private_config.h" 57 58/* -------------------------------------------------------------------- 59 * Conflict skel management 60 */ 61 62svn_skel_t * 63svn_wc__conflict_skel_create(apr_pool_t *result_pool) 64{ 65 svn_skel_t *conflict_skel = svn_skel__make_empty_list(result_pool); 66 67 /* Add empty CONFLICTS list */ 68 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel); 69 70 /* Add empty WHY list */ 71 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel); 72 73 return conflict_skel; 74} 75 76svn_error_t * 77svn_wc__conflict_skel_is_complete(svn_boolean_t *complete, 78 const svn_skel_t *conflict_skel) 79{ 80 *complete = FALSE; 81 82 if (svn_skel__list_length(conflict_skel) < 2) 83 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL, 84 _("Not a conflict skel")); 85 86 if (svn_skel__list_length(conflict_skel->children) < 2) 87 return SVN_NO_ERROR; /* WHY is not set */ 88 89 if (svn_skel__list_length(conflict_skel->children->next) == 0) 90 return SVN_NO_ERROR; /* No conflict set */ 91 92 *complete = TRUE; 93 return SVN_NO_ERROR; 94} 95 96/* Serialize a svn_wc_conflict_version_t before the existing data in skel */ 97static svn_error_t * 98conflict__prepend_location(svn_skel_t *skel, 99 const svn_wc_conflict_version_t *location, 100 svn_boolean_t allow_NULL, 101 apr_pool_t *result_pool, 102 apr_pool_t *scratch_pool) 103{ 104 svn_skel_t *loc; 105 SVN_ERR_ASSERT(location || allow_NULL); 106 107 if (!location) 108 { 109 svn_skel__prepend(svn_skel__make_empty_list(result_pool), skel); 110 return SVN_NO_ERROR; 111 } 112 113 /* ("subversion" repos_root_url repos_uuid repos_relpath rev kind) */ 114 loc = svn_skel__make_empty_list(result_pool); 115 116 svn_skel__prepend_str(svn_node_kind_to_word(location->node_kind), 117 loc, result_pool); 118 119 svn_skel__prepend_int(location->peg_rev, loc, result_pool); 120 121 svn_skel__prepend_str(apr_pstrdup(result_pool, location->path_in_repos), loc, 122 result_pool); 123 124 if (!location->repos_uuid) /* Can theoretically be NULL */ 125 svn_skel__prepend(svn_skel__make_empty_list(result_pool), loc); 126 else 127 svn_skel__prepend_str(location->repos_uuid, loc, result_pool); 128 129 svn_skel__prepend_str(apr_pstrdup(result_pool, location->repos_url), loc, 130 result_pool); 131 132 svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION, loc, result_pool); 133 134 svn_skel__prepend(loc, skel); 135 return SVN_NO_ERROR; 136} 137 138/* Deserialize a svn_wc_conflict_version_t from the skel. 139 Set *LOCATION to NULL when the data is not a svn_wc_conflict_version_t. */ 140static svn_error_t * 141conflict__read_location(svn_wc_conflict_version_t **location, 142 const svn_skel_t *skel, 143 apr_pool_t *result_pool, 144 apr_pool_t *scratch_pool) 145{ 146 const char *repos_root_url; 147 const char *repos_uuid; 148 const char *repos_relpath; 149 svn_revnum_t revision; 150 apr_int64_t v; 151 svn_node_kind_t node_kind; /* note that 'none' is a legitimate value */ 152 const char *kind_str; 153 154 const svn_skel_t *c = skel->children; 155 156 if (!svn_skel__matches_atom(c, SVN_WC__CONFLICT_SRC_SUBVERSION)) 157 { 158 *location = NULL; 159 return SVN_NO_ERROR; 160 } 161 c = c->next; 162 163 repos_root_url = apr_pstrmemdup(result_pool, c->data, c->len); 164 c = c->next; 165 166 if (c->is_atom) 167 repos_uuid = apr_pstrmemdup(result_pool, c->data, c->len); 168 else 169 repos_uuid = NULL; 170 c = c->next; 171 172 repos_relpath = apr_pstrmemdup(result_pool, c->data, c->len); 173 c = c->next; 174 175 SVN_ERR(svn_skel__parse_int(&v, c, scratch_pool)); 176 revision = (svn_revnum_t)v; 177 c = c->next; 178 179 kind_str = apr_pstrmemdup(scratch_pool, c->data, c->len); 180 node_kind = svn_node_kind_from_word(kind_str); 181 182 *location = svn_wc_conflict_version_create2(repos_root_url, 183 repos_uuid, 184 repos_relpath, 185 revision, 186 node_kind, 187 result_pool); 188 return SVN_NO_ERROR; 189} 190 191/* Get the operation part of CONFLICT_SKELL or NULL if no operation is set 192 at this time */ 193static svn_error_t * 194conflict__get_operation(svn_skel_t **why, 195 const svn_skel_t *conflict_skel) 196{ 197 SVN_ERR_ASSERT(conflict_skel 198 && conflict_skel->children 199 && conflict_skel->children->next 200 && !conflict_skel->children->next->is_atom); 201 202 *why = conflict_skel->children; 203 204 if (!(*why)->children) 205 *why = NULL; /* Operation is not set yet */ 206 207 return SVN_NO_ERROR; 208} 209 210 211svn_error_t * 212svn_wc__conflict_skel_set_op_update(svn_skel_t *conflict_skel, 213 const svn_wc_conflict_version_t *original, 214 const svn_wc_conflict_version_t *target, 215 apr_pool_t *result_pool, 216 apr_pool_t *scratch_pool) 217{ 218 svn_skel_t *why; 219 svn_skel_t *origins; 220 221 SVN_ERR_ASSERT(conflict_skel 222 && conflict_skel->children 223 && conflict_skel->children->next 224 && !conflict_skel->children->next->is_atom); 225 226 SVN_ERR(conflict__get_operation(&why, conflict_skel)); 227 228 SVN_ERR_ASSERT(why == NULL); /* No operation set */ 229 230 why = conflict_skel->children; 231 232 origins = svn_skel__make_empty_list(result_pool); 233 234 SVN_ERR(conflict__prepend_location(origins, target, TRUE, 235 result_pool, scratch_pool)); 236 SVN_ERR(conflict__prepend_location(origins, original, TRUE, 237 result_pool, scratch_pool)); 238 239 svn_skel__prepend(origins, why); 240 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_UPDATE, why, result_pool); 241 242 return SVN_NO_ERROR; 243} 244 245svn_error_t * 246svn_wc__conflict_skel_set_op_switch(svn_skel_t *conflict_skel, 247 const svn_wc_conflict_version_t *original, 248 const svn_wc_conflict_version_t *target, 249 apr_pool_t *result_pool, 250 apr_pool_t *scratch_pool) 251{ 252 svn_skel_t *why; 253 svn_skel_t *origins; 254 255 SVN_ERR_ASSERT(conflict_skel 256 && conflict_skel->children 257 && conflict_skel->children->next 258 && !conflict_skel->children->next->is_atom); 259 260 SVN_ERR(conflict__get_operation(&why, conflict_skel)); 261 262 SVN_ERR_ASSERT(why == NULL); /* No operation set */ 263 264 why = conflict_skel->children; 265 266 origins = svn_skel__make_empty_list(result_pool); 267 268 SVN_ERR(conflict__prepend_location(origins, target, TRUE, 269 result_pool, scratch_pool)); 270 SVN_ERR(conflict__prepend_location(origins, original, TRUE, 271 result_pool, scratch_pool)); 272 273 svn_skel__prepend(origins, why); 274 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_SWITCH, why, result_pool); 275 276 return SVN_NO_ERROR; 277} 278 279svn_error_t * 280svn_wc__conflict_skel_set_op_merge(svn_skel_t *conflict_skel, 281 const svn_wc_conflict_version_t *left, 282 const svn_wc_conflict_version_t *right, 283 apr_pool_t *result_pool, 284 apr_pool_t *scratch_pool) 285{ 286 svn_skel_t *why; 287 svn_skel_t *origins; 288 289 SVN_ERR_ASSERT(conflict_skel 290 && conflict_skel->children 291 && conflict_skel->children->next 292 && !conflict_skel->children->next->is_atom); 293 294 SVN_ERR(conflict__get_operation(&why, conflict_skel)); 295 296 SVN_ERR_ASSERT(why == NULL); /* No operation set */ 297 298 why = conflict_skel->children; 299 300 origins = svn_skel__make_empty_list(result_pool); 301 302 SVN_ERR(conflict__prepend_location(origins, right, TRUE, 303 result_pool, scratch_pool)); 304 305 SVN_ERR(conflict__prepend_location(origins, left, TRUE, 306 result_pool, scratch_pool)); 307 308 svn_skel__prepend(origins, why); 309 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_MERGE, why, result_pool); 310 311 return SVN_NO_ERROR; 312} 313 314/* Gets the conflict data of the specified type CONFLICT_TYPE from 315 CONFLICT_SKEL, or NULL if no such conflict is recorded */ 316static svn_error_t * 317conflict__get_conflict(svn_skel_t **conflict, 318 const svn_skel_t *conflict_skel, 319 const char *conflict_type) 320{ 321 svn_skel_t *c; 322 323 SVN_ERR_ASSERT(conflict_skel 324 && conflict_skel->children 325 && conflict_skel->children->next 326 && !conflict_skel->children->next->is_atom); 327 328 for(c = conflict_skel->children->next->children; 329 c; 330 c = c->next) 331 { 332 if (svn_skel__matches_atom(c->children, conflict_type)) 333 { 334 *conflict = c; 335 return SVN_NO_ERROR; 336 } 337 } 338 339 *conflict = NULL; 340 341 return SVN_NO_ERROR; 342} 343 344svn_error_t * 345svn_wc__conflict_skel_add_text_conflict(svn_skel_t *conflict_skel, 346 svn_wc__db_t *db, 347 const char *wri_abspath, 348 const char *mine_abspath, 349 const char *their_old_abspath, 350 const char *their_abspath, 351 apr_pool_t *result_pool, 352 apr_pool_t *scratch_pool) 353{ 354 svn_skel_t *text_conflict; 355 svn_skel_t *markers; 356 357 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel, 358 SVN_WC__CONFLICT_KIND_TEXT)); 359 360 SVN_ERR_ASSERT(!text_conflict); /* ### Use proper error? */ 361 362 /* Current skel format 363 ("text" 364 (OLD MINE OLD-THEIRS THEIRS)) */ 365 366 text_conflict = svn_skel__make_empty_list(result_pool); 367 markers = svn_skel__make_empty_list(result_pool); 368 369if (their_abspath) 370 { 371 const char *their_relpath; 372 373 SVN_ERR(svn_wc__db_to_relpath(&their_relpath, 374 db, wri_abspath, their_abspath, 375 result_pool, scratch_pool)); 376 svn_skel__prepend_str(their_relpath, markers, result_pool); 377 } 378 else 379 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers); 380 381 if (mine_abspath) 382 { 383 const char *mine_relpath; 384 385 SVN_ERR(svn_wc__db_to_relpath(&mine_relpath, 386 db, wri_abspath, mine_abspath, 387 result_pool, scratch_pool)); 388 svn_skel__prepend_str(mine_relpath, markers, result_pool); 389 } 390 else 391 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers); 392 393 if (their_old_abspath) 394 { 395 const char *original_relpath; 396 397 SVN_ERR(svn_wc__db_to_relpath(&original_relpath, 398 db, wri_abspath, their_old_abspath, 399 result_pool, scratch_pool)); 400 svn_skel__prepend_str(original_relpath, markers, result_pool); 401 } 402 else 403 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers); 404 405 svn_skel__prepend(markers, text_conflict); 406 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TEXT, text_conflict, 407 result_pool); 408 409 /* And add it to the conflict skel */ 410 svn_skel__prepend(text_conflict, conflict_skel->children->next); 411 412 return SVN_NO_ERROR; 413} 414 415svn_error_t * 416svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel, 417 svn_wc__db_t *db, 418 const char *wri_abspath, 419 const char *marker_abspath, 420 const apr_hash_t *mine_props, 421 const apr_hash_t *their_old_props, 422 const apr_hash_t *their_props, 423 const apr_hash_t *conflicted_prop_names, 424 apr_pool_t *result_pool, 425 apr_pool_t *scratch_pool) 426{ 427 svn_skel_t *prop_conflict; 428 svn_skel_t *props; 429 svn_skel_t *conflict_names; 430 svn_skel_t *markers; 431 apr_hash_index_t *hi; 432 433 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel, 434 SVN_WC__CONFLICT_KIND_PROP)); 435 436 SVN_ERR_ASSERT(!prop_conflict); /* ### Use proper error? */ 437 438 /* This function currently implements: 439 ("prop" 440 ("marker_relpath") 441 prop-conflicted_prop_names 442 old-props 443 mine-props 444 their-props) 445 NULL lists are recorded as "" */ 446 /* ### Seems that this may not match what we read out. Read-out of 447 * 'theirs-old' comes as NULL. */ 448 449 prop_conflict = svn_skel__make_empty_list(result_pool); 450 451 if (their_props) 452 { 453 SVN_ERR(svn_skel__unparse_proplist(&props, their_props, result_pool)); 454 svn_skel__prepend(props, prop_conflict); 455 } 456 else 457 svn_skel__prepend_str("", prop_conflict, result_pool); /* No their_props */ 458 459 if (mine_props) 460 { 461 SVN_ERR(svn_skel__unparse_proplist(&props, mine_props, result_pool)); 462 svn_skel__prepend(props, prop_conflict); 463 } 464 else 465 svn_skel__prepend_str("", prop_conflict, result_pool); /* No mine_props */ 466 467 if (their_old_props) 468 { 469 SVN_ERR(svn_skel__unparse_proplist(&props, their_old_props, 470 result_pool)); 471 svn_skel__prepend(props, prop_conflict); 472 } 473 else 474 svn_skel__prepend_str("", prop_conflict, result_pool); /* No old_props */ 475 476 conflict_names = svn_skel__make_empty_list(result_pool); 477 for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)conflicted_prop_names); 478 hi; 479 hi = apr_hash_next(hi)) 480 { 481 svn_skel__prepend_str(apr_pstrdup(result_pool, apr_hash_this_key(hi)), 482 conflict_names, 483 result_pool); 484 } 485 svn_skel__prepend(conflict_names, prop_conflict); 486 487 markers = svn_skel__make_empty_list(result_pool); 488 489 if (marker_abspath) 490 { 491 const char *marker_relpath; 492 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, wri_abspath, 493 marker_abspath, 494 result_pool, scratch_pool)); 495 496 svn_skel__prepend_str(marker_relpath, markers, result_pool); 497 } 498/*else // ### set via svn_wc__conflict_create_markers 499 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);*/ 500 501 svn_skel__prepend(markers, prop_conflict); 502 503 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_conflict, result_pool); 504 505 /* And add it to the conflict skel */ 506 svn_skel__prepend(prop_conflict, conflict_skel->children->next); 507 508 return SVN_NO_ERROR; 509} 510 511/* A map for svn_wc_conflict_reason_t values. */ 512static const svn_token_map_t reason_map[] = 513{ 514 { "edited", svn_wc_conflict_reason_edited }, 515 { "obstructed", svn_wc_conflict_reason_obstructed }, 516 { "deleted", svn_wc_conflict_reason_deleted }, 517 { "missing", svn_wc_conflict_reason_missing }, 518 { "unversioned", svn_wc_conflict_reason_unversioned }, 519 { "added", svn_wc_conflict_reason_added }, 520 { "replaced", svn_wc_conflict_reason_replaced }, 521 { "moved-away", svn_wc_conflict_reason_moved_away }, 522 { "moved-here", svn_wc_conflict_reason_moved_here }, 523 { NULL } 524}; 525 526static const svn_token_map_t action_map[] = 527{ 528 { "edited", svn_wc_conflict_action_edit }, 529 { "added", svn_wc_conflict_action_add }, 530 { "deleted", svn_wc_conflict_action_delete }, 531 { "replaced", svn_wc_conflict_action_replace }, 532 { NULL } 533}; 534 535svn_error_t * 536svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel, 537 svn_wc__db_t *db, 538 const char *wri_abspath, 539 svn_wc_conflict_reason_t reason, 540 svn_wc_conflict_action_t action, 541 const char *move_src_op_root_abspath, 542 const char *move_dst_op_root_abspath, 543 apr_pool_t *result_pool, 544 apr_pool_t *scratch_pool) 545{ 546 svn_skel_t *tree_conflict; 547 svn_skel_t *markers; 548 549 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel, 550 SVN_WC__CONFLICT_KIND_TREE)); 551 552 SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */ 553 554 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_moved_away 555 || !move_src_op_root_abspath); /* ### Use proper error? */ 556 557 tree_conflict = svn_skel__make_empty_list(result_pool); 558 559 if (reason == svn_wc_conflict_reason_moved_away) 560 { 561 if (move_dst_op_root_abspath) 562 { 563 const char *move_dst_op_root_relpath; 564 565 SVN_ERR(svn_wc__db_to_relpath(&move_dst_op_root_relpath, 566 db, wri_abspath, 567 move_dst_op_root_abspath, 568 result_pool, scratch_pool)); 569 570 svn_skel__prepend_str(move_dst_op_root_relpath, tree_conflict, 571 result_pool); 572 } 573 574 if (move_src_op_root_abspath) 575 { 576 const char *move_src_op_root_relpath; 577 578 SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath, 579 db, wri_abspath, 580 move_src_op_root_abspath, 581 result_pool, scratch_pool)); 582 583 svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict, 584 result_pool); 585 } 586 } 587 588 svn_skel__prepend_str(svn_token__to_word(action_map, action), 589 tree_conflict, result_pool); 590 591 svn_skel__prepend_str(svn_token__to_word(reason_map, reason), 592 tree_conflict, result_pool); 593 594 /* Tree conflicts have no marker files */ 595 markers = svn_skel__make_empty_list(result_pool); 596 svn_skel__prepend(markers, tree_conflict); 597 598 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE, tree_conflict, 599 result_pool); 600 601 /* And add it to the conflict skel */ 602 svn_skel__prepend(tree_conflict, conflict_skel->children->next); 603 604 return SVN_NO_ERROR; 605} 606 607svn_error_t * 608svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved, 609 svn_skel_t *conflict_skel, 610 svn_wc__db_t *db, 611 const char *wri_abspath, 612 svn_boolean_t resolve_text, 613 const char *resolve_prop, 614 svn_boolean_t resolve_tree, 615 apr_pool_t *result_pool, 616 apr_pool_t *scratch_pool) 617{ 618 svn_skel_t *op; 619 svn_skel_t **pconflict; 620 SVN_ERR(conflict__get_operation(&op, conflict_skel)); 621 622 if (!op) 623 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL, 624 _("Not a completed conflict skel")); 625 626 /* We are going to drop items from a linked list. Instead of keeping 627 a pointer to the item we want to drop we store a pointer to the 628 pointer of what we may drop, to allow setting it to the next item. */ 629 630 pconflict = &(conflict_skel->children->next->children); 631 while (*pconflict) 632 { 633 svn_skel_t *c = (*pconflict)->children; 634 635 if (resolve_text 636 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT)) 637 { 638 /* Remove the text conflict from the linked list */ 639 *pconflict = (*pconflict)->next; 640 continue; 641 } 642 else if (resolve_prop 643 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_PROP)) 644 { 645 svn_skel_t **ppropnames = &(c->next->next->children); 646 647 if (resolve_prop[0] == '\0') 648 *ppropnames = NULL; /* remove all conflicted property names */ 649 else 650 while (*ppropnames) 651 { 652 if (svn_skel__matches_atom(*ppropnames, resolve_prop)) 653 { 654 *ppropnames = (*ppropnames)->next; 655 break; 656 } 657 ppropnames = &((*ppropnames)->next); 658 } 659 660 /* If no conflicted property names left */ 661 if (!c->next->next->children) 662 { 663 /* Remove the propery conflict skel from the linked list */ 664 *pconflict = (*pconflict)->next; 665 continue; 666 } 667 } 668 else if (resolve_tree 669 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TREE)) 670 { 671 /* Remove the tree conflict from the linked list */ 672 *pconflict = (*pconflict)->next; 673 continue; 674 } 675 676 pconflict = &((*pconflict)->next); 677 } 678 679 if (completely_resolved) 680 { 681 /* Nice, we can just call the complete function */ 682 svn_boolean_t complete_conflict; 683 SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict, 684 conflict_skel)); 685 686 *completely_resolved = !complete_conflict; 687 } 688 return SVN_NO_ERROR; 689} 690 691 692/* A map for svn_wc_operation_t values. */ 693static const svn_token_map_t operation_map[] = 694{ 695 { "", svn_wc_operation_none }, 696 { SVN_WC__CONFLICT_OP_UPDATE, svn_wc_operation_update }, 697 { SVN_WC__CONFLICT_OP_SWITCH, svn_wc_operation_switch }, 698 { SVN_WC__CONFLICT_OP_MERGE, svn_wc_operation_merge }, 699 { NULL } 700}; 701 702svn_error_t * 703svn_wc__conflict_read_info(svn_wc_operation_t *operation, 704 const apr_array_header_t **locations, 705 svn_boolean_t *text_conflicted, 706 svn_boolean_t *prop_conflicted, 707 svn_boolean_t *tree_conflicted, 708 svn_wc__db_t *db, 709 const char *wri_abspath, 710 const svn_skel_t *conflict_skel, 711 apr_pool_t *result_pool, 712 apr_pool_t *scratch_pool) 713{ 714 svn_skel_t *op; 715 const svn_skel_t *c; 716 717 SVN_ERR(conflict__get_operation(&op, conflict_skel)); 718 719 if (!op) 720 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL, 721 _("Not a completed conflict skel")); 722 723 c = op->children; 724 if (operation) 725 { 726 int value = svn_token__from_mem(operation_map, c->data, c->len); 727 728 if (value != SVN_TOKEN_UNKNOWN) 729 *operation = value; 730 else 731 *operation = svn_wc_operation_none; 732 } 733 c = c->next; 734 735 if (locations && c->children) 736 { 737 const svn_skel_t *loc_skel; 738 svn_wc_conflict_version_t *loc; 739 apr_array_header_t *locs = apr_array_make(result_pool, 2, sizeof(loc)); 740 741 for (loc_skel = c->children; loc_skel; loc_skel = loc_skel->next) 742 { 743 SVN_ERR(conflict__read_location(&loc, loc_skel, result_pool, 744 scratch_pool)); 745 746 APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc; 747 } 748 749 *locations = locs; 750 } 751 else if (locations) 752 *locations = NULL; 753 754 if (text_conflicted) 755 { 756 svn_skel_t *c_skel; 757 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel, 758 SVN_WC__CONFLICT_KIND_TEXT)); 759 760 *text_conflicted = (c_skel != NULL); 761 } 762 763 if (prop_conflicted) 764 { 765 svn_skel_t *c_skel; 766 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel, 767 SVN_WC__CONFLICT_KIND_PROP)); 768 769 *prop_conflicted = (c_skel != NULL); 770 } 771 772 if (tree_conflicted) 773 { 774 svn_skel_t *c_skel; 775 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel, 776 SVN_WC__CONFLICT_KIND_TREE)); 777 778 *tree_conflicted = (c_skel != NULL); 779 } 780 781 return SVN_NO_ERROR; 782} 783 784 785svn_error_t * 786svn_wc__conflict_read_text_conflict(const char **mine_abspath, 787 const char **their_old_abspath, 788 const char **their_abspath, 789 svn_wc__db_t *db, 790 const char *wri_abspath, 791 const svn_skel_t *conflict_skel, 792 apr_pool_t *result_pool, 793 apr_pool_t *scratch_pool) 794{ 795 svn_skel_t *text_conflict; 796 const svn_skel_t *m; 797 798 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel, 799 SVN_WC__CONFLICT_KIND_TEXT)); 800 801 if (!text_conflict) 802 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set")); 803 804 m = text_conflict->children->next->children; 805 806 if (their_old_abspath) 807 { 808 if (m->is_atom) 809 { 810 const char *original_relpath; 811 812 original_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len); 813 SVN_ERR(svn_wc__db_from_relpath(their_old_abspath, 814 db, wri_abspath, original_relpath, 815 result_pool, scratch_pool)); 816 } 817 else 818 *their_old_abspath = NULL; 819 } 820 m = m->next; 821 822 if (mine_abspath) 823 { 824 if (m->is_atom) 825 { 826 const char *mine_relpath; 827 828 mine_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len); 829 SVN_ERR(svn_wc__db_from_relpath(mine_abspath, 830 db, wri_abspath, mine_relpath, 831 result_pool, scratch_pool)); 832 } 833 else 834 *mine_abspath = NULL; 835 } 836 m = m->next; 837 838 if (their_abspath) 839 { 840 if (m->is_atom) 841 { 842 const char *their_relpath; 843 844 their_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len); 845 SVN_ERR(svn_wc__db_from_relpath(their_abspath, 846 db, wri_abspath, their_relpath, 847 result_pool, scratch_pool)); 848 } 849 else 850 *their_abspath = NULL; 851 } 852 853 return SVN_NO_ERROR; 854} 855 856svn_error_t * 857svn_wc__conflict_read_prop_conflict(const char **marker_abspath, 858 apr_hash_t **mine_props, 859 apr_hash_t **their_old_props, 860 apr_hash_t **their_props, 861 apr_hash_t **conflicted_prop_names, 862 svn_wc__db_t *db, 863 const char *wri_abspath, 864 const svn_skel_t *conflict_skel, 865 apr_pool_t *result_pool, 866 apr_pool_t *scratch_pool) 867{ 868 svn_skel_t *prop_conflict; 869 const svn_skel_t *c; 870 871 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel, 872 SVN_WC__CONFLICT_KIND_PROP)); 873 874 if (!prop_conflict) 875 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set")); 876 877 c = prop_conflict->children; 878 879 c = c->next; /* Skip "prop" */ 880 881 /* Get marker file */ 882 if (marker_abspath) 883 { 884 const char *marker_relpath; 885 886 if (c->children && c->children->is_atom) 887 { 888 marker_relpath = apr_pstrmemdup(result_pool, c->children->data, 889 c->children->len); 890 891 SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath, 892 marker_relpath, 893 result_pool, scratch_pool)); 894 } 895 else 896 *marker_abspath = NULL; 897 } 898 c = c->next; 899 900 /* Get conflicted properties */ 901 if (conflicted_prop_names) 902 { 903 const svn_skel_t *name; 904 *conflicted_prop_names = apr_hash_make(result_pool); 905 906 for (name = c->children; name; name = name->next) 907 { 908 svn_hash_sets(*conflicted_prop_names, 909 apr_pstrmemdup(result_pool, name->data, name->len), 910 ""); 911 } 912 } 913 c = c->next; 914 915 /* Get original properties */ 916 if (their_old_props) 917 { 918 if (c->is_atom) 919 *their_old_props = apr_hash_make(result_pool); 920 else 921 SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool)); 922 } 923 c = c->next; 924 925 /* Get mine properties */ 926 if (mine_props) 927 { 928 if (c->is_atom) 929 *mine_props = apr_hash_make(result_pool); 930 else 931 SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool)); 932 } 933 c = c->next; 934 935 /* Get their properties */ 936 if (their_props) 937 { 938 if (c->is_atom) 939 *their_props = apr_hash_make(result_pool); 940 else 941 SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool)); 942 } 943 944 return SVN_NO_ERROR; 945} 946 947svn_error_t * 948svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *reason, 949 svn_wc_conflict_action_t *action, 950 const char **move_src_op_root_abspath, 951 const char **move_dst_op_root_abspath, 952 svn_wc__db_t *db, 953 const char *wri_abspath, 954 const svn_skel_t *conflict_skel, 955 apr_pool_t *result_pool, 956 apr_pool_t *scratch_pool) 957{ 958 svn_skel_t *tree_conflict; 959 const svn_skel_t *c; 960 svn_boolean_t is_moved_away = FALSE; 961 962 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel, 963 SVN_WC__CONFLICT_KIND_TREE)); 964 965 if (!tree_conflict) 966 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set")); 967 968 c = tree_conflict->children; 969 970 c = c->next; /* Skip "tree" */ 971 972 c = c->next; /* Skip markers */ 973 974 { 975 int value = svn_token__from_mem(reason_map, c->data, c->len); 976 977 if (reason) 978 { 979 if (value != SVN_TOKEN_UNKNOWN) 980 *reason = value; 981 else 982 *reason = svn_wc_conflict_reason_edited; 983 } 984 985 is_moved_away = (value == svn_wc_conflict_reason_moved_away); 986 } 987 c = c->next; 988 989 if (action) 990 { 991 int value = svn_token__from_mem(action_map, c->data, c->len); 992 993 if (value != SVN_TOKEN_UNKNOWN) 994 *action = value; 995 else 996 *action = svn_wc_conflict_action_edit; 997 } 998 999 c = c->next; 1000 1001 if (move_src_op_root_abspath || move_dst_op_root_abspath) 1002 { 1003 /* Only set for update and switch tree conflicts */ 1004 if (c && is_moved_away && move_src_op_root_abspath) 1005 { 1006 const char *move_src_op_root_relpath 1007 = apr_pstrmemdup(scratch_pool, c->data, c->len); 1008 1009 SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath, 1010 db, wri_abspath, 1011 move_src_op_root_relpath, 1012 result_pool, scratch_pool)); 1013 } 1014 else if (move_src_op_root_abspath) 1015 *move_src_op_root_abspath = NULL; 1016 1017 if (c) 1018 c = c->next; 1019 1020 if (c && is_moved_away && move_dst_op_root_abspath) 1021 { 1022 const char *move_dst_op_root_relpath 1023 = apr_pstrmemdup(scratch_pool, c->data, c->len); 1024 1025 SVN_ERR(svn_wc__db_from_relpath(move_dst_op_root_abspath, 1026 db, wri_abspath, 1027 move_dst_op_root_relpath, 1028 result_pool, scratch_pool)); 1029 } 1030 else if (move_dst_op_root_abspath) 1031 *move_dst_op_root_abspath = NULL; 1032 1033 } 1034 1035 return SVN_NO_ERROR; 1036} 1037 1038svn_error_t * 1039svn_wc__conflict_read_markers(const apr_array_header_t **markers, 1040 svn_wc__db_t *db, 1041 const char *wri_abspath, 1042 const svn_skel_t *conflict_skel, 1043 apr_pool_t *result_pool, 1044 apr_pool_t *scratch_pool) 1045{ 1046 const svn_skel_t *conflict; 1047 apr_array_header_t *list = NULL; 1048 1049 SVN_ERR_ASSERT(conflict_skel != NULL); 1050 1051 /* Walk the conflicts */ 1052 for (conflict = conflict_skel->children->next->children; 1053 conflict; 1054 conflict = conflict->next) 1055 { 1056 const svn_skel_t *marker; 1057 1058 /* Get the list of markers stored per conflict */ 1059 for (marker = conflict->children->next->children; 1060 marker; 1061 marker = marker->next) 1062 { 1063 /* Skip placeholders */ 1064 if (! marker->is_atom) 1065 continue; 1066 1067 if (! list) 1068 list = apr_array_make(result_pool, 4, sizeof(const char *)); 1069 1070 SVN_ERR(svn_wc__db_from_relpath( 1071 &APR_ARRAY_PUSH(list, const char*), 1072 db, wri_abspath, 1073 apr_pstrmemdup(scratch_pool, marker->data, 1074 marker->len), 1075 result_pool, scratch_pool)); 1076 } 1077 } 1078 *markers = list; 1079 1080 return SVN_NO_ERROR; 1081} 1082 1083/* -------------------------------------------------------------------- 1084 */ 1085 1086 1087svn_error_t * 1088svn_wc__conflict_create_markers(svn_skel_t **work_items, 1089 svn_wc__db_t *db, 1090 const char *local_abspath, 1091 svn_skel_t *conflict_skel, 1092 apr_pool_t *result_pool, 1093 apr_pool_t *scratch_pool) 1094{ 1095 svn_boolean_t prop_conflicted; 1096 svn_wc_operation_t operation; 1097 *work_items = NULL; 1098 1099 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, 1100 NULL, &prop_conflicted, NULL, 1101 db, local_abspath, 1102 conflict_skel, 1103 scratch_pool, scratch_pool)); 1104 1105 if (prop_conflicted) 1106 { 1107 const char *marker_abspath = NULL; 1108 svn_node_kind_t kind; 1109 const char *marker_dir; 1110 const char *marker_name; 1111 const char *marker_relpath; 1112 1113 /* Ok, currently we have to do a few things for property conflicts: 1114 - Create a marker file 1115 - Store the name in the conflict_skel 1116 - Create a WQ item that fills the marker with the expected data */ 1117 1118 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); 1119 1120 if (kind == svn_node_dir) 1121 { 1122 marker_dir = local_abspath; 1123 marker_name = SVN_WC__THIS_DIR_PREJ; 1124 } 1125 else 1126 svn_dirent_split(&marker_dir, &marker_name, local_abspath, 1127 scratch_pool); 1128 1129 SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath, 1130 marker_dir, 1131 marker_name, 1132 SVN_WC__PROP_REJ_EXT, 1133 svn_io_file_del_none, 1134 scratch_pool, scratch_pool)); 1135 1136 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, local_abspath, 1137 marker_abspath, result_pool, result_pool)); 1138 1139 /* And store the marker in the skel */ 1140 { 1141 svn_skel_t *prop_conflict; 1142 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel, 1143 SVN_WC__CONFLICT_KIND_PROP)); 1144 1145 svn_skel__prepend_str(marker_relpath, prop_conflict->children->next, 1146 result_pool); 1147 } 1148 SVN_ERR(svn_wc__wq_build_prej_install(work_items, 1149 db, local_abspath, 1150 scratch_pool, scratch_pool)); 1151 } 1152 1153 return SVN_NO_ERROR; 1154} 1155 1156/* Helper function for the three apply_* functions below, used when 1157 * merging properties together. 1158 * 1159 * Given property PROPNAME on LOCAL_ABSPATH, and four possible property 1160 * values, generate four tmpfiles and pass them to CONFLICT_FUNC callback. 1161 * This gives the client an opportunity to interactively resolve the 1162 * property conflict. 1163 * 1164 * BASE_VAL/WORKING_VAL represent the current state of the working 1165 * copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming 1166 * propchange. Any of these values might be NULL, indicating either 1167 * non-existence or intent-to-delete. 1168 * 1169 * If the callback isn't available, or if it responds with 1170 * 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return. 1171 * 1172 * If the callback responds with a choice of 'base', 'theirs', 'mine', 1173 * or 'merged', then install the proper value into ACTUAL_PROPS and 1174 * set *CONFLICT_REMAINS to FALSE. 1175 */ 1176static svn_error_t * 1177generate_propconflict(svn_boolean_t *conflict_remains, 1178 svn_wc__db_t *db, 1179 const char *local_abspath, 1180 svn_node_kind_t kind, 1181 svn_wc_operation_t operation, 1182 const svn_wc_conflict_version_t *left_version, 1183 const svn_wc_conflict_version_t *right_version, 1184 const char *propname, 1185 const svn_string_t *base_val, 1186 const svn_string_t *working_val, 1187 const svn_string_t *incoming_old_val, 1188 const svn_string_t *incoming_new_val, 1189 svn_wc_conflict_resolver_func2_t conflict_func, 1190 void *conflict_baton, 1191 svn_cancel_func_t cancel_func, 1192 void *cancel_baton, 1193 apr_pool_t *scratch_pool) 1194{ 1195 svn_wc_conflict_result_t *result = NULL; 1196 svn_wc_conflict_description2_t *cdesc; 1197 const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool); 1198 const svn_string_t *new_value = NULL; 1199 1200 cdesc = svn_wc_conflict_description_create_prop2( 1201 local_abspath, 1202 kind, 1203 propname, scratch_pool); 1204 1205 cdesc->operation = operation; 1206 cdesc->src_left_version = left_version; 1207 cdesc->src_right_version = right_version; 1208 1209 /* Create a tmpfile for each of the string_t's we've got. */ 1210 if (working_val) 1211 { 1212 const char *file_name; 1213 1214 SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data, 1215 working_val->len, 1216 svn_io_file_del_on_pool_cleanup, 1217 scratch_pool)); 1218 cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); 1219 cdesc->prop_value_working = working_val; 1220 } 1221 1222 if (incoming_new_val) 1223 { 1224 const char *file_name; 1225 1226 SVN_ERR(svn_io_write_unique(&file_name, dirpath, incoming_new_val->data, 1227 incoming_new_val->len, 1228 svn_io_file_del_on_pool_cleanup, 1229 scratch_pool)); 1230 1231 /* ### For property conflicts, cd2 stores prop_reject_abspath in 1232 * ### their_abspath, and stores theirs_abspath in merged_file. */ 1233 cdesc->merged_file = svn_dirent_join(dirpath, file_name, scratch_pool); 1234 cdesc->prop_value_incoming_new = incoming_new_val; 1235 } 1236 1237 if (!base_val && !incoming_old_val) 1238 { 1239 /* If base and old are both NULL, then that's fine, we just let 1240 base_file stay NULL as-is. Both agents are attempting to add a 1241 new property. */ 1242 } 1243 else if ((base_val && !incoming_old_val) 1244 || (!base_val && incoming_old_val)) 1245 { 1246 /* If only one of base and old are defined, then we've got a 1247 situation where one agent is attempting to add the property 1248 for the first time, and the other agent is changing a 1249 property it thinks already exists. In this case, we return 1250 whichever older-value happens to be defined, so that the 1251 conflict-callback can still attempt a 3-way merge. */ 1252 1253 const svn_string_t *conflict_base_val = base_val ? base_val 1254 : incoming_old_val; 1255 const char *file_name; 1256 1257 SVN_ERR(svn_io_write_unique(&file_name, dirpath, 1258 conflict_base_val->data, 1259 conflict_base_val->len, 1260 svn_io_file_del_on_pool_cleanup, 1261 scratch_pool)); 1262 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); 1263 } 1264 else /* base and old are both non-NULL */ 1265 { 1266 const svn_string_t *conflict_base_val; 1267 const char *file_name; 1268 1269 if (! svn_string_compare(base_val, incoming_old_val)) 1270 { 1271 /* What happens if 'base' and 'old' don't match up? In an 1272 ideal situation, they would. But if they don't, this is 1273 a classic example of a patch 'hunk' failing to apply due 1274 to a lack of context. For example: imagine that the user 1275 is busy changing the property from a value of "cat" to 1276 "dog", but the incoming propchange wants to change the 1277 same property value from "red" to "green". Total context 1278 mismatch. 1279 1280 HOWEVER: we can still pass one of the two base values as 1281 'base_file' to the callback anyway. It's still useful to 1282 present the working and new values to the user to 1283 compare. */ 1284 1285 if (working_val && svn_string_compare(base_val, working_val)) 1286 conflict_base_val = incoming_old_val; 1287 else 1288 conflict_base_val = base_val; 1289 } 1290 else 1291 { 1292 conflict_base_val = base_val; 1293 } 1294 1295 SVN_ERR(svn_io_write_unique(&file_name, dirpath, conflict_base_val->data, 1296 conflict_base_val->len, 1297 svn_io_file_del_on_pool_cleanup, scratch_pool)); 1298 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); 1299 1300 cdesc->prop_value_base = base_val; 1301 cdesc->prop_value_incoming_old = incoming_old_val; 1302 1303 if (working_val && incoming_new_val) 1304 { 1305 svn_stream_t *mergestream; 1306 svn_diff_t *diff; 1307 svn_diff_file_options_t *options = 1308 svn_diff_file_options_create(scratch_pool); 1309 1310 SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->prop_reject_abspath, 1311 NULL, svn_io_file_del_on_pool_cleanup, 1312 scratch_pool, scratch_pool)); 1313 SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val, 1314 working_val, 1315 incoming_new_val, options, scratch_pool)); 1316 SVN_ERR(svn_diff_mem_string_output_merge3(mergestream, diff, 1317 conflict_base_val, working_val, 1318 incoming_new_val, NULL, NULL, NULL, NULL, 1319 svn_diff_conflict_display_modified_latest, 1320 cancel_func, cancel_baton, scratch_pool)); 1321 SVN_ERR(svn_stream_close(mergestream)); 1322 1323 /* ### For property conflicts, cd2 stores prop_reject_abspath in 1324 * ### their_abspath, and stores theirs_abspath in merged_file. */ 1325 cdesc->their_abspath = cdesc->prop_reject_abspath; 1326 } 1327 } 1328 1329 if (!incoming_old_val && incoming_new_val) 1330 cdesc->action = svn_wc_conflict_action_add; 1331 else if (incoming_old_val && !incoming_new_val) 1332 cdesc->action = svn_wc_conflict_action_delete; 1333 else 1334 cdesc->action = svn_wc_conflict_action_edit; 1335 1336 if (base_val && !working_val) 1337 cdesc->reason = svn_wc_conflict_reason_deleted; 1338 else if (!base_val && working_val) 1339 cdesc->reason = svn_wc_conflict_reason_obstructed; 1340 else 1341 cdesc->reason = svn_wc_conflict_reason_edited; 1342 1343 /* Invoke the interactive conflict callback. */ 1344 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, 1345 scratch_pool)); 1346 if (result == NULL) 1347 { 1348 *conflict_remains = TRUE; 1349 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 1350 NULL, _("Conflict callback violated API:" 1351 " returned no results")); 1352 } 1353 1354 1355 switch (result->choice) 1356 { 1357 default: 1358 case svn_wc_conflict_choose_postpone: 1359 { 1360 *conflict_remains = TRUE; 1361 break; 1362 } 1363 case svn_wc_conflict_choose_mine_full: 1364 { 1365 /* No need to change actual_props; it already contains working_val */ 1366 *conflict_remains = FALSE; 1367 new_value = working_val; 1368 break; 1369 } 1370 /* I think _mine_full and _theirs_full are appropriate for prop 1371 behavior as well as the text behavior. There should even be 1372 analogous behaviors for _mine and _theirs when those are 1373 ready, namely: fold in all non-conflicting prop changes, and 1374 then choose _mine side or _theirs side for conflicting ones. */ 1375 case svn_wc_conflict_choose_theirs_full: 1376 { 1377 *conflict_remains = FALSE; 1378 new_value = incoming_new_val; 1379 break; 1380 } 1381 case svn_wc_conflict_choose_base: 1382 { 1383 *conflict_remains = FALSE; 1384 new_value = base_val; 1385 break; 1386 } 1387 case svn_wc_conflict_choose_merged: 1388 { 1389 if (!cdesc->merged_file 1390 && (!result->merged_file && !result->merged_value)) 1391 return svn_error_create 1392 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 1393 NULL, _("Conflict callback violated API:" 1394 " returned no merged file")); 1395 1396 if (result->merged_value) 1397 new_value = result->merged_value; 1398 else 1399 { 1400 svn_stringbuf_t *merged_stringbuf; 1401 1402 SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf, 1403 result->merged_file ? 1404 result->merged_file : 1405 cdesc->merged_file, 1406 scratch_pool)); 1407 new_value = svn_stringbuf__morph_into_string(merged_stringbuf); 1408 } 1409 *conflict_remains = FALSE; 1410 break; 1411 } 1412 } 1413 1414 if (!*conflict_remains) 1415 { 1416 apr_hash_t *props; 1417 1418 /* For now, just set the property values. This should really do some of the 1419 more advanced things from svn_wc_prop_set() */ 1420 1421 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool, 1422 scratch_pool)); 1423 1424 svn_hash_sets(props, propname, new_value); 1425 1426 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props, 1427 FALSE, NULL, NULL, 1428 scratch_pool)); 1429 } 1430 1431 return SVN_NO_ERROR; 1432} 1433 1434/* Perform a 3-way merge in which conflicts are expected, showing the 1435 * conflicts in the way specified by STYLE, and using MERGE_OPTIONS. 1436 * 1437 * The three input files are LEFT_ABSPATH (the base), DETRANSLATED_TARGET 1438 * and RIGHT_ABSPATH. The output is stored in a new temporary file, 1439 * whose name is put into *CHOSEN_ABSPATH. 1440 * 1441 * The output file will be deleted according to DELETE_WHEN. If 1442 * DELETE_WHEN is 'on pool cleanup', it refers to RESULT_POOL. 1443 * 1444 * DB and WRI_ABSPATH are used to choose a directory for the output file. 1445 * 1446 * Allocate *CHOSEN_ABSPATH in RESULT_POOL. Use SCRATCH_POOL for temporary 1447 * allocations. 1448 */ 1449static svn_error_t * 1450merge_showing_conflicts(const char **chosen_abspath, 1451 svn_wc__db_t *db, 1452 const char *wri_abspath, 1453 svn_diff_conflict_display_style_t style, 1454 const apr_array_header_t *merge_options, 1455 const char *left_abspath, 1456 const char *detranslated_target, 1457 const char *right_abspath, 1458 svn_io_file_del_t delete_when, 1459 svn_cancel_func_t cancel_func, 1460 void *cancel_baton, 1461 apr_pool_t *result_pool, 1462 apr_pool_t *scratch_pool) 1463{ 1464 const char *temp_dir; 1465 svn_stream_t *chosen_stream; 1466 svn_diff_t *diff; 1467 svn_diff_file_options_t *diff3_options; 1468 1469 diff3_options = svn_diff_file_options_create(scratch_pool); 1470 if (merge_options) 1471 SVN_ERR(svn_diff_file_options_parse(diff3_options, 1472 merge_options, 1473 scratch_pool)); 1474 1475 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db, 1476 wri_abspath, 1477 scratch_pool, scratch_pool)); 1478 /* We need to open the stream in RESULT_POOL because that controls the 1479 * lifetime of the file if DELETE_WHEN is 'on pool cleanup'. (We also 1480 * want to allocate CHOSEN_ABSPATH in RESULT_POOL, but we don't care 1481 * about the stream itself.) */ 1482 SVN_ERR(svn_stream_open_unique(&chosen_stream, chosen_abspath, 1483 temp_dir, delete_when, 1484 result_pool, scratch_pool)); 1485 SVN_ERR(svn_diff_file_diff3_2(&diff, 1486 left_abspath, 1487 detranslated_target, right_abspath, 1488 diff3_options, scratch_pool)); 1489 SVN_ERR(svn_diff_file_output_merge3(chosen_stream, diff, 1490 left_abspath, 1491 detranslated_target, 1492 right_abspath, 1493 NULL, NULL, NULL, NULL, /* markers */ 1494 style, cancel_func, cancel_baton, 1495 scratch_pool)); 1496 SVN_ERR(svn_stream_close(chosen_stream)); 1497 1498 return SVN_NO_ERROR; 1499} 1500 1501/* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the 1502 * working copy at DB/WRI_ABSPATH. 1503 * 1504 * Set *WORK_ITEMS to a new work item that, when run, will delete the 1505 * artifact file; or to NULL if there is no file to delete. 1506 * 1507 * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its 1508 * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND 1509 * may be NULL if not required. 1510 */ 1511static svn_error_t * 1512remove_artifact_file_if_exists(svn_skel_t **work_items, 1513 svn_boolean_t *file_found, 1514 svn_wc__db_t *db, 1515 const char *wri_abspath, 1516 const char *artifact_file_abspath, 1517 apr_pool_t *result_pool, 1518 apr_pool_t *scratch_pool) 1519{ 1520 *work_items = NULL; 1521 if (artifact_file_abspath) 1522 { 1523 svn_node_kind_t node_kind; 1524 1525 SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind, 1526 scratch_pool)); 1527 if (node_kind == svn_node_file) 1528 { 1529 SVN_ERR(svn_wc__wq_build_file_remove(work_items, 1530 db, wri_abspath, 1531 artifact_file_abspath, 1532 result_pool, scratch_pool)); 1533 if (file_found) 1534 *file_found = TRUE; 1535 } 1536 } 1537 1538 return SVN_NO_ERROR; 1539} 1540 1541/* Create a new file in the same directory as LOCAL_ABSPATH, with the 1542 same basename as LOCAL_ABSPATH, with a ".edited" extension, and set 1543 *WORK_ITEM to a new work item that will copy and translate from the file 1544 SOURCE_ABSPATH to that new file. It will be translated from repository- 1545 normal form to working-copy form according to the versioned properties 1546 of LOCAL_ABSPATH that are current when the work item is executed. 1547 1548 DB should have a write lock for the directory containing SOURCE. 1549 1550 Allocate *WORK_ITEM in RESULT_POOL. */ 1551static svn_error_t * 1552save_merge_result(svn_skel_t **work_item, 1553 svn_wc__db_t *db, 1554 const char *local_abspath, 1555 const char *source_abspath, 1556 apr_pool_t *result_pool, 1557 apr_pool_t *scratch_pool) 1558{ 1559 const char *edited_copy_abspath; 1560 const char *dir_abspath; 1561 const char *filename; 1562 1563 svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool); 1564 1565 /* ### Should use preserved-conflict-file-exts. */ 1566 /* Create the .edited file within this file's DIR_ABSPATH */ 1567 SVN_ERR(svn_io_open_uniquely_named(NULL, 1568 &edited_copy_abspath, 1569 dir_abspath, 1570 filename, 1571 ".edited", 1572 svn_io_file_del_none, 1573 scratch_pool, scratch_pool)); 1574 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item, 1575 db, local_abspath, 1576 source_abspath, 1577 edited_copy_abspath, 1578 result_pool, scratch_pool)); 1579 return SVN_NO_ERROR; 1580} 1581 1582 1583 1584/* Resolve the text conflict in CONFLICT, which is currently recorded 1585 * on DB/LOCAL_ABSPATH in the manner specified by CHOICE. 1586 * 1587 * Set *WORK_ITEMS to new work items that will make the on-disk changes 1588 * needed to complete the resolution (but not to mark it as resolved). 1589 * 1590 * Set *FOUND_ARTIFACT to true if conflict markers are removed; otherwise 1591 * (which is only if CHOICE is 'postpone') to false. 1592 * 1593 * CHOICE, MERGED_FILE and SAVE_MERGED are typically values provided by 1594 * the conflict resolver. 1595 * 1596 * MERGE_OPTIONS allows customizing the diff handling when using 1597 * per hunk conflict resolving. 1598 */ 1599static svn_error_t * 1600build_text_conflict_resolve_items(svn_skel_t **work_items, 1601 svn_boolean_t *found_artifact, 1602 svn_wc__db_t *db, 1603 const char *local_abspath, 1604 const svn_skel_t *conflict, 1605 svn_wc_conflict_choice_t choice, 1606 const char *merged_file, 1607 svn_boolean_t save_merged, 1608 const apr_array_header_t *merge_options, 1609 svn_cancel_func_t cancel_func, 1610 void *cancel_baton, 1611 apr_pool_t *result_pool, 1612 apr_pool_t *scratch_pool) 1613{ 1614 const char *mine_abspath; 1615 const char *their_old_abspath; 1616 const char *their_abspath; 1617 svn_skel_t *work_item; 1618 const char *install_from_abspath = NULL; 1619 svn_boolean_t remove_source = FALSE; 1620 1621 *work_items = NULL; 1622 1623 if (found_artifact) 1624 *found_artifact = FALSE; 1625 1626 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath, 1627 &their_old_abspath, 1628 &their_abspath, 1629 db, local_abspath, 1630 conflict, 1631 scratch_pool, scratch_pool)); 1632 1633 if (save_merged) 1634 SVN_ERR(save_merge_result(work_items, 1635 db, local_abspath, 1636 merged_file 1637 ? merged_file 1638 : local_abspath, 1639 result_pool, scratch_pool)); 1640 1641 if (choice == svn_wc_conflict_choose_postpone) 1642 return SVN_NO_ERROR; 1643 1644 switch (choice) 1645 { 1646 /* If the callback wants to use one of the fulltexts 1647 to resolve the conflict, so be it.*/ 1648 case svn_wc_conflict_choose_base: 1649 { 1650 install_from_abspath = their_old_abspath; 1651 break; 1652 } 1653 case svn_wc_conflict_choose_theirs_full: 1654 { 1655 install_from_abspath = their_abspath; 1656 break; 1657 } 1658 case svn_wc_conflict_choose_mine_full: 1659 { 1660 /* In case of selecting to resolve the conflict choosing the full 1661 own file, allow the text conflict resolution to just take the 1662 existing local file if no merged file was present (case: binary 1663 file conflicts do not generate a locally merge file). 1664 */ 1665 install_from_abspath = mine_abspath 1666 ? mine_abspath 1667 : local_abspath; 1668 break; 1669 } 1670 case svn_wc_conflict_choose_theirs_conflict: 1671 case svn_wc_conflict_choose_mine_conflict: 1672 { 1673 svn_diff_conflict_display_style_t style 1674 = choice == svn_wc_conflict_choose_theirs_conflict 1675 ? svn_diff_conflict_display_latest 1676 : svn_diff_conflict_display_modified; 1677 1678 if (mine_abspath == NULL) 1679 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 1680 _("Conflict on '%s' cannot be resolved to " 1681 "'theirs-conflict' or 'mine-conflict' " 1682 "because a merged version of the file " 1683 "cannot be created."), 1684 svn_dirent_local_style(local_abspath, 1685 scratch_pool)); 1686 1687 SVN_ERR(merge_showing_conflicts(&install_from_abspath, 1688 db, local_abspath, 1689 style, merge_options, 1690 their_old_abspath, 1691 mine_abspath, 1692 their_abspath, 1693 /* ### why not same as other caller? */ 1694 svn_io_file_del_none, 1695 cancel_func, cancel_baton, 1696 scratch_pool, scratch_pool)); 1697 remove_source = TRUE; 1698 break; 1699 } 1700 1701 /* For the case of 3-way file merging, we don't 1702 really distinguish between these return values; 1703 if the callback claims to have "generally 1704 resolved" the situation, we still interpret 1705 that as "OK, we'll assume the merged version is 1706 good to use". */ 1707 case svn_wc_conflict_choose_merged: 1708 { 1709 install_from_abspath = merged_file 1710 ? merged_file 1711 : local_abspath; 1712 break; 1713 } 1714 case svn_wc_conflict_choose_postpone: 1715 { 1716 /* Assume conflict remains. */ 1717 return SVN_NO_ERROR; 1718 } 1719 default: 1720 SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone); 1721 } 1722 1723 if (install_from_abspath == NULL) 1724 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 1725 _("Conflict on '%s' could not be resolved " 1726 "because the chosen version of the file " 1727 "is not available."), 1728 svn_dirent_local_style(local_abspath, 1729 scratch_pool)); 1730 1731 /* ### It would be nice if we could somehow pass RECORD_FILEINFO 1732 as true in some easy cases. */ 1733 SVN_ERR(svn_wc__wq_build_file_install(&work_item, 1734 db, local_abspath, 1735 install_from_abspath, 1736 FALSE /* use_commit_times */, 1737 FALSE /* record_fileinfo */, 1738 result_pool, scratch_pool)); 1739 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1740 1741 if (remove_source) 1742 { 1743 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, 1744 db, local_abspath, 1745 install_from_abspath, 1746 result_pool, scratch_pool)); 1747 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1748 } 1749 1750 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, 1751 db, local_abspath, 1752 their_old_abspath, 1753 result_pool, scratch_pool)); 1754 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1755 1756 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, 1757 db, local_abspath, 1758 their_abspath, 1759 result_pool, scratch_pool)); 1760 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1761 1762 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, 1763 db, local_abspath, 1764 mine_abspath, 1765 result_pool, scratch_pool)); 1766 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1767 1768 return SVN_NO_ERROR; 1769} 1770 1771 1772/* Set *DESC to a new description of the text conflict in 1773 * CONFLICT_SKEL. If there is no text conflict in CONFLICT_SKEL, return 1774 * an error. 1775 * 1776 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION, 1777 * rather than reading them from CONFLICT_SKEL. Use IS_BINARY and 1778 * MIME_TYPE for the corresponding fields of *DESC. 1779 * 1780 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary 1781 * allocations. */ 1782static svn_error_t * 1783read_text_conflict_desc(svn_wc_conflict_description2_t **desc, 1784 svn_wc__db_t *db, 1785 const char *local_abspath, 1786 const svn_skel_t *conflict_skel, 1787 const char *mime_type, 1788 svn_wc_operation_t operation, 1789 const svn_wc_conflict_version_t *left_version, 1790 const svn_wc_conflict_version_t *right_version, 1791 apr_pool_t *result_pool, 1792 apr_pool_t *scratch_pool) 1793{ 1794 *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool); 1795 (*desc)->mime_type = mime_type; 1796 (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE; 1797 (*desc)->operation = operation; 1798 (*desc)->src_left_version = left_version; 1799 (*desc)->src_right_version = right_version; 1800 1801 SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc)->my_abspath, 1802 &(*desc)->base_abspath, 1803 &(*desc)->their_abspath, 1804 db, local_abspath, 1805 conflict_skel, 1806 result_pool, scratch_pool)); 1807 (*desc)->merged_file = apr_pstrdup(result_pool, local_abspath); 1808 1809 return SVN_NO_ERROR; 1810} 1811 1812/* Set *CONFLICT_DESC to a new description of the tree conflict in 1813 * CONFLICT_SKEL. If there is no tree conflict in CONFLICT_SKEL, return 1814 * an error. 1815 * 1816 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION, 1817 * rather than reading them from CONFLICT_SKEL. 1818 * 1819 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary 1820 * allocations. */ 1821static svn_error_t * 1822read_tree_conflict_desc(svn_wc_conflict_description2_t **desc, 1823 svn_wc__db_t *db, 1824 const char *local_abspath, 1825 svn_node_kind_t node_kind, 1826 const svn_skel_t *conflict_skel, 1827 svn_wc_operation_t operation, 1828 const svn_wc_conflict_version_t *left_version, 1829 const svn_wc_conflict_version_t *right_version, 1830 apr_pool_t *result_pool, 1831 apr_pool_t *scratch_pool) 1832{ 1833 svn_node_kind_t local_kind; 1834 svn_wc_conflict_reason_t reason; 1835 svn_wc_conflict_action_t action; 1836 1837 SVN_ERR(svn_wc__conflict_read_tree_conflict( 1838 &reason, &action, NULL, NULL, 1839 db, local_abspath, conflict_skel, scratch_pool, scratch_pool)); 1840 1841 if (reason == svn_wc_conflict_reason_missing) 1842 local_kind = svn_node_none; 1843 else if (reason == svn_wc_conflict_reason_unversioned || 1844 reason == svn_wc_conflict_reason_obstructed) 1845 SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool)); 1846 else if (action == svn_wc_conflict_action_delete 1847 && left_version 1848 && (operation == svn_wc_operation_update 1849 ||operation == svn_wc_operation_switch) 1850 && (reason == svn_wc_conflict_reason_deleted 1851 || reason == svn_wc_conflict_reason_moved_away)) 1852 { 1853 /* We have nothing locally to take the kind from */ 1854 local_kind = left_version->node_kind; 1855 } 1856 else 1857 local_kind = node_kind; 1858 1859 *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind, 1860 operation, 1861 left_version, right_version, 1862 result_pool); 1863 (*desc)->reason = reason; 1864 (*desc)->action = action; 1865 1866 return SVN_NO_ERROR; 1867} 1868 1869/* Forward definition */ 1870static svn_error_t * 1871resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, 1872 svn_wc__db_t *db, 1873 const char *local_abspath, 1874 const svn_skel_t *conflict, 1875 svn_wc_conflict_choice_t conflict_choice, 1876 apr_hash_t *resolve_later, 1877 svn_wc_notify_func2_t notify_func, 1878 void *notify_baton, 1879 svn_cancel_func_t cancel_func, 1880 void *cancel_baton, 1881 apr_pool_t *scratch_pool); 1882 1883svn_error_t * 1884svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, 1885 const char *local_abspath, 1886 svn_node_kind_t kind, 1887 const svn_skel_t *conflict_skel, 1888 const apr_array_header_t *merge_options, 1889 svn_wc_conflict_resolver_func2_t resolver_func, 1890 void *resolver_baton, 1891 svn_cancel_func_t cancel_func, 1892 void *cancel_baton, 1893 apr_pool_t *scratch_pool) 1894{ 1895 svn_boolean_t text_conflicted; 1896 svn_boolean_t prop_conflicted; 1897 svn_boolean_t tree_conflicted; 1898 svn_wc_operation_t operation; 1899 const apr_array_header_t *locations; 1900 const svn_wc_conflict_version_t *left_version = NULL; 1901 const svn_wc_conflict_version_t *right_version = NULL; 1902 1903 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, 1904 &text_conflicted, &prop_conflicted, 1905 &tree_conflicted, 1906 db, local_abspath, conflict_skel, 1907 scratch_pool, scratch_pool)); 1908 1909 if (locations && locations->nelts > 0) 1910 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *); 1911 1912 if (locations && locations->nelts > 1) 1913 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *); 1914 1915 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers 1916 would want to look at all properties at the same time. 1917 1918 ### svn currently only invokes this from the merge code to collect the list of 1919 ### conflicted paths. Eventually this code will be the base for 'svn resolve' 1920 ### and at that time the test coverage will improve 1921 */ 1922 if (prop_conflicted) 1923 { 1924 apr_hash_t *old_props; 1925 apr_hash_t *mine_props; 1926 apr_hash_t *their_props; 1927 apr_hash_t *old_their_props; 1928 apr_hash_t *conflicted; 1929 apr_pool_t *iterpool; 1930 apr_hash_index_t *hi; 1931 svn_boolean_t mark_resolved = TRUE; 1932 1933 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL, 1934 &mine_props, 1935 &old_their_props, 1936 &their_props, 1937 &conflicted, 1938 db, local_abspath, 1939 conflict_skel, 1940 scratch_pool, scratch_pool)); 1941 1942 if (operation == svn_wc_operation_merge) 1943 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, 1944 scratch_pool, scratch_pool)); 1945 else 1946 old_props = old_their_props; 1947 1948 iterpool = svn_pool_create(scratch_pool); 1949 1950 for (hi = apr_hash_first(scratch_pool, conflicted); 1951 hi; 1952 hi = apr_hash_next(hi)) 1953 { 1954 const char *propname = apr_hash_this_key(hi); 1955 svn_boolean_t conflict_remains = TRUE; 1956 1957 svn_pool_clear(iterpool); 1958 1959 if (cancel_func) 1960 SVN_ERR(cancel_func(cancel_baton)); 1961 1962 SVN_ERR(generate_propconflict(&conflict_remains, 1963 db, local_abspath, kind, 1964 operation, 1965 left_version, 1966 right_version, 1967 propname, 1968 old_props 1969 ? svn_hash_gets(old_props, propname) 1970 : NULL, 1971 mine_props 1972 ? svn_hash_gets(mine_props, propname) 1973 : NULL, 1974 old_their_props 1975 ? svn_hash_gets(old_their_props, propname) 1976 : NULL, 1977 their_props 1978 ? svn_hash_gets(their_props, propname) 1979 : NULL, 1980 resolver_func, resolver_baton, 1981 cancel_func, cancel_baton, 1982 iterpool)); 1983 1984 if (conflict_remains) 1985 mark_resolved = FALSE; 1986 } 1987 1988 if (mark_resolved) 1989 { 1990 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath, 1991 scratch_pool)); 1992 } 1993 svn_pool_destroy(iterpool); 1994 } 1995 1996 if (text_conflicted) 1997 { 1998 svn_skel_t *work_items; 1999 svn_boolean_t was_resolved; 2000 svn_wc_conflict_description2_t *desc; 2001 apr_hash_t *props; 2002 svn_wc_conflict_result_t *result; 2003 2004 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, 2005 scratch_pool, scratch_pool)); 2006 2007 SVN_ERR(read_text_conflict_desc(&desc, 2008 db, local_abspath, conflict_skel, 2009 svn_prop_get_value(props, 2010 SVN_PROP_MIME_TYPE), 2011 operation, left_version, right_version, 2012 scratch_pool, scratch_pool)); 2013 2014 2015 work_items = NULL; 2016 was_resolved = FALSE; 2017 2018 /* Give the conflict resolution callback a chance to clean 2019 up the conflicts before we mark the file 'conflicted' */ 2020 2021 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool, 2022 scratch_pool)); 2023 if (result == NULL) 2024 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 2025 _("Conflict callback violated API:" 2026 " returned no results")); 2027 2028 SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved, 2029 db, local_abspath, 2030 conflict_skel, result->choice, 2031 result->merged_file, 2032 result->save_merged, 2033 merge_options, 2034 cancel_func, cancel_baton, 2035 scratch_pool, scratch_pool)); 2036 2037 if (result->choice != svn_wc_conflict_choose_postpone) 2038 { 2039 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, 2040 TRUE, FALSE, FALSE, 2041 work_items, scratch_pool)); 2042 SVN_ERR(svn_wc__wq_run(db, local_abspath, 2043 cancel_func, cancel_baton, 2044 scratch_pool)); 2045 } 2046 } 2047 2048 if (tree_conflicted) 2049 { 2050 svn_wc_conflict_result_t *result; 2051 svn_wc_conflict_description2_t *desc; 2052 svn_boolean_t resolved; 2053 svn_node_kind_t node_kind; 2054 2055 SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE, 2056 TRUE, FALSE, scratch_pool)); 2057 2058 SVN_ERR(read_tree_conflict_desc(&desc, 2059 db, local_abspath, node_kind, 2060 conflict_skel, 2061 operation, left_version, right_version, 2062 scratch_pool, scratch_pool)); 2063 2064 /* Tell the resolver func about this conflict. */ 2065 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool, 2066 scratch_pool)); 2067 2068 if (result == NULL) 2069 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 2070 _("Conflict callback violated API:" 2071 " returned no results")); 2072 2073 /* Pass retry hash to avoid erroring out on cases where update 2074 can continue safely. ### Need notify handling */ 2075 if (result->choice != svn_wc_conflict_choose_postpone) 2076 SVN_ERR(resolve_tree_conflict_on_node(&resolved, 2077 db, local_abspath, conflict_skel, 2078 result->choice, 2079 apr_hash_make(scratch_pool), 2080 NULL, NULL, /* ### notify */ 2081 cancel_func, cancel_baton, 2082 scratch_pool)); 2083 } 2084 2085 return SVN_NO_ERROR; 2086} 2087 2088/* Read all property conflicts contained in CONFLICT_SKEL into 2089 * individual conflict descriptions, and append those descriptions 2090 * to the CONFLICTS array. If there is no property conflict in 2091 * CONFLICT_SKEL, return an error. 2092 * 2093 * If NOT create_tempfiles, always create a legacy property conflict 2094 * descriptor. 2095 * 2096 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and 2097 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL. 2098 * 2099 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary 2100 * allocations. */ 2101static svn_error_t * 2102read_prop_conflict_descs(apr_array_header_t *conflicts, 2103 svn_wc__db_t *db, 2104 const char *local_abspath, 2105 svn_skel_t *conflict_skel, 2106 svn_boolean_t create_tempfiles, 2107 svn_node_kind_t node_kind, 2108 svn_wc_operation_t operation, 2109 const svn_wc_conflict_version_t *left_version, 2110 const svn_wc_conflict_version_t *right_version, 2111 apr_pool_t *result_pool, 2112 apr_pool_t *scratch_pool) 2113{ 2114 const char *prop_reject_abspath; 2115 apr_hash_t *base_props; 2116 apr_hash_t *my_props; 2117 apr_hash_t *their_old_props; 2118 apr_hash_t *their_props; 2119 apr_hash_t *conflicted_props; 2120 apr_hash_index_t *hi; 2121 apr_pool_t *iterpool; 2122 svn_boolean_t prop_conflicted; 2123 2124 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted, 2125 NULL, db, local_abspath, conflict_skel, 2126 scratch_pool, scratch_pool)); 2127 2128 if (!prop_conflicted) 2129 return SVN_NO_ERROR; 2130 2131 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath, 2132 &my_props, 2133 &their_old_props, 2134 &their_props, 2135 &conflicted_props, 2136 db, local_abspath, 2137 conflict_skel, 2138 scratch_pool, scratch_pool)); 2139 2140 prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath); 2141 2142 if (apr_hash_count(conflicted_props) == 0) 2143 { 2144 /* Legacy prop conflict with only a .reject file. */ 2145 svn_wc_conflict_description2_t *desc; 2146 2147 desc = svn_wc_conflict_description_create_prop2(local_abspath, 2148 node_kind, 2149 "", result_pool); 2150 2151 /* ### For property conflicts, cd2 stores prop_reject_abspath in 2152 * ### their_abspath, and stores theirs_abspath in merged_file. */ 2153 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */ 2154 desc->their_abspath = desc->prop_reject_abspath; 2155 2156 desc->operation = operation; 2157 desc->src_left_version = left_version; 2158 desc->src_right_version = right_version; 2159 2160 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc; 2161 2162 return SVN_NO_ERROR; 2163 } 2164 2165 if (operation == svn_wc_operation_merge) 2166 SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath, 2167 result_pool, scratch_pool)); 2168 else 2169 base_props = NULL; 2170 iterpool = svn_pool_create(scratch_pool); 2171 for (hi = apr_hash_first(scratch_pool, conflicted_props); 2172 hi; 2173 hi = apr_hash_next(hi)) 2174 { 2175 const char *propname = apr_hash_this_key(hi); 2176 svn_string_t *old_value; 2177 svn_string_t *my_value; 2178 svn_string_t *their_value; 2179 svn_wc_conflict_description2_t *desc; 2180 2181 svn_pool_clear(iterpool); 2182 2183 desc = svn_wc_conflict_description_create_prop2(local_abspath, 2184 node_kind, 2185 propname, 2186 result_pool); 2187 2188 desc->operation = operation; 2189 desc->src_left_version = left_version; 2190 desc->src_right_version = right_version; 2191 2192 desc->property_name = apr_pstrdup(result_pool, propname); 2193 2194 my_value = svn_hash_gets(my_props, propname); 2195 their_value = svn_hash_gets(their_props, propname); 2196 old_value = svn_hash_gets(their_old_props, propname); 2197 2198 /* Compute the incoming side of the conflict ('action'). */ 2199 if (their_value == NULL) 2200 desc->action = svn_wc_conflict_action_delete; 2201 else if (old_value == NULL) 2202 desc->action = svn_wc_conflict_action_add; 2203 else 2204 desc->action = svn_wc_conflict_action_edit; 2205 2206 /* Compute the local side of the conflict ('reason'). */ 2207 if (my_value == NULL) 2208 desc->reason = svn_wc_conflict_reason_deleted; 2209 else if (old_value == NULL) 2210 desc->reason = svn_wc_conflict_reason_added; 2211 else 2212 desc->reason = svn_wc_conflict_reason_edited; 2213 2214 /* ### For property conflicts, cd2 stores prop_reject_abspath in 2215 * ### their_abspath, and stores theirs_abspath in merged_file. */ 2216 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */ 2217 desc->their_abspath = desc->prop_reject_abspath; 2218 2219 desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname) 2220 : desc->prop_value_incoming_old; 2221 2222 if (my_value) 2223 { 2224 svn_stream_t *s; 2225 apr_size_t len; 2226 2227 if (create_tempfiles) 2228 { 2229 SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL, 2230 svn_io_file_del_on_pool_cleanup, 2231 result_pool, iterpool)); 2232 len = my_value->len; 2233 SVN_ERR(svn_stream_write(s, my_value->data, &len)); 2234 SVN_ERR(svn_stream_close(s)); 2235 } 2236 2237 desc->prop_value_working = svn_string_dup(my_value, result_pool); 2238 } 2239 2240 if (their_value) 2241 { 2242 svn_stream_t *s; 2243 apr_size_t len; 2244 2245 /* ### For property conflicts, cd2 stores prop_reject_abspath in 2246 * ### their_abspath, and stores theirs_abspath in merged_file. */ 2247 if (create_tempfiles) 2248 { 2249 SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL, 2250 svn_io_file_del_on_pool_cleanup, 2251 result_pool, iterpool)); 2252 len = their_value->len; 2253 SVN_ERR(svn_stream_write(s, their_value->data, &len)); 2254 SVN_ERR(svn_stream_close(s)); 2255 } 2256 2257 desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool); 2258 } 2259 2260 if (old_value) 2261 { 2262 svn_stream_t *s; 2263 apr_size_t len; 2264 2265 if (create_tempfiles) 2266 { 2267 SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL, 2268 svn_io_file_del_on_pool_cleanup, 2269 result_pool, iterpool)); 2270 len = old_value->len; 2271 SVN_ERR(svn_stream_write(s, old_value->data, &len)); 2272 SVN_ERR(svn_stream_close(s)); 2273 } 2274 2275 desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool); 2276 } 2277 2278 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc; 2279 } 2280 svn_pool_destroy(iterpool); 2281 2282 return SVN_NO_ERROR; 2283} 2284 2285svn_error_t * 2286svn_wc__read_conflicts(const apr_array_header_t **conflicts, 2287 svn_skel_t **conflict_skel, 2288 svn_wc__db_t *db, 2289 const char *local_abspath, 2290 svn_boolean_t create_tempfiles, 2291 svn_boolean_t only_tree_conflict, 2292 apr_pool_t *result_pool, 2293 apr_pool_t *scratch_pool) 2294{ 2295 svn_skel_t *the_conflict_skel; 2296 apr_array_header_t *cflcts; 2297 svn_boolean_t prop_conflicted; 2298 svn_boolean_t text_conflicted; 2299 svn_boolean_t tree_conflicted; 2300 svn_wc_operation_t operation; 2301 const apr_array_header_t *locations; 2302 const svn_wc_conflict_version_t *left_version = NULL; 2303 const svn_wc_conflict_version_t *right_version = NULL; 2304 svn_node_kind_t node_kind; 2305 apr_hash_t *props; 2306 2307 if (!conflict_skel) 2308 conflict_skel = &the_conflict_skel; 2309 2310 SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props, 2311 db, local_abspath, 2312 (conflict_skel == &the_conflict_skel) 2313 ? scratch_pool 2314 : result_pool, 2315 scratch_pool)); 2316 2317 if (!*conflict_skel) 2318 { 2319 /* Some callers expect not NULL */ 2320 *conflicts = apr_array_make(result_pool, 0, 2321 sizeof(svn_wc_conflict_description2_t *)); 2322 return SVN_NO_ERROR; 2323 } 2324 2325 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted, 2326 &prop_conflicted, &tree_conflicted, 2327 db, local_abspath, *conflict_skel, 2328 result_pool, scratch_pool)); 2329 2330 cflcts = apr_array_make(result_pool, 4, 2331 sizeof(svn_wc_conflict_description2_t *)); 2332 2333 if (locations && locations->nelts > 0) 2334 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *); 2335 if (locations && locations->nelts > 1) 2336 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *); 2337 2338 if (prop_conflicted && !only_tree_conflict) 2339 { 2340 SVN_ERR(read_prop_conflict_descs(cflcts, 2341 db, local_abspath, *conflict_skel, 2342 create_tempfiles, node_kind, 2343 operation, left_version, right_version, 2344 result_pool, scratch_pool)); 2345 } 2346 2347 if (text_conflicted && !only_tree_conflict) 2348 { 2349 svn_wc_conflict_description2_t *desc; 2350 2351 SVN_ERR(read_text_conflict_desc(&desc, 2352 db, local_abspath, *conflict_skel, 2353 svn_prop_get_value(props, 2354 SVN_PROP_MIME_TYPE), 2355 operation, left_version, right_version, 2356 result_pool, scratch_pool)); 2357 APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc; 2358 } 2359 2360 if (tree_conflicted) 2361 { 2362 svn_wc_conflict_description2_t *desc; 2363 2364 SVN_ERR(read_tree_conflict_desc(&desc, 2365 db, local_abspath, node_kind, 2366 *conflict_skel, 2367 operation, left_version, right_version, 2368 result_pool, scratch_pool)); 2369 2370 APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc; 2371 } 2372 2373 *conflicts = cflcts; 2374 return SVN_NO_ERROR; 2375} 2376 2377svn_error_t * 2378svn_wc__read_conflict_descriptions2_t(const apr_array_header_t **conflicts, 2379 svn_wc_context_t *wc_ctx, 2380 const char *local_abspath, 2381 apr_pool_t *result_pool, 2382 apr_pool_t *scratch_pool) 2383{ 2384 return svn_wc__read_conflicts(conflicts, NULL, wc_ctx->db, local_abspath, 2385 FALSE, FALSE, result_pool, scratch_pool); 2386} 2387 2388 2389/*** Resolving a conflict automatically ***/ 2390 2391/* 2392 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according 2393 * to CONFLICT_CHOICE. 2394 * 2395 * It is not an error if there is no prop conflict. If a prop conflict 2396 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE. 2397 * 2398 * Note: When there are no conflict markers on-disk to remove there is 2399 * no existing text conflict (unless we are still in the process of 2400 * creating the text conflict and we didn't register a marker file yet). 2401 * In this case the database contains old information, which we should 2402 * remove to avoid checking the next time. Resolving a property conflict 2403 * by just removing the marker file is a fully supported scenario since 2404 * Subversion 1.0. 2405 * 2406 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ. 2407 * In my opinion, 'mine_full'/'theirs_full' should select 2408 * the entire set of properties from 'mine' or 'theirs' respectively, 2409 * while 'mine_conflict'/'theirs_conflict' should select just the 2410 * properties that are in conflict. Or, '_full' should select the 2411 * entire property whereas '_conflict' should do a text merge within 2412 * each property, selecting hunks. Or all three kinds of behaviour 2413 * should be available (full set of props, full value of conflicting 2414 * props, or conflicting text hunks). 2415 * ### BH: If we make *_full select the full set of properties, we should 2416 * check if we shouldn't make it also select the full text for files. 2417 * 2418 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc 2419 * but in a layer above. 2420 * 2421 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and 2422 * for 'theirs' -- choose full set of props, full value of conflicting 2423 * props, or conflicting text hunks. 2424 * 2425 */ 2426static svn_error_t * 2427resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, 2428 svn_wc__db_t *db, 2429 const char *local_abspath, 2430 svn_skel_t *conflicts, 2431 const char *conflicted_propname, 2432 svn_wc_conflict_choice_t conflict_choice, 2433 const char *merged_file, 2434 const svn_string_t *merged_value, 2435 svn_cancel_func_t cancel_func, 2436 void *cancel_baton, 2437 apr_pool_t *scratch_pool) 2438{ 2439 const char *prop_reject_file; 2440 apr_hash_t *mine_props; 2441 apr_hash_t *their_old_props; 2442 apr_hash_t *their_props; 2443 apr_hash_t *conflicted_props; 2444 apr_hash_t *old_props; 2445 apr_hash_t *resolve_from = NULL; 2446 svn_skel_t *work_items = NULL; 2447 svn_wc_operation_t operation; 2448 svn_boolean_t prop_conflicted; 2449 apr_hash_t *actual_props; 2450 svn_boolean_t resolved_all, resolved_all_prop; 2451 2452 *did_resolve = FALSE; 2453 2454 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted, 2455 NULL, db, local_abspath, conflicts, 2456 scratch_pool, scratch_pool)); 2457 if (!prop_conflicted) 2458 return SVN_NO_ERROR; 2459 2460 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file, 2461 &mine_props, &their_old_props, 2462 &their_props, &conflicted_props, 2463 db, local_abspath, conflicts, 2464 scratch_pool, scratch_pool)); 2465 2466 if (!conflicted_props) 2467 { 2468 /* We have a pre 1.8 property conflict. Just mark it resolved */ 2469 2470 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve, 2471 db, local_abspath, prop_reject_file, 2472 scratch_pool, scratch_pool)); 2473 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE, 2474 work_items, scratch_pool)); 2475 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2476 scratch_pool)); 2477 return SVN_NO_ERROR; 2478 } 2479 2480 if (conflicted_propname[0] != '\0' 2481 && !svn_hash_gets(conflicted_props, conflicted_propname)) 2482 { 2483 return SVN_NO_ERROR; /* This property is not conflicted! */ 2484 } 2485 2486 if (operation == svn_wc_operation_merge) 2487 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, 2488 scratch_pool, scratch_pool)); 2489 else 2490 old_props = their_old_props; 2491 2492 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, 2493 scratch_pool, scratch_pool)); 2494 2495 /* We currently handle *_conflict as *_full as this argument is currently 2496 always applied for all conflicts on a node at the same time. Giving 2497 an error would break some tests that assumed that this would just 2498 resolve property conflicts to working. 2499 2500 An alternative way to handle these conflicts would be to just copy all 2501 property state from mine/theirs on the _full option instead of just the 2502 conflicted properties. In some ways this feels like a sensible option as 2503 that would take both properties and text from mine/theirs, but when not 2504 both properties and text are conflicted we would fail in doing so. 2505 */ 2506 switch (conflict_choice) 2507 { 2508 case svn_wc_conflict_choose_base: 2509 resolve_from = their_old_props ? their_old_props : old_props; 2510 break; 2511 case svn_wc_conflict_choose_mine_full: 2512 case svn_wc_conflict_choose_mine_conflict: 2513 resolve_from = mine_props; 2514 break; 2515 case svn_wc_conflict_choose_theirs_full: 2516 case svn_wc_conflict_choose_theirs_conflict: 2517 resolve_from = their_props; 2518 break; 2519 case svn_wc_conflict_choose_merged: 2520 if ((merged_file || merged_value) && conflicted_propname[0] != '\0') 2521 { 2522 resolve_from = apr_hash_copy(scratch_pool, actual_props); 2523 2524 if (!merged_value) 2525 { 2526 svn_stringbuf_t *merged_propval; 2527 2528 SVN_ERR(svn_stringbuf_from_file2(&merged_propval, merged_file, 2529 scratch_pool)); 2530 2531 merged_value = svn_stringbuf__morph_into_string(merged_propval); 2532 } 2533 svn_hash_sets(resolve_from, conflicted_propname, merged_value); 2534 } 2535 else 2536 resolve_from = NULL; 2537 break; 2538 default: 2539 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 2540 _("Invalid 'conflict_result' argument")); 2541 } 2542 2543 2544 if (resolve_from) 2545 { 2546 apr_hash_index_t *hi; 2547 apr_hash_t *apply_on_props; 2548 2549 if (conflicted_propname[0] == '\0') 2550 { 2551 /* Apply to all conflicted properties */ 2552 apply_on_props = conflicted_props; 2553 } 2554 else 2555 { 2556 /* Apply to a single property */ 2557 apply_on_props = apr_hash_make(scratch_pool); 2558 svn_hash_sets(apply_on_props, conflicted_propname, ""); 2559 } 2560 2561 /* Apply the selected changes */ 2562 for (hi = apr_hash_first(scratch_pool, apply_on_props); 2563 hi; 2564 hi = apr_hash_next(hi)) 2565 { 2566 const char *propname = apr_hash_this_key(hi); 2567 svn_string_t *new_value = NULL; 2568 2569 new_value = svn_hash_gets(resolve_from, propname); 2570 2571 svn_hash_sets(actual_props, propname, new_value); 2572 } 2573 } 2574 /*else the user accepted the properties as-is */ 2575 2576 /* This function handles conflicted_propname "" as resolving 2577 all property conflicts... Just what we need here */ 2578 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts, 2579 db, local_abspath, 2580 FALSE, conflicted_propname, 2581 FALSE, 2582 scratch_pool, scratch_pool)); 2583 2584 if (!resolved_all) 2585 { 2586 /* Are there still property conflicts left? (or only...) */ 2587 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted, 2588 NULL, db, local_abspath, conflicts, 2589 scratch_pool, scratch_pool)); 2590 2591 resolved_all_prop = (! prop_conflicted); 2592 } 2593 else 2594 { 2595 resolved_all_prop = TRUE; 2596 conflicts = NULL; 2597 } 2598 2599 if (resolved_all_prop) 2600 { 2601 /* Legacy behavior: Only report property conflicts as resolved when the 2602 property reject file exists 2603 2604 If not the UI shows the conflict as already resolved 2605 (and in this case we just remove the in-db conflict) */ 2606 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve, 2607 db, local_abspath, 2608 prop_reject_file, 2609 scratch_pool, scratch_pool)); 2610 } 2611 else 2612 { 2613 /* Create a new prej file, based on the remaining conflicts */ 2614 SVN_ERR(svn_wc__wq_build_prej_install(&work_items, 2615 db, local_abspath, 2616 scratch_pool, scratch_pool)); 2617 *did_resolve = TRUE; /* We resolved a property conflict */ 2618 } 2619 2620 /* This installs the updated conflict skel */ 2621 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props, 2622 FALSE, conflicts, work_items, 2623 scratch_pool)); 2624 2625 if (resolved_all) 2626 { 2627 /* Remove the whole conflict. Should probably be integrated 2628 into the op_set_props() call */ 2629 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, 2630 FALSE, TRUE, FALSE, 2631 NULL, scratch_pool)); 2632 } 2633 2634 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2635 scratch_pool)); 2636 2637 return SVN_NO_ERROR; 2638} 2639 2640/* 2641 * Record a tree conflict resolution failure due to error condition ERR 2642 * in the RESOLVE_LATER hash table. If the hash table is not available 2643 * (meaning the caller does not wish to retry resolution later), or if 2644 * the error condition does not indicate circumstances where another 2645 * existing tree conflict is blocking the resolution attempt, then 2646 * return the error ERR itself. 2647 */ 2648static svn_error_t * 2649handle_tree_conflict_resolution_failure(const char *local_abspath, 2650 svn_error_t *err, 2651 apr_hash_t *resolve_later) 2652{ 2653 const char *dup_abspath; 2654 2655 if (!resolve_later 2656 || (err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE 2657 && err->apr_err != SVN_ERR_WC_FOUND_CONFLICT)) 2658 return svn_error_trace(err); /* Give up. Do not retry resolution later. */ 2659 2660 svn_error_clear(err); 2661 dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later), 2662 local_abspath); 2663 2664 svn_hash_sets(resolve_later, dup_abspath, dup_abspath); 2665 2666 return SVN_NO_ERROR; /* Caller may retry after resolving other conflicts. */ 2667} 2668 2669/* 2670 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to 2671 * CONFLICT_CHOICE. 2672 * 2673 * It is not an error if there is no tree conflict. If a tree conflict 2674 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE. 2675 * 2676 * It is not an error if there is no tree conflict. 2677 * 2678 * If the conflict can't be resolved yet (e.g. because another tree conflict 2679 * is blocking a storage location), and RESOLVE_LATER is not NULL, store the 2680 * tree conflict in RESOLVE_LATER and do not mark the conflict resolved. 2681 * Else if RESOLVE_LATER is NULL, do not mark the conflict resolved and 2682 * return the error which prevented the conflict from being marked resolved. 2683 */ 2684static svn_error_t * 2685resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, 2686 svn_wc__db_t *db, 2687 const char *local_abspath, 2688 const svn_skel_t *conflicts, 2689 svn_wc_conflict_choice_t conflict_choice, 2690 apr_hash_t *resolve_later, 2691 svn_wc_notify_func2_t notify_func, 2692 void *notify_baton, 2693 svn_cancel_func_t cancel_func, 2694 void *cancel_baton, 2695 apr_pool_t *scratch_pool) 2696{ 2697 svn_wc_conflict_reason_t reason; 2698 svn_wc_conflict_action_t action; 2699 svn_wc_operation_t operation; 2700 svn_boolean_t tree_conflicted; 2701 const char *src_op_root_abspath; 2702 2703 *did_resolve = FALSE; 2704 2705 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, 2706 &tree_conflicted, db, local_abspath, 2707 conflicts, scratch_pool, scratch_pool)); 2708 if (!tree_conflicted) 2709 return SVN_NO_ERROR; 2710 2711 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, 2712 &src_op_root_abspath, 2713 NULL, db, local_abspath, 2714 conflicts, 2715 scratch_pool, scratch_pool)); 2716 2717 if (operation == svn_wc_operation_update 2718 || operation == svn_wc_operation_switch) 2719 { 2720 svn_error_t *err; 2721 if (reason == svn_wc_conflict_reason_deleted || 2722 reason == svn_wc_conflict_reason_replaced) 2723 { 2724 if (conflict_choice == svn_wc_conflict_choose_merged) 2725 { 2726 /* Break moves for any children moved out of this directory, 2727 * and leave this directory deleted. */ 2728 2729 if (action != svn_wc_conflict_action_delete) 2730 { 2731 SVN_ERR(svn_wc__db_op_break_moved_away( 2732 db, local_abspath, src_op_root_abspath, TRUE, 2733 notify_func, notify_baton, 2734 scratch_pool)); 2735 *did_resolve = TRUE; 2736 return SVN_NO_ERROR; /* Marked resolved by function*/ 2737 } 2738 /* else # The move is/moves are already broken */ 2739 2740 2741 *did_resolve = TRUE; 2742 } 2743 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict) 2744 { 2745 svn_skel_t *new_conflicts; 2746 2747 /* Raise local moved-away vs. incoming edit conflicts on 2748 * any children moved out of this directory, and leave 2749 * this directory as-is. 2750 * 2751 * The newly conflicted moved-away children will be updated 2752 * if they are resolved with 'mine_conflict' as well. */ 2753 err = svn_wc__db_op_raise_moved_away( 2754 db, local_abspath, notify_func, notify_baton, 2755 scratch_pool); 2756 2757 if (err) 2758 SVN_ERR(handle_tree_conflict_resolution_failure( 2759 local_abspath, err, resolve_later)); 2760 2761 /* We might now have a moved-away on *this* path, let's 2762 try to resolve that directly if that is the case */ 2763 SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL, 2764 db, local_abspath, 2765 scratch_pool, scratch_pool)); 2766 2767 if (new_conflicts) 2768 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL, 2769 &tree_conflicted, 2770 db, local_abspath, 2771 new_conflicts, 2772 scratch_pool, 2773 scratch_pool)); 2774 2775 if (!new_conflicts || !tree_conflicted) 2776 { 2777 /* TC is marked resolved by calling 2778 svn_wc__db_op_raise_moved_away */ 2779 *did_resolve = TRUE; 2780 return SVN_NO_ERROR; 2781 } 2782 2783 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, 2784 &src_op_root_abspath, 2785 NULL, 2786 db, local_abspath, 2787 new_conflicts, 2788 scratch_pool, 2789 scratch_pool)); 2790 2791 if (reason != svn_wc_conflict_reason_moved_away) 2792 { 2793 *did_resolve = TRUE; 2794 return SVN_NO_ERROR; /* We fixed one, but... */ 2795 } 2796 2797 conflicts = new_conflicts; 2798 /* Fall through in moved_away handling */ 2799 } 2800 else 2801 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 2802 NULL, 2803 _("Tree conflict can only be resolved to " 2804 "'working' or 'mine-conflict' state; " 2805 "'%s' not resolved"), 2806 svn_dirent_local_style(local_abspath, 2807 scratch_pool)); 2808 } 2809 2810 if (reason == svn_wc_conflict_reason_moved_away 2811 && action == svn_wc_conflict_action_edit) 2812 { 2813 /* After updates, we can resolve local moved-away 2814 * vs. any incoming change, either by updating the 2815 * moved-away node (mine-conflict) or by breaking the 2816 * move (theirs-conflict). */ 2817 if (conflict_choice == svn_wc_conflict_choose_mine_conflict) 2818 { 2819 err = svn_wc__db_update_moved_away_conflict_victim( 2820 db, local_abspath, src_op_root_abspath, 2821 operation, action, reason, 2822 cancel_func, cancel_baton, 2823 notify_func, notify_baton, 2824 scratch_pool); 2825 2826 if (err) 2827 SVN_ERR(handle_tree_conflict_resolution_failure( 2828 local_abspath, err, resolve_later)); 2829 else 2830 *did_resolve = TRUE; 2831 } 2832 else if (conflict_choice == svn_wc_conflict_choose_merged) 2833 { 2834 /* We must break the move if the user accepts the current 2835 * working copy state instead of updating the move. 2836 * Else the move would be left in an invalid state. */ 2837 2838 SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath, 2839 src_op_root_abspath, TRUE, 2840 notify_func, notify_baton, 2841 scratch_pool)); 2842 *did_resolve = TRUE; 2843 return SVN_NO_ERROR; /* Conflict is marked resolved */ 2844 } 2845 else 2846 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 2847 NULL, 2848 _("Tree conflict can only be resolved to " 2849 "'working' or 'mine-conflict' state; " 2850 "'%s' not resolved"), 2851 svn_dirent_local_style(local_abspath, 2852 scratch_pool)); 2853 } 2854 else if (reason == svn_wc_conflict_reason_moved_away 2855 && action != svn_wc_conflict_action_edit) 2856 { 2857 /* action added is impossible, because that would imply that 2858 something was added, but before that already moved... 2859 (which would imply a replace) */ 2860 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete 2861 || action == svn_wc_conflict_action_replace); 2862 2863 if (conflict_choice == svn_wc_conflict_choose_merged) 2864 { 2865 /* Whatever was moved is removed at its original location by the 2866 update. That must also remove the recording of the move, so 2867 we don't have to do anything here. */ 2868 2869 *did_resolve = TRUE; 2870 } 2871 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict) 2872 { 2873 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 2874 NULL, 2875 _("Tree conflict can only be " 2876 "resolved to 'working' state; " 2877 "'%s' is no longer moved"), 2878 svn_dirent_local_style(local_abspath, 2879 scratch_pool)); 2880 } 2881 } 2882 } 2883 2884 if (! *did_resolve) 2885 { 2886 if (conflict_choice != svn_wc_conflict_choose_merged) 2887 { 2888 /* For other tree conflicts, there is no way to pick 2889 * theirs-full or mine-full, etc. Throw an error if the 2890 * user expects us to be smarter than we really are. */ 2891 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 2892 NULL, 2893 _("Tree conflict can only be " 2894 "resolved to 'working' state; " 2895 "'%s' not resolved"), 2896 svn_dirent_local_style(local_abspath, 2897 scratch_pool)); 2898 } 2899 else 2900 *did_resolve = TRUE; 2901 } 2902 2903 SVN_ERR_ASSERT(*did_resolve); 2904 2905 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE, 2906 NULL, scratch_pool)); 2907 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2908 scratch_pool)); 2909 return SVN_NO_ERROR; 2910} 2911 2912svn_error_t * 2913svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db, 2914 const char *local_abspath, 2915 svn_cancel_func_t cancel_func, 2916 void *cancel_baton, 2917 apr_pool_t *scratch_pool) 2918{ 2919 svn_skel_t *work_items; 2920 svn_skel_t *conflict; 2921 2922 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, 2923 db, local_abspath, 2924 scratch_pool, scratch_pool)); 2925 2926 if (!conflict) 2927 return SVN_NO_ERROR; 2928 2929 SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL, 2930 db, local_abspath, conflict, 2931 svn_wc_conflict_choose_merged, 2932 NULL, FALSE, NULL, 2933 cancel_func, cancel_baton, 2934 scratch_pool, scratch_pool)); 2935 2936 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE, 2937 work_items, scratch_pool)); 2938 2939 return svn_error_trace(svn_wc__wq_run(db, local_abspath, 2940 cancel_func, cancel_baton, 2941 scratch_pool)); 2942} 2943 2944svn_error_t * 2945svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db, 2946 const char *local_abspath, 2947 apr_pool_t *scratch_pool) 2948{ 2949 svn_boolean_t ignored_result; 2950 svn_skel_t *conflicts; 2951 2952 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, 2953 db, local_abspath, 2954 scratch_pool, scratch_pool)); 2955 2956 if (!conflicts) 2957 return SVN_NO_ERROR; 2958 2959 return svn_error_trace(resolve_prop_conflict_on_node( 2960 &ignored_result, 2961 db, local_abspath, conflicts, "", 2962 svn_wc_conflict_choose_merged, 2963 NULL, NULL, 2964 NULL, NULL, 2965 scratch_pool)); 2966} 2967 2968 2969/* Baton for conflict_status_walker */ 2970struct conflict_status_walker_baton 2971{ 2972 svn_wc__db_t *db; 2973 svn_boolean_t resolve_text; 2974 const char *resolve_prop; 2975 svn_boolean_t resolve_tree; 2976 svn_wc_conflict_choice_t conflict_choice; 2977 svn_wc_conflict_resolver_func2_t conflict_func; 2978 void *conflict_baton; 2979 svn_cancel_func_t cancel_func; 2980 void *cancel_baton; 2981 svn_wc_notify_func2_t notify_func; 2982 void *notify_baton; 2983 svn_boolean_t resolved_one; 2984 apr_hash_t *resolve_later; 2985}; 2986 2987/* Implements svn_wc_notify_func2_t to collect new conflicts caused by 2988 resolving a tree conflict. */ 2989static void 2990tree_conflict_collector(void *baton, 2991 const svn_wc_notify_t *notify, 2992 apr_pool_t *pool) 2993{ 2994 struct conflict_status_walker_baton *cswb = baton; 2995 2996 if (cswb->notify_func) 2997 cswb->notify_func(cswb->notify_baton, notify, pool); 2998 2999 if (cswb->resolve_later 3000 && (notify->action == svn_wc_notify_tree_conflict 3001 || notify->prop_state == svn_wc_notify_state_conflicted 3002 || notify->content_state == svn_wc_notify_state_conflicted)) 3003 { 3004 if (!svn_hash_gets(cswb->resolve_later, notify->path)) 3005 { 3006 const char *dup_path; 3007 3008 dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later), 3009 notify->path); 3010 3011 svn_hash_sets(cswb->resolve_later, dup_path, dup_path); 3012 } 3013 } 3014} 3015 3016/* Implements svn_wc_status4_t to walk all conflicts to resolve. 3017 */ 3018static svn_error_t * 3019conflict_status_walker(void *baton, 3020 const char *local_abspath, 3021 const svn_wc_status3_t *status, 3022 apr_pool_t *scratch_pool) 3023{ 3024 struct conflict_status_walker_baton *cswb = baton; 3025 svn_wc__db_t *db = cswb->db; 3026 svn_wc_notify_action_t notify_action = svn_wc_notify_resolved; 3027 const apr_array_header_t *conflicts; 3028 apr_pool_t *iterpool; 3029 int i; 3030 svn_boolean_t resolved = FALSE; 3031 svn_skel_t *conflict; 3032 const svn_wc_conflict_description2_t *cd; 3033 3034 if (!status->conflicted) 3035 return SVN_NO_ERROR; 3036 3037 iterpool = svn_pool_create(scratch_pool); 3038 3039 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict, 3040 db, local_abspath, 3041 (cswb->conflict_func != NULL) /* tmp files */, 3042 FALSE /* only tree conflicts */, 3043 scratch_pool, iterpool)); 3044 3045 for (i = 0; i < conflicts->nelts; i++) 3046 { 3047 svn_boolean_t did_resolve; 3048 svn_wc_conflict_choice_t my_choice = cswb->conflict_choice; 3049 svn_wc_conflict_result_t *result = NULL; 3050 svn_skel_t *work_items; 3051 3052 cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *); 3053 3054 if ((cd->kind == svn_wc_conflict_kind_property 3055 && (!cswb->resolve_prop 3056 || (*cswb->resolve_prop != '\0' 3057 && strcmp(cswb->resolve_prop, cd->property_name) != 0))) 3058 || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text) 3059 || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree)) 3060 { 3061 continue; /* Easy out. Don't call resolver func and ignore result */ 3062 } 3063 3064 svn_pool_clear(iterpool); 3065 3066 if (my_choice == svn_wc_conflict_choose_unspecified) 3067 { 3068 if (!cswb->conflict_func) 3069 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3070 _("No conflict-callback and no " 3071 "pre-defined conflict-choice provided")); 3072 3073 SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton, 3074 iterpool, iterpool)); 3075 3076 my_choice = result->choice; 3077 } 3078 3079 3080 if (my_choice == svn_wc_conflict_choose_postpone) 3081 continue; 3082 3083 switch (cd->kind) 3084 { 3085 case svn_wc_conflict_kind_tree: 3086 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve, 3087 db, 3088 local_abspath, conflict, 3089 my_choice, 3090 cswb->resolve_later, 3091 tree_conflict_collector, 3092 cswb, 3093 cswb->cancel_func, 3094 cswb->cancel_baton, 3095 iterpool)); 3096 3097 if (did_resolve) 3098 { 3099 resolved = TRUE; 3100 notify_action = svn_wc_notify_resolved_tree; 3101 } 3102 break; 3103 3104 case svn_wc_conflict_kind_text: 3105 SVN_ERR(build_text_conflict_resolve_items( 3106 &work_items, 3107 &resolved, 3108 db, local_abspath, conflict, 3109 my_choice, 3110 result ? result->merged_file 3111 : NULL, 3112 result ? result->save_merged 3113 : FALSE, 3114 NULL /* merge_options */, 3115 cswb->cancel_func, 3116 cswb->cancel_baton, 3117 iterpool, iterpool)); 3118 3119 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, 3120 TRUE, FALSE, FALSE, 3121 work_items, iterpool)); 3122 SVN_ERR(svn_wc__wq_run(db, local_abspath, 3123 cswb->cancel_func, cswb->cancel_baton, 3124 iterpool)); 3125 if (resolved) 3126 notify_action = svn_wc_notify_resolved_text; 3127 break; 3128 3129 case svn_wc_conflict_kind_property: 3130 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve, 3131 db, 3132 local_abspath, 3133 conflict, 3134 cd->property_name, 3135 my_choice, 3136 result 3137 ? result->merged_file 3138 : NULL, 3139 result 3140 ? result->merged_value 3141 : NULL, 3142 cswb->cancel_func, 3143 cswb->cancel_baton, 3144 iterpool)); 3145 3146 if (did_resolve) 3147 { 3148 resolved = TRUE; 3149 notify_action = svn_wc_notify_resolved_prop; 3150 } 3151 break; 3152 3153 default: 3154 /* We can't resolve other conflict types */ 3155 break; 3156 } 3157 } 3158 3159 /* Notify */ 3160 if (cswb->notify_func && resolved) 3161 { 3162 svn_wc_notify_t *notify; 3163 3164 /* If our caller asked for all conflicts to be resolved, 3165 * send a general 'resolved' notification. */ 3166 if (cswb->resolve_text && cswb->resolve_tree && 3167 (cswb->resolve_prop == NULL || cswb->resolve_prop[0] == '\0')) 3168 notify_action = svn_wc_notify_resolved; 3169 3170 /* If we resolved a property conflict, but no specific property was 3171 * requested by the caller, send a general 'resolved' notification. */ 3172 if (notify_action == svn_wc_notify_resolved_prop && 3173 (cswb->resolve_prop == NULL || cswb->resolve_prop[0] == '\0')) 3174 notify_action = svn_wc_notify_resolved; 3175 3176 notify = svn_wc_create_notify(local_abspath, notify_action, iterpool); 3177 3178 /* Add the property name for property-specific notifications. */ 3179 if (notify_action == svn_wc_notify_resolved_prop) 3180 { 3181 notify->prop_name = cd->property_name; 3182 SVN_ERR_ASSERT(strlen(notify->prop_name) > 0); 3183 } 3184 3185 cswb->notify_func(cswb->notify_baton, notify, iterpool); 3186 3187 } 3188 if (resolved) 3189 cswb->resolved_one = TRUE; 3190 3191 svn_pool_destroy(iterpool); 3192 3193 return SVN_NO_ERROR; 3194} 3195 3196svn_error_t * 3197svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, 3198 const char *local_abspath, 3199 svn_depth_t depth, 3200 svn_boolean_t resolve_text, 3201 const char *resolve_prop, 3202 svn_boolean_t resolve_tree, 3203 svn_wc_conflict_choice_t conflict_choice, 3204 svn_wc_conflict_resolver_func2_t conflict_func, 3205 void *conflict_baton, 3206 svn_cancel_func_t cancel_func, 3207 void *cancel_baton, 3208 svn_wc_notify_func2_t notify_func, 3209 void *notify_baton, 3210 apr_pool_t *scratch_pool) 3211{ 3212 struct conflict_status_walker_baton cswb; 3213 apr_pool_t *iterpool = NULL; 3214 svn_error_t *err; 3215 3216 if (depth == svn_depth_unknown) 3217 depth = svn_depth_infinity; 3218 3219 cswb.db = wc_ctx->db; 3220 cswb.resolve_text = resolve_text; 3221 cswb.resolve_prop = resolve_prop; 3222 cswb.resolve_tree = resolve_tree; 3223 cswb.conflict_choice = conflict_choice; 3224 3225 cswb.conflict_func = conflict_func; 3226 cswb.conflict_baton = conflict_baton; 3227 3228 cswb.cancel_func = cancel_func; 3229 cswb.cancel_baton = cancel_baton; 3230 3231 cswb.notify_func = notify_func; 3232 cswb.notify_baton = notify_baton; 3233 3234 cswb.resolved_one = FALSE; 3235 cswb.resolve_later = (depth != svn_depth_empty) 3236 ? apr_hash_make(scratch_pool) 3237 : NULL; 3238 3239 if (notify_func) 3240 notify_func(notify_baton, 3241 svn_wc_create_notify(local_abspath, 3242 svn_wc_notify_conflict_resolver_starting, 3243 scratch_pool), 3244 scratch_pool); 3245 3246 err = svn_wc_walk_status(wc_ctx, 3247 local_abspath, 3248 depth, 3249 FALSE /* get_all */, 3250 FALSE /* no_ignore */, 3251 TRUE /* ignore_text_mods */, 3252 NULL /* ignore_patterns */, 3253 conflict_status_walker, &cswb, 3254 cancel_func, cancel_baton, 3255 scratch_pool); 3256 3257 /* If we got new tree conflicts (or delayed conflicts) during the initial 3258 walk, we now walk them one by one as closure. */ 3259 while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later)) 3260 { 3261 apr_hash_index_t *hi; 3262 svn_wc_status3_t *status = NULL; 3263 const char *tc_abspath = NULL; 3264 3265 if (iterpool) 3266 svn_pool_clear(iterpool); 3267 else 3268 iterpool = svn_pool_create(scratch_pool); 3269 3270 hi = apr_hash_first(scratch_pool, cswb.resolve_later); 3271 cswb.resolve_later = apr_hash_make(scratch_pool); 3272 cswb.resolved_one = FALSE; 3273 3274 for (; hi && !err; hi = apr_hash_next(hi)) 3275 { 3276 const char *relpath; 3277 svn_pool_clear(iterpool); 3278 3279 tc_abspath = apr_hash_this_key(hi); 3280 3281 if (cancel_func) 3282 SVN_ERR(cancel_func(cancel_baton)); 3283 3284 relpath = svn_dirent_skip_ancestor(local_abspath, 3285 tc_abspath); 3286 3287 if (!relpath 3288 || (depth >= svn_depth_empty 3289 && depth < svn_depth_infinity 3290 && strchr(relpath, '/'))) 3291 { 3292 continue; 3293 } 3294 3295 SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath, 3296 iterpool, iterpool)); 3297 3298 if (depth == svn_depth_files 3299 && status->kind == svn_node_dir) 3300 continue; 3301 3302 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath, 3303 status, scratch_pool)); 3304 } 3305 3306 /* None of the remaining conflicts got resolved, and non did provide 3307 an error... 3308 3309 We can fix that if we disable the 'resolve_later' option... 3310 */ 3311 if (!cswb.resolved_one && !err && tc_abspath 3312 && apr_hash_count(cswb.resolve_later)) 3313 { 3314 /* Run the last resolve operation again. We still have status 3315 and tc_abspath for that one. */ 3316 3317 cswb.resolve_later = NULL; /* Produce proper error! */ 3318 3319 /* Recreate the error */ 3320 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath, 3321 status, scratch_pool)); 3322 3323 SVN_ERR_ASSERT(err != NULL); 3324 3325 err = svn_error_createf( 3326 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err, 3327 _("Unable to resolve pending conflict on '%s'"), 3328 svn_dirent_local_style(tc_abspath, scratch_pool)); 3329 break; 3330 } 3331 } 3332 3333 if (iterpool) 3334 svn_pool_destroy(iterpool); 3335 3336 if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE) 3337 err = svn_error_createf( 3338 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err, 3339 _("Unable to resolve conflicts on '%s'"), 3340 svn_dirent_local_style(local_abspath, scratch_pool)); 3341 3342 SVN_ERR(err); 3343 3344 if (notify_func) 3345 notify_func(notify_baton, 3346 svn_wc_create_notify(local_abspath, 3347 svn_wc_notify_conflict_resolver_done, 3348 scratch_pool), 3349 scratch_pool); 3350 3351 return SVN_NO_ERROR; 3352} 3353 3354svn_error_t * 3355svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx, 3356 const char *local_abspath, 3357 svn_depth_t depth, 3358 svn_boolean_t resolve_text, 3359 const char *resolve_prop, 3360 svn_boolean_t resolve_tree, 3361 svn_wc_conflict_choice_t conflict_choice, 3362 svn_cancel_func_t cancel_func, 3363 void *cancel_baton, 3364 svn_wc_notify_func2_t notify_func, 3365 void *notify_baton, 3366 apr_pool_t *scratch_pool) 3367{ 3368 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath, 3369 depth, resolve_text, 3370 resolve_prop, resolve_tree, 3371 conflict_choice, 3372 NULL, NULL, 3373 cancel_func, cancel_baton, 3374 notify_func, notify_baton, 3375 scratch_pool)); 3376} 3377 3378/* Constructor for the result-structure returned by conflict callbacks. */ 3379svn_wc_conflict_result_t * 3380svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice, 3381 const char *merged_file, 3382 apr_pool_t *pool) 3383{ 3384 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result)); 3385 result->choice = choice; 3386 result->merged_file = apr_pstrdup(pool, merged_file); 3387 result->save_merged = FALSE; 3388 result->merged_value = NULL; 3389 3390 /* If we add more fields to svn_wc_conflict_result_t, add them here. */ 3391 3392 return result; 3393} 3394 3395svn_error_t * 3396svn_wc__conflict_text_mark_resolved(svn_wc_context_t *wc_ctx, 3397 const char *local_abspath, 3398 svn_wc_conflict_choice_t choice, 3399 svn_cancel_func_t cancel_func, 3400 void *cancel_baton, 3401 svn_wc_notify_func2_t notify_func, 3402 void *notify_baton, 3403 apr_pool_t *scratch_pool) 3404{ 3405 svn_skel_t *work_items; 3406 svn_skel_t *conflict; 3407 svn_boolean_t did_resolve; 3408 3409 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, 3410 wc_ctx->db, local_abspath, 3411 scratch_pool, scratch_pool)); 3412 3413 if (!conflict) 3414 return SVN_NO_ERROR; 3415 3416 SVN_ERR(build_text_conflict_resolve_items(&work_items, &did_resolve, 3417 wc_ctx->db, local_abspath, 3418 conflict, choice, 3419 NULL, FALSE, NULL, 3420 cancel_func, cancel_baton, 3421 scratch_pool, scratch_pool)); 3422 3423 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath, 3424 TRUE, FALSE, FALSE, 3425 work_items, scratch_pool)); 3426 3427 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, 3428 cancel_func, cancel_baton, 3429 scratch_pool)); 3430 3431 if (did_resolve && notify_func) 3432 notify_func(notify_baton, 3433 svn_wc_create_notify(local_abspath, 3434 svn_wc_notify_resolved_text, 3435 scratch_pool), 3436 scratch_pool); 3437 3438 return SVN_NO_ERROR; 3439} 3440 3441svn_error_t * 3442svn_wc__conflict_prop_mark_resolved(svn_wc_context_t *wc_ctx, 3443 const char *local_abspath, 3444 const char *propname, 3445 svn_wc_conflict_choice_t choice, 3446 const svn_string_t *merged_value, 3447 svn_wc_notify_func2_t notify_func, 3448 void *notify_baton, 3449 apr_pool_t *scratch_pool) 3450{ 3451 svn_boolean_t did_resolve; 3452 svn_skel_t *conflicts; 3453 3454 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, 3455 wc_ctx->db, local_abspath, 3456 scratch_pool, scratch_pool)); 3457 3458 if (!conflicts) 3459 return SVN_NO_ERROR; 3460 3461 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve, wc_ctx->db, 3462 local_abspath, conflicts, 3463 propname, choice, NULL, merged_value, 3464 NULL, NULL, scratch_pool)); 3465 3466 if (did_resolve && notify_func) 3467 { 3468 svn_wc_notify_t *notify; 3469 3470 /* Send a general notification if no specific property was requested. */ 3471 if (propname == NULL || propname[0] == '\0') 3472 { 3473 notify = svn_wc_create_notify(local_abspath, 3474 svn_wc_notify_resolved, 3475 scratch_pool); 3476 } 3477 else 3478 { 3479 notify = svn_wc_create_notify(local_abspath, 3480 svn_wc_notify_resolved_prop, 3481 scratch_pool); 3482 notify->prop_name = propname; 3483 } 3484 3485 notify_func(notify_baton, notify, scratch_pool); 3486 } 3487 return SVN_NO_ERROR; 3488} 3489 3490svn_error_t * 3491svn_wc__conflict_tree_update_break_moved_away(svn_wc_context_t *wc_ctx, 3492 const char *local_abspath, 3493 svn_cancel_func_t cancel_func, 3494 void *cancel_baton, 3495 svn_wc_notify_func2_t notify_func, 3496 void *notify_baton, 3497 apr_pool_t *scratch_pool) 3498{ 3499 svn_wc_conflict_reason_t reason; 3500 svn_wc_conflict_action_t action; 3501 svn_wc_operation_t operation; 3502 svn_boolean_t tree_conflicted; 3503 const char *src_op_root_abspath; 3504 const apr_array_header_t *conflicts; 3505 svn_skel_t *conflict_skel; 3506 3507 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel, 3508 wc_ctx->db, local_abspath, 3509 FALSE, /* no tempfiles */ 3510 FALSE, /* only tree conflicts */ 3511 scratch_pool, scratch_pool)); 3512 3513 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, 3514 &tree_conflicted, wc_ctx->db, 3515 local_abspath, conflict_skel, 3516 scratch_pool, scratch_pool)); 3517 if (!tree_conflicted) 3518 return SVN_NO_ERROR; 3519 3520 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, 3521 &src_op_root_abspath, NULL, 3522 wc_ctx->db, local_abspath, 3523 conflict_skel, 3524 scratch_pool, scratch_pool)); 3525 3526 /* Make sure the expected conflict is recorded. */ 3527 if (operation != svn_wc_operation_update && 3528 operation != svn_wc_operation_switch) 3529 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3530 _("Unexpected conflict operation '%s' on '%s'"), 3531 svn_token__to_word(operation_map, operation), 3532 svn_dirent_local_style(local_abspath, 3533 scratch_pool)); 3534 if (reason != svn_wc_conflict_reason_deleted && 3535 reason != svn_wc_conflict_reason_replaced && 3536 reason != svn_wc_conflict_reason_moved_away) 3537 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3538 _("Unexpected conflict reason '%s' on '%s'"), 3539 svn_token__to_word(reason_map, reason), 3540 svn_dirent_local_style(local_abspath, 3541 scratch_pool)); 3542 3543 /* Break moves for any children moved out of this directory, 3544 * and leave this directory deleted. */ 3545 if (action != svn_wc_conflict_action_delete) 3546 { 3547 SVN_ERR(svn_wc__db_op_break_moved_away( 3548 wc_ctx->db, local_abspath, src_op_root_abspath, TRUE, 3549 notify_func, notify_baton, scratch_pool)); 3550 /* Conflict was marked resolved by db_op_break_moved_away() call .*/ 3551 3552 if (notify_func) 3553 notify_func(notify_baton, 3554 svn_wc_create_notify(local_abspath, 3555 svn_wc_notify_resolved_tree, 3556 scratch_pool), 3557 scratch_pool); 3558 return SVN_NO_ERROR; 3559 } 3560 /* else # The move is/moves are already broken */ 3561 3562 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath, 3563 FALSE, FALSE, TRUE, 3564 NULL, scratch_pool)); 3565 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton, 3566 scratch_pool)); 3567 3568 if (notify_func) 3569 notify_func(notify_baton, 3570 svn_wc_create_notify(local_abspath, 3571 svn_wc_notify_resolved_tree, 3572 scratch_pool), 3573 scratch_pool); 3574 3575 return SVN_NO_ERROR; 3576} 3577 3578svn_error_t * 3579svn_wc__conflict_tree_update_raise_moved_away(svn_wc_context_t *wc_ctx, 3580 const char *local_abspath, 3581 svn_cancel_func_t cancel_func, 3582 void *cancel_baton, 3583 svn_wc_notify_func2_t notify_func, 3584 void *notify_baton, 3585 apr_pool_t *scratch_pool) 3586{ 3587 svn_wc_conflict_reason_t reason; 3588 svn_wc_conflict_action_t action; 3589 svn_wc_operation_t operation; 3590 svn_boolean_t tree_conflicted; 3591 const apr_array_header_t *conflicts; 3592 svn_skel_t *conflict_skel; 3593 3594 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel, 3595 wc_ctx->db, local_abspath, 3596 FALSE, /* no tempfiles */ 3597 FALSE, /* only tree conflicts */ 3598 scratch_pool, scratch_pool)); 3599 3600 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, 3601 &tree_conflicted, wc_ctx->db, 3602 local_abspath, conflict_skel, 3603 scratch_pool, scratch_pool)); 3604 if (!tree_conflicted) 3605 return SVN_NO_ERROR; 3606 3607 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL, NULL, 3608 wc_ctx->db, local_abspath, 3609 conflict_skel, 3610 scratch_pool, scratch_pool)); 3611 3612 /* Make sure the expected conflict is recorded. */ 3613 if (operation != svn_wc_operation_update && 3614 operation != svn_wc_operation_switch) 3615 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3616 _("Unexpected conflict operation '%s' on '%s'"), 3617 svn_token__to_word(operation_map, operation), 3618 svn_dirent_local_style(local_abspath, 3619 scratch_pool)); 3620 if (reason != svn_wc_conflict_reason_deleted && 3621 reason != svn_wc_conflict_reason_replaced) 3622 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3623 _("Unexpected conflict reason '%s' on '%s'"), 3624 svn_token__to_word(reason_map, reason), 3625 svn_dirent_local_style(local_abspath, 3626 scratch_pool)); 3627 if (action != svn_wc_conflict_action_edit) 3628 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3629 _("Unexpected conflict action '%s' on '%s'"), 3630 svn_token__to_word(action_map, action), 3631 svn_dirent_local_style(local_abspath, 3632 scratch_pool)); 3633 3634 /* Raise local moved-away vs. incoming edit conflicts on any children 3635 * moved out of this directory, and leave this directory as-is. 3636 * The user may choose to update newly conflicted moved-away children 3637 * when resolving them. If this function raises an error, the conflict 3638 * cannot be resolved yet because other conflicts or obstructions 3639 * prevent us from propagating the conflict to moved-away children. */ 3640 SVN_ERR(svn_wc__db_op_raise_moved_away(wc_ctx->db, local_abspath, 3641 notify_func, notify_baton, 3642 scratch_pool)); 3643 3644 /* The conflict was marked resolved by svn_wc__db_op_raise_moved_away(). */ 3645 if (notify_func) 3646 notify_func(notify_baton, 3647 svn_wc_create_notify(local_abspath, 3648 svn_wc_notify_resolved_tree, 3649 scratch_pool), 3650 scratch_pool); 3651 3652 return SVN_NO_ERROR; 3653} 3654 3655svn_error_t * 3656svn_wc__conflict_tree_update_moved_away_node(svn_wc_context_t *wc_ctx, 3657 const char *local_abspath, 3658 svn_cancel_func_t cancel_func, 3659 void *cancel_baton, 3660 svn_wc_notify_func2_t notify_func, 3661 void *notify_baton, 3662 apr_pool_t *scratch_pool) 3663{ 3664 svn_wc_conflict_reason_t reason; 3665 svn_wc_conflict_action_t action; 3666 svn_wc_operation_t operation; 3667 svn_boolean_t tree_conflicted; 3668 const char *src_op_root_abspath; 3669 const apr_array_header_t *conflicts; 3670 svn_skel_t *conflict_skel; 3671 3672 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel, 3673 wc_ctx->db, local_abspath, 3674 FALSE, /* no tempfiles */ 3675 FALSE, /* only tree conflicts */ 3676 scratch_pool, scratch_pool)); 3677 3678 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, 3679 &tree_conflicted, wc_ctx->db, 3680 local_abspath, conflict_skel, 3681 scratch_pool, scratch_pool)); 3682 if (!tree_conflicted) 3683 return SVN_NO_ERROR; 3684 3685 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, 3686 &src_op_root_abspath, NULL, 3687 wc_ctx->db, local_abspath, 3688 conflict_skel, 3689 scratch_pool, scratch_pool)); 3690 3691 /* Make sure the expected conflict is recorded. */ 3692 if (operation != svn_wc_operation_update && 3693 operation != svn_wc_operation_switch) 3694 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3695 _("Unexpected conflict operation '%s' on '%s'"), 3696 svn_token__to_word(operation_map, operation), 3697 svn_dirent_local_style(local_abspath, 3698 scratch_pool)); 3699 if (reason != svn_wc_conflict_reason_moved_away) 3700 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3701 _("Unexpected conflict reason '%s' on '%s'"), 3702 svn_token__to_word(reason_map, reason), 3703 svn_dirent_local_style(local_abspath, 3704 scratch_pool)); 3705 if (action != svn_wc_conflict_action_edit) 3706 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3707 _("Unexpected conflict action '%s' on '%s'"), 3708 svn_token__to_word(action_map, action), 3709 svn_dirent_local_style(local_abspath, 3710 scratch_pool)); 3711 3712 /* Update the moved-away conflict victim. */ 3713 SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(wc_ctx->db, 3714 local_abspath, 3715 src_op_root_abspath, 3716 operation, 3717 action, 3718 reason, 3719 cancel_func, 3720 cancel_baton, 3721 notify_func, 3722 notify_baton, 3723 scratch_pool)); 3724 3725 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath, 3726 FALSE, FALSE, TRUE, 3727 NULL, scratch_pool)); 3728 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton, 3729 scratch_pool)); 3730 3731 if (notify_func) 3732 notify_func(notify_baton, 3733 svn_wc_create_notify(local_abspath, 3734 svn_wc_notify_resolved_tree, 3735 scratch_pool), 3736 scratch_pool); 3737 3738 return SVN_NO_ERROR; 3739} 3740 3741svn_error_t * 3742svn_wc__conflict_tree_update_incoming_move(svn_wc_context_t *wc_ctx, 3743 const char *local_abspath, 3744 const char *dest_abspath, 3745 svn_cancel_func_t cancel_func, 3746 void *cancel_baton, 3747 svn_wc_notify_func2_t notify_func, 3748 void *notify_baton, 3749 apr_pool_t *scratch_pool) 3750{ 3751 svn_wc_conflict_reason_t local_change; 3752 svn_wc_conflict_action_t incoming_change; 3753 svn_wc_operation_t operation; 3754 svn_boolean_t tree_conflicted; 3755 const apr_array_header_t *conflicts; 3756 svn_skel_t *conflict_skel; 3757 3758 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel, 3759 wc_ctx->db, local_abspath, 3760 FALSE, /* no tempfiles */ 3761 FALSE, /* only tree conflicts */ 3762 scratch_pool, scratch_pool)); 3763 3764 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, 3765 &tree_conflicted, wc_ctx->db, 3766 local_abspath, conflict_skel, 3767 scratch_pool, scratch_pool)); 3768 if (!tree_conflicted) 3769 return SVN_NO_ERROR; 3770 3771 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, &incoming_change, 3772 NULL, NULL, wc_ctx->db, 3773 local_abspath, conflict_skel, 3774 scratch_pool, scratch_pool)); 3775 3776 /* Make sure the expected conflict is recorded. */ 3777 if (operation != svn_wc_operation_update && 3778 operation != svn_wc_operation_switch && 3779 operation != svn_wc_operation_merge) 3780 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3781 _("Unexpected conflict operation '%s' on '%s'"), 3782 svn_token__to_word(operation_map, operation), 3783 svn_dirent_local_style(local_abspath, 3784 scratch_pool)); 3785 if (local_change != svn_wc_conflict_reason_edited) 3786 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3787 _("Unexpected conflict reason '%s' on '%s'"), 3788 svn_token__to_word(reason_map, local_change), 3789 svn_dirent_local_style(local_abspath, 3790 scratch_pool)); 3791 if (incoming_change != svn_wc_conflict_action_delete) 3792 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3793 _("Unexpected conflict action '%s' on '%s'"), 3794 svn_token__to_word(action_map, incoming_change), 3795 svn_dirent_local_style(local_abspath, 3796 scratch_pool)); 3797 3798 SVN_ERR(svn_wc__db_update_incoming_move(wc_ctx->db, local_abspath, 3799 dest_abspath, operation, 3800 incoming_change, local_change, 3801 cancel_func, cancel_baton, 3802 notify_func, notify_baton, 3803 scratch_pool)); 3804 3805 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton, 3806 scratch_pool)); 3807 3808 return SVN_NO_ERROR; 3809} 3810 3811svn_error_t * 3812svn_wc__conflict_tree_update_local_add(svn_wc_context_t *wc_ctx, 3813 const char *local_abspath, 3814 svn_cancel_func_t cancel_func, 3815 void *cancel_baton, 3816 svn_wc_notify_func2_t notify_func, 3817 void *notify_baton, 3818 apr_pool_t *scratch_pool) 3819{ 3820 svn_wc_conflict_reason_t local_change; 3821 svn_wc_conflict_action_t incoming_change; 3822 svn_wc_operation_t operation; 3823 svn_boolean_t tree_conflicted; 3824 const apr_array_header_t *conflicts; 3825 svn_skel_t *conflict_skel; 3826 3827 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel, 3828 wc_ctx->db, local_abspath, 3829 FALSE, /* no tempfiles */ 3830 FALSE, /* only tree conflicts */ 3831 scratch_pool, scratch_pool)); 3832 3833 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, 3834 &tree_conflicted, wc_ctx->db, 3835 local_abspath, conflict_skel, 3836 scratch_pool, scratch_pool)); 3837 if (!tree_conflicted) 3838 return SVN_NO_ERROR; 3839 3840 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, &incoming_change, 3841 NULL, NULL, wc_ctx->db, 3842 local_abspath, conflict_skel, 3843 scratch_pool, scratch_pool)); 3844 3845 /* Make sure the expected conflict is recorded. */ 3846 if (operation != svn_wc_operation_update) 3847 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3848 _("Unexpected conflict operation '%s' on '%s'"), 3849 svn_token__to_word(operation_map, operation), 3850 svn_dirent_local_style(local_abspath, 3851 scratch_pool)); 3852 if (local_change != svn_wc_conflict_reason_added) 3853 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3854 _("Unexpected conflict reason '%s' on '%s'"), 3855 svn_token__to_word(reason_map, local_change), 3856 svn_dirent_local_style(local_abspath, 3857 scratch_pool)); 3858 if (incoming_change != svn_wc_conflict_action_add) 3859 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 3860 _("Unexpected conflict action '%s' on '%s'"), 3861 svn_token__to_word(action_map, incoming_change), 3862 svn_dirent_local_style(local_abspath, 3863 scratch_pool)); 3864 3865 SVN_ERR(svn_wc__db_update_local_add(wc_ctx->db, local_abspath, 3866 cancel_func, cancel_baton, 3867 notify_func, notify_baton, 3868 scratch_pool)); 3869 3870 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton, 3871 scratch_pool)); 3872 3873 return SVN_NO_ERROR; 3874} 3875 3876svn_error_t * 3877svn_wc__guess_incoming_move_target_nodes(apr_array_header_t **possible_targets, 3878 svn_wc_context_t *wc_ctx, 3879 const char *victim_abspath, 3880 svn_node_kind_t victim_node_kind, 3881 const char *moved_to_repos_relpath, 3882 apr_pool_t *result_pool, 3883 apr_pool_t *scratch_pool) 3884{ 3885 apr_array_header_t *candidates; 3886 apr_pool_t *iterpool; 3887 int i; 3888 apr_size_t longest_ancestor_len = 0; 3889 3890 *possible_targets = apr_array_make(result_pool, 1, sizeof(const char *)); 3891 SVN_ERR(svn_wc__db_find_repos_node_in_wc(&candidates, wc_ctx->db, victim_abspath, 3892 moved_to_repos_relpath, 3893 scratch_pool, scratch_pool)); 3894 3895 /* Find a "useful move target" node in our set of candidates. 3896 * Since there is no way to be certain, filter out nodes which seem 3897 * unlikely candidates, and return the first node which is "good enough". 3898 * Nodes which are tree conflict victims don't count, and nodes which 3899 * cannot be modified (e.g. replaced or deleted nodes) don't count. 3900 * Nodes which are of a different node kind don't count either. 3901 * Ignore switched nodes as well, since that is an unlikely case during 3902 * update/swtich/merge conflict resolution. And externals shouldn't even 3903 * be on our candidate list in the first place. 3904 * If multiple candidates match these criteria, choose the one which 3905 * shares the longest common ancestor with the victim. */ 3906 iterpool = svn_pool_create(scratch_pool); 3907 for (i = 0; i < candidates->nelts; i++) 3908 { 3909 const char *local_abspath; 3910 const char *ancestor_abspath; 3911 apr_size_t ancestor_len; 3912 svn_boolean_t tree_conflicted; 3913 svn_wc__db_status_t status; 3914 svn_boolean_t is_wcroot; 3915 svn_boolean_t is_switched; 3916 svn_node_kind_t node_kind; 3917 const char *moved_to_abspath; 3918 int insert_index; 3919 3920 svn_pool_clear(iterpool); 3921 3922 local_abspath = APR_ARRAY_IDX(candidates, i, const char *); 3923 3924 SVN_ERR(svn_wc__internal_conflicted_p(NULL, NULL, &tree_conflicted, 3925 wc_ctx->db, local_abspath, 3926 iterpool)); 3927 if (tree_conflicted) 3928 continue; 3929 3930 SVN_ERR(svn_wc__db_read_info(&status, &node_kind, 3931 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3932 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3933 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3934 NULL, NULL, NULL, NULL, 3935 wc_ctx->db, local_abspath, iterpool, 3936 iterpool)); 3937 if (status != svn_wc__db_status_normal && 3938 status != svn_wc__db_status_added) 3939 continue; 3940 3941 if (victim_node_kind != svn_node_none && node_kind != victim_node_kind) 3942 continue; 3943 3944 SVN_ERR(svn_wc__db_is_switched(&is_wcroot, &is_switched, NULL, 3945 wc_ctx->db, local_abspath, iterpool)); 3946 if (is_wcroot || is_switched) 3947 continue; 3948 3949 /* This might be a move target. Fingers crossed ;-) */ 3950 moved_to_abspath = apr_pstrdup(result_pool, local_abspath); 3951 3952 /* Insert the move target into the list. Targets which are closer 3953 * (path-wise) to the conflict victim are more likely to be a good 3954 * match, so put them at the front of the list. */ 3955 ancestor_abspath = svn_dirent_get_longest_ancestor(local_abspath, 3956 victim_abspath, 3957 iterpool); 3958 ancestor_len = strlen(ancestor_abspath); 3959 if (ancestor_len >= longest_ancestor_len) 3960 { 3961 longest_ancestor_len = ancestor_len; 3962 insert_index = 0; /* prepend */ 3963 } 3964 else 3965 { 3966 insert_index = (*possible_targets)->nelts; /* append */ 3967 } 3968 SVN_ERR(svn_sort__array_insert2(*possible_targets, &moved_to_abspath, 3969 insert_index)); 3970 } 3971 3972 svn_pool_destroy(iterpool); 3973 3974 return SVN_NO_ERROR; 3975} 3976