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_string_private.h" 54 55#include "svn_private_config.h" 56 57/* -------------------------------------------------------------------- 58 * Conflict skel management 59 */ 60 61svn_skel_t * 62svn_wc__conflict_skel_create(apr_pool_t *result_pool) 63{ 64 svn_skel_t *conflict_skel = svn_skel__make_empty_list(result_pool); 65 66 /* Add empty CONFLICTS list */ 67 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel); 68 69 /* Add empty WHY list */ 70 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel); 71 72 return conflict_skel; 73} 74 75svn_error_t * 76svn_wc__conflict_skel_is_complete(svn_boolean_t *complete, 77 const svn_skel_t *conflict_skel) 78{ 79 *complete = FALSE; 80 81 if (svn_skel__list_length(conflict_skel) < 2) 82 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL, 83 _("Not a conflict skel")); 84 85 if (svn_skel__list_length(conflict_skel->children) < 2) 86 return SVN_NO_ERROR; /* WHY is not set */ 87 88 if (svn_skel__list_length(conflict_skel->children->next) == 0) 89 return SVN_NO_ERROR; /* No conflict set */ 90 91 *complete = TRUE; 92 return SVN_NO_ERROR; 93} 94 95/* Serialize a svn_wc_conflict_version_t before the existing data in skel */ 96static svn_error_t * 97conflict__prepend_location(svn_skel_t *skel, 98 const svn_wc_conflict_version_t *location, 99 svn_boolean_t allow_NULL, 100 apr_pool_t *result_pool, 101 apr_pool_t *scratch_pool) 102{ 103 svn_skel_t *loc; 104 SVN_ERR_ASSERT(location || allow_NULL); 105 106 if (!location) 107 { 108 svn_skel__prepend(svn_skel__make_empty_list(result_pool), skel); 109 return SVN_NO_ERROR; 110 } 111 112 /* ("subversion" repos_root_url repos_uuid repos_relpath rev kind) */ 113 loc = svn_skel__make_empty_list(result_pool); 114 115 svn_skel__prepend_str(svn_node_kind_to_word(location->node_kind), 116 loc, result_pool); 117 118 svn_skel__prepend_int(location->peg_rev, loc, result_pool); 119 120 svn_skel__prepend_str(apr_pstrdup(result_pool, location->path_in_repos), loc, 121 result_pool); 122 123 if (!location->repos_uuid) /* Can theoretically be NULL */ 124 svn_skel__prepend(svn_skel__make_empty_list(result_pool), loc); 125 else 126 svn_skel__prepend_str(location->repos_uuid, loc, result_pool); 127 128 svn_skel__prepend_str(apr_pstrdup(result_pool, location->repos_url), loc, 129 result_pool); 130 131 svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION, loc, result_pool); 132 133 svn_skel__prepend(loc, skel); 134 return SVN_NO_ERROR; 135} 136 137/* Deserialize a svn_wc_conflict_version_t from the skel. 138 Set *LOCATION to NULL when the data is not a svn_wc_conflict_version_t. */ 139static svn_error_t * 140conflict__read_location(svn_wc_conflict_version_t **location, 141 const svn_skel_t *skel, 142 apr_pool_t *result_pool, 143 apr_pool_t *scratch_pool) 144{ 145 const char *repos_root_url; 146 const char *repos_uuid; 147 const char *repos_relpath; 148 svn_revnum_t revision; 149 apr_int64_t v; 150 svn_node_kind_t node_kind; /* note that 'none' is a legitimate value */ 151 const char *kind_str; 152 153 const svn_skel_t *c = skel->children; 154 155 if (!svn_skel__matches_atom(c, SVN_WC__CONFLICT_SRC_SUBVERSION)) 156 { 157 *location = NULL; 158 return SVN_NO_ERROR; 159 } 160 c = c->next; 161 162 repos_root_url = apr_pstrmemdup(result_pool, c->data, c->len); 163 c = c->next; 164 165 if (c->is_atom) 166 repos_uuid = apr_pstrmemdup(result_pool, c->data, c->len); 167 else 168 repos_uuid = NULL; 169 c = c->next; 170 171 repos_relpath = apr_pstrmemdup(result_pool, c->data, c->len); 172 c = c->next; 173 174 SVN_ERR(svn_skel__parse_int(&v, c, scratch_pool)); 175 revision = (svn_revnum_t)v; 176 c = c->next; 177 178 kind_str = apr_pstrmemdup(scratch_pool, c->data, c->len); 179 node_kind = svn_node_kind_from_word(kind_str); 180 181 *location = svn_wc_conflict_version_create2(repos_root_url, 182 repos_uuid, 183 repos_relpath, 184 revision, 185 node_kind, 186 result_pool); 187 return SVN_NO_ERROR; 188} 189 190/* Get the operation part of CONFLICT_SKELL or NULL if no operation is set 191 at this time */ 192static svn_error_t * 193conflict__get_operation(svn_skel_t **why, 194 const svn_skel_t *conflict_skel) 195{ 196 SVN_ERR_ASSERT(conflict_skel 197 && conflict_skel->children 198 && conflict_skel->children->next 199 && !conflict_skel->children->next->is_atom); 200 201 *why = conflict_skel->children; 202 203 if (!(*why)->children) 204 *why = NULL; /* Operation is not set yet */ 205 206 return SVN_NO_ERROR; 207} 208 209 210svn_error_t * 211svn_wc__conflict_skel_set_op_update(svn_skel_t *conflict_skel, 212 const svn_wc_conflict_version_t *original, 213 const svn_wc_conflict_version_t *target, 214 apr_pool_t *result_pool, 215 apr_pool_t *scratch_pool) 216{ 217 svn_skel_t *why; 218 svn_skel_t *origins; 219 220 SVN_ERR_ASSERT(conflict_skel 221 && conflict_skel->children 222 && conflict_skel->children->next 223 && !conflict_skel->children->next->is_atom); 224 225 SVN_ERR(conflict__get_operation(&why, conflict_skel)); 226 227 SVN_ERR_ASSERT(why == NULL); /* No operation set */ 228 229 why = conflict_skel->children; 230 231 origins = svn_skel__make_empty_list(result_pool); 232 233 SVN_ERR(conflict__prepend_location(origins, target, TRUE, 234 result_pool, scratch_pool)); 235 SVN_ERR(conflict__prepend_location(origins, original, TRUE, 236 result_pool, scratch_pool)); 237 238 svn_skel__prepend(origins, why); 239 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_UPDATE, why, result_pool); 240 241 return SVN_NO_ERROR; 242} 243 244svn_error_t * 245svn_wc__conflict_skel_set_op_switch(svn_skel_t *conflict_skel, 246 const svn_wc_conflict_version_t *original, 247 const svn_wc_conflict_version_t *target, 248 apr_pool_t *result_pool, 249 apr_pool_t *scratch_pool) 250{ 251 svn_skel_t *why; 252 svn_skel_t *origins; 253 254 SVN_ERR_ASSERT(conflict_skel 255 && conflict_skel->children 256 && conflict_skel->children->next 257 && !conflict_skel->children->next->is_atom); 258 259 SVN_ERR(conflict__get_operation(&why, conflict_skel)); 260 261 SVN_ERR_ASSERT(why == NULL); /* No operation set */ 262 263 why = conflict_skel->children; 264 265 origins = svn_skel__make_empty_list(result_pool); 266 267 SVN_ERR(conflict__prepend_location(origins, target, TRUE, 268 result_pool, scratch_pool)); 269 SVN_ERR(conflict__prepend_location(origins, original, TRUE, 270 result_pool, scratch_pool)); 271 272 svn_skel__prepend(origins, why); 273 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_SWITCH, why, result_pool); 274 275 return SVN_NO_ERROR; 276} 277 278svn_error_t * 279svn_wc__conflict_skel_set_op_merge(svn_skel_t *conflict_skel, 280 const svn_wc_conflict_version_t *left, 281 const svn_wc_conflict_version_t *right, 282 apr_pool_t *result_pool, 283 apr_pool_t *scratch_pool) 284{ 285 svn_skel_t *why; 286 svn_skel_t *origins; 287 288 SVN_ERR_ASSERT(conflict_skel 289 && conflict_skel->children 290 && conflict_skel->children->next 291 && !conflict_skel->children->next->is_atom); 292 293 SVN_ERR(conflict__get_operation(&why, conflict_skel)); 294 295 SVN_ERR_ASSERT(why == NULL); /* No operation set */ 296 297 why = conflict_skel->children; 298 299 origins = svn_skel__make_empty_list(result_pool); 300 301 SVN_ERR(conflict__prepend_location(origins, right, TRUE, 302 result_pool, scratch_pool)); 303 304 SVN_ERR(conflict__prepend_location(origins, left, TRUE, 305 result_pool, scratch_pool)); 306 307 svn_skel__prepend(origins, why); 308 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_MERGE, why, result_pool); 309 310 return SVN_NO_ERROR; 311} 312 313/* Gets the conflict data of the specified type CONFLICT_TYPE from 314 CONFLICT_SKEL, or NULL if no such conflict is recorded */ 315static svn_error_t * 316conflict__get_conflict(svn_skel_t **conflict, 317 const svn_skel_t *conflict_skel, 318 const char *conflict_type) 319{ 320 svn_skel_t *c; 321 322 SVN_ERR_ASSERT(conflict_skel 323 && conflict_skel->children 324 && conflict_skel->children->next 325 && !conflict_skel->children->next->is_atom); 326 327 for(c = conflict_skel->children->next->children; 328 c; 329 c = c->next) 330 { 331 if (svn_skel__matches_atom(c->children, conflict_type)) 332 { 333 *conflict = c; 334 return SVN_NO_ERROR; 335 } 336 } 337 338 *conflict = NULL; 339 340 return SVN_NO_ERROR; 341} 342 343svn_error_t * 344svn_wc__conflict_skel_add_text_conflict(svn_skel_t *conflict_skel, 345 svn_wc__db_t *db, 346 const char *wri_abspath, 347 const char *mine_abspath, 348 const char *their_old_abspath, 349 const char *their_abspath, 350 apr_pool_t *result_pool, 351 apr_pool_t *scratch_pool) 352{ 353 svn_skel_t *text_conflict; 354 svn_skel_t *markers; 355 356 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel, 357 SVN_WC__CONFLICT_KIND_TEXT)); 358 359 SVN_ERR_ASSERT(!text_conflict); /* ### Use proper error? */ 360 361 /* Current skel format 362 ("text" 363 (OLD MINE OLD-THEIRS THEIRS)) */ 364 365 text_conflict = svn_skel__make_empty_list(result_pool); 366 markers = svn_skel__make_empty_list(result_pool); 367 368if (their_abspath) 369 { 370 const char *their_relpath; 371 372 SVN_ERR(svn_wc__db_to_relpath(&their_relpath, 373 db, wri_abspath, their_abspath, 374 result_pool, scratch_pool)); 375 svn_skel__prepend_str(their_relpath, markers, result_pool); 376 } 377 else 378 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers); 379 380 if (mine_abspath) 381 { 382 const char *mine_relpath; 383 384 SVN_ERR(svn_wc__db_to_relpath(&mine_relpath, 385 db, wri_abspath, mine_abspath, 386 result_pool, scratch_pool)); 387 svn_skel__prepend_str(mine_relpath, markers, result_pool); 388 } 389 else 390 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers); 391 392 if (their_old_abspath) 393 { 394 const char *original_relpath; 395 396 SVN_ERR(svn_wc__db_to_relpath(&original_relpath, 397 db, wri_abspath, their_old_abspath, 398 result_pool, scratch_pool)); 399 svn_skel__prepend_str(original_relpath, markers, result_pool); 400 } 401 else 402 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers); 403 404 svn_skel__prepend(markers, text_conflict); 405 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TEXT, text_conflict, 406 result_pool); 407 408 /* And add it to the conflict skel */ 409 svn_skel__prepend(text_conflict, conflict_skel->children->next); 410 411 return SVN_NO_ERROR; 412} 413 414svn_error_t * 415svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel, 416 svn_wc__db_t *db, 417 const char *wri_abspath, 418 const char *marker_abspath, 419 const apr_hash_t *mine_props, 420 const apr_hash_t *their_old_props, 421 const apr_hash_t *their_props, 422 const apr_hash_t *conflicted_prop_names, 423 apr_pool_t *result_pool, 424 apr_pool_t *scratch_pool) 425{ 426 svn_skel_t *prop_conflict; 427 svn_skel_t *props; 428 svn_skel_t *conflict_names; 429 svn_skel_t *markers; 430 apr_hash_index_t *hi; 431 432 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel, 433 SVN_WC__CONFLICT_KIND_PROP)); 434 435 SVN_ERR_ASSERT(!prop_conflict); /* ### Use proper error? */ 436 437 /* This function currently implements: 438 ("prop" 439 ("marker_relpath") 440 prop-conflicted_prop_names 441 old-props 442 mine-props 443 their-props) 444 NULL lists are recorded as "" */ 445 /* ### Seems that this may not match what we read out. Read-out of 446 * 'theirs-old' comes as NULL. */ 447 448 prop_conflict = svn_skel__make_empty_list(result_pool); 449 450 if (their_props) 451 { 452 SVN_ERR(svn_skel__unparse_proplist(&props, their_props, result_pool)); 453 svn_skel__prepend(props, prop_conflict); 454 } 455 else 456 svn_skel__prepend_str("", prop_conflict, result_pool); /* No their_props */ 457 458 if (mine_props) 459 { 460 SVN_ERR(svn_skel__unparse_proplist(&props, mine_props, result_pool)); 461 svn_skel__prepend(props, prop_conflict); 462 } 463 else 464 svn_skel__prepend_str("", prop_conflict, result_pool); /* No mine_props */ 465 466 if (their_old_props) 467 { 468 SVN_ERR(svn_skel__unparse_proplist(&props, their_old_props, 469 result_pool)); 470 svn_skel__prepend(props, prop_conflict); 471 } 472 else 473 svn_skel__prepend_str("", prop_conflict, result_pool); /* No old_props */ 474 475 conflict_names = svn_skel__make_empty_list(result_pool); 476 for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)conflicted_prop_names); 477 hi; 478 hi = apr_hash_next(hi)) 479 { 480 svn_skel__prepend_str(apr_pstrdup(result_pool, 481 svn__apr_hash_index_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 local_change_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 incoming_change_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 local_change, 540 svn_wc_conflict_action_t incoming_change, 541 const char *move_src_op_root_abspath, 542 apr_pool_t *result_pool, 543 apr_pool_t *scratch_pool) 544{ 545 svn_skel_t *tree_conflict; 546 svn_skel_t *markers; 547 548 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel, 549 SVN_WC__CONFLICT_KIND_TREE)); 550 551 SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */ 552 553 SVN_ERR_ASSERT(local_change == svn_wc_conflict_reason_moved_away 554 || !move_src_op_root_abspath); /* ### Use proper error? */ 555 556 tree_conflict = svn_skel__make_empty_list(result_pool); 557 558 if (local_change == svn_wc_conflict_reason_moved_away 559 && move_src_op_root_abspath) 560 { 561 const char *move_src_op_root_relpath; 562 563 SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath, 564 db, wri_abspath, 565 move_src_op_root_abspath, 566 result_pool, scratch_pool)); 567 568 svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict, 569 result_pool); 570 } 571 572 svn_skel__prepend_str( 573 svn_token__to_word(incoming_change_map, incoming_change), 574 tree_conflict, result_pool); 575 576 svn_skel__prepend_str( 577 svn_token__to_word(local_change_map, local_change), 578 tree_conflict, result_pool); 579 580 /* Tree conflicts have no marker files */ 581 markers = svn_skel__make_empty_list(result_pool); 582 svn_skel__prepend(markers, tree_conflict); 583 584 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE, tree_conflict, 585 result_pool); 586 587 /* And add it to the conflict skel */ 588 svn_skel__prepend(tree_conflict, conflict_skel->children->next); 589 590 return SVN_NO_ERROR; 591} 592 593svn_error_t * 594svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved, 595 svn_skel_t *conflict_skel, 596 svn_wc__db_t *db, 597 const char *wri_abspath, 598 svn_boolean_t resolve_text, 599 const char *resolve_prop, 600 svn_boolean_t resolve_tree, 601 apr_pool_t *result_pool, 602 apr_pool_t *scratch_pool) 603{ 604 svn_skel_t *op; 605 svn_skel_t **pconflict; 606 SVN_ERR(conflict__get_operation(&op, conflict_skel)); 607 608 if (!op) 609 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL, 610 _("Not a completed conflict skel")); 611 612 /* We are going to drop items from a linked list. Instead of keeping 613 a pointer to the item we want to drop we store a pointer to the 614 pointer of what we may drop, to allow setting it to the next item. */ 615 616 pconflict = &(conflict_skel->children->next->children); 617 while (*pconflict) 618 { 619 svn_skel_t *c = (*pconflict)->children; 620 621 if (resolve_text 622 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT)) 623 { 624 /* Remove the text conflict from the linked list */ 625 *pconflict = (*pconflict)->next; 626 continue; 627 } 628 else if (resolve_prop 629 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_PROP)) 630 { 631 svn_skel_t **ppropnames = &(c->next->next->children); 632 633 if (resolve_prop[0] == '\0') 634 *ppropnames = NULL; /* remove all conflicted property names */ 635 else 636 while (*ppropnames) 637 { 638 if (svn_skel__matches_atom(*ppropnames, resolve_prop)) 639 { 640 *ppropnames = (*ppropnames)->next; 641 break; 642 } 643 ppropnames = &((*ppropnames)->next); 644 } 645 646 /* If no conflicted property names left */ 647 if (!c->next->next->children) 648 { 649 /* Remove the propery conflict skel from the linked list */ 650 *pconflict = (*pconflict)->next; 651 continue; 652 } 653 } 654 else if (resolve_tree 655 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TREE)) 656 { 657 /* Remove the tree conflict from the linked list */ 658 *pconflict = (*pconflict)->next; 659 continue; 660 } 661 662 pconflict = &((*pconflict)->next); 663 } 664 665 if (completely_resolved) 666 { 667 /* Nice, we can just call the complete function */ 668 svn_boolean_t complete_conflict; 669 SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict, 670 conflict_skel)); 671 672 *completely_resolved = !complete_conflict; 673 } 674 return SVN_NO_ERROR; 675} 676 677 678/* A map for svn_wc_operation_t values. */ 679static const svn_token_map_t operation_map[] = 680{ 681 { "", svn_wc_operation_none }, 682 { SVN_WC__CONFLICT_OP_UPDATE, svn_wc_operation_update }, 683 { SVN_WC__CONFLICT_OP_SWITCH, svn_wc_operation_switch }, 684 { SVN_WC__CONFLICT_OP_MERGE, svn_wc_operation_merge }, 685 { NULL } 686}; 687 688svn_error_t * 689svn_wc__conflict_read_info(svn_wc_operation_t *operation, 690 const apr_array_header_t **locations, 691 svn_boolean_t *text_conflicted, 692 svn_boolean_t *prop_conflicted, 693 svn_boolean_t *tree_conflicted, 694 svn_wc__db_t *db, 695 const char *wri_abspath, 696 const svn_skel_t *conflict_skel, 697 apr_pool_t *result_pool, 698 apr_pool_t *scratch_pool) 699{ 700 svn_skel_t *op; 701 const svn_skel_t *c; 702 703 SVN_ERR(conflict__get_operation(&op, conflict_skel)); 704 705 if (!op) 706 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL, 707 _("Not a completed conflict skel")); 708 709 c = op->children; 710 if (operation) 711 { 712 int value = svn_token__from_mem(operation_map, c->data, c->len); 713 714 if (value != SVN_TOKEN_UNKNOWN) 715 *operation = value; 716 else 717 *operation = svn_wc_operation_none; 718 } 719 c = c->next; 720 721 if (locations && c->children) 722 { 723 const svn_skel_t *loc_skel; 724 svn_wc_conflict_version_t *loc; 725 apr_array_header_t *locs = apr_array_make(result_pool, 2, sizeof(loc)); 726 727 for (loc_skel = c->children; loc_skel; loc_skel = loc_skel->next) 728 { 729 SVN_ERR(conflict__read_location(&loc, loc_skel, result_pool, 730 scratch_pool)); 731 732 APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc; 733 } 734 735 *locations = locs; 736 } 737 else if (locations) 738 *locations = NULL; 739 740 if (text_conflicted) 741 { 742 svn_skel_t *c_skel; 743 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel, 744 SVN_WC__CONFLICT_KIND_TEXT)); 745 746 *text_conflicted = (c_skel != NULL); 747 } 748 749 if (prop_conflicted) 750 { 751 svn_skel_t *c_skel; 752 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel, 753 SVN_WC__CONFLICT_KIND_PROP)); 754 755 *prop_conflicted = (c_skel != NULL); 756 } 757 758 if (tree_conflicted) 759 { 760 svn_skel_t *c_skel; 761 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel, 762 SVN_WC__CONFLICT_KIND_TREE)); 763 764 *tree_conflicted = (c_skel != NULL); 765 } 766 767 return SVN_NO_ERROR; 768} 769 770 771svn_error_t * 772svn_wc__conflict_read_text_conflict(const char **mine_abspath, 773 const char **their_old_abspath, 774 const char **their_abspath, 775 svn_wc__db_t *db, 776 const char *wri_abspath, 777 const svn_skel_t *conflict_skel, 778 apr_pool_t *result_pool, 779 apr_pool_t *scratch_pool) 780{ 781 svn_skel_t *text_conflict; 782 const svn_skel_t *m; 783 784 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel, 785 SVN_WC__CONFLICT_KIND_TEXT)); 786 787 if (!text_conflict) 788 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set")); 789 790 m = text_conflict->children->next->children; 791 792 if (their_old_abspath) 793 { 794 if (m->is_atom) 795 { 796 const char *original_relpath; 797 798 original_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len); 799 SVN_ERR(svn_wc__db_from_relpath(their_old_abspath, 800 db, wri_abspath, original_relpath, 801 result_pool, scratch_pool)); 802 } 803 else 804 *their_old_abspath = NULL; 805 } 806 m = m->next; 807 808 if (mine_abspath) 809 { 810 if (m->is_atom) 811 { 812 const char *mine_relpath; 813 814 mine_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len); 815 SVN_ERR(svn_wc__db_from_relpath(mine_abspath, 816 db, wri_abspath, mine_relpath, 817 result_pool, scratch_pool)); 818 } 819 else 820 *mine_abspath = NULL; 821 } 822 m = m->next; 823 824 if (their_abspath) 825 { 826 if (m->is_atom) 827 { 828 const char *their_relpath; 829 830 their_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len); 831 SVN_ERR(svn_wc__db_from_relpath(their_abspath, 832 db, wri_abspath, their_relpath, 833 result_pool, scratch_pool)); 834 } 835 else 836 *their_abspath = NULL; 837 } 838 839 return SVN_NO_ERROR; 840} 841 842svn_error_t * 843svn_wc__conflict_read_prop_conflict(const char **marker_abspath, 844 apr_hash_t **mine_props, 845 apr_hash_t **their_old_props, 846 apr_hash_t **their_props, 847 apr_hash_t **conflicted_prop_names, 848 svn_wc__db_t *db, 849 const char *wri_abspath, 850 const svn_skel_t *conflict_skel, 851 apr_pool_t *result_pool, 852 apr_pool_t *scratch_pool) 853{ 854 svn_skel_t *prop_conflict; 855 const svn_skel_t *c; 856 857 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel, 858 SVN_WC__CONFLICT_KIND_PROP)); 859 860 if (!prop_conflict) 861 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set")); 862 863 c = prop_conflict->children; 864 865 c = c->next; /* Skip "prop" */ 866 867 /* Get marker file */ 868 if (marker_abspath) 869 { 870 const char *marker_relpath; 871 872 if (c->children && c->children->is_atom) 873 { 874 marker_relpath = apr_pstrmemdup(result_pool, c->children->data, 875 c->children->len); 876 877 SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath, 878 marker_relpath, 879 result_pool, scratch_pool)); 880 } 881 else 882 *marker_abspath = NULL; 883 } 884 c = c->next; 885 886 /* Get conflicted properties */ 887 if (conflicted_prop_names) 888 { 889 const svn_skel_t *name; 890 *conflicted_prop_names = apr_hash_make(result_pool); 891 892 for (name = c->children; name; name = name->next) 893 { 894 svn_hash_sets(*conflicted_prop_names, 895 apr_pstrmemdup(result_pool, name->data, name->len), 896 ""); 897 } 898 } 899 c = c->next; 900 901 /* Get original properties */ 902 if (their_old_props) 903 { 904 if (c->is_atom) 905 *their_old_props = apr_hash_make(result_pool); 906 else 907 SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool)); 908 } 909 c = c->next; 910 911 /* Get mine properties */ 912 if (mine_props) 913 { 914 if (c->is_atom) 915 *mine_props = apr_hash_make(result_pool); 916 else 917 SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool)); 918 } 919 c = c->next; 920 921 /* Get their properties */ 922 if (their_props) 923 { 924 if (c->is_atom) 925 *their_props = apr_hash_make(result_pool); 926 else 927 SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool)); 928 } 929 930 return SVN_NO_ERROR; 931} 932 933svn_error_t * 934svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *local_change, 935 svn_wc_conflict_action_t *incoming_change, 936 const char **move_src_op_root_abspath, 937 svn_wc__db_t *db, 938 const char *wri_abspath, 939 const svn_skel_t *conflict_skel, 940 apr_pool_t *result_pool, 941 apr_pool_t *scratch_pool) 942{ 943 svn_skel_t *tree_conflict; 944 const svn_skel_t *c; 945 svn_boolean_t is_moved_away = FALSE; 946 947 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel, 948 SVN_WC__CONFLICT_KIND_TREE)); 949 950 if (!tree_conflict) 951 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set")); 952 953 c = tree_conflict->children; 954 955 c = c->next; /* Skip "tree" */ 956 957 c = c->next; /* Skip markers */ 958 959 { 960 int value = svn_token__from_mem(local_change_map, c->data, c->len); 961 962 if (local_change) 963 { 964 if (value != SVN_TOKEN_UNKNOWN) 965 *local_change = value; 966 else 967 *local_change = svn_wc_conflict_reason_edited; 968 } 969 970 is_moved_away = (value == svn_wc_conflict_reason_moved_away); 971 } 972 c = c->next; 973 974 if (incoming_change) 975 { 976 int value = svn_token__from_mem(incoming_change_map, c->data, c->len); 977 978 if (value != SVN_TOKEN_UNKNOWN) 979 *incoming_change = value; 980 else 981 *incoming_change = svn_wc_conflict_action_edit; 982 } 983 984 c = c->next; 985 986 if (move_src_op_root_abspath) 987 { 988 /* Only set for update and switch tree conflicts */ 989 if (c && is_moved_away) 990 { 991 const char *move_src_op_root_relpath 992 = apr_pstrmemdup(scratch_pool, c->data, c->len); 993 994 SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath, 995 db, wri_abspath, 996 move_src_op_root_relpath, 997 result_pool, scratch_pool)); 998 } 999 else 1000 *move_src_op_root_abspath = NULL; 1001 } 1002 1003 return SVN_NO_ERROR; 1004} 1005 1006svn_error_t * 1007svn_wc__conflict_read_markers(const apr_array_header_t **markers, 1008 svn_wc__db_t *db, 1009 const char *wri_abspath, 1010 const svn_skel_t *conflict_skel, 1011 apr_pool_t *result_pool, 1012 apr_pool_t *scratch_pool) 1013{ 1014 const svn_skel_t *conflict; 1015 apr_array_header_t *list = NULL; 1016 1017 SVN_ERR_ASSERT(conflict_skel != NULL); 1018 1019 /* Walk the conflicts */ 1020 for (conflict = conflict_skel->children->next->children; 1021 conflict; 1022 conflict = conflict->next) 1023 { 1024 const svn_skel_t *marker; 1025 1026 /* Get the list of markers stored per conflict */ 1027 for (marker = conflict->children->next->children; 1028 marker; 1029 marker = marker->next) 1030 { 1031 /* Skip placeholders */ 1032 if (! marker->is_atom) 1033 continue; 1034 1035 if (! list) 1036 list = apr_array_make(result_pool, 4, sizeof(const char *)); 1037 1038 SVN_ERR(svn_wc__db_from_relpath( 1039 &APR_ARRAY_PUSH(list, const char*), 1040 db, wri_abspath, 1041 apr_pstrmemdup(scratch_pool, marker->data, 1042 marker->len), 1043 result_pool, scratch_pool)); 1044 } 1045 } 1046 *markers = list; 1047 1048 return SVN_NO_ERROR; 1049} 1050 1051/* -------------------------------------------------------------------- 1052 */ 1053/* Helper for svn_wc__conflict_create_markers */ 1054static svn_skel_t * 1055prop_conflict_skel_new(apr_pool_t *result_pool) 1056{ 1057 svn_skel_t *operation = svn_skel__make_empty_list(result_pool); 1058 svn_skel_t *result = svn_skel__make_empty_list(result_pool); 1059 1060 svn_skel__prepend(operation, result); 1061 return result; 1062} 1063 1064 1065/* Helper for prop_conflict_skel_add */ 1066static void 1067prepend_prop_value(const svn_string_t *value, 1068 svn_skel_t *skel, 1069 apr_pool_t *result_pool) 1070{ 1071 svn_skel_t *value_skel = svn_skel__make_empty_list(result_pool); 1072 1073 if (value != NULL) 1074 { 1075 const void *dup = apr_pmemdup(result_pool, value->data, value->len); 1076 1077 svn_skel__prepend(svn_skel__mem_atom(dup, value->len, result_pool), 1078 value_skel); 1079 } 1080 1081 svn_skel__prepend(value_skel, skel); 1082} 1083 1084 1085/* Helper for svn_wc__conflict_create_markers */ 1086static svn_error_t * 1087prop_conflict_skel_add( 1088 svn_skel_t *skel, 1089 const char *prop_name, 1090 const svn_string_t *original_value, 1091 const svn_string_t *mine_value, 1092 const svn_string_t *incoming_value, 1093 const svn_string_t *incoming_base_value, 1094 apr_pool_t *result_pool, 1095 apr_pool_t *scratch_pool) 1096{ 1097 svn_skel_t *prop_skel = svn_skel__make_empty_list(result_pool); 1098 1099 /* ### check that OPERATION has been filled in. */ 1100 1101 /* See notes/wc-ng/conflict-storage */ 1102 prepend_prop_value(incoming_base_value, prop_skel, result_pool); 1103 prepend_prop_value(incoming_value, prop_skel, result_pool); 1104 prepend_prop_value(mine_value, prop_skel, result_pool); 1105 prepend_prop_value(original_value, prop_skel, result_pool); 1106 svn_skel__prepend_str(apr_pstrdup(result_pool, prop_name), prop_skel, 1107 result_pool); 1108 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_skel, result_pool); 1109 1110 /* Now we append PROP_SKEL to the end of the provided conflict SKEL. */ 1111 svn_skel__append(skel, prop_skel); 1112 1113 return SVN_NO_ERROR; 1114} 1115 1116svn_error_t * 1117svn_wc__conflict_create_markers(svn_skel_t **work_items, 1118 svn_wc__db_t *db, 1119 const char *local_abspath, 1120 svn_skel_t *conflict_skel, 1121 apr_pool_t *result_pool, 1122 apr_pool_t *scratch_pool) 1123{ 1124 svn_boolean_t prop_conflicted; 1125 svn_wc_operation_t operation; 1126 *work_items = NULL; 1127 1128 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, 1129 NULL, &prop_conflicted, NULL, 1130 db, local_abspath, 1131 conflict_skel, 1132 scratch_pool, scratch_pool)); 1133 1134 if (prop_conflicted) 1135 { 1136 const char *marker_abspath = NULL; 1137 svn_node_kind_t kind; 1138 const char *marker_dir; 1139 const char *marker_name; 1140 const char *marker_relpath; 1141 1142 /* Ok, currently we have to do a few things for property conflicts: 1143 - Create a marker file 1144 - Create a WQ item that sets the marker name 1145 - Create a WQ item that fills the marker with the expected data 1146 1147 This can be simplified once we really store conflict_skel in wc.db */ 1148 1149 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); 1150 1151 if (kind == svn_node_dir) 1152 { 1153 marker_dir = local_abspath; 1154 marker_name = SVN_WC__THIS_DIR_PREJ; 1155 } 1156 else 1157 svn_dirent_split(&marker_dir, &marker_name, local_abspath, 1158 scratch_pool); 1159 1160 SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath, 1161 marker_dir, 1162 marker_name, 1163 SVN_WC__PROP_REJ_EXT, 1164 svn_io_file_del_none, 1165 scratch_pool, scratch_pool)); 1166 1167 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, local_abspath, 1168 marker_abspath, result_pool, result_pool)); 1169 1170 /* And store the marker in the skel */ 1171 { 1172 svn_skel_t *prop_conflict; 1173 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel, 1174 SVN_WC__CONFLICT_KIND_PROP)); 1175 1176 svn_skel__prepend_str(marker_relpath, prop_conflict->children->next, 1177 result_pool); 1178 } 1179 1180 /* Store the data in the WQ item in the same format used as 1.7. 1181 Once we store the data in DB it is easier to just read it back 1182 from the workqueue */ 1183 { 1184 svn_skel_t *prop_data; 1185 apr_hash_index_t *hi; 1186 apr_hash_t *old_props; 1187 apr_hash_t *mine_props; 1188 apr_hash_t *their_original_props; 1189 apr_hash_t *their_props; 1190 apr_hash_t *conflicted_props; 1191 1192 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL, 1193 &mine_props, 1194 &their_original_props, 1195 &their_props, 1196 &conflicted_props, 1197 db, local_abspath, 1198 conflict_skel, 1199 scratch_pool, 1200 scratch_pool)); 1201 1202 if (operation == svn_wc_operation_merge) 1203 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, 1204 scratch_pool, scratch_pool)); 1205 else 1206 old_props = their_original_props; 1207 1208 prop_data = prop_conflict_skel_new(result_pool); 1209 1210 for (hi = apr_hash_first(scratch_pool, conflicted_props); 1211 hi; 1212 hi = apr_hash_next(hi)) 1213 { 1214 const char *propname = svn__apr_hash_index_key(hi); 1215 1216 SVN_ERR(prop_conflict_skel_add( 1217 prop_data, propname, 1218 old_props 1219 ? svn_hash_gets(old_props, propname) 1220 : NULL, 1221 mine_props 1222 ? svn_hash_gets(mine_props, propname) 1223 : NULL, 1224 their_props 1225 ? svn_hash_gets(their_props, propname) 1226 : NULL, 1227 their_original_props 1228 ? svn_hash_gets(their_original_props, propname) 1229 : NULL, 1230 result_pool, scratch_pool)); 1231 } 1232 1233 SVN_ERR(svn_wc__wq_build_prej_install(work_items, 1234 db, local_abspath, 1235 prop_data, 1236 scratch_pool, scratch_pool)); 1237 } 1238 } 1239 1240 return SVN_NO_ERROR; 1241} 1242 1243/* Helper function for the three apply_* functions below, used when 1244 * merging properties together. 1245 * 1246 * Given property PROPNAME on LOCAL_ABSPATH, and four possible property 1247 * values, generate four tmpfiles and pass them to CONFLICT_FUNC callback. 1248 * This gives the client an opportunity to interactively resolve the 1249 * property conflict. 1250 * 1251 * BASE_VAL/WORKING_VAL represent the current state of the working 1252 * copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming 1253 * propchange. Any of these values might be NULL, indicating either 1254 * non-existence or intent-to-delete. 1255 * 1256 * If the callback isn't available, or if it responds with 1257 * 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return. 1258 * 1259 * If the callback responds with a choice of 'base', 'theirs', 'mine', 1260 * or 'merged', then install the proper value into ACTUAL_PROPS and 1261 * set *CONFLICT_REMAINS to FALSE. 1262 */ 1263static svn_error_t * 1264generate_propconflict(svn_boolean_t *conflict_remains, 1265 svn_wc__db_t *db, 1266 const char *local_abspath, 1267 svn_wc_operation_t operation, 1268 const svn_wc_conflict_version_t *left_version, 1269 const svn_wc_conflict_version_t *right_version, 1270 const char *propname, 1271 const svn_string_t *base_val, 1272 const svn_string_t *working_val, 1273 const svn_string_t *incoming_old_val, 1274 const svn_string_t *incoming_new_val, 1275 svn_wc_conflict_resolver_func2_t conflict_func, 1276 void *conflict_baton, 1277 apr_pool_t *scratch_pool) 1278{ 1279 svn_wc_conflict_result_t *result = NULL; 1280 svn_wc_conflict_description2_t *cdesc; 1281 const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool); 1282 svn_node_kind_t kind; 1283 const svn_string_t *new_value = NULL; 1284 1285 SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, 1286 FALSE /* allow_missing */, 1287 FALSE /* show_deleted */, 1288 FALSE /* show_hidden */, 1289 scratch_pool)); 1290 1291 if (kind == svn_node_none) 1292 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1293 _("The node '%s' was not found."), 1294 svn_dirent_local_style(local_abspath, 1295 scratch_pool)); 1296 1297 cdesc = svn_wc_conflict_description_create_prop2( 1298 local_abspath, 1299 (kind == svn_node_dir) ? svn_node_dir : svn_node_file, 1300 propname, scratch_pool); 1301 1302 cdesc->operation = operation; 1303 cdesc->src_left_version = left_version; 1304 cdesc->src_right_version = right_version; 1305 1306 /* Create a tmpfile for each of the string_t's we've got. */ 1307 if (working_val) 1308 { 1309 const char *file_name; 1310 1311 SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data, 1312 working_val->len, 1313 svn_io_file_del_on_pool_cleanup, 1314 scratch_pool)); 1315 cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); 1316 } 1317 1318 if (incoming_new_val) 1319 { 1320 const char *file_name; 1321 1322 SVN_ERR(svn_io_write_unique(&file_name, dirpath, incoming_new_val->data, 1323 incoming_new_val->len, 1324 svn_io_file_del_on_pool_cleanup, 1325 scratch_pool)); 1326 cdesc->their_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); 1327 } 1328 1329 if (!base_val && !incoming_old_val) 1330 { 1331 /* If base and old are both NULL, then that's fine, we just let 1332 base_file stay NULL as-is. Both agents are attempting to add a 1333 new property. */ 1334 } 1335 1336 else if ((base_val && !incoming_old_val) 1337 || (!base_val && incoming_old_val)) 1338 { 1339 /* If only one of base and old are defined, then we've got a 1340 situation where one agent is attempting to add the property 1341 for the first time, and the other agent is changing a 1342 property it thinks already exists. In this case, we return 1343 whichever older-value happens to be defined, so that the 1344 conflict-callback can still attempt a 3-way merge. */ 1345 1346 const svn_string_t *conflict_base_val = base_val ? base_val 1347 : incoming_old_val; 1348 const char *file_name; 1349 1350 SVN_ERR(svn_io_write_unique(&file_name, dirpath, 1351 conflict_base_val->data, 1352 conflict_base_val->len, 1353 svn_io_file_del_on_pool_cleanup, 1354 scratch_pool)); 1355 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); 1356 } 1357 1358 else /* base and old are both non-NULL */ 1359 { 1360 const svn_string_t *conflict_base_val; 1361 const char *file_name; 1362 1363 if (! svn_string_compare(base_val, incoming_old_val)) 1364 { 1365 /* What happens if 'base' and 'old' don't match up? In an 1366 ideal situation, they would. But if they don't, this is 1367 a classic example of a patch 'hunk' failing to apply due 1368 to a lack of context. For example: imagine that the user 1369 is busy changing the property from a value of "cat" to 1370 "dog", but the incoming propchange wants to change the 1371 same property value from "red" to "green". Total context 1372 mismatch. 1373 1374 HOWEVER: we can still pass one of the two base values as 1375 'base_file' to the callback anyway. It's still useful to 1376 present the working and new values to the user to 1377 compare. */ 1378 1379 if (working_val && svn_string_compare(base_val, working_val)) 1380 conflict_base_val = incoming_old_val; 1381 else 1382 conflict_base_val = base_val; 1383 } 1384 else 1385 { 1386 conflict_base_val = base_val; 1387 } 1388 1389 SVN_ERR(svn_io_write_unique(&file_name, dirpath, conflict_base_val->data, 1390 conflict_base_val->len, 1391 svn_io_file_del_on_pool_cleanup, scratch_pool)); 1392 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); 1393 1394 if (working_val && incoming_new_val) 1395 { 1396 svn_stream_t *mergestream; 1397 svn_diff_t *diff; 1398 svn_diff_file_options_t *options = 1399 svn_diff_file_options_create(scratch_pool); 1400 1401 SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->merged_file, 1402 NULL, svn_io_file_del_on_pool_cleanup, 1403 scratch_pool, scratch_pool)); 1404 SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val, 1405 working_val, 1406 incoming_new_val, options, scratch_pool)); 1407 SVN_ERR(svn_diff_mem_string_output_merge2 1408 (mergestream, diff, conflict_base_val, working_val, 1409 incoming_new_val, NULL, NULL, NULL, NULL, 1410 svn_diff_conflict_display_modified_latest, scratch_pool)); 1411 SVN_ERR(svn_stream_close(mergestream)); 1412 } 1413 } 1414 1415 if (!incoming_old_val && incoming_new_val) 1416 cdesc->action = svn_wc_conflict_action_add; 1417 else if (incoming_old_val && !incoming_new_val) 1418 cdesc->action = svn_wc_conflict_action_delete; 1419 else 1420 cdesc->action = svn_wc_conflict_action_edit; 1421 1422 if (base_val && !working_val) 1423 cdesc->reason = svn_wc_conflict_reason_deleted; 1424 else if (!base_val && working_val) 1425 cdesc->reason = svn_wc_conflict_reason_obstructed; 1426 else 1427 cdesc->reason = svn_wc_conflict_reason_edited; 1428 1429 /* Invoke the interactive conflict callback. */ 1430 { 1431 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, 1432 scratch_pool)); 1433 } 1434 if (result == NULL) 1435 { 1436 *conflict_remains = TRUE; 1437 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 1438 NULL, _("Conflict callback violated API:" 1439 " returned no results")); 1440 } 1441 1442 1443 switch (result->choice) 1444 { 1445 default: 1446 case svn_wc_conflict_choose_postpone: 1447 { 1448 *conflict_remains = TRUE; 1449 break; 1450 } 1451 case svn_wc_conflict_choose_mine_full: 1452 { 1453 /* No need to change actual_props; it already contains working_val */ 1454 *conflict_remains = FALSE; 1455 new_value = working_val; 1456 break; 1457 } 1458 /* I think _mine_full and _theirs_full are appropriate for prop 1459 behavior as well as the text behavior. There should even be 1460 analogous behaviors for _mine and _theirs when those are 1461 ready, namely: fold in all non-conflicting prop changes, and 1462 then choose _mine side or _theirs side for conflicting ones. */ 1463 case svn_wc_conflict_choose_theirs_full: 1464 { 1465 *conflict_remains = FALSE; 1466 new_value = incoming_new_val; 1467 break; 1468 } 1469 case svn_wc_conflict_choose_base: 1470 { 1471 *conflict_remains = FALSE; 1472 new_value = base_val; 1473 break; 1474 } 1475 case svn_wc_conflict_choose_merged: 1476 { 1477 svn_stringbuf_t *merged_stringbuf; 1478 1479 if (!cdesc->merged_file && !result->merged_file) 1480 return svn_error_create 1481 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 1482 NULL, _("Conflict callback violated API:" 1483 " returned no merged file")); 1484 1485 SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf, 1486 result->merged_file ? 1487 result->merged_file : 1488 cdesc->merged_file, 1489 scratch_pool)); 1490 new_value = svn_stringbuf__morph_into_string(merged_stringbuf); 1491 *conflict_remains = FALSE; 1492 break; 1493 } 1494 } 1495 1496 if (!*conflict_remains) 1497 { 1498 apr_hash_t *props; 1499 1500 /* For now, just set the property values. This should really do some of the 1501 more advanced things from svn_wc_prop_set() */ 1502 1503 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool, 1504 scratch_pool)); 1505 1506 svn_hash_sets(props, propname, new_value); 1507 1508 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props, 1509 FALSE, NULL, NULL, 1510 scratch_pool)); 1511 } 1512 1513 return SVN_NO_ERROR; 1514} 1515 1516/* Resolve the text conflict on DB/LOCAL_ABSPATH in the manner specified 1517 * by CHOICE. 1518 * 1519 * Set *WORK_ITEMS to new work items that will make the on-disk changes 1520 * needed to complete the resolution (but not to mark it as resolved). 1521 * Set *IS_RESOLVED to true if the conflicts are resolved; otherwise 1522 * (which is only if CHOICE is 'postpone') to false. 1523 * 1524 * LEFT_ABSPATH, RIGHT_ABSPATH, and DETRANSLATED_TARGET are the 1525 * input files to the 3-way merge that will be performed if CHOICE is 1526 * 'theirs-conflict' or 'mine-conflict'. LEFT_ABSPATH is also the file 1527 * that will be used if CHOICE is 'base', and RIGHT_ABSPATH if CHOICE is 1528 * 'theirs-full'. MERGED_ABSPATH will be used if CHOICE is 'merged'. 1529 * 1530 * DETRANSLATED_TARGET is the detranslated version of 'mine' (see 1531 * detranslate_wc_file() above). MERGE_OPTIONS are passed to the 1532 * diff3 implementation in case a 3-way merge has to be carried out. 1533 */ 1534static svn_error_t * 1535eval_text_conflict_func_result(svn_skel_t **work_items, 1536 svn_boolean_t *is_resolved, 1537 svn_wc__db_t *db, 1538 const char *local_abspath, 1539 svn_wc_conflict_choice_t choice, 1540 const apr_array_header_t *merge_options, 1541 const char *left_abspath, 1542 const char *right_abspath, 1543 const char *merged_abspath, 1544 const char *detranslated_target, 1545 apr_pool_t *result_pool, 1546 apr_pool_t *scratch_pool) 1547{ 1548 const char *install_from_abspath = NULL; 1549 svn_boolean_t remove_source = FALSE; 1550 1551 *work_items = NULL; 1552 1553 switch (choice) 1554 { 1555 /* If the callback wants to use one of the fulltexts 1556 to resolve the conflict, so be it.*/ 1557 case svn_wc_conflict_choose_base: 1558 { 1559 install_from_abspath = left_abspath; 1560 *is_resolved = TRUE; 1561 break; 1562 } 1563 case svn_wc_conflict_choose_theirs_full: 1564 { 1565 install_from_abspath = right_abspath; 1566 *is_resolved = TRUE; 1567 break; 1568 } 1569 case svn_wc_conflict_choose_mine_full: 1570 { 1571 install_from_abspath = detranslated_target; 1572 *is_resolved = TRUE; 1573 break; 1574 } 1575 case svn_wc_conflict_choose_theirs_conflict: 1576 case svn_wc_conflict_choose_mine_conflict: 1577 { 1578 const char *chosen_abspath; 1579 const char *temp_dir; 1580 svn_stream_t *chosen_stream; 1581 svn_diff_t *diff; 1582 svn_diff_conflict_display_style_t style; 1583 svn_diff_file_options_t *diff3_options; 1584 1585 diff3_options = svn_diff_file_options_create(scratch_pool); 1586 1587 if (merge_options) 1588 SVN_ERR(svn_diff_file_options_parse(diff3_options, 1589 merge_options, 1590 scratch_pool)); 1591 1592 style = choice == svn_wc_conflict_choose_theirs_conflict 1593 ? svn_diff_conflict_display_latest 1594 : svn_diff_conflict_display_modified; 1595 1596 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db, 1597 local_abspath, 1598 scratch_pool, scratch_pool)); 1599 SVN_ERR(svn_stream_open_unique(&chosen_stream, &chosen_abspath, 1600 temp_dir, svn_io_file_del_none, 1601 scratch_pool, scratch_pool)); 1602 1603 SVN_ERR(svn_diff_file_diff3_2(&diff, 1604 left_abspath, 1605 detranslated_target, right_abspath, 1606 diff3_options, scratch_pool)); 1607 SVN_ERR(svn_diff_file_output_merge2(chosen_stream, diff, 1608 left_abspath, 1609 detranslated_target, 1610 right_abspath, 1611 /* markers ignored */ 1612 NULL, NULL, 1613 NULL, NULL, 1614 style, 1615 scratch_pool)); 1616 SVN_ERR(svn_stream_close(chosen_stream)); 1617 1618 install_from_abspath = chosen_abspath; 1619 remove_source = TRUE; 1620 *is_resolved = TRUE; 1621 break; 1622 } 1623 1624 /* For the case of 3-way file merging, we don't 1625 really distinguish between these return values; 1626 if the callback claims to have "generally 1627 resolved" the situation, we still interpret 1628 that as "OK, we'll assume the merged version is 1629 good to use". */ 1630 case svn_wc_conflict_choose_merged: 1631 { 1632 install_from_abspath = merged_abspath; 1633 *is_resolved = TRUE; 1634 break; 1635 } 1636 case svn_wc_conflict_choose_postpone: 1637 default: 1638 { 1639 /* Assume conflict remains. */ 1640 *is_resolved = FALSE; 1641 return SVN_NO_ERROR; 1642 } 1643 } 1644 1645 SVN_ERR_ASSERT(install_from_abspath != NULL); 1646 1647 { 1648 svn_skel_t *work_item; 1649 1650 SVN_ERR(svn_wc__wq_build_file_install(&work_item, 1651 db, local_abspath, 1652 install_from_abspath, 1653 FALSE /* use_commit_times */, 1654 FALSE /* record_fileinfo */, 1655 result_pool, scratch_pool)); 1656 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1657 1658 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath, 1659 result_pool, scratch_pool)); 1660 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1661 1662 if (remove_source) 1663 { 1664 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, 1665 db, local_abspath, 1666 install_from_abspath, 1667 result_pool, scratch_pool)); 1668 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1669 } 1670 } 1671 1672 return SVN_NO_ERROR; 1673} 1674 1675 1676/* Create a new file in the same directory as LOCAL_ABSPATH, with the 1677 same basename as LOCAL_ABSPATH, with a ".edited" extension, and set 1678 *WORK_ITEM to a new work item that will copy and translate from the file 1679 SOURCE_ABSPATH to that new file. It will be translated from repository- 1680 normal form to working-copy form according to the versioned properties 1681 of LOCAL_ABSPATH that are current when the work item is executed. 1682 1683 DB should have a write lock for the directory containing SOURCE. 1684 1685 Allocate *WORK_ITEM in RESULT_POOL. */ 1686static svn_error_t * 1687save_merge_result(svn_skel_t **work_item, 1688 svn_wc__db_t *db, 1689 const char *local_abspath, 1690 const char *source_abspath, 1691 apr_pool_t *result_pool, 1692 apr_pool_t *scratch_pool) 1693{ 1694 const char *edited_copy_abspath; 1695 const char *dir_abspath; 1696 const char *filename; 1697 1698 svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool); 1699 1700 /* ### Should use preserved-conflict-file-exts. */ 1701 /* Create the .edited file within this file's DIR_ABSPATH */ 1702 SVN_ERR(svn_io_open_uniquely_named(NULL, 1703 &edited_copy_abspath, 1704 dir_abspath, 1705 filename, 1706 ".edited", 1707 svn_io_file_del_none, 1708 scratch_pool, scratch_pool)); 1709 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item, 1710 db, local_abspath, 1711 source_abspath, 1712 edited_copy_abspath, 1713 result_pool, scratch_pool)); 1714 return SVN_NO_ERROR; 1715} 1716 1717 1718/* Call the conflict resolver callback for a text conflict, and resolve 1719 * the conflict if it tells us to do so. 1720 * 1721 * Assume that there is a text conflict on the path DB/LOCAL_ABSPATH. 1722 * 1723 * Call CONFLICT_FUNC with CONFLICT_BATON to find out whether and how 1724 * it wants to resolve the conflict. Pass it a conflict description 1725 * containing OPERATION, LEFT/RIGHT_ABSPATH, LEFT/RIGHT_VERSION, 1726 * RESULT_TARGET and DETRANSLATED_TARGET. 1727 * 1728 * If the callback returns a resolution other than 'postpone', then 1729 * perform that requested resolution and prepare to mark the conflict 1730 * as resolved. 1731 * 1732 * Return *WORK_ITEMS that will do the on-disk work required to complete 1733 * the resolution (but not to mark the conflict as resolved), and set 1734 * *WAS_RESOLVED to true, if it was resolved. Set *WORK_ITEMS to NULL 1735 * and *WAS_RESOLVED to FALSE otherwise. 1736 * 1737 * RESULT_TARGET is the path to the merged file produced by the internal 1738 * or external 3-way merge, which may contain conflict markers, in 1739 * repository normal form. DETRANSLATED_TARGET is the 'mine' version of 1740 * the file, also in RNF. 1741 */ 1742static svn_error_t * 1743resolve_text_conflict(svn_skel_t **work_items, 1744 svn_boolean_t *was_resolved, 1745 svn_wc__db_t *db, 1746 const char *local_abspath, 1747 const apr_array_header_t *merge_options, 1748 svn_wc_operation_t operation, 1749 const char *left_abspath, 1750 const char *right_abspath, 1751 const svn_wc_conflict_version_t *left_version, 1752 const svn_wc_conflict_version_t *right_version, 1753 const char *result_target, 1754 const char *detranslated_target, 1755 svn_wc_conflict_resolver_func2_t conflict_func, 1756 void *conflict_baton, 1757 apr_pool_t *result_pool, 1758 apr_pool_t *scratch_pool) 1759{ 1760 svn_wc_conflict_result_t *result; 1761 svn_skel_t *work_item; 1762 svn_wc_conflict_description2_t *cdesc; 1763 apr_hash_t *props; 1764 1765 *work_items = NULL; 1766 *was_resolved = FALSE; 1767 1768 /* Give the conflict resolution callback a chance to clean 1769 up the conflicts before we mark the file 'conflicted' */ 1770 1771 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, 1772 scratch_pool, scratch_pool)); 1773 1774 cdesc = svn_wc_conflict_description_create_text2(local_abspath, 1775 scratch_pool); 1776 cdesc->is_binary = FALSE; 1777 cdesc->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE); 1778 cdesc->base_abspath = left_abspath; 1779 cdesc->their_abspath = right_abspath; 1780 cdesc->my_abspath = detranslated_target; 1781 cdesc->merged_file = result_target; 1782 cdesc->operation = operation; 1783 cdesc->src_left_version = left_version; 1784 cdesc->src_right_version = right_version; 1785 1786 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, 1787 scratch_pool)); 1788 if (result == NULL) 1789 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 1790 _("Conflict callback violated API:" 1791 " returned no results")); 1792 1793 if (result->save_merged) 1794 { 1795 SVN_ERR(save_merge_result(work_items, 1796 db, local_abspath, 1797 /* Look for callback's own 1798 merged-file first: */ 1799 result->merged_file 1800 ? result->merged_file 1801 : result_target, 1802 result_pool, scratch_pool)); 1803 } 1804 1805 if (result->choice != svn_wc_conflict_choose_postpone) 1806 { 1807 SVN_ERR(eval_text_conflict_func_result(&work_item, 1808 was_resolved, 1809 db, local_abspath, 1810 result->choice, 1811 merge_options, 1812 left_abspath, 1813 right_abspath, 1814 /* ### Sure this is an abspath? */ 1815 result->merged_file 1816 ? result->merged_file 1817 : result_target, 1818 detranslated_target, 1819 result_pool, scratch_pool)); 1820 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 1821 } 1822 else 1823 *was_resolved = FALSE; 1824 1825 return SVN_NO_ERROR; 1826} 1827 1828 1829static svn_error_t * 1830setup_tree_conflict_desc(svn_wc_conflict_description2_t **desc, 1831 svn_wc__db_t *db, 1832 const char *local_abspath, 1833 svn_wc_operation_t operation, 1834 const svn_wc_conflict_version_t *left_version, 1835 const svn_wc_conflict_version_t *right_version, 1836 svn_wc_conflict_reason_t local_change, 1837 svn_wc_conflict_action_t incoming_change, 1838 apr_pool_t *result_pool, 1839 apr_pool_t *scratch_pool) 1840{ 1841 svn_node_kind_t tc_kind; 1842 1843 if (left_version) 1844 tc_kind = left_version->node_kind; 1845 else if (right_version) 1846 tc_kind = right_version->node_kind; 1847 else 1848 tc_kind = svn_node_file; /* Avoid assertion */ 1849 1850 *desc = svn_wc_conflict_description_create_tree2(local_abspath, tc_kind, 1851 operation, 1852 left_version, right_version, 1853 result_pool); 1854 (*desc)->reason = local_change; 1855 (*desc)->action = incoming_change; 1856 1857 return SVN_NO_ERROR; 1858} 1859 1860 1861svn_error_t * 1862svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, 1863 const char *local_abspath, 1864 const svn_skel_t *conflict_skel, 1865 const apr_array_header_t *merge_options, 1866 svn_wc_conflict_resolver_func2_t resolver_func, 1867 void *resolver_baton, 1868 svn_cancel_func_t cancel_func, 1869 void *cancel_baton, 1870 apr_pool_t *scratch_pool) 1871{ 1872 svn_boolean_t text_conflicted; 1873 svn_boolean_t prop_conflicted; 1874 svn_boolean_t tree_conflicted; 1875 svn_wc_operation_t operation; 1876 const apr_array_header_t *locations; 1877 const svn_wc_conflict_version_t *left_version = NULL; 1878 const svn_wc_conflict_version_t *right_version = NULL; 1879 1880 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, 1881 &text_conflicted, &prop_conflicted, 1882 &tree_conflicted, 1883 db, local_abspath, conflict_skel, 1884 scratch_pool, scratch_pool)); 1885 1886 if (locations && locations->nelts > 0) 1887 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *); 1888 1889 if (locations && locations->nelts > 1) 1890 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *); 1891 1892 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers 1893 would want to look at all properties at the same time. 1894 1895 ### svn currently only invokes this from the merge code to collect the list of 1896 ### conflicted paths. Eventually this code will be the base for 'svn resolve' 1897 ### and at that time the test coverage will improve 1898 */ 1899 if (prop_conflicted) 1900 { 1901 apr_hash_t *old_props; 1902 apr_hash_t *mine_props; 1903 apr_hash_t *their_props; 1904 apr_hash_t *old_their_props; 1905 apr_hash_t *conflicted; 1906 apr_pool_t *iterpool; 1907 apr_hash_index_t *hi; 1908 svn_boolean_t mark_resolved = TRUE; 1909 1910 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL, 1911 &mine_props, 1912 &old_their_props, 1913 &their_props, 1914 &conflicted, 1915 db, local_abspath, 1916 conflict_skel, 1917 scratch_pool, scratch_pool)); 1918 1919 if (operation == svn_wc_operation_merge) 1920 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, 1921 scratch_pool, scratch_pool)); 1922 else 1923 old_props = old_their_props; 1924 1925 iterpool = svn_pool_create(scratch_pool); 1926 1927 for (hi = apr_hash_first(scratch_pool, conflicted); 1928 hi; 1929 hi = apr_hash_next(hi)) 1930 { 1931 const char *propname = svn__apr_hash_index_key(hi); 1932 svn_boolean_t conflict_remains = TRUE; 1933 1934 svn_pool_clear(iterpool); 1935 1936 if (cancel_func) 1937 SVN_ERR(cancel_func(cancel_baton)); 1938 1939 SVN_ERR(generate_propconflict(&conflict_remains, 1940 db, local_abspath, 1941 operation, 1942 left_version, 1943 right_version, 1944 propname, 1945 old_props 1946 ? svn_hash_gets(old_props, propname) 1947 : NULL, 1948 mine_props 1949 ? svn_hash_gets(mine_props, propname) 1950 : NULL, 1951 old_their_props 1952 ? svn_hash_gets(old_their_props, propname) 1953 : NULL, 1954 their_props 1955 ? svn_hash_gets(their_props, propname) 1956 : NULL, 1957 resolver_func, resolver_baton, 1958 iterpool)); 1959 1960 if (conflict_remains) 1961 mark_resolved = FALSE; 1962 } 1963 1964 if (mark_resolved) 1965 { 1966 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath, 1967 scratch_pool)); 1968 } 1969 } 1970 1971 if (text_conflicted) 1972 { 1973 const char *mine_abspath; 1974 const char *their_original_abspath; 1975 const char *their_abspath; 1976 svn_skel_t *work_items; 1977 svn_boolean_t was_resolved; 1978 1979 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath, 1980 &their_original_abspath, 1981 &their_abspath, 1982 db, local_abspath, 1983 conflict_skel, 1984 scratch_pool, scratch_pool)); 1985 1986 SVN_ERR(resolve_text_conflict(&work_items, &was_resolved, 1987 db, local_abspath, 1988 merge_options, 1989 operation, 1990 their_original_abspath, their_abspath, 1991 left_version, right_version, 1992 local_abspath, mine_abspath, 1993 resolver_func, resolver_baton, 1994 scratch_pool, scratch_pool)); 1995 1996 if (was_resolved) 1997 { 1998 if (work_items) 1999 { 2000 SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_items, 2001 scratch_pool)); 2002 SVN_ERR(svn_wc__wq_run(db, local_abspath, 2003 cancel_func, cancel_baton, 2004 scratch_pool)); 2005 } 2006 SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath, 2007 scratch_pool)); 2008 } 2009 } 2010 2011 if (tree_conflicted) 2012 { 2013 svn_wc_conflict_reason_t local_change; 2014 svn_wc_conflict_action_t incoming_change; 2015 svn_wc_conflict_result_t *result; 2016 svn_wc_conflict_description2_t *desc; 2017 2018 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, 2019 &incoming_change, 2020 NULL, 2021 db, local_abspath, 2022 conflict_skel, 2023 scratch_pool, scratch_pool)); 2024 2025 SVN_ERR(setup_tree_conflict_desc(&desc, 2026 db, local_abspath, 2027 operation, left_version, right_version, 2028 local_change, incoming_change, 2029 scratch_pool, scratch_pool)); 2030 2031 /* Tell the resolver func about this conflict. */ 2032 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool, 2033 scratch_pool)); 2034 2035 /* Ignore the result. We cannot apply it here since this code runs 2036 * during an update or merge operation. Tree conflicts are always 2037 * postponed and resolved after the operation has completed. */ 2038 } 2039 2040 return SVN_NO_ERROR; 2041} 2042 2043/* Read all property conflicts contained in CONFLICT_SKEL into 2044 * individual conflict descriptions, and append those descriptions 2045 * to the CONFLICTS array. 2046 * 2047 * If NOT create_tempfiles, always create a legacy property conflict 2048 * descriptor. 2049 * 2050 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and 2051 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL. 2052 * 2053 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary 2054 * allocations. */ 2055static svn_error_t * 2056read_prop_conflicts(apr_array_header_t *conflicts, 2057 svn_wc__db_t *db, 2058 const char *local_abspath, 2059 svn_skel_t *conflict_skel, 2060 svn_boolean_t create_tempfiles, 2061 svn_node_kind_t node_kind, 2062 svn_wc_operation_t operation, 2063 const svn_wc_conflict_version_t *left_version, 2064 const svn_wc_conflict_version_t *right_version, 2065 apr_pool_t *result_pool, 2066 apr_pool_t *scratch_pool) 2067{ 2068 const char *prop_reject_file; 2069 apr_hash_t *my_props; 2070 apr_hash_t *their_old_props; 2071 apr_hash_t *their_props; 2072 apr_hash_t *conflicted_props; 2073 apr_hash_index_t *hi; 2074 apr_pool_t *iterpool; 2075 2076 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file, 2077 &my_props, 2078 &their_old_props, 2079 &their_props, 2080 &conflicted_props, 2081 db, local_abspath, 2082 conflict_skel, 2083 scratch_pool, scratch_pool)); 2084 2085 if ((! create_tempfiles) || apr_hash_count(conflicted_props) == 0) 2086 { 2087 /* Legacy prop conflict with only a .reject file. */ 2088 svn_wc_conflict_description2_t *desc; 2089 2090 desc = svn_wc_conflict_description_create_prop2(local_abspath, 2091 node_kind, 2092 "", result_pool); 2093 2094 /* ### This should be changed. The prej file should be stored 2095 * ### separately from the other files. We need to rev the 2096 * ### conflict description struct for this. */ 2097 desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file); 2098 2099 desc->operation = operation; 2100 desc->src_left_version = left_version; 2101 desc->src_right_version = right_version; 2102 2103 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc; 2104 2105 return SVN_NO_ERROR; 2106 } 2107 2108 iterpool = svn_pool_create(scratch_pool); 2109 for (hi = apr_hash_first(scratch_pool, conflicted_props); 2110 hi; 2111 hi = apr_hash_next(hi)) 2112 { 2113 const char *propname = svn__apr_hash_index_key(hi); 2114 svn_string_t *old_value; 2115 svn_string_t *my_value; 2116 svn_string_t *their_value; 2117 svn_wc_conflict_description2_t *desc; 2118 2119 svn_pool_clear(iterpool); 2120 2121 desc = svn_wc_conflict_description_create_prop2(local_abspath, 2122 node_kind, 2123 propname, 2124 result_pool); 2125 2126 desc->operation = operation; 2127 desc->src_left_version = left_version; 2128 desc->src_right_version = right_version; 2129 2130 desc->property_name = apr_pstrdup(result_pool, propname); 2131 2132 my_value = svn_hash_gets(my_props, propname); 2133 their_value = svn_hash_gets(their_props, propname); 2134 old_value = svn_hash_gets(their_old_props, propname); 2135 2136 /* Compute the incoming side of the conflict ('action'). */ 2137 if (their_value == NULL) 2138 desc->action = svn_wc_conflict_action_delete; 2139 else if (old_value == NULL) 2140 desc->action = svn_wc_conflict_action_add; 2141 else 2142 desc->action = svn_wc_conflict_action_edit; 2143 2144 /* Compute the local side of the conflict ('reason'). */ 2145 if (my_value == NULL) 2146 desc->reason = svn_wc_conflict_reason_deleted; 2147 else if (old_value == NULL) 2148 desc->reason = svn_wc_conflict_reason_added; 2149 else 2150 desc->reason = svn_wc_conflict_reason_edited; 2151 2152 /* ### This should be changed. The prej file should be stored 2153 * ### separately from the other files. We need to rev the 2154 * ### conflict description struct for this. */ 2155 desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file); 2156 2157 /* ### This should be changed. The conflict description for 2158 * ### props should contain these values as svn_string_t, 2159 * ### rather than in temporary files. We need to rev the 2160 * ### conflict description struct for this. */ 2161 if (my_value) 2162 { 2163 svn_stream_t *s; 2164 apr_size_t len; 2165 2166 SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL, 2167 svn_io_file_del_on_pool_cleanup, 2168 result_pool, iterpool)); 2169 len = my_value->len; 2170 SVN_ERR(svn_stream_write(s, my_value->data, &len)); 2171 SVN_ERR(svn_stream_close(s)); 2172 } 2173 2174 if (their_value) 2175 { 2176 svn_stream_t *s; 2177 apr_size_t len; 2178 2179 /* ### Currently, their_abspath is used for the prop reject file. 2180 * ### Put their value into merged instead... 2181 * ### We need to rev the conflict description struct to fix this. */ 2182 SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL, 2183 svn_io_file_del_on_pool_cleanup, 2184 result_pool, iterpool)); 2185 len = their_value->len; 2186 SVN_ERR(svn_stream_write(s, their_value->data, &len)); 2187 SVN_ERR(svn_stream_close(s)); 2188 } 2189 2190 if (old_value) 2191 { 2192 svn_stream_t *s; 2193 apr_size_t len; 2194 2195 SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL, 2196 svn_io_file_del_on_pool_cleanup, 2197 result_pool, iterpool)); 2198 len = old_value->len; 2199 SVN_ERR(svn_stream_write(s, old_value->data, &len)); 2200 SVN_ERR(svn_stream_close(s)); 2201 } 2202 2203 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc; 2204 } 2205 svn_pool_destroy(iterpool); 2206 2207 return SVN_NO_ERROR; 2208} 2209 2210svn_error_t * 2211svn_wc__read_conflicts(const apr_array_header_t **conflicts, 2212 svn_wc__db_t *db, 2213 const char *local_abspath, 2214 svn_boolean_t create_tempfiles, 2215 apr_pool_t *result_pool, 2216 apr_pool_t *scratch_pool) 2217{ 2218 svn_skel_t *conflict_skel; 2219 apr_array_header_t *cflcts; 2220 svn_boolean_t prop_conflicted; 2221 svn_boolean_t text_conflicted; 2222 svn_boolean_t tree_conflicted; 2223 svn_wc_operation_t operation; 2224 const apr_array_header_t *locations; 2225 const svn_wc_conflict_version_t *left_version = NULL; 2226 const svn_wc_conflict_version_t *right_version = NULL; 2227 2228 SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, local_abspath, 2229 scratch_pool, scratch_pool)); 2230 2231 if (!conflict_skel) 2232 { 2233 /* Some callers expect not NULL */ 2234 *conflicts = apr_array_make(result_pool, 0, 2235 sizeof(svn_wc_conflict_description2_t*));; 2236 return SVN_NO_ERROR; 2237 } 2238 2239 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted, 2240 &prop_conflicted, &tree_conflicted, 2241 db, local_abspath, conflict_skel, 2242 result_pool, scratch_pool)); 2243 2244 cflcts = apr_array_make(result_pool, 4, 2245 sizeof(svn_wc_conflict_description2_t*)); 2246 2247 if (locations && locations->nelts > 0) 2248 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *); 2249 if (locations && locations->nelts > 1) 2250 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *); 2251 2252 if (prop_conflicted) 2253 { 2254 svn_node_kind_t node_kind 2255 = left_version ? left_version->node_kind : svn_node_unknown; 2256 2257 SVN_ERR(read_prop_conflicts(cflcts, db, local_abspath, conflict_skel, 2258 create_tempfiles, node_kind, 2259 operation, left_version, right_version, 2260 result_pool, scratch_pool)); 2261 } 2262 2263 if (text_conflicted) 2264 { 2265 svn_wc_conflict_description2_t *desc; 2266 desc = svn_wc_conflict_description_create_text2(local_abspath, 2267 result_pool); 2268 2269 desc->operation = operation; 2270 desc->src_left_version = left_version; 2271 desc->src_right_version = right_version; 2272 2273 SVN_ERR(svn_wc__conflict_read_text_conflict(&desc->my_abspath, 2274 &desc->base_abspath, 2275 &desc->their_abspath, 2276 db, local_abspath, 2277 conflict_skel, 2278 result_pool, scratch_pool)); 2279 2280 desc->merged_file = apr_pstrdup(result_pool, local_abspath); 2281 2282 APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t*) = desc; 2283 } 2284 2285 if (tree_conflicted) 2286 { 2287 svn_wc_conflict_reason_t local_change; 2288 svn_wc_conflict_action_t incoming_change; 2289 svn_wc_conflict_description2_t *desc; 2290 2291 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, 2292 &incoming_change, 2293 NULL, 2294 db, local_abspath, 2295 conflict_skel, 2296 scratch_pool, scratch_pool)); 2297 2298 SVN_ERR(setup_tree_conflict_desc(&desc, 2299 db, local_abspath, 2300 operation, left_version, right_version, 2301 local_change, incoming_change, 2302 result_pool, scratch_pool)); 2303 2304 APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc; 2305 } 2306 2307 *conflicts = cflcts; 2308 return SVN_NO_ERROR; 2309} 2310 2311 2312/*** Resolving a conflict automatically ***/ 2313 2314/* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the 2315 * working copy at DB/WRI_ABSPATH. 2316 * 2317 * Set *WORK_ITEMS to a new work item that, when run, will delete the 2318 * artifact file; or to NULL if there is no file to delete. 2319 * 2320 * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its 2321 * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND 2322 * may be NULL if not required. 2323 */ 2324static svn_error_t * 2325remove_artifact_file_if_exists(svn_skel_t **work_items, 2326 svn_boolean_t *file_found, 2327 svn_wc__db_t *db, 2328 const char *wri_abspath, 2329 const char *artifact_file_abspath, 2330 apr_pool_t *result_pool, 2331 apr_pool_t *scratch_pool) 2332{ 2333 *work_items = NULL; 2334 if (artifact_file_abspath) 2335 { 2336 svn_node_kind_t node_kind; 2337 2338 SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind, 2339 scratch_pool)); 2340 if (node_kind == svn_node_file) 2341 { 2342 SVN_ERR(svn_wc__wq_build_file_remove(work_items, 2343 db, wri_abspath, 2344 artifact_file_abspath, 2345 result_pool, scratch_pool)); 2346 if (file_found) 2347 *file_found = TRUE; 2348 } 2349 } 2350 2351 return SVN_NO_ERROR; 2352} 2353 2354/* 2355 * Resolve the text conflict found in DB/LOCAL_ABSPATH according 2356 * to CONFLICT_CHOICE. 2357 * 2358 * It is not an error if there is no text conflict. If a text conflict 2359 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE. 2360 * 2361 * Note: When there are no conflict markers to remove there is no existing 2362 * text conflict; just a database containing old information, which we should 2363 * remove to avoid checking all the time. Resolving a text conflict by 2364 * removing all the marker files is a fully supported scenario since 2365 * Subversion 1.0. 2366 */ 2367static svn_error_t * 2368resolve_text_conflict_on_node(svn_boolean_t *did_resolve, 2369 svn_wc__db_t *db, 2370 const char *local_abspath, 2371 svn_wc_conflict_choice_t conflict_choice, 2372 const char *merged_file, 2373 svn_cancel_func_t cancel_func, 2374 void *cancel_baton, 2375 apr_pool_t *scratch_pool) 2376{ 2377 const char *conflict_old = NULL; 2378 const char *conflict_new = NULL; 2379 const char *conflict_working = NULL; 2380 const char *auto_resolve_src; 2381 svn_skel_t *work_item; 2382 svn_skel_t *work_items = NULL; 2383 svn_skel_t *conflicts; 2384 svn_wc_operation_t operation; 2385 svn_boolean_t text_conflicted; 2386 2387 *did_resolve = FALSE; 2388 2389 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, 2390 scratch_pool, scratch_pool)); 2391 if (!conflicts) 2392 return SVN_NO_ERROR; 2393 2394 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, &text_conflicted, 2395 NULL, NULL, db, local_abspath, conflicts, 2396 scratch_pool, scratch_pool)); 2397 if (!text_conflicted) 2398 return SVN_NO_ERROR; 2399 2400 SVN_ERR(svn_wc__conflict_read_text_conflict(&conflict_working, 2401 &conflict_old, 2402 &conflict_new, 2403 db, local_abspath, conflicts, 2404 scratch_pool, scratch_pool)); 2405 2406 /* Handle automatic conflict resolution before the temporary files are 2407 * deleted, if necessary. */ 2408 switch (conflict_choice) 2409 { 2410 case svn_wc_conflict_choose_base: 2411 auto_resolve_src = conflict_old; 2412 break; 2413 case svn_wc_conflict_choose_mine_full: 2414 auto_resolve_src = conflict_working; 2415 break; 2416 case svn_wc_conflict_choose_theirs_full: 2417 auto_resolve_src = conflict_new; 2418 break; 2419 case svn_wc_conflict_choose_merged: 2420 auto_resolve_src = merged_file; 2421 break; 2422 case svn_wc_conflict_choose_theirs_conflict: 2423 case svn_wc_conflict_choose_mine_conflict: 2424 { 2425 if (conflict_old && conflict_working && conflict_new) 2426 { 2427 const char *temp_dir; 2428 svn_stream_t *tmp_stream = NULL; 2429 svn_diff_t *diff; 2430 svn_diff_conflict_display_style_t style = 2431 conflict_choice == svn_wc_conflict_choose_theirs_conflict 2432 ? svn_diff_conflict_display_latest 2433 : svn_diff_conflict_display_modified; 2434 2435 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db, 2436 local_abspath, 2437 scratch_pool, 2438 scratch_pool)); 2439 SVN_ERR(svn_stream_open_unique(&tmp_stream, 2440 &auto_resolve_src, 2441 temp_dir, 2442 svn_io_file_del_on_pool_cleanup, 2443 scratch_pool, scratch_pool)); 2444 2445 SVN_ERR(svn_diff_file_diff3_2(&diff, 2446 conflict_old, 2447 conflict_working, 2448 conflict_new, 2449 svn_diff_file_options_create( 2450 scratch_pool), 2451 scratch_pool)); 2452 SVN_ERR(svn_diff_file_output_merge2(tmp_stream, diff, 2453 conflict_old, 2454 conflict_working, 2455 conflict_new, 2456 /* markers ignored */ 2457 NULL, NULL, NULL, NULL, 2458 style, 2459 scratch_pool)); 2460 SVN_ERR(svn_stream_close(tmp_stream)); 2461 } 2462 else 2463 auto_resolve_src = NULL; 2464 break; 2465 } 2466 default: 2467 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 2468 _("Invalid 'conflict_result' argument")); 2469 } 2470 2471 if (auto_resolve_src) 2472 { 2473 SVN_ERR(svn_wc__wq_build_file_copy_translated( 2474 &work_item, db, local_abspath, 2475 auto_resolve_src, local_abspath, scratch_pool, scratch_pool)); 2476 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2477 2478 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, 2479 local_abspath, 2480 scratch_pool, scratch_pool)); 2481 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2482 } 2483 2484 /* Legacy behavior: Only report text conflicts as resolved when at least 2485 one conflict marker file exists. 2486 2487 If not the UI shows the conflict as already resolved 2488 (and in this case we just remove the in-db conflict) */ 2489 2490 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, 2491 db, local_abspath, conflict_old, 2492 scratch_pool, scratch_pool)); 2493 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2494 2495 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, 2496 db, local_abspath, conflict_new, 2497 scratch_pool, scratch_pool)); 2498 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2499 2500 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, 2501 db, local_abspath, conflict_working, 2502 scratch_pool, scratch_pool)); 2503 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2504 2505 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, 2506 TRUE, FALSE, FALSE, 2507 work_items, scratch_pool)); 2508 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2509 scratch_pool)); 2510 2511 return SVN_NO_ERROR; 2512} 2513 2514/* 2515 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according 2516 * to CONFLICT_CHOICE. 2517 * 2518 * It is not an error if there is no prop conflict. If a prop conflict 2519 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE. 2520 * 2521 * Note: When there are no conflict markers on-disk to remove there is 2522 * no existing text conflict (unless we are still in the process of 2523 * creating the text conflict and we didn't register a marker file yet). 2524 * In this case the database contains old information, which we should 2525 * remove to avoid checking the next time. Resolving a property conflict 2526 * by just removing the marker file is a fully supported scenario since 2527 * Subversion 1.0. 2528 * 2529 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ. 2530 * In my opinion, 'mine_full'/'theirs_full' should select 2531 * the entire set of properties from 'mine' or 'theirs' respectively, 2532 * while 'mine_conflict'/'theirs_conflict' should select just the 2533 * properties that are in conflict. Or, '_full' should select the 2534 * entire property whereas '_conflict' should do a text merge within 2535 * each property, selecting hunks. Or all three kinds of behaviour 2536 * should be available (full set of props, full value of conflicting 2537 * props, or conflicting text hunks). 2538 * ### BH: If we make *_full select the full set of properties, we should 2539 * check if we shouldn't make it also select the full text for files. 2540 * 2541 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc 2542 * but in a layer above. 2543 * 2544 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and 2545 * for 'theirs' -- choose full set of props, full value of conflicting 2546 * props, or conflicting text hunks. 2547 * 2548 */ 2549static svn_error_t * 2550resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, 2551 svn_wc__db_t *db, 2552 const char *local_abspath, 2553 const char *conflicted_propname, 2554 svn_wc_conflict_choice_t conflict_choice, 2555 const char *merged_file, 2556 svn_cancel_func_t cancel_func, 2557 void *cancel_baton, 2558 apr_pool_t *scratch_pool) 2559{ 2560 const char *prop_reject_file; 2561 apr_hash_t *mine_props; 2562 apr_hash_t *their_old_props; 2563 apr_hash_t *their_props; 2564 apr_hash_t *conflicted_props; 2565 apr_hash_t *old_props; 2566 apr_hash_t *resolve_from = NULL; 2567 svn_skel_t *work_items = NULL; 2568 svn_skel_t *conflicts; 2569 svn_wc_operation_t operation; 2570 svn_boolean_t prop_conflicted; 2571 2572 *did_resolve = FALSE; 2573 2574 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, 2575 scratch_pool, scratch_pool)); 2576 2577 if (!conflicts) 2578 return SVN_NO_ERROR; 2579 2580 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted, 2581 NULL, db, local_abspath, conflicts, 2582 scratch_pool, scratch_pool)); 2583 if (!prop_conflicted) 2584 return SVN_NO_ERROR; 2585 2586 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file, 2587 &mine_props, &their_old_props, 2588 &their_props, &conflicted_props, 2589 db, local_abspath, conflicts, 2590 scratch_pool, scratch_pool)); 2591 2592 if (operation == svn_wc_operation_merge) 2593 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, 2594 scratch_pool, scratch_pool)); 2595 else 2596 old_props = their_old_props; 2597 2598 /* We currently handle *_conflict as *_full as this argument is currently 2599 always applied for all conflicts on a node at the same time. Giving 2600 an error would break some tests that assumed that this would just 2601 resolve property conflicts to working. 2602 2603 An alternative way to handle these conflicts would be to just copy all 2604 property state from mine/theirs on the _full option instead of just the 2605 conflicted properties. In some ways this feels like a sensible option as 2606 that would take both properties and text from mine/theirs, but when not 2607 both properties and text are conflicted we would fail in doing so. 2608 */ 2609 switch (conflict_choice) 2610 { 2611 case svn_wc_conflict_choose_base: 2612 resolve_from = their_old_props ? their_old_props : old_props; 2613 break; 2614 case svn_wc_conflict_choose_mine_full: 2615 case svn_wc_conflict_choose_mine_conflict: 2616 resolve_from = mine_props; 2617 break; 2618 case svn_wc_conflict_choose_theirs_full: 2619 case svn_wc_conflict_choose_theirs_conflict: 2620 resolve_from = their_props; 2621 break; 2622 case svn_wc_conflict_choose_merged: 2623 if (merged_file && conflicted_propname[0] != '\0') 2624 { 2625 apr_hash_t *actual_props; 2626 svn_stream_t *stream; 2627 svn_string_t *merged_propval; 2628 2629 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, 2630 scratch_pool, scratch_pool)); 2631 resolve_from = actual_props; 2632 2633 SVN_ERR(svn_stream_open_readonly(&stream, merged_file, 2634 scratch_pool, scratch_pool)); 2635 SVN_ERR(svn_string_from_stream(&merged_propval, stream, 2636 scratch_pool, scratch_pool)); 2637 svn_hash_sets(resolve_from, conflicted_propname, merged_propval); 2638 } 2639 else 2640 resolve_from = NULL; 2641 break; 2642 default: 2643 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 2644 _("Invalid 'conflict_result' argument")); 2645 } 2646 2647 if (conflicted_props && apr_hash_count(conflicted_props) && resolve_from) 2648 { 2649 apr_hash_index_t *hi; 2650 apr_hash_t *actual_props; 2651 2652 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, 2653 scratch_pool, scratch_pool)); 2654 2655 for (hi = apr_hash_first(scratch_pool, conflicted_props); 2656 hi; 2657 hi = apr_hash_next(hi)) 2658 { 2659 const char *propname = svn__apr_hash_index_key(hi); 2660 svn_string_t *new_value = NULL; 2661 2662 new_value = svn_hash_gets(resolve_from, propname); 2663 2664 svn_hash_sets(actual_props, propname, new_value); 2665 } 2666 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props, 2667 FALSE, NULL, NULL, 2668 scratch_pool)); 2669 } 2670 2671 /* Legacy behavior: Only report property conflicts as resolved when the 2672 property reject file exists 2673 2674 If not the UI shows the conflict as already resolved 2675 (and in this case we just remove the in-db conflict) */ 2676 2677 { 2678 svn_skel_t *work_item; 2679 2680 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, 2681 db, local_abspath, prop_reject_file, 2682 scratch_pool, scratch_pool)); 2683 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2684 } 2685 2686 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE, 2687 work_items, scratch_pool)); 2688 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2689 scratch_pool)); 2690 2691 return SVN_NO_ERROR; 2692} 2693 2694/* 2695 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to 2696 * CONFLICT_CHOICE. 2697 * 2698 * It is not an error if there is no tree conflict. If a tree conflict 2699 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE. 2700 * 2701 * It is not an error if there is no tree conflict. 2702 */ 2703static svn_error_t * 2704resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, 2705 svn_wc__db_t *db, 2706 const char *local_abspath, 2707 svn_wc_conflict_choice_t conflict_choice, 2708 svn_wc_notify_func2_t notify_func, 2709 void *notify_baton, 2710 svn_cancel_func_t cancel_func, 2711 void *cancel_baton, 2712 apr_pool_t *scratch_pool) 2713{ 2714 svn_wc_conflict_reason_t reason; 2715 svn_wc_conflict_action_t action; 2716 svn_skel_t *conflicts; 2717 svn_wc_operation_t operation; 2718 svn_boolean_t tree_conflicted; 2719 2720 *did_resolve = FALSE; 2721 2722 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, 2723 scratch_pool, scratch_pool)); 2724 if (!conflicts) 2725 return SVN_NO_ERROR; 2726 2727 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, 2728 &tree_conflicted, db, local_abspath, 2729 conflicts, scratch_pool, scratch_pool)); 2730 if (!tree_conflicted) 2731 return SVN_NO_ERROR; 2732 2733 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL, 2734 db, local_abspath, 2735 conflicts, 2736 scratch_pool, scratch_pool)); 2737 2738 if (operation == svn_wc_operation_update 2739 || operation == svn_wc_operation_switch) 2740 { 2741 if (reason == svn_wc_conflict_reason_deleted || 2742 reason == svn_wc_conflict_reason_replaced) 2743 { 2744 if (conflict_choice == svn_wc_conflict_choose_merged) 2745 { 2746 /* Break moves for any children moved out of this directory, 2747 * and leave this directory deleted. */ 2748 SVN_ERR(svn_wc__db_resolve_break_moved_away_children( 2749 db, local_abspath, notify_func, notify_baton, 2750 scratch_pool)); 2751 *did_resolve = TRUE; 2752 } 2753 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict) 2754 { 2755 /* Raised moved-away conflicts on any children moved out of 2756 * this directory, and leave this directory deleted. 2757 * The newly conflicted moved-away children will be updated 2758 * if they are resolved with 'mine_conflict' as well. */ 2759 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away( 2760 db, local_abspath, notify_func, notify_baton, 2761 scratch_pool)); 2762 *did_resolve = TRUE; 2763 } 2764 else 2765 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 2766 NULL, 2767 _("Tree conflict can only be resolved to " 2768 "'working' or 'mine-conflict' state; " 2769 "'%s' not resolved"), 2770 svn_dirent_local_style(local_abspath, 2771 scratch_pool)); 2772 } 2773 else if (reason == svn_wc_conflict_reason_moved_away 2774 && action == svn_wc_conflict_action_edit) 2775 { 2776 /* After updates, we can resolve local moved-away 2777 * vs. any incoming change, either by updating the 2778 * moved-away node (mine-conflict) or by breaking the 2779 * move (theirs-conflict). */ 2780 if (conflict_choice == svn_wc_conflict_choose_mine_conflict) 2781 { 2782 SVN_ERR(svn_wc__db_update_moved_away_conflict_victim( 2783 db, local_abspath, 2784 notify_func, notify_baton, 2785 cancel_func, cancel_baton, 2786 scratch_pool)); 2787 *did_resolve = TRUE; 2788 } 2789 else if (conflict_choice == svn_wc_conflict_choose_merged) 2790 { 2791 /* We must break the move if the user accepts the current 2792 * working copy state instead of updating the move. 2793 * Else the move would be left in an invalid state. */ 2794 2795 /* ### This breaks the move but leaves the conflict 2796 ### involving the move until 2797 ### svn_wc__db_op_mark_resolved. */ 2798 SVN_ERR(svn_wc__db_resolve_break_moved_away(db, local_abspath, 2799 notify_func, 2800 notify_baton, 2801 scratch_pool)); 2802 *did_resolve = TRUE; 2803 } 2804 else 2805 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 2806 NULL, 2807 _("Tree conflict can only be resolved to " 2808 "'working' or 'mine-conflict' state; " 2809 "'%s' not resolved"), 2810 svn_dirent_local_style(local_abspath, 2811 scratch_pool)); 2812 } 2813 } 2814 2815 if (! *did_resolve && conflict_choice != svn_wc_conflict_choose_merged) 2816 { 2817 /* For other tree conflicts, there is no way to pick 2818 * theirs-full or mine-full, etc. Throw an error if the 2819 * user expects us to be smarter than we really are. */ 2820 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 2821 NULL, 2822 _("Tree conflict can only be " 2823 "resolved to 'working' state; " 2824 "'%s' not resolved"), 2825 svn_dirent_local_style(local_abspath, 2826 scratch_pool)); 2827 } 2828 2829 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE, 2830 NULL, scratch_pool)); 2831 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2832 scratch_pool)); 2833 return SVN_NO_ERROR; 2834} 2835 2836svn_error_t * 2837svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db, 2838 const char *local_abspath, 2839 apr_pool_t *scratch_pool) 2840{ 2841 svn_boolean_t ignored_result; 2842 2843 return svn_error_trace(resolve_text_conflict_on_node( 2844 &ignored_result, 2845 db, local_abspath, 2846 svn_wc_conflict_choose_merged, NULL, 2847 NULL, NULL, 2848 scratch_pool)); 2849} 2850 2851svn_error_t * 2852svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db, 2853 const char *local_abspath, 2854 apr_pool_t *scratch_pool) 2855{ 2856 svn_boolean_t ignored_result; 2857 2858 return svn_error_trace(resolve_prop_conflict_on_node( 2859 &ignored_result, 2860 db, local_abspath, "", 2861 svn_wc_conflict_choose_merged, NULL, 2862 NULL, NULL, 2863 scratch_pool)); 2864} 2865 2866 2867/* Baton for conflict_status_walker */ 2868struct conflict_status_walker_baton 2869{ 2870 svn_wc__db_t *db; 2871 svn_boolean_t resolve_text; 2872 const char *resolve_prop; 2873 svn_boolean_t resolve_tree; 2874 svn_wc_conflict_choice_t conflict_choice; 2875 svn_wc_conflict_resolver_func2_t conflict_func; 2876 void *conflict_baton; 2877 svn_cancel_func_t cancel_func; 2878 void *cancel_baton; 2879 svn_wc_notify_func2_t notify_func; 2880 void *notify_baton; 2881}; 2882 2883/* Implements svn_wc_status4_t to walk all conflicts to resolve. 2884 */ 2885static svn_error_t * 2886conflict_status_walker(void *baton, 2887 const char *local_abspath, 2888 const svn_wc_status3_t *status, 2889 apr_pool_t *scratch_pool) 2890{ 2891 struct conflict_status_walker_baton *cswb = baton; 2892 svn_wc__db_t *db = cswb->db; 2893 2894 const apr_array_header_t *conflicts; 2895 apr_pool_t *iterpool; 2896 int i; 2897 svn_boolean_t resolved = FALSE; 2898 2899 if (!status->conflicted) 2900 return SVN_NO_ERROR; 2901 2902 iterpool = svn_pool_create(scratch_pool); 2903 2904 SVN_ERR(svn_wc__read_conflicts(&conflicts, db, local_abspath, TRUE, 2905 scratch_pool, iterpool)); 2906 2907 for (i = 0; i < conflicts->nelts; i++) 2908 { 2909 const svn_wc_conflict_description2_t *cd; 2910 svn_boolean_t did_resolve; 2911 svn_wc_conflict_choice_t my_choice = cswb->conflict_choice; 2912 const char *merged_file = NULL; 2913 2914 cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *); 2915 2916 svn_pool_clear(iterpool); 2917 2918 if (my_choice == svn_wc_conflict_choose_unspecified) 2919 { 2920 svn_wc_conflict_result_t *result; 2921 2922 if (!cswb->conflict_func) 2923 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 2924 _("No conflict-callback and no " 2925 "pre-defined conflict-choice provided")); 2926 2927 SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton, 2928 iterpool, iterpool)); 2929 2930 my_choice = result->choice; 2931 merged_file = result->merged_file; 2932 /* ### Bug: ignores result->save_merged */ 2933 } 2934 2935 2936 if (my_choice == svn_wc_conflict_choose_postpone) 2937 continue; 2938 2939 switch (cd->kind) 2940 { 2941 case svn_wc_conflict_kind_tree: 2942 if (!cswb->resolve_tree) 2943 break; 2944 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve, 2945 db, 2946 local_abspath, 2947 my_choice, 2948 cswb->notify_func, 2949 cswb->notify_baton, 2950 cswb->cancel_func, 2951 cswb->cancel_baton, 2952 iterpool)); 2953 2954 resolved = TRUE; 2955 break; 2956 2957 case svn_wc_conflict_kind_text: 2958 if (!cswb->resolve_text) 2959 break; 2960 2961 SVN_ERR(resolve_text_conflict_on_node(&did_resolve, 2962 db, 2963 local_abspath, 2964 my_choice, 2965 merged_file, 2966 cswb->cancel_func, 2967 cswb->cancel_baton, 2968 iterpool)); 2969 2970 if (did_resolve) 2971 resolved = TRUE; 2972 break; 2973 2974 case svn_wc_conflict_kind_property: 2975 if (!cswb->resolve_prop) 2976 break; 2977 2978 if (*cswb->resolve_prop != '\0' && 2979 strcmp(cswb->resolve_prop, cd->property_name) != 0) 2980 { 2981 break; /* This is not the property we want to resolve. */ 2982 } 2983 2984 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve, 2985 db, 2986 local_abspath, 2987 cd->property_name, 2988 my_choice, 2989 merged_file, 2990 cswb->cancel_func, 2991 cswb->cancel_baton, 2992 iterpool)); 2993 2994 if (did_resolve) 2995 resolved = TRUE; 2996 break; 2997 2998 default: 2999 /* We can't resolve other conflict types */ 3000 break; 3001 } 3002 } 3003 3004 /* Notify */ 3005 if (cswb->notify_func && resolved) 3006 cswb->notify_func(cswb->notify_baton, 3007 svn_wc_create_notify(local_abspath, 3008 svn_wc_notify_resolved, 3009 iterpool), 3010 iterpool); 3011 3012 svn_pool_destroy(iterpool); 3013 3014 return SVN_NO_ERROR; 3015} 3016 3017svn_error_t * 3018svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, 3019 const char *local_abspath, 3020 svn_depth_t depth, 3021 svn_boolean_t resolve_text, 3022 const char *resolve_prop, 3023 svn_boolean_t resolve_tree, 3024 svn_wc_conflict_choice_t conflict_choice, 3025 svn_wc_conflict_resolver_func2_t conflict_func, 3026 void *conflict_baton, 3027 svn_cancel_func_t cancel_func, 3028 void *cancel_baton, 3029 svn_wc_notify_func2_t notify_func, 3030 void *notify_baton, 3031 apr_pool_t *scratch_pool) 3032{ 3033 svn_node_kind_t kind; 3034 svn_boolean_t conflicted; 3035 struct conflict_status_walker_baton cswb; 3036 3037 /* ### the underlying code does NOT support resolving individual 3038 ### properties. bail out if the caller tries it. */ 3039 if (resolve_prop != NULL && *resolve_prop != '\0') 3040 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 3041 U_("Resolving a single property is not (yet) " 3042 "supported.")); 3043 3044 /* ### Just a versioned check? */ 3045 /* Conflicted is set to allow invoking on actual only nodes */ 3046 SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL, 3047 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3048 NULL, NULL, NULL, NULL, NULL, NULL, &conflicted, 3049 NULL, NULL, NULL, NULL, NULL, NULL, 3050 wc_ctx->db, local_abspath, 3051 scratch_pool, scratch_pool)); 3052 3053 /* When the implementation still used the entry walker, depth 3054 unknown was translated to infinity. */ 3055 if (kind != svn_node_dir) 3056 depth = svn_depth_empty; 3057 else if (depth == svn_depth_unknown) 3058 depth = svn_depth_infinity; 3059 3060 cswb.db = wc_ctx->db; 3061 cswb.resolve_text = resolve_text; 3062 cswb.resolve_prop = resolve_prop; 3063 cswb.resolve_tree = resolve_tree; 3064 cswb.conflict_choice = conflict_choice; 3065 3066 cswb.conflict_func = conflict_func; 3067 cswb.conflict_baton = conflict_baton; 3068 3069 cswb.cancel_func = cancel_func; 3070 cswb.cancel_baton = cancel_baton; 3071 3072 cswb.notify_func = notify_func; 3073 cswb.notify_baton = notify_baton; 3074 3075 if (notify_func) 3076 notify_func(notify_baton, 3077 svn_wc_create_notify(local_abspath, 3078 svn_wc_notify_conflict_resolver_starting, 3079 scratch_pool), 3080 scratch_pool); 3081 3082 SVN_ERR(svn_wc_walk_status(wc_ctx, 3083 local_abspath, 3084 depth, 3085 FALSE /* get_all */, 3086 FALSE /* no_ignore */, 3087 TRUE /* ignore_text_mods */, 3088 NULL /* ignore_patterns */, 3089 conflict_status_walker, &cswb, 3090 cancel_func, cancel_baton, 3091 scratch_pool)); 3092 3093 if (notify_func) 3094 notify_func(notify_baton, 3095 svn_wc_create_notify(local_abspath, 3096 svn_wc_notify_conflict_resolver_done, 3097 scratch_pool), 3098 scratch_pool); 3099 3100 return SVN_NO_ERROR; 3101} 3102 3103svn_error_t * 3104svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx, 3105 const char *local_abspath, 3106 svn_depth_t depth, 3107 svn_boolean_t resolve_text, 3108 const char *resolve_prop, 3109 svn_boolean_t resolve_tree, 3110 svn_wc_conflict_choice_t conflict_choice, 3111 svn_cancel_func_t cancel_func, 3112 void *cancel_baton, 3113 svn_wc_notify_func2_t notify_func, 3114 void *notify_baton, 3115 apr_pool_t *scratch_pool) 3116{ 3117 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath, 3118 depth, resolve_text, 3119 resolve_prop, resolve_tree, 3120 conflict_choice, 3121 NULL, NULL, 3122 cancel_func, cancel_baton, 3123 notify_func, notify_baton, 3124 scratch_pool)); 3125} 3126 3127/* Constructor for the result-structure returned by conflict callbacks. */ 3128svn_wc_conflict_result_t * 3129svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice, 3130 const char *merged_file, 3131 apr_pool_t *pool) 3132{ 3133 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result)); 3134 result->choice = choice; 3135 result->merged_file = merged_file; 3136 result->save_merged = FALSE; 3137 3138 /* If we add more fields to svn_wc_conflict_result_t, add them here. */ 3139 3140 return result; 3141} 3142